Posted: 21st May 2017 3:05
Hello the community !

i finally release my AppGameKit Multiplayer Server for Linux. (Now with Network Prediction/Reconciliation/Interpolation ! see below...)

It's a daemon written in PHP to allow easy customization and interactions between your games, websites and databases (MySQL for example). So the server could also work on MacOS X , and Windows in debug mode only (not as a daemon). You only need PHP-CLI and major modules installed.

The server is just... one PHP file and should be compatible PHP5>PHP7 (maybe PHP4 but not tested). You put it on your linux server. You chmod +x it, you launch it (even without any customization) and you're ready to go.

I've made the server fully compatible with almost all AppGameKit network commands.

However, I had to resolve one thing : supporting a lot of players without broadcasting all the local network variables to all the server-connected players. Players are generally "isolated" in "party","room" or "game" virtual spaces. or maybe in a totally different agk game !

So,I have added one important thing : Channels. Virtual channels (or rooms) can isolate group of clients/players as if they were on an independant host server. So, only one instance of the Server could host multiple Network games or apps.

To change default channel (channelNumber 0), your App Client only need to call the AppGameKit Command SetNetworkLocalInteger( networkId, "SERVER_CHANNEL", channel_number) and the server will do the rest and notify correctly each client which is concerned for join/quits/variables/updates/messages etc..


Of course, the AppGameKit Server is customizable interacting with network events as explained in the help documentation :

