sync.lua -- high-level api to make writing multiplayer games easier
sync.lua -- high-level api to make writing multiplayer games easier
I've been working on a client-server entity replication API (like the ones in Unity or Unreal) based on lua-enet. You can write your game's code mostly as if you are writing a single-computer game and have automatic synchronization of your game state across multiple computers.
Here is the code for the game in the image above: https://github.com/expo/ghost-multi/blo ... e_warz.lua . You can see how the code is basically all focused on gameplay and not much netcode if at all. I was able to play the game with friends by hosting it on a server with a public IP!
You can find the library along with tutorials and reference documentation on the GitHub page at: https://github.com/expo/sync.lua
It's been tested on Windows, macOS, Linux, iOS, and probably works on Android because it just uses cross-platform Love libraries (but haven't tested it there).
Last edited by nikki93 on Wed Nov 14, 2018 8:16 am, edited 5 times in total.
- ArchAngel075
- Party member
- Posts: 319
- Joined: Mon Jun 24, 2013 5:16 am
Re: sync.lua -- high-level client-server entity replication api for multiplayer games
This could be exactly what i need as i was about to move from love2d to unity because i was having too many issues with syncing physics over network.
Questions though :
1. How often does position/velocity data for bodies send? if its constant would that not be excessive with MANY bodies?
1.1 Is there simulation or prediction in case of high body count?
2. Do you have plans to implement RPC and Command methods too? eg :
At moment i have clients send a RPC to server on key-up/down events.
Server updates the new key states on clients - the player object is dirty so is sent to clients that update.
Questions though :
1. How often does position/velocity data for bodies send? if its constant would that not be excessive with MANY bodies?
1.1 Is there simulation or prediction in case of high body count?
2. Do you have plans to implement RPC and Command methods too? eg :
At moment i have clients send a RPC to server on key-up/down events.
Server updates the new key states on clients - the player object is dirty so is sent to clients that update.
Re: sync.lua -- high-level client-server entity replication api for multiplayer games
Awesome! This is exactly the use case I was hoping for. If you end up using it I could try to make fixes / additions as you need.
To answer your questions:
1. You control this explicitly by calling self.__mgr:sync(<the entity>) (update is sent in next :process() call on server). If no entities have this called in a particular frame, nothing is sent. Thus you're in control.
2. Yup! The client is free to do any logic locally and eventually the server data will overwrite. Check out calling :update() in the client in the physics example. If you implement :willSync(newInfo) on an entity and return false you can even skip the default overwrite behavior and do your own sync'ing.
3. Any method call on client.controller is an RPC to the server controller instance for that client. This is how input works in the "triangle warz" example. This way there is an inherent discerning of which client sent the RPC and to you it's just a method call. I like to send a higher level of RPC (like "walk forward" instead of "up key pressed") to let clients take care of differences between mobile / desktop or due to window size or user settings etc.
So it pretty much implements what you say in your last paragraph. The tutorial is only the basics. Really glad to hear you're interested. Will be updating the tutorial tomorrow to cover these things too!
To answer your questions:
1. You control this explicitly by calling self.__mgr:sync(<the entity>) (update is sent in next :process() call on server). If no entities have this called in a particular frame, nothing is sent. Thus you're in control.
2. Yup! The client is free to do any logic locally and eventually the server data will overwrite. Check out calling :update() in the client in the physics example. If you implement :willSync(newInfo) on an entity and return false you can even skip the default overwrite behavior and do your own sync'ing.
3. Any method call on client.controller is an RPC to the server controller instance for that client. This is how input works in the "triangle warz" example. This way there is an inherent discerning of which client sent the RPC and to you it's just a method call. I like to send a higher level of RPC (like "walk forward" instead of "up key pressed") to let clients take care of differences between mobile / desktop or due to window size or user settings etc.
So it pretty much implements what you say in your last paragraph. The tutorial is only the basics. Really glad to hear you're interested. Will be updating the tutorial tomorrow to cover these things too!
Re: sync.lua -- high-level client-server entity replication api for multiplayer games
So to update on my previous answer,
I updated the tutorial to cover the `:sync` call and the way controller types can have RPCs happen. Check out the new section here!ArchAngel075 wrote: ↑Tue Nov 06, 2018 4:55 am 1. How often does position/velocity data for bodies send? if its constant would that not be excessive with MANY bodies?
2. Do you have plans to implement RPC and Command methods too?
Re: sync.lua -- high-level client-server entity replication api for multiplayer games
Added a couple new features -- ':isRelevant' (for entities) and '.getRelevants' (for types) to filter what entities are sent to each client, and `Client:serverTime()` and `dt` passing to `:willSync` to allow local interpolation based on predicted server time. https://github.com/expo/sync.lua/blob/m ... e_huge.lua is an example that simulates 20000 rotating rectangles but only sends visible ones to clients.
Also started working on reference docs: https://github.com/expo/sync.lua/blob/m ... ference.md
Also started working on reference docs: https://github.com/expo/sync.lua/blob/m ... ference.md
Re: sync.lua -- high-level client-server entity replication api for multiplayer games
Wow this is really cool ... such posts immediately trigger sections in my brain trying to create ideas for games I'd love to make using the lib/features (how much I'd love it if there were 48 hours in a day)
Re: sync.lua -- high-level api to make writing multiplayer games easier
Really cool and simple to use (at least it look simple to use).
Just a question tho. Is it possible to join a server and to send previous values for Player? (previous to the connection)
Like you previously create the Player table and send it as it when you connect.
It seems that you are obligated to state the starting values of Player in Player:didSpawn() when you connect to a server.
I tried a little bit of everything but the best I could do was to send it once but the nexts instances of clients copied the values of the first instance of client who joined.
My question may seems dumb but I couldn't really find how to do it.
Just a question tho. Is it possible to join a server and to send previous values for Player? (previous to the connection)
Like you previously create the Player table and send it as it when you connect.
It seems that you are obligated to state the starting values of Player in Player:didSpawn() when you connect to a server.
I tried a little bit of everything but the best I could do was to send it once but the nexts instances of clients copied the values of the first instance of client who joined.
My question may seems dumb but I couldn't really find how to do it.
Re: sync.lua -- high-level api to make writing multiplayer games easier
Nelvin-- Thanks! Excited to see what you may make with it!
Yaagher-- There are a couple things you may have meant, I'll cover all of them--
1. Create a bunch of Players and have them visible when a client joins. One way to do this is after sync.newServer(...) on the server computer, do server:spawn('Player', ...) how many ever times you want (the server is the 'self.__mgr'). Any client that connects immediately gets the game state so far so it sees this. You could just assign an unclaimed player to it.
2. Have a client create a Player when it joins, but reuse the same instance if it disconnects and rejoins. For this you'll have to remember the playerId in the client and then call client.controller:usePlayerId(savesPlayerId) or something and then you could reuse the old one. Make sure not to despawn that Player on disconnect so it can be reused, or you could despawn after some time automatically assuming the client won't reconnect.
Note that :didSpawn() is only called on the server, in response to the controller being spawned for that client, in the tutorial. You can also just spawn stuff on your own on the server whenever you want, as described in #1.
Yaagher-- There are a couple things you may have meant, I'll cover all of them--
1. Create a bunch of Players and have them visible when a client joins. One way to do this is after sync.newServer(...) on the server computer, do server:spawn('Player', ...) how many ever times you want (the server is the 'self.__mgr'). Any client that connects immediately gets the game state so far so it sees this. You could just assign an unclaimed player to it.
2. Have a client create a Player when it joins, but reuse the same instance if it disconnects and rejoins. For this you'll have to remember the playerId in the client and then call client.controller:usePlayerId(savesPlayerId) or something and then you could reuse the old one. Make sure not to despawn that Player on disconnect so it can be reused, or you could despawn after some time automatically assuming the client won't reconnect.
Note that :didSpawn() is only called on the server, in response to the controller being spawned for that client, in the tutorial. You can also just spawn stuff on your own on the server whenever you want, as described in #1.
Re: sync.lua -- high-level api to make writing multiplayer games easier
Maybe I wasn't clear in my question (there was only one, not two, but I appreciate the second answer, it may be useful latter) english isn't my main language sorry.
But thanks for the detailed answer.
Imagine you want to make I don't know, an RPG where you can play solo and join other players whenever you want while keeping your current values (position, health, inventory, etc...). How do you do that?
From what I've read, and you say it again in your first answer, when a client join its values are defined by the server with :didSpawn() or :spawn() (which the both are server-side if I understand well) and you have no way to send local player table to the server (for exemple when you join, doing "sync.newClient={address="ip",Player}" where Player is an already existing table with x,y,etc... created BEFORE joining and is CLIENT-side)
But thanks for the detailed answer.
Imagine you want to make I don't know, an RPG where you can play solo and join other players whenever you want while keeping your current values (position, health, inventory, etc...). How do you do that?
From what I've read, and you say it again in your first answer, when a client join its values are defined by the server with :didSpawn() or :spawn() (which the both are server-side if I understand well) and you have no way to send local player table to the server (for exemple when you join, doing "sync.newClient={address="ip",Player}" where Player is an already existing table with x,y,etc... created BEFORE joining and is CLIENT-side)
Re: sync.lua -- high-level api to make writing multiplayer games easier
The Player could just do `client.controller:setData(data)` and the controller could tell the Player object its data to set, right? Just like sending input. The only way to do this in general is to wait for connect and then send info, so this makes sense to me. The controller could just not create a Player object till this is sent. So I mean something like this:
Does this make sense? The paradigm is: server -> client data is through sync'ing entities, and client -> server data is through the controller. You don't need to make a `Player` initially, the only entity automatically created for clients is the controller. You can do all the other logic however you wish, you can wait for a client message and then create, etc.
In general (and this is out of the scope of sync.lua) you might want to actually have some persistent data in the server (or a remote database it talks to) that stores user IDs and their data so that users can't just send bogus data from the client / can log in on other computers.
Code: Select all
local Controller = sync.registerType('Controller')
function Controller:didSpawn()
self.playerId = nil -- Just to be clear
end
function Controller:willDespawn()
if self.playerId then
self.__mgr:despawn(self.playerId)
end
end
function Controller:createPlayer(initialData)
if not self.playerId then -- Make sure not already created (up to you what to do otherwise)
self.playerId = self.__mgr:spawn('Player', initialData)
end
end
-- ...
local playerCreated = false
function love.update()
-- ...
-- Tell server to create a `Player` for us if we didn't already
if client and client.controller and not playerCreated then
client.controller:createPlayer(initialData) -- Put your initial data here
playerCreated = true -- Don't do this again (again up to you what to do)
end
-- ...
end
In general (and this is out of the scope of sync.lua) you might want to actually have some persistent data in the server (or a remote database it talks to) that stores user IDs and their data so that users can't just send bogus data from the client / can log in on other computers.
Who is online
Users browsing this forum: Bing [Bot] and 6 guests