Skip to content

Using different languages for Extension development

We have mentioned in our overview that Java isn't the only language the can be used for developing Extensions. In fact most of the JVM supported languages can be used to write Extensions.

We recommend the following:

These are great alternatives to Java that provide excellent interop with the Java API and can be comfortably used for writing server Extensions. A special mention goes to Groovy which can also work as a scripting language without the need to compile and produce a jar file. This can come in handy when prototyping or tinkering with different ideas.

In this tutorial we'll go ahead and re-write the simple Extension from the our intro with these languages.

Kotlin

The easiest way to work in Kotlin is to use JetBrain's IntelliJ IDEA, which comes with full support for the language. From there you can create a new project, link the two libraries used for the Java project (sfs3-core.jar and sfs3.jar) and create your Extension class:

class MyKotlinExtension: SFSExtension()
{
    override fun init()
    {
        trace("My first Kotlin Extension!")

        // Add a new Request Handler
        addRequestHandler("sum", this::onSumRequest)
    }

    private fun onSumRequest(sender: User, params: ISFSObject, txType: TransportType)
    {
        // Get the client parameters
        val n1 = params.getInt("n1")
        val n2 = params.getInt("n2")

        // Create a response object
        val resObj: ISFSObject = SFSObject()
        resObj.putInt("res", n1 + n2)

        // Send response back
        send("sum", resObj, sender)
    }
}

Very simple and readable. Note how we're passing a method reference to the addRequestHandler, instead of a class as we did in Java. For the record, his same approach can be used in Java as well, with the same syntax used here, if you prefer.

Important tip:

When exporting the artifact (i.e. the jar file) from IntelliJ pay close attention to whether or not you want to include the Kotlin runtime inside the jar itself.

While this is the most convenient approach, if you intend to deploy multiple Kotlin extensions you will end up packing multiple runtimes which is a waste of space and, more importantly, it will make SmartFoxServer load multiple versions of those libraries, which is a significant waste of memory.

What we recommend is the following:

  • deploy one copy of the Kotlin runtime to extensions/_shared/
  • configure IntelliJ to generate all your Kotlin artifacts without the Kotlin runtime

This will result in SmartFoxServer loading one single Kotlin runtime and use it for all Extensions. To learn more on this topic, see our documentation on class loading.

Scala

We're going to use the same approach we used for Kotlin as well. The difference with Scala is that IntelliJ does not come with a direct support out of the box but a plugin can be easily installed.

Once this is done you can create a new project, link the two libraries used for the Java project (sfs3-core.jar and sfs3.jar) and create your Extension class:

class MyScalaExtension extends SFSExtension
{
override def init(): Unit = {
    trace("My first Scala Extension!")

    // Add a new Request Handler
    addRequestHandler("sum", this.onSumRequest);
}

private def onSumRequest(sender: User, params: ISFSObject, txType: TransportType): Unit = {
    // Get the client parameters
    val n1 = params.getInt("n1")
    val n2 = params.getInt("n2")

    // Create a response object
    val resObj = new SFSObject()
    resObj.putInt("res", n1 + n2)

    // Send response back
    send("sum", resObj, sender)
}
}

We recommend following the same suggestions given for the Kotlin runtime when working in Scala.

Groovy

Groovy is an interesting language that mixes the traits of a scripting with the standard syntax of Java. It is also very flexible and can be used in one of two ways with SmartFoxServer:

  1. as a traditional, compiled language such as Java/Kotlin etc... where you create the project in an IDE, compile your code and bundle it in a jar file

  2. as a scripting language, where you directly deploy the .groovy script file without any compilation or bundling steps

Compiled Groovy

SmartFoxServer 3 already comes with a bundled Groovy runtime (currently version 4.0.26), so it's important that you build your code with a version that is lower or equal to what's provided.

Similar to what we've seen in Kotlin and Scala we'll need to add the usual dependencies (sfs3-core.jar and sfs3.jar) to the project and we should be good to go.

class MyGroovyExtension extends SFSExtension
{
    @Override
    void init() {
        trace("My first Groovy Extension")
        addRequestHandler("sum", this::onSumRequest)
    }

    def onSumRequest(User sender, ISFSObject sfso, TransportType txType) {
        def n1 = sfso.getInt("n1")
        def n2 = sfso.getInt("n2")

        def res = new SFSObject()
        res.putInt("res", n1 + n2)

        send("sum", res, sender)
    }
}

When creating the final .jar file make sure you do not bundle any Groovy runtime, since it's already provided under lib/groovy/ in your SmartFoxServer installation.

Groovy scripts

Writing scripts has a few minor differences because we don't directly inherit from the SFSExtension class, as we do in compiled languages.

import com.smartfoxserver.core.*;
import com.smartfoxserver.entities.data.*;

def init() 
{
    trace("My first Groovy Extension!")

    base.addEventListener(SFSEventType.SERVER_READY, this::onServerReady)
}

def destroy() { }

def onServerReady(evt)
{
    println("Server ready was handled")
}

def handleClientRequest(cmdName, sender, params, txType)
{
    if (cmdName == "sum")
    {
        def n1 = params.getInt("n1")
        def n2 = params.getInt("n2")

        // Create a response object
        def resObj = new SFSObject()
        resObj.putInt("res", n1 + n2)

        base.send("sum", resObj, sender)
    }
}

These are the differences:

  • both an init() and destroy() methods are required in every script
  • it's not possible to register separate request handlers, instead every request is handled by the handleClientRequest() method
  • there are several pre-defined objects that are available to every script in their global scope:
    • sfs: the global SmartFoxServer object
    • api: reference to the main SFS API
    • base: reference to the top level Java wrapper (of type BaseSFSExtension)

In the example above we've also included a listener for the SERVER_READY event, to show how server events can be handled. The event triggers after all Zones have been initialized and the server is ready to receive requests.

Script deployment

Similar to compiled Extension you will need to create a folder under server/extensions/ and save your .groovy scripts there.

Example:

server/
    └── extensions/
        ├── GroovyExt/
        │       └── MyGroovyExtension.groovy
        │       └── PokerGame.groovy
        │       └── ChatRoomExt.groovy
        │       └── etc...
        └── _shared/

Under the Zone Configurator in the AdminTool you will be able to specify which scripts should be used as Extension: Groovy Extension