+ Code Snippet
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
//
//                                _____ _  __   _____
//                          /\   / ____| |/ /  / ____|
//                         /  \ | |  __| ' /  | (___   ___ _ ____   _____ _ __
//                        / /\ \| | |_ |  <    \___ \ / _ \ '__\ \ / / _ \ '__|
//                       / ____ \ |__| | . \   ____) |  __/ |   \ V /  __/ |
//                      /_/    \_\_____|_|\_\ |_____/ \___|_|    \_/ \___|_|
//
//                                            By MikeMax
//                                       (mikemaxfr@gmail.com)
//
//
// * AGK Server Command-line options :
//   ---------------------------------
//
//    Please first ! ===>>   chmod +x AGKServer.php
//
//  and then :
//
//    ./AGKServer.php debug             => Start AGKServer in debug mode (direct terminal output ... CTRL + C to stop server)
//    ./AGKServer.php start             => Start AGKServer as a daemon
//    ./AGKServer.php stop              => Stop AGKServer daemon
//
//
//
// * Available functions for Events :
//   --------------------------------
//
// - onAGKClientJoin($iClientID) => Triggered when a client connects to this server
// - onAGKClientQuit($iClientID) => Triggered when a client disconnects from this server
// - onAGKClientChangeChannel($iClientID, $iOldChannelNumber, $iNewChannelNumber) => Triggered when client is joining a new channel
// - onAGKClientNetworkMessage($iSenderID, $iDestinationID, $Message) => Triggered when a client has sent a message to any client(s)
// - onAGKClientUpdateVariable($iClientID,$sVariableName,$mVariableValue) => Triggered when a client has updated a variable.
// - onAGKServerRefreshTimer() => Triggered every SERVER_REFRESH_INTERVAL nanoseconds.
//
// * Available Methods/Commands within Events :
//   ------------------------------------------
//
// - writeLog($sString) => echoes $string in terminal in debug mode or in a log file (AGKServer.php.log beside the php file) in daemon mode.
//
// - GetClientName($iClientID) => (string) Retrieve name of the client
// - GetClientIP($iClientID) => (string) Retrieve client's IP Address
// - GetClientChannelNumber($iClientID) => (integer) Retrieve current client channel number
// - GetNetworkClientVariable($iClientID, $sVariableName) => (variant) Get Local variable (integer or float) from client (Respect the AGK Network ResetMode)
// - GetNetworkClientInteger($iClientID, $sVariableName) => (integer) Alias of GetNetworkClientVariable command
// - GetNetworkClientFloat($iClientID, $sVariableName) => (float) Alias of GetNetworkClientVariable command
// - SetNetworkLocalInteger($ChannelNumber, $sVariableName, $iValue [, $ResetMode = 0]) => Sets/updates a server local integer variable which will be transmitted to clients of $iChannelNumber
// - SetNetworkLocalFloat($ChannelNumber, $sVariableName, $fValue [, $ResetMode = 0]) => Sets/updates a server local float variable which will be transmitted to clients of $iChannelNumber
//
// - GetChannelClientsCount($iChannelNumber) => (integer) Retrieve number of clients in $iChannelNumber
// - GetChannelClientsList($iChannelNumber) => (array of ("ID","Name","ChannelNumber")) Retrieve Clients Names,IDs,ChannelNumber in $iChannelNumber ($iChannelNumber = -1 to retrieve ALL connected Clients with respective ChannelNumber)
// - GetChannelsList() => (array of('ChannelNumber')) => Retrieve IDs of nonempty channels
// - forceChannelJoin($PlayerID, $ChannelNumber) => Make client to join a specific channel number (warning : doesn't not update his SERVER_CHANNEL variable !)
//
// - GetNetworkMessageString($Message) => (string) Extract expected String from the message and advances the message pointer
// - GetNetworkMessageInteger($Message) => (integer) Extract expected Integer from the message and advances the message pointer
// - GetNetworkMessageFloat($Message) => (float) Extract expected Float from the message and advances the message pointer
//
// - StopPropagation() => Tell explicitely to the server to DO NOT transmit received message or variables to targetted client(s). (Very special cases)
//
// - Class NetworkMessage() => You can create one or more messages with differents data types (string, integer, float)
//
//            Methods :
//            ---------
//
//            - AddNetworkMessageString(string)         => Add a string into the message instance buffer
//            - AddNetworkMessageInteger(integer)     => Add an integer value into the message instance buffer
//            - AddNetworkMessageFloat(float)         => Add an float value into the message instance buffer
//            - Send(ClientID)                        => Send the message to ClientID and clear the buffer (ClientID=0 to send to ALL clients connected to the server (over the Channel isolation))
//            - SendChannel(ChannelNumber)            => Send the message to all Clients present in ChannelNumber and clear the buffer
//
//            Usage Example 1 (Usual Way) :
//            -----------------------------
//
//            $msg1 = new NetworkMessage(); // Can be instantiated once and be used multiple times in events to avoid recreating for each message.
//
//            $msg1->AddNetworkMessageString("This is an example message for $ClientID1 with a string and integer and float");
//            $msg1->AddNetworkMessageInteger(123456);
//            $msg1->AddNetworkMessageFloat(123.456);
//            $msg1->Send($ClientID1); // Send the message and clear the buffer automatically if you want to reuse the same instance for another message
//
//            $msg1->AddNetworkMessageString("This is an example message for $ClientID1 with a string and integer and float");
//            $msg1->AddNetworkMessageInteger(123456);
//            $msg1->AddNetworkMessageFloat(123.456);
//            $msg1->Send($ClientID2); // Send Message and Clear buffer...
//
//
//
//            Usage Example 2 (Fluent Interface Way) :
//            ----------------------------------------
//
//             $msg2 = new NetworkMessage();
//             $msg2->AddNetworkMessageString("This is an example message with a string and integer and float")
//                  ->AddNetworkMessageInteger(123456)
//                  ->AddNetworkMessageFloat(123.456)
//                  ->Send($ClientID1)
//                  ->AddNetworkMessageString("This is another example message with a string and Float")
//                  ->AddNetworkMessageFloat(123456789)
//                  ->AddNetworkMessageFloat(123.456)
//                  ->Send($ClientID2);
//
//
//
//* TODO :
//  ------
//
// - Commands to create Virtual Client/Players (bots) and interact with.
// - Make the server able to work in a cluster (with mirrors to prevent server failures)
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////





Edit 03/06/2017 : AGK Server has now a Plugin called "NetGamePlugin" for realtime Network games which needs Client-Side Prediction, Reconciliation and Interpolation to bring smooth animations !

For these kind of realtime game, The server is authoritative. Client simply sends movements (direction and Velocity but not the global position !) and Server will calculates World Position of each client and send them back to clients (with updatePlayer events). To prevent local latencies, Local player has its own event which use prediction and reconciliation. Thje plugin have been made to be basically 2D/3D Compatible.

