How to use Realtime.Co for a turn-based multiplayer game

As @zxretrosoft wanted to add a multiplayer mode to his game, the Realtime module of @Martin came to my mind. In a turn-based game, this message based service is perfect for turn-based games where you don't need send data back an forth as fast as possible. At the time of writing this tutorial, Realtime.co offers a free plan which has enough messages/connection a month for an indie game to be used for a message-based multiplayer experience. Of course, you can always spend some money to it size up. It is also perfect if you just want to add a in-game chatroom for your app.

Attention: Currently the module works only for HTML5 but I have contacted Martin to look into its Android implementation. Realtime offers solutions for a lot of platforms, so there would be a way to add our other platforms to it too.

I have attached a sample project so you can see how it works. But before all this, you need to do some steps first.

1) Create an account at Realtime.co and subscribe to the free plan for their message service.

2) Note down your application key, which you need to insert into the sample code:

1549103905084.png


3) Download Martin's module from GitHub and copy the content of its module_ext folder into a location where you like to store your 3rd party modules.

4) Download the attached sample project, extract it to a location you like and add your application key to the script in line 7 here:

Cerberus X:
Const APP_KEY:String = "your_application_key_goes_here"
5) Build and run the sample, after a few seconds it will connect and present you this screen:

1549104356566.png


You can run several instances of the script and see how they interact with each other. You switch between player 1 and 2 by pressing the corresponding keys on your keyboard.

Now let me explain what is going on in this script. You see two spaceships on the screen. Depending if you select player 1 or 2, you control the blue or the red ship. At the start you are player 1.
Now click on the screen with your mouse and the ship moves towards the location. The ships get their move orders by an incoming message. When you click, you sent out a message formated in a specific format. To achieve this, I show you the important parts of the script.

In line 7 you need to tell the script your application key that you got when you signed up at Realtime.Co:

Cerberus X:
Const APP_KEY:String = "your_application_key_goes_here"
Your main class needs to implement the IRealtimeCallback interface. That is needed so you can act on the incoming messages inside OnRealtime... callback methods we will define below:

Cerberus X:
Class MyApp Extends App Implements IRealtimeCallback
Then during the OnCreate method, the game initializes Realtime and sets some status fields from line 100 on:

Cerberus X:
        ' Create an instance of Realtime, intialize it with your APP_KEY and set some values
       _realtime = New Realtime( Self )
        _realtime.init( APP_KEY )
        _channelName = "TestGame_CX"
        _isInitialized = False
        _isConnected = False
        _isSubscribed = False
Besides the callback methods, the real action happens during OnUpdate. First we need to make sure that the asynchronous events are updated, or you won't see any callbacks:

Cerberus X:
        ' Update the asynchronos events for Realtime
        UpdateAsyncEvents()
Next we check if Realtime is initialized and set a status field:

Cerberus X:
        ' Initialize RealTime
        If( Not _isInitialized )
            _isInitialized = _realtime.isInitialized()
        Endif
Once it is initialized, we can try to connect to Realtime. You can also set the ID and the metadata. Both can be used to identify the sender of a message:

Cerberus X:
        ' Connect to RealTime
        If( _isInitialized And Not _isConnected )
            _realtime.setId(1234)
            _realtime.setConnectionMetadata("P1")
            _realtime.connect( APP_KEY )
        Endif
If we are connected, we should subscribe to a channel to actually get the messages from the server back:

Cerberus X:
        ' Subscribe to a channel
        If( _isConnected And Not _isSubscribed )
            _realtime.subscribe( _channelName )
            _isSubscribed = True
        Endif
When we are finally subscribed to a channel, we can start sending messages out to the server. As we want to do this when we click with the mouse on the screen, we need to check for a MouseHit. Of course only when we are subscribed. The message we are sending is a composed string in a format, that is easily split up when we get it back:

Cerberus X:
        ' Send new coordinates to the server if the mouse was pressed
        ' The message is composed in the following order separated by :
        ' COMMAND:XPOS:YPOS:PLAYERID
        If MouseHit(MOUSE_LEFT)
            If( _isSubscribed )
                _realtime.send( _channelName, "MOVESHIP:"+MouseX()+":"+MouseY()+":"+_realtime.getConnectionMetadata() )
            Endif
        Endif
The rest of the OnUpdate method is checking for key 1 or 2 and setting the metadata accordingly. The metadata is one way of identifying the client who sends out a message. You can also set an ID as you have seen above when we connect to the server.

At the end of OnUpdate, we update the ships positions which is handled by the cShip class.

As you can see, we are not handling the unsubscribing and disconnecting of a client. But I think that this is something you can figure out on your own. Just look at the methods the Realtime class provides.

Now lets talk about the callback methods we have to implement.

OnRealtimeConnected
This method is called back when the connection to the server was established. In it we are subscribing to some global announcement channels to receive notifications, when a client has (dis)connected or (un)subsribed.

