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.