As for the AppGameKit Server, everything is made in a "Event Driven Architecture". The plugin consists of two files : a server side plugin (AGKServer_NetGamePlugin.php to include in AppGameKit Server Core file) and a AppGameKit Client Plugin ( NetGamePlugin.agc )

Here is the documentation of the Server-side plugin :

+ Code Snippet
////////////////////////////////////////////////////////////////////////////////////////////////
//
//
//          _   _      _   _____                       ______ _             _
//         | \ | |    | | |  __ \                      | ___ \ |           (_)
//         |  \| | ___| |_| |  \/ __ _ _ __ ___   ___  | |_/ / |_   _  __ _ _ _ __
//         | . ` |/ _ \ __| | __ / _` | '_ ` _ \ / _ \ |  __/| | | | |/ _` | | '_ \
//         | |\  |  __/ |_| |_\ \ (_| | | | | | |  __/ | |   | | |_| | (_| | | | | |
//         \_| \_/\___|\__|\____/\__,_|_| |_| |_|\___| \_|   |_|\__,_|\__, |_|_| |_|
//                                                                     __/ |
//                                                                    |___/
//
//                                   For AGK Server
//
//                                     By MikeMax
//                               (mikemaxfr@gmail.com)
//
//
//
//
//
//
//
// * Available Methods/Commands within Events :
//   ------------------------------------------
//
//      * Relative event's methods :
//        --------------------------
//
//      - NGP_sendWorldState($ChannelNumber) => Send WorlState (Players positions) to all clients in a channel (should always be in Server Timer 'onAGKServerRefreshTimer' event)
//
//      - NGP_initClient($iClientID) => Initialize new client state and variables and Send WORLD_STEP_MS to client (very important) (should always be in the AGK Server 'onAGKClientJoin' event)
//      - NGP_destroyClient($iClientID) => Free the client state and variables (should always be in the AGK Server 'onAGKClientQuit' event)
//
//      - NGP_CheckForReceivedMovements($iSenderID, $iDestinationID, $Message) => Forward received movement message and process them. (should always be in beginning of the 'onAGKClientNetworkMessage' event)
//
//      * Client Properties methods :
//        ---------------------------
//
//      - NGP_SetClientState($iClientID, $SlotNumber, $Value) => Force client to specific state defining SLOT Values (positions/angles)
//      - NGP_GetClientState($iClientID) => Retrieve Slot values of current State (positions/angles) of a Client
//
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////


here is the documentation of the Client-side plugin :

