Approach to basic server-interaction

Hi!

I’ve been using Haxe/OpenFL for roughly a year.
I’ve mainly developed minimalistic prototypes but would now like to take it a step further.

I’ve begun looking into making another minimalistic project involving some sort of server interaction between two clients. It could literaly be anything bare minimum. I’m a web application developer by trade so I’m stepping into unknown grounds here and would like a piece of advice.

How would a server interaction look when it comes to updating a user object?

For example:
In a game loop you update each frame.
To send a server request each frame to update a players position sounds nuts to me since it would put so much strain on the server but I’m guessing that’s how it’s done?

When you create an entity on the client, do you replicate it on the server and then sync for conflicts?
Since you’re never to trust the client I’m guessing you’d need a way to handle a conflict.

For example:
Client request: Set my level to 10.
Server respond: No, my records says that you’re still level 1. Denies request.

Is there a specific data-model i should work with?
Specifically any established design-patterns?

Does it involve server-side rendering or how do you make two clients see the same entity?

SQL or Mongo?
Mainly asking this because I’ve exclusively worked with Mongo over the last two years and would prefer to keep it that way but if there are some obvious flaws I’d like to hear them!

Would a nodejs server be sufficient for cross-platform?
(Android, IOS, Windows, Mac)
Not sure if its even remotely related to the specific platforms since the server wont be running on them…

As you can probably tell by now, I’m only scratching the surface but would love to dig deeper into the server-side game programming!

Thank you.

1 Like

Here’s how I’d do it:

the client doesn’t ask for update but the server has its own loop and periodically send the required information,

the server would be fully authoritative and the client would only display the game, though it would simulate it since the server doesn’t send information each frame,

when the client receive the information from the server it update it model, there are some techniques to ease between the old and new value to avoid sudden changes,

so when the client do something you inform the server (like for example creating an entity) but you’d still do it on the client right now (and not after the server send back the confirmation) to avoid a feeling of lag,

the server runs the game, but only the logic without graphic output,

I don’t think there’s a good answer between SQL and Mongo, depends on how you represent your data,

a nodejs server would be able to serve an android, ios, windows and mac client since they can all use tcp sockets,
if you use haxe serialization to move data between the client and the server you shouldn’t have any issues.

Good luck :slight_smile:

I think there are 2 types of client-server games:

  1. pretty slow games (turn-based), where everything is controlled on the server.
  2. fast games (shooters, mmorpg), where server manage only global state, but some minor details are managed on the client.

I don’t have expirience with 2nd type, so everything I write below is for 1th type.

How would a server interaction look when it comes to updating a user object?
When you create an entity on the client, do you replicate it on the server and then sync for conflicts?

Everything is calculated on server. The same things also calculated on client, but server calculations are more important.

  1. Client send an event to the server. Event is something like “turn right”, “move forward”, “attack”, etc… At the same time, game client display required animation.
  2. If event is valid, server reply: “OK”
  3. If event is not valid, sync was lost, and server send full game state to the client.
  4. Game client also listen for server events, something like “opponent moved”, “opponent attach”, etc…

Does it involve server-side rendering or how do you make two clients see the same entity?

Rendering is only on client, but server keeps entire game state, and can send that state to client.

SQL or Mongo?

Probably Mongo will be better.

Would a nodejs server be sufficient for cross-platform?

Yup, just use JSON for data exchange between server and client.


Another great article: http://buildnewgames.com/real-time-multiplayer/


Example of API for simple turn-based game

Server has only one method “sync-state”

IN: {
    "stateUid": "...",
    "authorization": "...",
    "events": [ ... ]
}

stateUid - unique state identifier. after each sync identifier should be incremented.

