Networking Variables

From GMod Wiki

Jump to: navigation, search
Lua: Networking Variables
Page white text.png Description:Learn how to send information from the server to client and vice versa.
link=User:Joudoki Original Author:Joudoki
Calendar.png Created:18th June 2008


Very often in coding any Lua script, the need will arise for a way to access a variable on both the server and the client; depending on the type of variable, how often it needs to be updated etc, there are various methods of accomplishing this.

Contents

Shared.lua

The first way is to put the variable in a script that's loaded by both the server and the client. This is most common in SWEPs, SENTs, and SNPCs. This method is good for when the variable in question isn't going to be changing after it is initialized.

Things like maximum health, mass, sound paths, etc, are good candidates for this.

Example of this in a SENT's shared.lua:

 
-- Spawnable by whom?
ENT.Spawnable			= true;
ENT.AdminSpawnable		= true;
 
ENT.BulletDamage = .1; -- Any damage taken from bullets will be 1/10 of normal
ENT.ExplosiveDamage = 1;
ENT.BoostCooldown = 5; -- Time between boosts (in seconds)
ENT.JumpCooldown = 3; -- Time between jumps ( in seconds ) - we're not hudini, we don't levitate
ENT.UseCooldown = 2; -- How long between when the user enters and when he's allowed to exit. (seconds)
ENT.EnterSound = "npc/roller/mine/rmine_reprogram.wav"; -- When someone gets into the ball
ENT.ExitSound = "npc/roller/mine/rmine_predetonate.wav"; -- When the controller gets out
 

To access these variables in either the clientside or the serverside lua for the SENT, all that is needed is to directly reference the variable:

 
print( self.Spawnable );
damage = self.BulletDamage * dmg;
self:EmitSound( self.EnterSound, 100, 100 );
 

However, when the variable is changed on the clientside, it will only affect the client. If a variable is changed serverside, then the variable will be only be changed on the server:

 
-- Shared.lua:
  ENT.Test = "Shared";
 
  if (CLIENT) then -- Do some tests on the client
      print( self.Test ); -- Output: "Shared"
      self.Test = "Client";
      print( self.Test ); -- Output: "Client"
  end
  if (SERVER) then -- Now check if it's changed on the server
      print( self.Test ); -- Output: "Shared"
  end
 

As you can see, this method has limitations, and is generally good for constants.

Server to Client

Notepad-48.png SetNetworkedVariable should only be used when you want to notify every client about a change. To send a message to a single client use User Messages.


SetNetworkedVariable

On every entity object, there is a set of method that can be used to store data in that Entity:

( These aren't all of the functions; check the Server Function Dump for others )

Which are all paired up with a matching Get* method:

This method is useful for SENTs in particular; use this method when you have an entity handy to store data in. It is good for variables such as cooldowns, health, etc.

This method has a small limitation; you have to know what type of variable you are sending ( if you are sending an entity, you had better use SetNetworkedEntity ).

Example:

 
-- where myEnt is the entity we're storing data on
 
-- this is on the SERVER
function updateValue( vector )
  myEnt:SetNetworkedVector( "myVector", vector );    
end
 
-- this is on the CLIENT
function doStuff()
 local myVec = myEnt:GetNetworkedVector( "myVector" );
 print( "The vector is: " .. tostring( myVec ) .. "." );
 if ( !myVec ) then
  -- It's nil; either it wasn't set right, there was an error, or something else.
  print( "The value is nil! Problems." );
 else
  -- We now have a vector
  print( "We have a vector =D" );
 end
end
 

Note that, however, this method is only useful for stuff that's read on a "need to know basis" - aka, information that's only presented when asked for. If you want to constantly update information, and it changes constantly, this method will still work; however, if you want to know what it is constantly, and it doesn't change constantly, then this method isn't the most effective. Take, for example, this:

 
-- SERVER
function updateEnt( myString )
  myEnt:SetNetworkedString( "myString", myString );
end
 
lastValue = "";
-- CLIENT
function checkString( )
  if ( myEnt:GetNetworkedString( "myString" ) != lastValue ) then
    print( "Value Updated!" );
    lastValue = myEnt:GetNetworkedString( "myString" );
   doStuff();
  end
end
hook.Add( "Think", "checkForUpdate", checkString );
 

This method is incredibly inefficient; it is not good when the string only changes every once in a while.

User Messages

When you have a value that only updates every once in a while, it's better to let the client know when the value changes, rather than having the client asking the server constantly if the value has changed. To do this, we use User Messages. User Messages are basically a letter message that is filled with information, then sent to the client. When it gets to the client, a hook is called ( similar to game mode hooks ) that calls the callback function.

Server

To send a message from the server, you first open a message using umsg.Start. Then, you fill the user message with info using the various User Message functions. Finally, you end the message with umsg.End.

Here's an example on the serverside, which sends a message containing a string, an integer, and an entity ( the order here is important ):

 
function sendStuff( myString, myInt, myEntity )
 umsg.Start( "myMessage" );
  umsg.String( myString );
  umsg.Integer( myInt );
  umsg.Entity( myEntity );
 umsg.End();
end
 

As you can see, it's not that difficult to send messages from the server. You may have also noticed that in the umsg.Start function that there is an argument - this is the name of the user message, and will be used to indentify the message when it reaches the client.

There is also a second argument, used to specify which players to send the user message to. There are typically three values that are used:

umsg.Start( "myMessage" );
umsg.Start( "myMessage", player.GetByID( 1 ) );
 
local rp = RecipientFilter();
rp:AddPlayer( player.GetByID(1) );
rp:AddPlayer( player.GetByID(2) );
umsg.Start( "myMessage", rp );
 

Client

On the client side, messages are received by hooks - that is, before the message is received, a hook is added that says "When we receive a message called "blah", send it to function "getBlah". This is what this looks like in code:

 
-- Client
-- this is the function to be called
function getBlah( um )
  print( um:ReadString() );
end
-- this is where we hook it.
usermessage.Hook("blah", getBlah );
 
-- Here's the serverside of this:
function sendBlah( blahString )
 umsg.Start( "blah" );
  umsg.String( "Blahblahblah" );
 umsg.End();
end
 

The um argument is actually an object - bf_read. Use the functions of the object to get the messages inside.

player.SendLua

The last method is the ugliest - it basically just sends the client the lua that the server wants it to run. So, if you wanted to send a variable to a client, you could do something like this:

 
-- value = blah
player.GetByID(1):SendLua( "myVariable=\"" .. value .. \"";" );

And the client would get this lua:

 
myVariable="blah";
 

Data Tables (From Garry's Blog)

Right now in GMod the entity’s custom variables are networked using an internal usermessage system. When you join the server it sends you a bunch of usermessages containing all the entity’s variables, then after that it sends updates every time the entity changes. This isn’t ideal, but it was the best I could do at the time, since the default network code wasn’t able to handle the amount of variables we needed on some things.

But this presents a problem when dealing with prediction, because the new networked vars might arrive early, or late, and be processed by the wrong tick etc. So networked vars don’t work well for that stuff.

So in the next update I’ve added DTVars, Data Table Vars. The base entity in GMod has a 24 extra variables on it, networked. This is kind of icky, but it’s the best way to do it. There’s 4 of each type available – int, float, vector, angle, ehandle, bool. On the ground level there’s functions for each type to get and set these variables – ent:SetDTBool( int, value ) – ent:GetDTBool( int ).

Higher up, in SENTs, to make things easier, things work like this (this funtion gets automatically called at the right times (in initialize and onrestore))

 
function ENT:SetupDataTables()
 
self:DTVar( "Int", 0, "Key" );
self:DTVar( "Bool", 0, "On" );
self:DTVar( "Vector", 0, "vecTrack" );
self:DTVar( "Entity", 0, "entTrack" );
self:DTVar( "Entity", 1, "Player" );
 
end
 

Then you can access them via -

 
self.dt.Key
self.dt.On
self.dt.vecTrack
 

- just like they were normal variables – except they will be automatically syced between client and server.

I’m recommending that if you can fit all your SENT/SWEP’s data in this variables, then use them. They’re the best option for networked variables. String data, or other variables should use the old method.

Client to Server

Console Commands

The only way for the client to talk to the server is to use console commands; for more information on console commands, see the concommand page.

More Information

Entity:SetNetworkedAngle

Entity:SetNetworkedBool

Entity:SetNetworkedEntity

Entity:SetNetworkedFloat

Entity:SetNetworkedInt

Entity:SetNetworkedNumber

Entity:SetNetworkedString

Entity:SetNetworkedVar

Entity:SetNetworkedVarProxy

Entity:SetNetworkedVector

User Messages

umsg.Angle

umsg.Bool

umsg.Char

umsg.End

umsg.Entity

umsg.Float

umsg.Long

umsg.PoolString

umsg.Short

umsg.Start

umsg.String

umsg.Vector

umsg.VectorNormal

bf_read.ReadAngle

bf_read.ReadBool

bf_read.ReadChar

bf_read.ReadEntity

bf_read.ReadFloat

bf_read.ReadLong

bf_read.ReadShort

bf_read.ReadString

bf_read.ReadVector

bf_read.ReadVectorNormal

bf_read.Reset

Recipient Filter

player.SendLua

concommand

concommand.Add

Personal tools
Namespaces
Variants
Actions
Navigation
Lua Scripting
Functions
Hooks
Toolbox