Cerberus X:
    Method OnRealtimeConnected:Void()
        Print ("Connected!")
        _isConnected = True
        'Subscribe to the global announcement channels
        _realtime.subscribe( "ortcClientConnected" )
        _realtime.subscribe( "ortcClientDisconnected" )
        _realtime.subscribe( "ortcClientSubscribed" )
        _realtime.subscribe( "ortcClientUnsubscribed" )
    End
OnRealtimeDisconnected
This method is called back when the server is disconnected. Our logic above in OnUpdate will then try to connect and subscribe again automatically:

Cerberus X:
    Method OnRealtimeDisconnected:Void()
        Print ("Disconnected!")
        _isConnected = False;
        _isSubscribed = False;
    End
OnRealtimeSubscribed
This method is called when we have subscribed to a channel.

Cerberus X:
    Method OnRealtimeSubscribed:Void( channelName:String )
        Print ("Subscribed to channel " + channelName)
    End
OnRealtimeUnsubscribed
This method is called when we have unsubscribed from a channel.

Cerberus X:
    Method OnRealtimeUnsubscribed:Void( channelName:String )
        Print ("Unsubscribed from channel " + channelName)
    End
OnRealtimeException
This method is called when an expection has occured.

Cerberus X:
    Method OnRealtimeException:Void( exception:String )
        Print ("Exception happened: " + exception)
    End
OnRealtimeReconnecting
This method is called when Realtime tries to reconnect.

Cerberus X:
    Method OnRealtimeReconnecting:Void()
        Print ("Reconnecting...")
    End
OnRealtimeReconnected
This method is called when the reconnection of Realtime was successful.

Cerberus X:
    Method OnRealtimeReconnected:Void()
        Print ("Reconnected!")
    End
OnRealtimeNewMessage
This method is being called whenever there was a message send out to the channel. You will receive the channel name and the message string.
In our example the message is formatted in a certain way. We will split it up and move the ship accordingly.

Cerberus X:
    ' This method handles the communication
    Method OnRealtimeNewMessage:Void( channelName:String, message:String )
        Print ("Message " + message + " sent on channel " + channelName)
       
        ' Next we split the message we have received and act accordingly
        ' The message is composed in the following order separated by :
        ' COMMAND:XPOS:YPOS:PLAYERID

        Local msgPart:String[] = message.Split(":")
        If msgPart[0] = "MOVESHIP"
            If msgPart[3] = "P1"
                ship1.MoveTo(Int(msgPart[1]), Int(msgPart[2]))
            Else
                ship2.MoveTo(Int(msgPart[1]), Int(msgPart[2]))
            Endif
        Endif
    End
OnRealtimeClientConnected
This method is called when a client has connected.

Cerberus X:
    Method OnRealtimeClientConnected:Void( metadata:String )
        Print ("Client connected (metadata): " + metadata)
    End
OnRealtimeClientDisconnected
This method is called when a client has disconnected. You can determine if it was by choice or timeout.

Cerberus X:
    Method OnRealtimeClientDisconnected:Void( metadata:String, typeOfDisconnect:Int )
        _message = "Client disconnected (metadata, type): " + metadata
        If( typeOfDisconnect = 0 )
            _message += ", normal"
        Else
            _message += ", timeout"
        Endif
        Print(_message)
    End
OnRealtimeClientSubscribed
This method is called when a client has subscribed to a channel.

Cerberus X:
    Method OnRealtimeClientSubscribed:Void( metadata:String, channelName:String )
        Print ("Client subscribed to " + channelName + ", metadata: " + metadata+ ", clientId: " + _realtime.getId())
    End
OnRealtimeClientUnsubscribed
This method is called when a client has unsubscribed from a channel.

Cerberus X:
    Method OnRealtimeClientUnsubscribed:Void( metadata:String, channelName:String )
        Print ("Client unsubscribed from " + channelName + ", metadata: " + metadata)
    End

That is all for now. As you can see it is very easy to use. With the free plan, I would not use it in real-time multiplayer scenario. Just imagine sending a message in every frame. That means 60 messages per second if you don't watch out. You can use up your free 3.000.000 messages per month very quickly this way.

Feel free to ask away. I am by no means an expert on this topic but I will try my best to answer your questions.

Cheers
Michael
 
Last edited:
Wow! I didn't know this kind of thing was possible. I'm definitely going to try and use this for my next jam.
 
Very good info Mike. Thank you for your time. such a detail explanation.

This topic makes me wonder, how about realtime multiplayer. What is the good tools out there.:rolleyes:
 
Thanks for this Mike. Great stuff!

This topic makes me wonder, how about real time multiplayer. What is the good tools out there
This weekend I started looking at Real time multiplayer.
As Android is my background, I've started looking at GooglePlays implementation
This may take a while to write a wrapper.

any other cross platform real time implementations I can try to wrap?

Ive not tried, but could the messaging of RealTime.co be used for real time multiplayer? I can imagine the number of packets being sent could soon add up
 
Top Bottom