sync.lua -- high-level api to make writing multiplayer games easier

Showcase your libraries, tools and other projects that help your fellow love users.
User avatar
nikki93
Prole
Posts: 12
Joined: Mon Mar 19, 2018 9:52 am

sync.lua -- high-level api to make writing multiplayer games easier

Post by nikki93 » Mon Nov 05, 2018 11:46 pm

Image

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.

User avatar
ArchAngel075
Party member
Posts: 317
Joined: Mon Jun 24, 2013 5:16 am

Re: sync.lua -- high-level client-server entity replication api for multiplayer games

Post by ArchAngel075 » Tue Nov 06, 2018 4:55 am

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.

User avatar
nikki93
Prole
Posts: 12
Joined: Mon Mar 19, 2018 9:52 am

Re: sync.lua -- high-level client-server entity replication api for multiplayer games

Post by nikki93 » Tue Nov 06, 2018 5:15 am

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!

User avatar
nikki93
Prole
Posts: 12
Joined: Mon Mar 19, 2018 9:52 am

Re: sync.lua -- high-level client-server entity replication api for multiplayer games

Post by nikki93 » Tue Nov 06, 2018 11:08 pm

So to update on my previous answer,
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?
I updated the tutorial to cover the `:sync` call and the way controller types can have RPCs happen. Check out the new section here!

User avatar
nikki93
Prole
Posts: 12
Joined: Mon Mar 19, 2018 9:52 am

Re: sync.lua -- high-level client-server entity replication api for multiplayer games

Post by nikki93 » Wed Nov 14, 2018 1:08 am

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

Nelvin
Citizen
Posts: 69
Joined: Mon Sep 12, 2016 7:52 am
Location: Germany

Re: sync.lua -- high-level client-server entity replication api for multiplayer games

Post by Nelvin » Wed Nov 14, 2018 7:51 am

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)

Yaaghër
Prole
Posts: 6
Joined: Sat May 07, 2016 6:19 pm

Re: sync.lua -- high-level api to make writing multiplayer games easier

Post by Yaaghër » Thu Nov 15, 2018 10:39 pm

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.

User avatar
nikki93
Prole
Posts: 12
Joined: Mon Mar 19, 2018 9:52 am

Re: sync.lua -- high-level api to make writing multiplayer games easier

Post by nikki93 » Fri Nov 16, 2018 2:57 am

Nelvin-- Thanks! :D 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.

Yaaghër
Prole
Posts: 6
Joined: Sat May 07, 2016 6:19 pm

Re: sync.lua -- high-level api to make writing multiplayer games easier

Post by Yaaghër » Fri Nov 16, 2018 9:33 am

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)

User avatar
nikki93
Prole
Posts: 12
Joined: Mon Mar 19, 2018 9:52 am

Re: sync.lua -- high-level api to make writing multiplayer games easier

Post by nikki93 » Fri Nov 16, 2018 11:29 pm

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:

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
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.

Post Reply

Who is online

Users browsing this forum: No registered users and 8 guests