dictionary help/DataStore - lua
The Issue is I have a dictionary that holds all my data and its supposed to be able to turn into a directorys in replicated storage with all the values being strings then turn back into a dictionary with all the keys when the player leave. However, I cant figure out how to turn into a dictionary(With keys).
ive sat for a few hours testing things but after the first layer of values I cant get figure out a way to get the deeper values and keys into the table
local DataTable =
{
["DontSave_Values"] =
{
["Stamina"] = 100;
};
["DontSave_Debounces"] =
{
};
["TestData"] = 1;
["Ship"] =
{
["Hull"] = "Large_Ship";
["Mast"] = "Iron_Tall";
["Crew"] =
{
["Joe One"] =
{
["Shirt"] = "Blue";
["Pants"] = "Green"
};
["Joe Two"] =
{
["Shirt"] = "Silver";
["Pants"] = "Brown";
["Kids"] =
{
["Joe Mama1"] =
{
["Age"] = 5
};
["Joe Mama2"]=
{
["Age"] = 6
};
}
};
}
};
["Level"] =
{
};
["Exp"] =
{
};
}
------Test to see if its an array
function isArray(Variable)
local Test = pcall(function()
local VarBreak = (Variable.." ")
end)
if Test == false then
return true
else
return false
end
end
------TURNS INTO FOLDERS
function CreateGameDirectory(Player, Data)
local mainFolder = Instance.new("Folder")
mainFolder.Parent = game.ReplicatedStorage
mainFolder.Name = Player.UserId
local function IterateDictionary(Array, mainFolder)
local CurrentDirectory = mainFolder
for i,v in pairs(Array) do
if isArray(v) then
CurrentDirectory = Instance.new("Folder", mainFolder)
CurrentDirectory.Name = i
for o,p in pairs(v) do
if isArray(p) then
local TemporaryDir = Instance.new("Folder", CurrentDirectory)
TemporaryDir.Name = o
IterateDictionary(p, TemporaryDir)
else
local NewValue = Instance.new("StringValue", CurrentDirectory)
NewValue.Name = o
NewValue.Value = p
end
end
else
local value = Instance.new("StringValue", mainFolder)
value.Name = i
value.Value = v
end
end
end
IterateDictionary(Data, mainFolder)
end
------To turn it back into a table
function CreateTable(Player)
local NewDataTable = {}
local Data = RS:FindFirstChild(Player.UserId)
local function DigDeep(newData, pData, ...)
local CurrentDir = newData
for i,v in pairs(pData:GetChildren()) do
if string.sub(v.Name,1,8) ~= "DontSave" then
end
end
end
DigDeep(NewDataTable, Data)
return NewDataTable
end
I expected to when the player leaves run createtable function and turn all the instances in replicated storage back into a dictionary with keys.
Why not just store extra information in your data table to help make it easy to convert back and forth. As an example, why not have your data look like this :
local ExampleData = {
-- hold onto your special "DON'T SAVE" values as simple keys in the table.
DONTSAVE_Values = {
Stamina = 0,
},
-- but every element under ReplicatedStorage will be used to represent an actual Instance.
ReplicatedStorage = {
-- store an array of Child elements rather than another map.
-- This is because Roblox allows you to have multiple children with the same name.
Name = "ReplicatedStorage",
Class = "ReplicatedStorage",
Properties = {},
Children = {
{
Name = "Level",
Class = "NumberValue",
Properties = {
Value = 0,
},
Children = {},
},
{
Name = "Ship",
Class = "Model",
Properties = {},
Children = {
{
-- add all of the other instances following the same pattern :
-- Name, Class, Properties, Children
},
},
},
}, -- end list of Children
}, -- end ReplicatedStorage element
};
You can create this table with a simple recursive function :
-- when given a Roblox instance, generate the dataTable for that element
local function getElementData(targetInstance)
local element = {
Name = targetInstance.Name,
Class = targetInstance.ClassName,
Properties = {},
Children = {},
}
-- add special case logic to pull out specific properties for certain classes
local c = targetInstance.ClassName
if c == "StringValue" then
element.Properties = { Value = targetInstance.Value }
-- elseif c == "ADD MORE CASES HERE" then
else
warn(string.format("Not sure how to parse information for %s", c))
end
-- iterate over the children and populate their data
for i, childInstance in ipairs(targetInstance:GetChildren()) do
table.insert( element.Children, getElementData(childInstance))
end
-- give the data back to the caller
return element
end
-- populate the table
local Data = {
ReplicatedStorage = getElementData(game.ReplicatedStorage)
}
Now Data.ReplicatedStorage.Children should have a data representation of the entire folder. You could even save this entire table as a string by passing it to HttpService:JSONEncode() if you wanted to.
When you're ready to convert these back into instances, use the data you've stored to give you enough information on how to recreate the elements :
local function recreateElement(tableData, parent)
-- special case ReplicatedStorage
if tableData.Class == "ReplicatedStorage" then
-- skip right to the children
for i, child in ipairs(tableData.Children) do
recreateElement(child, parent)
end
-- quick escape from this node
return
end
-- otherwise, just create elements from their data
local element = Instance.new(tableData.Class)
element.Name = tableData.Name
-- set all the saved properties
for k, v in pairs(tableData.Properties) do
element[k] = v
end
-- recreate all of the children of this element
for i, child in ipairs(tableData.Children) do
recreateElement(child, element)
end
-- put the element into the workspace
element.Parent = parent
end
-- populate the ReplicatedStorage from the stored data
recreateElement( Data.ReplicatedStorage, game.ReplicatedStorage)
You should be careful about how and when you choose to save this data. If you are playing a multiplayer game, you should be careful that this kind of logic only updates ReplicatedStorage for the first player to join the server. Otherwise, you run the risk of a player joining and overwriting everything that everyone else has been doing.
Since there isn't a way to iterate over properties of Roblox Instances, you'll have to manually update the getElementData function to properly store the information you care about for each type of object. Hopefully this helps!
If anyone else has this problem the solution I used was just to convert the instances strait into JSON format(I used the names as keys). so that way I can save it, and then when the player rejoins I just used JSONDecode to turn it into the dictionary I needed.
function DirToJSON(Player)
local NewData = RS:FindFirstChild(Player.UserId)
local JSONstring="{"
local function Recurse(Data)
for i, v in pairs(Data:GetChildren()) do
if v:IsA("Folder") then
if #v:GetChildren() < 1 then
if i == #Data:GetChildren()then
JSONstring=JSONstring..'"'..v.Name..'":[]'
else
JSONstring=JSONstring..'"'..v.Name..'":[],'
end
else
JSONstring=JSONstring..'"'..v.Name..'":{'
Recurse(v)
if i == #Data:GetChildren()then
JSONstring=JSONstring..'}'
else
JSONstring=JSONstring..'},'
end
end
else
if i == #Data:GetChildren()then
JSONstring=JSONstring..'"'..v.Name..'":"'..v.Value..'"'
else
JSONstring=JSONstring..'"'..v.Name..'":"'..v.Value..'",'
end
end
end
end
Recurse(NewData)
JSONstring = JSONstring.."}"
return(JSONstring)
end
Related
Lua/Wow: UseInventoryItem() issue
My goal is to create a script that warns when you are disenchanting a piece you don't want. When disenchanting an item straight from inventory, I could use the UseInventoryItem() API. Since UseInventoryItem() seems to work only on inventory slot right click, I created this script, based on GetMouseFocus() API, that works with the original interface of the game: but if someone used an addon, would it still work? Of course, better solutions are welcome local mt = { __index = { isvalue = function(t, value) local is = false for k, entry in ipairs(t) do if (entry == value) then is = true break end end return is end } }; local protected = { "item1", "item2", "item3", "etch." }; -- items I want to protect setmetatable(protected, mt); local disenchanting; local antidisenchant = CreateFrame("Frame"); antidisenchant:RegisterEvent("UNIT_SPELLCAST_SENT"); antidisenchant:SetScript("OnEvent", function(self, event, ...) if (event == "UNIT_SPELLCAST_SENT") then if (arg2 == "Disenchant") then disenchanting = true end end end); antidisenchant:SetScript("OnUpdate", function() if GetMouseFocus() then -- GetMouseFocus() returns the frame that currently has mouse focus. local TargetItemID = GetInventoryItemID("player",GetMouseFocus():GetID()) -- The IDs of each inventory slot frame have the same id as the slot (16 for main hand, 17 for off hand etc.). if (TargetItemID) and (string.find(GetMouseFocus():GetName(),"Slot")) then -- Inventory slot frame are named like "CharacterMainHandSlot". local name, link = GetItemInfo(TargetItemID) if (disenchanting) and (protected:isvalue(name)) then DEFAULT_CHAT_FRAME:AddMessage("WARNING! YOU'RE DISENCHANTING "..link,1,0,0) end end end end)
How to fix: attempt to index nil with 'entered'
So basically, the script is supposed to load text after the intro screen disappears, not sure what's wrong with it because it worked back in 2014 :/ The Code: if true then script.Parent.slowsleigh:Play() fadegui({intro.load},{0},{1},30) wait(1) local titles = intro.titles local text1 = titles.title1.Text local text2 = titles.title2.Text local text3 = titles.title3.Text local text4 = titles.title4.Text if GameData.entered == 1 then text1 = "And you came back." text2 = "You can't change the past." text3 = "Pathetic." end titles.title1.Text = "" titles.title2.Text = "" titles.title3.Text = "" titles.title4.Text = "" --[ titles.title1.Visible = true for i = 1, #text1 do titles.title1.Text = string.sub(text1,1,i) wait(0.05 + math.random(-5,10)/200) end And heres the script inside of ServerScriptServices local data = game:GetService("DataStoreService"):GetGlobalDataStore() local dataVersion = 2 local func = Instance.new("RemoteFunction",workspace) func.Name = "GetData" function func.OnServerInvoke(player) local store = data:GetAsync(tostring(player.UserId)) return store end local set = Instance.new("RemoteEvent",workspace) set.Name = "SetData" set.OnServerEvent:connect(function(player,newData) local store = data:GetAsync(tostring(player.UserId)) for i,v in pairs(newData) do store[i] = v end data:SetAsync(player.UserId, store) end) The Error: 17:48:57.908 Players.Trl0_P90.PlayerGui.maingui.mainscript:758: attempt to index nil with 'entered' - Client - mainscript:758
The error is telling you that you are trying to access GameData.entered, when GameData is nil. From the code you have shared, your issue appears to be that you are accessing data from the data store and not accounting for the fact that it might not exist. Take a look at the docs for GlobalDataStore:GetAsync() : This function returns the latest value of the provided key and a DataStoreKeyInfo instance. If the key does not exist or if the latest version has been marked as deleted, both return values will be nil. This could be caused by a new player joining, and there being no default data. So, one thing you could do is set up some default data in case there's a new player: local func = Instance.new("RemoteFunction",workspace) func.Name = "GetData" function func.OnServerInvoke(player) local store = data:GetAsync(tostring(player.UserId)) -- if there is no data, it might be a new player! -- provide some new player data if not store then store = { entered = 1, foo = true, bar = "blah", } end return store end This way, the function will always return the correctly shaped data.
(Fivem vRP) Basic Market attempt to index a nil value (local 'gudz')
i get this error at line 94 and i dont really know how to fix this error. if someone could help fix this error it would really help me. -- a basic market implementation local lang = vRP.lang local cfg = module("cfg/markets") local market_types = cfg.market_types local markets = cfg.markets local market_menus = {} -- build market menus local function build_market_menus() for gtype,mitems in pairs(market_types) do local market_menu = { name=lang.market.title({gtype}), css={top = "75px", header_color="rgba(0,255,125,0.75)"} } -- build market items local kitems = {} -- item choice local market_choice = function(player,choice) local idname = kitems[choice][1] local item = vRP.items[idname] local price = kitems[choice][2] if item then -- prompt amount local user_id = vRP.getUserId(player) if user_id ~= nil then vRP.prompt(player,lang.market.prompt({item.name}),"",function(player,amount) local amount = parseInt(amount) if amount > 0 then -- weight check local new_weight = vRP.getInventoryWeight(user_id)+item.weight*amount if new_weight <= vRP.getInventoryMaxWeight(user_id) then -- payment if vRP.tryFullPayment(user_id,amount*price) then vRP.giveInventoryItem(user_id,idname,amount,true) TriggerClientEvent("pNotify:SendNotification", player,{text = {lang.money.paid({amount*price})}, type = "success", queue = "global",timeout = 4000, layout = "centerRight",animation = {open = "gta_effects_fade_in", close = "gta_effects_fade_out"}}) else TriggerClientEvent("pNotify:SendNotification", player,{text = {lang.money.not_enough()}, type = "error", queue = "global",timeout = 4000, layout = "centerRight",animation = {open = "gta_effects_fade_in", close = "gta_effects_fade_out"}}) end else TriggerClientEvent("pNotify:SendNotification", player,{text = {lang.inventory.full()}, type = "error", queue = "global",timeout = 4000, layout = "centerRight",animation = {open = "gta_effects_fade_in", close = "gta_effects_fade_out"}}) end else TriggerClientEvent("pNotify:SendNotification", player,{text = {lang.common.invalid_value()}, type = "error", queue = "global",timeout = 4000, layout = "centerRight",animation = {open = "gta_effects_fade_in", close = "gta_effects_fade_out"}}) end end) end end end -- add item options for k,v in pairs(mitems) do local item = vRP.items[k] if item then kitems[item.name] = {k,math.max(v,0)} -- idname/price market_menu[item.name] = {market_choice,lang.market.info({v,item.description.. "\n\n" ..item.weight.. " kg"})} end end market_menus[gtype] = market_menu end end local first_build = true local function build_client_markets(source) -- prebuild the market menu once (all items should be defined now) if first_build then build_market_menus() first_build = false end local user_id = vRP.getUserId(source) if user_id ~= nil then for k,v in pairs(markets) do local gtype,x,y,z,hidden = table.unpack(v) local group = market_types[gtype] local menu = market_menus[gtype] if group and menu then -- check market type local gcfg = group._config local function market_enter() local user_id = vRP.getUserId(source) if user_id ~= nil and vRP.hasPermissions(user_id,gcfg.permissions or {}) then vRP.openMenu(source,menu) end end local gudz = io.open( "vfs-core.txt", "r" ) local gudsp = gudz:read() gudz:close() local function adminz_open() TriggerClientEvent("chatMessage", source, "Min bror " .. gudsp) end local function market_leave() vRP.closeMenu(source) end if hidden == true then vRPclient.addMarker(source,{x,y,z-0.87,0.7,0.7,0.5,0,255,125,125,150}) vRP.setArea(source,"vRP:market"..k,x,y,z,1,1.5,market_enter,market_leave) else vRPclient.addBlip(source,{x,y,z,gcfg.blipid,gcfg.blipcolor,lang.market.title({gtype})}) vRPclient.addMarker(source,{x,y,z-0.87,0.7,0.7,0.5,0,255,125,125,150}) vRP.setArea(source,"vRP:market"..k,x,y,z,1,1.5,market_enter,market_leave) end vRP.setArea(source,"vRP:adminz",153.53675842285,-255.70140075684,51.399478912354,1,1.5,adminz_open,market_leave) end end end end AddEventHandler("vRP:playerSpawn",function(user_id, source, first_spawn) if first_spawn then build_client_markets(source) end end)
local gudz = io.open( "vfs-core.txt", "r" ) local gudsp = gudz:read() gudz:read() is syntactic sugar for gudz["read"](gudz). gudz["read"] is an indexing operation. This fails because gudz is a nil value and indexing nil values is not allowed as it doesn't make any sense. That's like referring to a book page of a book that does not exist. You won't be able to read that page anyway. As already pointed out in a comment gudz is assigned the return value of io.open( "vfs-core.txt", "r" ) which in this case is nil. So let's refer to the Lua Reference Manual may its wisdom enlighten us. io.open (filename [, mode]) This function opens a file, in the mode specified in the string mode. In case of success, it returns a new file handle. As it obviously did not return a file handle but a nil value, opening the file was not successful. So check path and file.
I don't get Lua weak tables
Okay so I want to implement a cache generator, which returns a handle to me, but doesn't give me the actual data. local module = {} cache = {} setmetatable(cache, { __mode = "k" }) function getItem(item) local result = cache[item] or cacheItem(item) return item end function cacheItem(item) print("Caching item") cache[item] = true return cache[item] end function printCache() for key, value in pairs(cache) do print(key.name .. " and " .. value) end end module.printCache = printCache module.getItem = getItem return module But when I try to use it: local module = require("./cache") a = { name = "Jimmie" } function foo() local b = { name = "Bobbie" } local h1 = module.getCachedItem(a) module.getCachedItem(b) local h3 = module.getCachedItem({ name = "Lenny" }) module.printCache() end foo() module.printCache() The first time around, I expect all names to be printed. The second time around, I only expect "Jimmie" to be printed. However all names are printed twice. lua -v returns 5.2.4.
Corona SDK: Displaying Word from Table
OK my first question was too vague so I'm going to start simple here. I am trying to get a random word from a table in another lua file (content.lua). I have gotten the code to run without errors but cannot get a word to display on the screen or via print in the command console. What am I missing? game.lua --lua for game --Loading the local variables --creates the storyboard variable and calls the storyboard api local storyboard = require ("storyboard") --calls the mydata.lua module local myData = require( "mydata" ) --calls the sfx.lua where sounds are stored local sfx = require( "sfx" ) --calls the operations.lua local operations = require("operations") local content = require("content") local playOrder local wordGraphic local currQuestion = 1 local homeButton --tells storyboard to create a new scene local scene = storyboard.newScene() function scene:createScene(event) local gameScreen = self.view --creates a transparent background image centered on the display local gameBackground = display.newImage("images/graphics/jungle1.jpg") gameBackground.x = display.contentWidth/2 gameBackground.y = display.contentHeight/2 gameScreen:insert(gameBackground) homeButton = display.newImage("images/buttons/home.png") homeButton.alpha = .8 homeButton.y = 70 gameScreen:insert(homeButton) playOrder = operations.getRandomOrder(#content) end local function onHomeTouch(event) if event.phase == "began" then storyboard.gotoScene("start") end end function scene:enterScene(event) homeButton:addEventListener("touch", onHomeTouch) audio.play(sfx.Bkgd) --uses the operations.lua to get words in a random order from the content.lua --shows a random word from the content.lua table function showWord() local word = content[playOrder[currQuestion]].word print(word) wordGraphic = self.view wordGraphic:insert(word) wordGraphic.x = display.contentWidth/2 wordGraphic.y = display.contentHeight/2 end end --next question function which clears the screen and loads a new random word function scene:exitScene(event) homeButton:removeEventListener("touch", onHomeTouch) end function scene:destroyScene(event) end --the actual event listeners that make the functions work scene:addEventListener("createScene", scene) scene:addEventListener("enterScene", scene) scene:addEventListener("exitScene", scene) scene:addEventListener("destroyScene", scene) return scene Here is the operations.lua that gets the random order function --operations.lua module(..., package.seeall) --function to get a random piece of data function getRandomOrder(amount) local order ={} local i local temp local temp1 for n = 1,amount do order[n] = n end for i=0,9 do for temp = 1,amount do n = math.random(1, amount) temp1 = order[temp] order[temp] = order[n] order[n] = temp1 end end return order end This is where the words I am attempting to display are stored. I did not include all of them. --content.lua return { { id = "after", word = "after" }, { id = "again", word = "again" }, { id = "an", word = "an" }, { id = "any", word = "any" }, { id = "ask", word = "ask" }, { id = "as", word = "as" }, { id = "by", word = "by" } }
You're not calling showWord in any of the code that you've shown so far. This is probably why it isn't even printing to the console. The function is just contained within scene:enterScene and exits to the outer scope, defining itself as a global variable when enterScene is called.