• Trouble with glon
    15 replies, posted
  • Avatar of joyenusi
  • I'm having a bit of trouble with glon. I have a table that I am encoding and sending to the client through a usermessage. The table that I am encoding has this kind of structure. [lua] RP.Config.Player.InitData = { ["rp_name"] = "John Doe", ["rp_money"] = 10000, ["rp_moneyInBank"] = 10000, ["rp_arrested"] = 0, ["rp_inventory"] = { [1] = { item = "milk", quantity = 3, }, [2] = { item = "weapon_p90", quantity = 1, }, }, ["rp_inventoryCapacity"] = 200 } [/lua] I'm sending it to the client using this code; [lua] function GM:ShowHelp(ply) local playerData = {} for k,v in pairs(RP.Config.Player.InitData) do playerData[k] = ply:GetNWString(k) end playerData["rp_inventory"] = glon.decode(playerData["rp_inventory"]) playerData.inventoryWeight = WeightOfInventory(ply) umsg.Start("OpenInventory", ply) umsg.String(glon.encode(playerData)) umsg.End() end [/lua] the code to receive the usermessage is as follows; [lua] function OpenInventory(data) playerData = glon.decode(data:ReadString()) for k,v in pairs(playerData.inventoryItems) do Msg("slot: "..k.." item: "..v.item.." quantity: "..v.quantity.."\n") end ShowInventory() end usermessage.Hook("OpenInventory", OpenInventory) [/lua] when I try and loop through the data in playerData.inventoryItems I receive the error message; bad argument #1 to 'pairs' (table expected, got nil) also, note, that I have previously encoded the table in playerData["inventory"]. Any help would be much apprecated, cheers.
  • Avatar of SeveredSkull
  • Well first of all. You do not have any table called "inventoryItems" on your player... [code] for k,v in pairs(playerData.inventoryItems) do [/code] Second. That encoding is HORRIBLE. You are overwriting data with a decoded version, rendering it useless later on!!!! Thirdly, Usermessage Strings are limited to 253 characters. If you go over this, it will not work. You will need to split up the string and send them in fragments, and concatanate them on the client end. Lastly, you need to ENCODE it before you can decode it [code] playerData["rp_inventory"] = glon.decode(playerData["rp_inventory"]) [/code] Wrong one. glon.encode ... not decode. [editline]29th June 2012[/editline] Thinking about it further, your data structure is crap too. Set the item to be the Key, and the amount to be the Quantity. Like so: rp_inventory["Milk"] = 3 This results in less shit to send/store. It will also save you the hassle of iterating through the entire table and doing checks on the name. This way, just check if rp_inventory[YOURITEMHERE] exists... and youre done. [editline]29th June 2012[/editline] [lua] function GM:ShowHelp(ply) --[[ This is stupid. What are you even acomplishing?! It does not copy anything over! so you are wasting your time local playerData = {} for k,v in pairs(RP.Config.Player.InitData) do playerData[k] = ply:GetNWString(k) end ]] -- Dont EVER do this. You do this again, I will hunt you down and kill you. -- You are doing this wrong anyway. //playerData["rp_inventory"] = glon.decode(playerData["rp_inventory"]) --Again. Useless. It is not saved anywhere. //playerData.inventoryWeight = WeightOfInventory(ply) local Weight = WeightOfInventory(ply) local NumItems = #ply.rp_inventory umsg.Start("OpenInventory", ply) umsg.Short(NumItems) -- The number of different items we have umsg.Short(Weight) for k, v in pairs (ply.rp_inventory) do //Send the item umsg.String(k) umsg.Short(v) end umsg.End() end function OpenInventory(data) local ply = LocalPlayer() -- our player local Amount = data:ReadShort() -- Number of items ply.inventoryWeight = data:ReadShort() -- the weight of our player ply.rp_inventory = {} -- Make a new table and fill it for i = 1, Amount do ply.rp_inventory[data:ReadString()] = data:ReadShort() -- ["milk"] = 3 for example... end ShowInventory() end usermessage.Hook("OpenInventory", OpenInventory) [/lua] There. Don't use Glon for something like this... ever. Glon is meant for compressing data. In this case, you don't want to since it is easier just to send it, on top of this it takes time to encode / decode it.
  • Avatar of joyenusi
  • How would I retrieve the name the value is stored under, for example; If I want to retrieve Milk from rp_inventory["Milk"] = 3. Also, I don't think you understand my datastructure, the code; [lua]for k,v in pairs RP.Config.Player.InitData do playerData[k] = ply:GetNWString(k)end[/lua] loops through a table using the keys to retrieve the stored strings for the player. I don't quite understand your use of the umsg yet, I don't understand how you can send two Shorts and distinguish between them both clientside.
  • Avatar of SeveredSkull
  • Well obviously you aren't performing any calls/modification/changes on the name, now are you? You are displaying them! So in your "for" loop to actually create the icons and such, you use the key... Like this: [code] for k, v in pairs(ply.rp_inventory) do local name = k local value = v --(Do your creation shit here...) end [/code] [editline]29th June 2012[/editline] Bah. It put it all on one line again Ill try again. [lua] for k, v in pairs(ply.rp_inventory) do local name = k local value = v --(Do your creation shit here...) end [/lua] [editline]29th June 2012[/editline] [QUOTE=joyenusi;36551489] Also, I don't think you understand my datastructure, the code; [lua]for k,v in pairs RP.Config.Player.InitData do playerData[k] = ply:GetNWString(k)end[/lua] loops through a table using the keys to retrieve the stored strings for the player. I don't quite understand your use of the umsg yet, I don't understand how you can send two Shorts and distinguish between them both clientside.[/QUOTE] No. Trust me. I understand your structure PERFECTLY... It is not effecient. Usermessages MUST be received in the order they are sent. so if I send 1,3,5,7,8 in that order, i will receive them in that order as well. With this in mind, I know what is coming in and I can put them in their appropriate variables. [editline]29th June 2012[/editline] [QUOTE=joyenusi;36551489]How would I retrieve the name the value is stored under, for example; If I want to retrieve Milk from rp_inventory["Milk"] = 3. Also, I don't think you understand my datastructure, the code; [lua]for k,v in pairs RP.Config.Player.InitData do playerData[k] = ply:GetNWString(k)end[/lua] loops through a table using the keys to retrieve the stored strings for the player. I don't quite understand your use of the umsg yet, I don't understand how you can send two Shorts and distinguish between them both clientside.[/QUOTE] Why the HELL do you have the item names stored in Networked Strings? You are doing "hi" = "hi" with that! It is pointless.
  • Avatar of joyenusi
  • no, playerData is a table that is created for the sole purpose of holding the information to be sent in the usermessage. It holds no information prior to the function GM:ShowHelp. Note in the first post. playerData = {} How else can I loop through a bunch of network strings and store them in a table to be encoded by glon ? Edit: BTW, I fixed it, I'll post the code so you can criticise in a second :P The data that is stored in Network Strings by key [lua] RP.Config.Player.InitData = { ["rp_name"] = "John Doe", ["rp_money"] = 10000, ["rp_moneyInBank"] = 10000, ["rp_arrested"] = 0, ["rp_inventory"] = { [1] = { item = "milk", quantity = 3, }, [2] = { item = "weapon_p90", quantity = 1, }, }, ["rp_inventoryCapacity"] = 200 } [/lua] The GM:ShowHelp(ply) [lua] function GM:ShowHelp(ply) local playerData = {} for k,v in pairs(RP.Config.Player.InitData) do playerData[k] = ply:GetNWString(k) end playerData.inventoryWeight = WeightOfInventory(ply) umsg.Start("SetPlayerDataGUI", ply) umsg.String(glon.encode(playerData)) umsg.End() umsg.Start("SendInventoryGUI", ply) umsg.String(ply:GetNWString("rp_inventory")) umsg.End() end [/lua] The hooks [lua] function SetPlayerData(data) playerData = glon.decode(data:ReadString()) Msg("inventory items\n") end usermessage.Hook("SetPlayerDataGUI", SetPlayerData) function SetInventory(data) playerData.inventory = glon.decode(data:ReadString()) ShowInventory() end [/lua]
  • Avatar of SeveredSkull
  • You still arent getting the point. That entire playerData table is pointless. Stop using glon! You dont need it. Second, you are wasting time storing it all in network strings. NW vars are already synched with clients. You don't need to store the names of the items because you already have them in 2 different locations. You have not fixed your structure like I suggested you to either, nor did you even attempt to understand the answer that I had given you. I GAVE you what you needed and you simply ignored me.
  • Avatar of joyenusi
  • You're wrong in saying that I didn't attempt to understand what you said, but I couldn't and your explanations didn't help. Don't get so mad, what I did works for me... For now. How do I fix my structure? What are Network Vars? and how do I use them? They sound interesting if they are synced with the client automatically.
  • Avatar of SeveredSkull
  • [QUOTE=joyenusi;36553364]You're wrong in saying that I didn't attempt to understand what you said, but I couldn't and your explanations didn't help. Don't get so mad, what I did works for me... For now. How do I fix my structure? What are Network Vars? and how do I use them? They sound interesting if they are synced with the client automatically.[/QUOTE] If I was mad, I wouldn't still be putting the effort trying to help you. I'm still trying to bash these pennies into your thick skull. :V: I honestly don't see how it works... because data is being mutated in ways it shouldn't. From this post, I am certain now that you really don't know much about scripting so far. I suggest you [url=http://maurits.tv/data/garrysmod/wiki/wiki.garrysmod.com/index1752.html]this page [/url] (and the others at the bottom) regarding networked variables. I also suggested you using your table structure in this manner: [lua] RP.Config.Player.InitData = { ["rp_name"] = "John Doe", ["rp_money"] = 10000, ["rp_moneyInBank"] = 10000, ["rp_arrested"] = 0, ["rp_inventory"] = { ["milk"] = 3, [ "weapon_p90"] = 1 }, ["rp_inventoryCapacity"] = 200 } [/lua] because not only does it save a bit of space, it will also result in faster, more efficient tables for when you are looking up items in your inventory. "Milk" would be the key and also the name, whereas its value would be the amount. Simple, and easy to compute. The reason I keep insisting you not using glon, again is because you dont need it. As I mentioned before, the maximum number of characters you can send in a umsg.String() is 253 characters. If you go over this limit, you not only will get an error, but you will get corrupted information. [code] playerData["rp_inventory"] = glon.decode(playerData["rp_inventory"]) [/code] You should NEVER do this. By doing this, you corrupt your original information because you overwrote it with an encoded version (You used the wrong function. I am sure you meant to use glon.encode, as you never encode your data anywhere.) Not only are you corrupting your data, but you should have encoded this before hand while you were accessing it. the following code [code] local playerData = {} for k,v in pairs(RP.Config.Player.InitData) do playerData[k] = ply:GetNWString(k) end [/code] as I stated is completely useless. Not only are you ALWAYS going to have the starting items in your inventory, but they will always show up even if you do not HAVE them. I had said you are not transferring anything, which was my fault as I used poor wording. What I had meant to say was that these were not being transferred from the players inventory. Instead, they are coming from the default starting inventory, resulting in what I had previously stated: always showing these even if you didn't have them. Instead of having this send these tables using glon, you need to use usermessages and just send the keys and the amount to the user without encoding/decoding them. Reason being, is it takes time to encode the table as well as decode them, so not only are you wasting time, but you are wasting processing power. Glon is meant to be used when storing data in a more permanent system such as in SQL or in Text files. It really isn't meant to be called each time the player opens his inventory. The networked variables you are using are cool... yes, but you are not using them for any purpose than what I see other than just holding the names of the item: [code] playerData[k] = ply:GetNWString(k) [/code] not only does this not work, but it is pointless. It does not work since you still need to access the sub table "rp_inventories", so I call bullshit on you saying it works. Furthermore, all that does is hold a string. so you are saying "PlayerData's table with index NAMEHERE equals the networked string called NAMEHERE's value." That's pointless. Just set it. If you are on the server, then just use the server information that you already have access to. *Ding Ding* Cool. I just got off of work. Ill write more about some of the bad habits/errors you have shown when I get back home. [editline]29th June 2012[/editline] Ugh. Fuck this browser man. It erases all my formatting and squishes everything on one line. [editline]29th June 2012[/editline] Yay. Back at home... Hopefully this computer doesn't fuck over my formatting. Allright. I had suggested you using usermessages. You responded that you did not understand. I shouldn't have to spell everything out for you. You need to learn this stuff on your own. There is a complete set of documentation available for every built in GMod function you can think of ( or damn close to it) As I had said, Usermessages are received in the order they are sent. So if I sent [I]"Bob" , 4, "weapon_ak47", 4,2,76[/I] in that order, I could expect to receive [I]"Bob" , 4, "weapon_ak47", 4,2,76[/I]. I will have to take into account the different types of data and use the respective functions such as ReadString, ReadShort, etc. In the case that I had shown, I had sent a short containing the number of items. This is then read by the client so we know how many times to loop and expect information. I then did a For Loop and sent the key, and the value in that order. On the client, I did the same thing, except I am reading instead of sending. Given the way the usermessages work, I can expect the first segment of data after the short for the # of items to be the key for the first item, followed by its value. I then proceeded to make an entry with the key being the key received, and the value being the value received... Simple enough? I get efficient data sent to the client, and I do not waste time encoding it, breaking it up into multiple strings due to the size limitation of [B]umsg.String[/B]. If I did, I would have to waste even more time putting the strings back together, and then decoding it, then setting it again. [I]I do everything in 1 pass.[/I]
  • Avatar of SeveredSkull
  • You should avoid using anything in Networked Variables unless you absolutely need it every single frame, and need constant client side reference to it. If you did the entire inventory for the player in networked variables, it would be VERY laggy in-game as the server will constantly be updating the player's variables and sending them to the client every time they change. You do not want this... Trust me. The effects with grow exponentially with the number of players, as well as the amount of items the player has. Instead, you only send the player the information when they need it. In this case, opening the menu for their inventory. EDIT: Wow it didn't auto-merge :suicide:
  • Avatar of joyenusi
  • Okay, but it really does work ? So you either don't understand my code properly or are wrong, either way, my codeworks all the same. I don't have any reason to lie about it. Rather than stray from the point, I have said that playerData is only used to store the values in a variable that is suitable to send from the server to the client. It does not hold anything prior. Using the following code; for k,v in pairs(RP.Config.Player.InitData) do playerData[k] = ply:GetNWString(k)end Loops through each value in my initial data table only using the KEYS, not the values. This allows me to create a table with all the NWStrings stored by my gamemode. As the table holding the ACTUAL values does not exist, I need to loop through the table in InitData (InitData is the table that is looped through to set the NWStrings) and gain the keys for each value stored by the gamemode. This is as opposed to doing; playerData["rp_name"] = self:GetNWString("rp_name")playerData["rp_money"] = self:GetNWString("rp_money")playerData["rp_moneyInBank"] = self:GetNWString("rp_moneyInBank)playerData["rp_etc..."] = self:GetNWString("rp_etc...") So there is no table actually holding all the information stored on the player there is only the snapshot of information which is the table playerData that is only created when information is to be sent to the client. And you know what, I didn't even know you could retrieve NWVariables clientside :v: How dumb. That will solve a lot of issues. BTW, you're right, I don't really know much about scripting. A picture of the working inventory: [IMG]http://puu.sh/EKKw[/IMG]Lol, I just noticed there is something terribly wrong with the inventory weight bar. I hope what I said makes any sense at all, because reading it over, I can understand how someone may not be able to understand it at all. I also just want to say thanks for putting so much effort into helping.
  • Avatar of SeveredSkull
  • Allright. Well I am glad you have it working. I strongly suggest moving to something more simplistic such as just storing the players inventory on the server and its players. Because as I said, too many networked variables will lag the player. It also doesn't make sense to send them to the client in a compressed table format when they already have access (But you didn't know this... And now you do.) I really think you should adopt my methods in my very first post, but that is up to you. Everyone has to learn by their own mistakes. If you still do not understand my code from the beginning. I can comment on it line by line for you so you know how it works, and what makes it so efficient.
  • Avatar of joyenusi
  • The only thing I didn't understand was how to distinguish between multiple strings, shorts, etc. I Understand it now, but I don't even think I'm going to use a usermessage, just retrieve the NWString clientside.
  • Avatar of SeveredSkull
  • You're still not getting the point that it is going to lag like hell. But ok. Do what you will.
  • Avatar of joyenusi
  • I've never fully understood storing variables on the player, is it as simple as: [lua] ply.inventory = {} ply.inventory["milk"] = 3 ply.etc... [/lua]
  • Avatar of SeveredSkull
  • Exactly. Its that simple. [editline]30th June 2012[/editline] Just an FYI: That applies for just about anything. Entities, weapons, players, npcs, and even props
  • [QUOTE=joyenusi;36564378]The only thing I didn't understand was how to distinguish between multiple strings, shorts, etc. I Understand it now, but I don't even think I'm going to use a usermessage, just retrieve the NWString clientside.[/QUOTE] Trust him, usermessages are really useful and better than Networking every variable, a example would be, lets say that you need to send the variable to only ONE player or to a small group of players(e.g: members of a faction inside your gamemode.) you use usermessages as you can address the targets of them, rather than having it sent to every single player, even those who don't need the info and who can try some malicious code if the server doesn't has scriptenforcer on. And by the distinguish of multiple strings/integers, imagine the data sent at a usermessage as a argument of a function, if you have a usermessage the following way: [lua] umsg:Start("MyUMSG") umsg:String("John") umsg:String("Stewart") umsg:short("16") umsg:short("17") umsg:End() [/lua] When you go to read the usermessage values, you MUST read them in same order. You'd have to first read a string that would be "John", then read another string that would be "Stewart", to then read a number that would be 16, to just then, retrieve the last short that would be 17. Imagine it as pipe, the first thing you throw at one of the pipe ends will be the first thing you will receive at the other end of the pipe, the second thing you throw inside the pipe, will be the second thing you will receive at the other endof the pipeand so on.