1. Post #1
    joyenusi's Avatar
    May 2006
    148 Posts
    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.
    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
    }
    



    I'm sending it to the client using this code;
    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
    

    the code to receive the usermessage is as follows;
    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)
    


    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.

  2. Post #2
    SeveredSkull's Avatar
    October 2008
    1,316 Posts
    Well first of all. You do not have any table called "inventoryItems" on your player...
    Code:
     for k,v in pairs(playerData.inventoryItems) do
    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"])
    Wrong one. glon.encode ... not decode.

    Edited:

    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.

    Edited:

    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)
    

    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.
    Reply With Quote Edit / Delete Reply Windows 7 United States Show Events Funny Funny x 1 (list)

  3. Post #3
    joyenusi's Avatar
    May 2006
    148 Posts
    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;

    for k,v in pairs RP.Config.Player.InitData do    playerData[k] = ply:GetNWString(k)end
    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.
    Reply With Quote Edit / Delete Reply Windows 7 United Kingdom Show Events Funny Funny x 1 (list)

  4. Post #4
    SeveredSkull's Avatar
    October 2008
    1,316 Posts
    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
    Edited:

    Bah. It put it all on one line again Ill try again.

    for k, v in pairs(ply.rp_inventory) do
    	local name = k
    	local value = v
    	--(Do your creation shit here...)
    end
    

    Edited:

    Also, I don't think you understand my datastructure, the code;
    for k,v in pairs RP.Config.Player.InitData do    playerData[k] = ply:GetNWString(k)end
    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.
    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.

    Edited:

    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;
    for k,v in pairs RP.Config.Player.InitData do    playerData[k] = ply:GetNWString(k)end
    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.
    Why the HELL do you have the item names stored in Networked Strings? You are doing "hi" = "hi" with that! It is pointless.

  5. Post #5
    joyenusi's Avatar
    May 2006
    148 Posts
    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
    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
    }
    

    The GM:ShowHelp(ply)
    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
    

    The hooks
    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
    
    Reply With Quote Edit / Delete Reply Windows 7 United Kingdom Show Events Dumb Dumb x 1 (list)

  6. Post #6
    SeveredSkull's Avatar
    October 2008
    1,316 Posts
    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.

  7. Post #7
    joyenusi's Avatar
    May 2006
    148 Posts
    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.

  8. Post #8
    SeveredSkull's Avatar
    October 2008
    1,316 Posts
    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.
    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 this page (and the others at the bottom) regarding networked variables. I also suggested you using your table structure in this manner:
     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 } 
    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"])
    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
    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)
    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.

    Edited:

    Ugh. Fuck this browser man. It erases all my formatting and squishes everything on one line.

    Edited:

    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 "Bob" , 4, "weapon_ak47", 4,2,76 in that order, I could expect to receive "Bob" , 4, "weapon_ak47", 4,2,76. 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 umsg.String. 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 do everything in 1 pass.

  9. Post #9
    SeveredSkull's Avatar
    October 2008
    1,316 Posts
    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

  10. Post #10
    joyenusi's Avatar
    May 2006
    148 Posts
    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 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:

    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.

  11. Post #11
    SeveredSkull's Avatar
    October 2008
    1,316 Posts
    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.

  12. Post #12
    joyenusi's Avatar
    May 2006
    148 Posts
    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.

  13. Post #13
    SeveredSkull's Avatar
    October 2008
    1,316 Posts
    You're still not getting the point that it is going to lag like hell. But ok. Do what you will.

  14. Post #14
    joyenusi's Avatar
    May 2006
    148 Posts
    I've never fully understood storing variables on the player, is it as simple as:
    ply.inventory = {}
    ply.inventory["milk"] = 3
    ply.etc...
    
    Reply With Quote Edit / Delete Reply Windows 7 United Kingdom Show Events Agree Agree x 1 (list)

  15. Post #15
    SeveredSkull's Avatar
    October 2008
    1,316 Posts
    Exactly. Its that simple.

    Edited:

    Just an FYI: That applies for just about anything. Entities, weapons, players, npcs, and even props
    Reply With Quote Edit / Delete Reply Windows Vista United States Show Events Lua Helper Lua Helper x 1 (list)

  16. Post #16

    February 2010
    129 Posts
    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.
    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:

    
    umsg:Start("MyUMSG")
        umsg:String("John")
        umsg:String("Stewart")
        umsg:short("16")
        umsg:short("17")
    umsg:End()
    
    
    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.
    Reply With Quote Edit / Delete Reply Windows 7 Brazil Show Events Lua Helper Lua Helper x 1 (list)