Kotlin and SmartFoxServer 2X, part 3

This is the third and final chapter of our explorations with Kotlin and SmartFoxServer 2X. After building the first Kotlin extension in part one and creating a basic client app in part two we’re now completing the picture with a client/server example.

Word of advice: if you haven’t followed the two previous chapters make sure to go back and review them before starting with this last chapter.

» Putting it all together

In this final installment we’re going to translate in Kotlin our client and server quick start example from the documentation, applying what we have learned so far about Kotlin.

Server side

package sfs2x.kt

import com.smartfoxserver.v2.core.ISFSEvent
import com.smartfoxserver.v2.core.SFSEventType
import com.smartfoxserver.v2.entities.User
import com.smartfoxserver.v2.entities.data.ISFSObject
import com.smartfoxserver.v2.entities.data.SFSObject
import com.smartfoxserver.v2.extensions.BaseClientRequestHandler
import com.smartfoxserver.v2.extensions.SFSExtension

val CMD_SUM = "sum"

class SumHandler : BaseClientRequestHandler()
{
    override fun handleClientRequest(user: User, params: ISFSObject)
    {
        if (params.containsKey("n1") && params.containsKey("n2"))
        {
            val n1 = params.getInt("n1")
            val n2 = params.getInt("n2")

            val resObj = SFSObject()
            resObj.putInt("res", n1 + n2)

            send(CMD_SUM, resObj, user)
        }
    }
}

class KotlinTestExtension : SFSExtension()
{
    override  fun init()
    {
        trace("Kotlin Extension ready!")

        addRequestHandler(CMD_SUM, SumHandler());
    }

    override fun destroy() {
        super.destroy()

        trace("Kotlin Extension shutting down.");
    }
}

We have already learned how to create an Extension class by inheriting from the parent SFSExtension class and overriding the init() method. This time we’re also implementing a request handler by creating a class that extends BaseClientRequestHandler.

Our SumHandler implementation takes two integer parameters, sums them together and sends them back to the client, wrapped in an SFSObject.

As a side note, it’s worth noticing how Kotlin is more flexible with class definitions as we’re not constrained to the one-class-per-file rule of Java development.

Client side

import com.smartfoxserver.v2.entities.data.SFSObject
import com.sun.xml.internal.rngom.parse.host.Base
import sfs2x.client.SmartFox
import sfs2x.client.core.BaseEvent
import sfs2x.client.core.IEventListener
import sfs2x.client.core.SFSEvent
import sfs2x.client.requests.ExtensionRequest
import sfs2x.client.requests.LoginRequest
import sfs2x.client.util.ConfigData

class SFSEventListener : IEventListener
{
    override fun dispatch(evt: BaseEvent?)
    {

        when (evt?.type)
        {
            SFSEvent.CONNECTION -> onConnection(evt)
            SFSEvent.CONNECTION_LOST -> println("Connection lost")
            SFSEvent.LOGIN -> onLogin(evt)
            SFSEvent.LOGIN_ERROR ->onLoginError(evt)
            SFSEvent.EXTENSION_RESPONSE ->onExtensionResp(evt)
        }
    }

    fun onConnection(evt:BaseEvent)
    {
        val success = evt.arguments.get("success") as Boolean

        if (success)
        {
            println("Connected to server...")
            sfs.send(LoginRequest("", "", cfg.zone))
        }

        else println("Connection failed!")
    }

    fun onLogin(evt:BaseEvent)
    {
        println("Logged in as: " + sfs.mySelf.name)
        
        val sfso = SFSObject()
        sfso.putInt("n1", 15)
        sfso.putInt("n2", 50)

        sfs.send( ExtensionRequest("sum", sfso) )
    }

    fun onLoginError(evt:BaseEvent)
    {
        val errorMsg = evt.arguments.get("errorMessage");
        println("Login failed: ${errorMsg}")
    }

    fun onExtensionResp(evt:BaseEvent)
    {
        val params = evt.arguments.get("params") as SFSObject
        println("Result = " + params.getInt("res"))
    }
}

val sfs = SmartFox()
val cfg = ConfigData()
val listener = SFSEventListener()

fun main(args: Array<String>)
{
    println("SFS2X API Version: ${sfs.version}")

    cfg.host = "127.0.0.1"
    cfg.port = 9933
    cfg.zone = "BasicExamples"
    cfg.isDebug = false

    sfs.setClientDetails("Kotlin", "")
    sfs.addEventListener(SFSEvent.CONNECTION, listener)
    sfs.addEventListener(SFSEvent.CONNECTION_LOST, listener)
    sfs.addEventListener(SFSEvent.LOGIN, listener)
    sfs.addEventListener(SFSEvent.LOGIN_ERROR, listener)
    sfs.addEventListener(SFSEvent.EXTENSION_RESPONSE, listener)

    // Start connection
    sfs.connect(cfg)
}

As seen in our previous chapter in Kotlin we’re free to write functions outside of classes, so our main() method doesn’t require any class definitions around it. Here we define our connection parameters an register the events we’re going to listen to.

Also notice the usage of the setClientDetails() method to identify the client’s technology. This is an optional step that can be useful to distinguish between different client technologies in our server logs and usage statistics, for example using the Analytics module.

Sending and receiving extension messages is no different from the Java counterpart. All we need to do is wrapping the request (or response) parameters in an SFSObject and send them to the other side. In this case we’re sending two integers (called “n1” and “n2) and we’re expecting an integer (called “res”) in the server response.

» Conclusions

We have found Kotlin a solid alternative to Java development for both client and server sides. It’s relatively easy to get started with and it’s definitely less strict and verbose than Java. Paired with its functional style, modern approach to OOP and interoperability with Java, it is a great tool for developing with SmartFoxServer and building multiplayer apps and games.