OUT: {
    "stateUid": "...",
    "serverTimestamp": ...,
    "state": {
        "gameState": "notPlaying|searchingForOpponent|myTurn|opponentTurn",
        "playersOnline": ...,
        "me": {
            "name": "...",
            "avatarUrl": "...",
            "ratingPosition": ...,
            "winCount": ...,
            "looseCount": ...,
            "robotType": 0 | 1 | 2 | 3
        },
        "opponent": {
            "name": "...",
            "avatarUrl": "...",
            "ratingPosition": ...,
            "winCount": ...,
            "looseCount": ...,
            "robotType": 1 | 2 | 3
        },
        "board": {
            "nextTurnTimestamp": ...,
            "me": {
                "row": ...,
                "col": ...,
                "wallsLeft": ...,
                "rowToReach": ...
            },
            "opponent": {
                "row": ...,
                "col": ...,
                "wallsLeft": ...,
                "rowToReach": ...
            },
            "walls": [
                { "row": ..., "col": ..., "vert": true|false },
                ...
            ]
        },
    },
    "events": [ ... ]
}

“state” is sent only when stateUid on client differs from stateUid on server (or server detects invalid event).

Events from game to server

Find opponent event - when player searches for opponent:

{
    "event": "findOpponent",
    data: {
        "robotType": "..."
    }
}

Cancel find event - when player close “opponent search” popup:

{
    "event": "cancelFind",
}

Make move event - when player make move:

{
    "event": "makeMove",
    data: {
        "row": ...,
        "col": ...
    }
}

Place wall event - when player want to place wall:

{
    "event": "placeWall",
    data: {
        "row": ...,
        "col": ...,
        "vert": true|false
    }
}

Events from server to game

When the game is in “searchingForOpponent” state, and players online count is changed, server send following event:

{
    "event": "playersOnlineUpdated",
    "data": {
        "playersOnline": ...
    }
}

When opponent found:

{
    "event": "opponentFound",
    "data": {
        "name": "...",
        "avatarUrl": "...",
        "ratingPosition": ...,
        "winCount": ...,
        "looseCount": ...,
        "robotType": "..."
    }
}

Next event server send immediately after “opponentFound”:

{
    "event": "initialState",
    "data": {
        "gameState": "myTurn|opponentTurn",
        "board": {
            "nextTurnTimestamp": ...,
            "me": {
                "row": ...,
                "col": ...,
                "rowToReach": ...,
                "wallsLeft": ...
            },
            "opponent": {
                "row": ...,
                "col": ...,
                "rowToReach": ...,
                "wallsLeft": ...
            }
        }
    }
}

When player or opponent wins:

{
    "event": "winOrLoose",
    "data": {
        "win": true|false,
        "byTime": true|false,
        "me": {
            "ratingPosition": ...,
            "winCount": ...,
            "looseCount": ...
        },
        "opponent": {
            "ratingPosition": ...,
            "winCount": ...,
            "looseCount": ...
        }
    }
}

When opponent moved:

{
    "event": "opponentMoved",
    "data": {
        "row": ...,
        "col": ...
    }
}

When opponent placed wall:

{
    "event": "opponentPlacedWall",
    "data": {
        "row": ...,
        "col": ...,
        "vert": true|false
    }
}

Next turn event:

{
    "event": "nextTurn",
    "data": {
        "gameState": "myTurn|opponentTurn",
        "nextTurnTimestamp": ...,
    }
}
2 Likes

The most important consideration, in any client/server scenario, is to design the whole thing such that both computers “have a valid job to do” even though neither one of them will ever be “instantaneously aware” of what the other machine is doing. They will always, so to speak, “be mailing letters to one another.” Although the TCP/IP protocol (but not “IP” alone!) does promise that messages will eventually be delivered and in the order sent, it is unpredictable how long the transfer will take, and “the game cannot wait for it.”

A server can reasonably act as “an ‘overall referee’ and ‘recent-status keeper,’” but its perception of the game-world will always be “somewhat out-of-date,” and its messages sent in response to these things will always be “a little late in arriving.” (Communications links operate on the order of “multiple *milli-*seconds,” while game logic runs on the order of *micro-*seconds.)