+ Code Snippet
////////////////////////////////////////////////////////////////////////////////////////////////
//
//
//          _   _      _   _____                       ______ _             _
//         | \ | |    | | |  __ \                      | ___ \ |           (_)
//         |  \| | ___| |_| |  \/ __ _ _ __ ___   ___  | |_/ / |_   _  __ _ _ _ __
//         | . ` |/ _ \ __| | __ / _` | '_ ` _ \ / _ \ |  __/| | | | |/ _` | | '_ \
//         | |\  |  __/ |_| |_\ \ (_| | | | | | |  __/ | |   | | |_| | (_| | | | | |
//         \_| \_/\___|\__|\____/\__,_|_| |_| |_|\___| \_|   |_|\__,_|\__, |_|_| |_|
//                                                                     __/ |
//                                                                    |___/
//
//                                   For AGK Client
//
//                                     By MikeMax
//                               (mikemaxfr@gmail.com)
//
//
//
//
//
//	* Available Methods :
//	  -------------------
//
//  - NGP_JoinNetwork(ServerHost as string,ServerPort as integer, Nickname as string, NetworkLatency as integer)
//						=> Joins network, initialize Datas and Returns Network ID and should replace the original "JoinNetwork" AGK Command.
//
//  - NGP_UpdateNetwork(iNetID as integer) 	
//						=> Handles the Server communication and update the WorldState Slot of all users and Call Plugin Events
//						=> !!!! Should always be in Main Program Loop !!!
//
//  - NGP_CloseNetwork(iNetID as integer) 
//						=> Close the network and free all datas and should replace the original "CloseNetwork" AGK Command.
//
//  - NGP_SendMovement(iNetID as integer,Slot as integer, Velocity as integer,Speed as float)
//						=> Send to the server the movement to apply to a specific Slot (defined by a velocity in negative or positive depending on the direction you want)
//						=> 12 Slots are available to use freely : 
//								- 3 Slots for Position (2D/3D) : POS_X, POS_Y, POS_Z  => Will be linear interpolated automatically in Player's update event (see available events below)
//								- 3 Slots for Rotation (2D/3D) : ANG_X, ANG_Y, ANG_Z  => Will be angular interpolated automatically in Player's update event (see available events below)
//
//								And 6 others similar slots if you need to handle a second local player or others thing linked to your player (for example a tank (defined by 6 first slots) with rotating canon (defined by 6 other slots or only rotations slots) or something like that)
//			
//						- Constants Slots are defined as follow :
//
//	 						POS_X => 1
// 							POS_Y => 2
// 							POS_Z => 3
// 							ANG_X => 4
// 							ANG_Y => 5
// 							ANG_Z => 6
//
//	 						POS_X2 => 7
// 							POS_Y2 => 8
// 							POS_Z2 => 9
// 							ANG_X2 => 10
// 							ANG_Y2 => 11
// 							ANG_Z2 => 12			
//
//
//	* Available Events :
//	  ------------------
//
// - NGP_onNetworkJoined(iNetID, localClientID) 
//						=> Triggered when your client is connected, bringing you the network ID and the local Client ID
//
// - NGP_onNetworkClosed(iNetID) 
//						=> Triggered when your client is disonnected
//
// - NGP_onNetworkPlayerConnect(iNetID, ClientID) 
//						=> Triggered when a client has just connected the server/channel (includes your own join).
//
// - NGP_onNetworkPlayerDisconnect(iNetID, ClientID)
//						=> Triggered when other client has just quitted the server/channel.
//
// - NGP_onLocalPlayerMoveUpdate(iNetID, UpdatedMove as NGP_Slot)
//						=> Triggered when your own Slots are updated (after automatic prediction and reconciliation)
//
// - NGP_onNetworkPlayerMoveUpdate(iNetID, ClientID as integer, UpdatedMove as NGP_Slot)
//						=> Triggered when other client's Slots are updated (after automatic interpolation)
//
// - NGP_onNetworkMessage(ServerCommand as integer,idMessage as integer)
//						=> Triggered when a network message arrive (other than NPG specific messages)
//						=> Note that all your other network messages should integrate a command identifier as an integer at the beginning of each message
//
////////////////////////////////////////////////////////////////////////////////////////////////



