Efficient networked variables

More efficient networked variables. It should be as light as networking a single value with user messages. You can also specify custom recipients and create callbacks for the variables.

The only thing that will be sent to the client is the value of the variable. The variable indices are contained as binary in the user message names (which are then pooled after the first message), and the variables won’t be sent to the client unless the value differs. The variables will also be sent only if the entity is visible to the recipient.

You should note that this library isn’t thoroughly tested yet. If you encounter any issues, post here and I’ll do my best to help you out.

View latest at BitBucket
Download latest from BitBucket

[h2]Example usage[/h2]
[lua]require “enwv”

– Variables need to be registered before use. These should be called in the same order at both the server and the client.

enwv.Register(“some stuff”, “String”)

– The third parameter specifies if the variable should be sent always, or only when the entity is visible. It defaults
– to the latter.

enwv.Register(“some stuff that is always sent”, “String”, TRANSMIT_ALWAYS)

– The fourth parameter can be used to specify the recipients.

enwv.Register(“private stuff to Entity(1)”, “Short”, TRANSMIT_PVS, Entity(1))
enwv.Register(“private team stuff”, “Char”, TRANSMIT_PVS, function(player) return player:Team() == TEAM_UNASSIGNED end)

if CLIENT then
– Callbacks can be specified for variables.

enwv.Hook("some stuff", print)
enwv.Hook("some stuff that is always sent", print)
enwv.Hook("private stuff to Entity(1)", print)
enwv.Hook("private team stuff", print)

end

if SERVER then
Entity(1):SetNetworkedVariableEx(“some stuff”, “lorem ipsum dolor sit amet”)
Entity(1):SetNetworkedVariableEx(“some stuff that is always sent”, “hi”)
Entity(1):SetNetworkedVariableEx(“private stuff to Entity(1)”, 4000)

-- SetNWVarEx is an alias to SetNetworkedVariableEx.

Entity(1):SetNWVarEx("private team stuff", 12)

end[/lua]

awesome, was going to make my own of this

What most of these nwvars rewrites seem to miss are a couple rather important cases:

  1. What happens when a player joins? You need to sync the current state of the data to that player.

  2. How do you handle updates to entities that are outside a player’s PVS? Both variables on the entity and variables that point to an entity. If you send them before they exist then you either don’t get the variables or your variable points to a NULL entity.

I created my own and I:

  1. I have the player request a sync of all the variables every now and then (and especially when they initially join) but they cannot spam the request.
  2. I create my own table and I save and grab the data by the entity index (sent over by the server), not by the entity’s table or reference.

Garbage collection?

If you’re talking about deleting the data when an entity doesn’t exist anymore, I can always just network the entity index over on the deletion of the entity.

My data storing starts out with just one table.
[lua]
local entDataTable = {}
[/lua]
Then another table for individual entindexes:
[lua]
entDataTable[ EntityIndex ] = entDataTable[ EntityIndex ] or {}
[/lua]
Then when adding data / changing data in the entity.
[lua]
entDataTable[ EntityIndex ][ NetworkedVarID ] = “this is data”
[/lua]

In all honesty your method isn’t the most efficient way to make networked variables, but neither is mine.

I thought that I’d let the users handle that themselves, but it might be simpler if I did it in the library instead. I’ll include this in the next revision.

The variables are stored by their unique index, not by their entity - so they would be handled correctly. As for the latter, it would point to a NULL entity, but I think that this could be fixed by sending over the entity index instead of umsg.Entity.

I pushed out an update. Turns out that the entities outside the PVS weren’t handled correctly after all. It should work now though. The variables are now automatically synchronized too.

I also moved the registration and hook functions from the entity metatable to the library itself (under the names efficient_networked.variables.Register and efficient_networked.variables.Hook), and the registration function now takes a transmit state parameter and the recipient parameter.

Man, these function names are too long.

There are aliases at the bottom of the file.


_R.Entity.GetNWVarEx = _R.Entity.GetNetworkedVariableEx
_R.Entity.SetNWVarEx = _R.Entity.SetNetworkedVariableEx

Should I also shorten efficient_networked_variables to just networked_variables?

enwv

you keep giving things long names, we don’t like long names.

Shortened it to enwv. Networked entities are now also sent and stored by their indices.

What happens if an ent goes out of pvs and is removed, then another ent takes up its old ent index

Are you sure that can even happen?

To add on to Azu’s points, there’s one no one seems to notice which is the fact that source networking is UDP which means dropped packets == the data never gets there. No one ever seems to write in a system to verify that a client actually received the data which tends to break these things at high concurrency.

User messages use the reliable data channel which guarantees delivery of data.

usermessage data is still lost somethimes though. And I think datastream actually verified if packets arrived clientside. Might be wrong though, been a long time since I looked at that code…

Usermessages are only lost if the connection to the server is lost completely. Source’s reliable network stream guarantees that data will arrive, even if packets are dropped, so long as packets can still make their way from the server to the client.