Therefore, each client(!) must be primarily responsible for “keeping its user happy,” and must be able to be autonomous in that role … making timing-critical decisions on its own. The server must be able to make decisions based on “recent, at best” game-status information, and to issue ‘meaningful’ responses despite the fact that those responses will take “more *milli-*seconds” to arrive.

Sorry for the delayed reply!
Thank you for the explanation!

TCP vs UDP is something I’ve read about lately when it comes to communication between the client and server. Is there a prefered communication?

Would I be using http://api.haxe.org/sys/net/Socket.html to communicate with my nodejs server? (To make sure that i can compile cross-platform)
It’s silly of me to ask since I could just test my way through but I won’t be able to do that until sunday so It’s mainly for my curiosity.

@sundialservices Very interesting! Do you happen to have any good recommendations for reading material on what the different protocols pros/cons are and what one should look at when choosing server communication?

Thank you!

You will undoubtedly wish to use TCP/IP. UDP is a very low-level “broadcast-only” protocol that doesn’t even guarantee that the messages will arrive, at all!

Furthermore, always be on the lookout for already-existing solutions. Actum Ne Agas: Do Not Do A Thing Already Done.” I don’t have any particular recommendations but “just look around.” The world is fairly stuffed with high-performance servers who are communicating with many clients, and likewise with the clients who are communicating with those servers. Therefore, look upon your task as a “steal vs. build” :wink: decision.

I think decision upon TCP/UDP is based on speed of communication; don’t know how it is today, but in the past fast games like FPS used UDP, turn based games used TCP.
Few informations are here
http://fabiensanglard.net/quakeSource/quakeSourceNetWork.php
and here
http://fabiensanglard.net/quakeSource/quakeSourcePrediction.php
It’s about how quake 1 engine works, with dividing data into reliable and reliable parts. Second link is about prediction the time gap between server and client.
This engine is 20 years old, and network is faster nowadays, so You have probably check is TCP enough for Your needs in terms of speed.
With UDP You have to write probably mechanism checking whether data arrived and if yes, are they corrupted, so it’s more complicated also.

… and I would cordially suggest that, in doing so, “you’d just be doing TCP/IP the hard way.” :slightly_smiling:

However, that pithy comment might be entirely incorrect! The prologue comments in the WikiPedia article on “UDP” are very well-written. Maybe your server needs to send packets very quickly, doesn’t care if a particular packet doesn’t arrive, the packets in question are short and independent. In that case, UDP might be exactly the protocol to use.

And the good thing is … it does not have to be the only protocol that you do use! This is not an “either/or” decision. Here you have two protocols, each designed with very different purposes in mind, and both of them are at your disposal if you so choose.

I’ve chosen to make use of the TCP-protocol to communicate between my haxe application and nodejs server which seems to be working fine across the different platforms. I’m hoping to be able to make use of Socket.IO but not entirely sure how that would be done or if it’s even relevant…

I’ve yet to do anything but to connect and send/retrieve a package between the two but since that’s working, the rest should too.

What I’m not entirely able to comprehend or I might just be dumb, but how do you tell each client to draw graphics shareable between two clients?

Let’s say you have two Players and you’re in player ones client.

Server reports that PlayerOne is at xy-position and that PlayerTwo just appeared at another xy-position. How do u let PlayerOne know what PlayerTwo looks like?

It might just be a case of me being dumb but If you could shed some light on this I’d be very happy.
Thank you!

You could send to player one that there is another player at position x,y with image img.png or something like that.

You can give all the information you want :slight_smile:

It just seems resource-heavy to send the sprites regarding another player.

Is there a standardized way to do this? :stuck_out_tongue:

You don’t send the sprite, you send the name of the resource,
could be an image name or an id in a texture atlas.

And you’d send it only the first time.

That does make more sense. Thx!