You can simply compile and try the Sample AppGameKit Tier1 Game (very basic race game :p), it is configured by default to connect to my 24/7 AppGameKit Server Linux Box and contains a lot of comments. Use the key 'G' to make your network ghost appears.
Works Also on mobile devices (added a little Virtual Joystick

The plugin is surely not perfect and there is a lot of variant negotiations for Prediction/Reconciliation/Interpolation... (for example, Extrapolation !)

You can obviously write your own plugin or modify this one !

The basic AppGameKit Server PHP Core File is attached

The NetGamePlugin (AGK Server PHP Core + NGP Plugin + Sample AppGameKit App) is also Attached

Feedbacks or ideas will be appreciated !
Posted: 21st May 2017 7:08
Very very nice, i got it set up and running on my vps. Nice work! i can now actually make a multiplayer game easily. this is something that needs to be included with agk.
Posted: 21st May 2017 21:29
New commands added / renamed for compatibility :

- GetNetworkClientVariable($ClientID, $VariableName) => Get Local variable (integer or float) from client (Now Respect the AppGameKit Network ResetMode)
- GetNetworkClientInteger($ClientID, $VariableName) => Alias of GetNetworkClientVariable command
- GetNetworkClientFloat($ClientID, $VariableName) => Alias of GetNetworkClientVariable command
- SetNetworkLocalInteger($ChannelNumber, $VariableName, $Value [, $ResetMode = 0]) => Sets/updates a server local integer variable which will be transmitted to clients of $ChannelNumber
- SetNetworkLocalFloat($ChannelNumber, $VariableName, $Value [, $ResetMode = 0]) => Sets/updates a server local float variable which will be transmitted to clients of $ChannelNumber


First post updated !
Posted: 22nd May 2017 5:17
Thanks for the update MikeMax, im following this closely. One quick question for you, I noticed that when i connected with another player via the demo on my server the other players were somewhat jittery in their movement. Is this normal, or what can be done to make it more smooth?
Posted: 22nd May 2017 6:53
Cor,

It's normal and you are now facing to the real difficulty of Internet multiplayer games ! I was waiting for this kind of questions

but first thing, have you played with ServerLatency variable (which uses SetNetworkLatency AppGameKit command) on the App side ? more you down it, more it will smooth. But if your client connection is not fast enough it will not be sufficient (which is generally the case for games which require instant-reaction). Is the jitter effect the same when you connect on my demo server ? maybe your VPS internet connection is slower than mine.

Multiplayer over the internet is totally different of multiplayer over Local Area Network in terms of latency and so, smooth. it's a big subject (except maybe if you have fibered connection from all clients to server ...).

I think this server will now bring the subject of "Network Prediction / Compensation / Interpolation" in this place . There are few solutions and here are some ressources :

- All the parts of this article are very interesting to understand : http://www.gabrielgambetta.com/fpm_live.html)

- https://en.wikipedia.org/wiki/Client-side_prediction

- https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking

- And surely on main TGC Forums ! Try to search about "network latency" or something like that in DBP forums maybe

There is a lot of ressources on internet about multiplayer latency compensation/prediction/interpolation and google should help you to use the better technique for your case.

I will also modify the Demo app and adapt server events to add compensation/prediction/interpolation to help everyone to understand with AppGameKit Code in a near future
Posted: 23rd May 2017 0:57
few optimizations and New commands added :

- GetChannelClientsCount($ChannelNumber) => (integer) Retrieve number of clients in $ChannelNumber
- GetChannelClientsList($ChannelNumber) => (array of ("ID","Name","ChannelNumber")) Retrieve Clients Names,IDs,ChannelNumber in $ChannelNumber ($ChannelNumber = -1 to retrieve ALL connected Clients with respective ChannelNumber)

- New method in class NetworkMessage : SendChannel(ChannelNumber) => Send the message to each Client present in ChannelNumber and clear the buffer

First post updated.
Posted: 25th May 2017 2:14
awesome thanks Mike Max! what else do you have in the works for this?
Posted: 25th May 2017 14:54
Completely awesome in every way.
Posted: 25th May 2017 16:22
Thanks for your comments !

To answer to Cor, i'm working on :

- An easy way to implement Network Prediction/interpolation in multiplayer AppGameKit games (on server side and with a set of commands as a library in AppGameKit for clients side)
- Commands to create Virtual Client/Players (bots) and interact with.
- Make the server able to work in a cluster (with server mirrors to prevent server failures)
Posted: 26th May 2017 16:52
Event added :

- onAGKClientUpdateVariable($ClientID,$VariableName,$VariableValue) => Triggered when a client has updated a local variable.

Method Added :

- StopPropagation() => Tell explicitely to the server to DO NOT transmit received message or variables to concerned client(s) (or ALL client if DestinationID=0) (To use only in very special cases... but the method exists !). should be in onAGKClientNetworkMessage event server's function or in onAGKClientUpdateVariable event server's function.

First post updated.
Posted: 1st Jun 2017 17:35
I have now a working bundle solution for "live" networking games (Multiplayer PHP Linux Server + little AppGameKit Tier1 .agc Lib) which handles Client Side Prediction, Reconciliation and linear and angular Interpolations using an easy implementation (i think !).

So i will release the Multiplayer Server updated with a simple Tier1 example very soon !

But for the moment i have to write the documentation !
Posted: 1st Jun 2017 19:02
Awesome work MikeMax, i look forward to trying it out!
Posted: 2nd Jun 2017 7:34
Awesome stuff. Out of curiosity, is there a special reason to use PHP for that? Probably because it's already installed everywhere? Have you tested how many connections this solution could handle (I know it depends on the server)?
Posted: 2nd Jun 2017 8:00
@Xaron, you're right.

Server side needs (depending on each game), more or less server code. But sometimes a lot of logic is on server side. I think the most widespread and easy "server" language is PHP. Actually the server emulate a multi-thread server but i will turn it on a real multi-thread server app. (And later, i will do another version in C/C++ later maybe depending on performance issue).

I will have to do a benchmark soon and i will ask for evryone help here to connect at the same time !
Posted: 2nd Jun 2017 8:24
Nice! Actually I planned to do something in C++ or C# for the server side.

Out of curiosity (as I'm still new to AppGameKit and its multiplayer stuff): Is there an advantage to use that build in AppGameKit multiplayer commands vs the standard sockets stuff?
I mean the fancy multiplayer commands in AppGameKit do probably something in the background like client connection handling (detect disconnects etc) already? If so, how do you emulate that, I mean you need the internal protocol for that?
Posted: 2nd Jun 2017 9:41
This Multiplayer PHP server is aimed to bring an easy way for Tier1 Coders with PHP Knowledges to add Multiplayer capabilities to their projects. (a lot of AppGameKit Tier1 Coders also knows the PHP Language).

Actually AppGameKit doesn't support UDP Sockets in Tier1 (which is the better way for realtime multiplayer games). The AppGameKit Multiplayer protocol only relies on TCP and have many commands already known by the community. So i have decided to use these built-in commands with reverse engineering and help from AppGameKit Team.

Except for the protocol used (TCP instead of UDP), the TCP protocol behind AppGameKit Multiplayer commands is well thought and already optimized (client variables, client list, etc etc...) . So it would be useless to rewrite another client side (Tier1) protocol which could be slower than the biuilt-in one. I am talking about "basic" multiplayer commands with no need of Prediction/reconciliation/interpolation.

For realtime multiplayer games, i have created a Tier1 library for client side, and a NetGamePlugin (in PHP which is simply included in the "core version" of my Multiplayer PHP Server) for server side with a client side events driven architecture to make the things easier (as it already done in core server side). You will see that very soon

The error should be to consider PHP as only a "web language" or a poor "script" language ... it can do almost the same things as C/C++ in most cases and it is cross-platform and well known by a lot of coders. It can uses linux forks, multi-thread, etc... . The only problem could be, indeed, the performance ((due to "only" ByteCode compilation but it produces very good results though but i have a lot of ideas to avoid these kinds of problems

For C++ i agree with you ... for C# i am more reserved (you can use mono or something like that but needs some requirements for Linux machines.. c# is still a Microsoft Language .
Posted: 2nd Jun 2017 9:46
Yes you're right, PHP can be very fast performance wise.

As I've got you here to answer questions and you seem to know the AppGameKit stuff pretty well: Does anything speak against to use AppGameKit Tier 1 directly for server side stuff. I know this would run on Windows only, but this isn't an issue for me.
Posted: 2nd Jun 2017 9:56
You have a simple example project in {AGK Path}\Projects\Multiplayer\GettingStarted which shows you the server side and the client side in the same app
Posted: 2nd Jun 2017 10:03
Yes I know, thanks! Question is, can it be used for classic multiplayer games where the server hosts, let's say, several hundreds of clients?

edit: Well I guess I'm just going to try.
Posted: 2nd Jun 2017 10:23
I don't know if TGC has done a benchmark with so much clients before with a Tier1 server. That's also a challenge for me to see if a PHP Server side version (with a always-on server box) can handle a lot of clients.

A Tier1 server can run on windows/mac/linux desktop (the problem is the miss of a command-line interpreter of the bytecode file (it may be a good feature !) ... so the server needs all the requirements of the AppGameKit Player (OpenGL Libs, etc,etc..). That's why it's difficult to keep a 24/24 AppGameKit Tier1 Server (but not impossible).

The only test i have done is with 16 Clients on the same machine, all connected to my Linux PHP server. and everything was working well

To demonstrate my NetGame Library, i will do something graphical with a ChatBox to encourage people to stay connected and see how it runs (and each person will be able to runs multiple instance of an AppGameKit Client if they want).