RS Mod is an OSRS framework made in Kotlin. The server started as a personal side-project, but then we had an idea of making it into more than just that. We will be providing a platform/community where both developers and server owners can benefit!
- Why Kotlin instead of Java?
Java is by far more popular in the RSPS scene, however Kotlin not only removes a lot of the boilerplate that comes with Java, but has a lot of other features which lack in native Java, including
Coroutines, giving us the ability to suspend/"pause" code.
- What is this platform you mentioned?
The basic idea of the platform is similar to Minecraft's Bukkit. We'll have a marketplace that developers can sell plugins and server owners can purchase them. Some info can be found near the media section of this post.
- Will I need to use this platform if I want to use the RS Mod server?
Of course not! RS Mod will be available from our platform, but will also be publicly released on GitHub (the link will be posted here once I have had some feedback on it from others).
- Can you show some examples of a plugin?
Before I show the code, I do have to mention that plugins use KotlinScript, which may seem weird at first, but it does decrease the amount of code you would have to write to simply register a plugin.
Searching bookcases:
Speaking to Abbot Langley in Edgeville Monastery
Since RS Mod tries its best to completely decouple content from one another, we're able to completely remove most content without affecting any other content! This of course has its limitations since some content directly communicate with one another, such as combat needing to get prayer information from the player. If you simply want a blank source with no content, you can easily remove all the content and not have to worry about any of the core features such as walking, logging in, etc!
I will be providing basic content including all content inside a single city (Edgeville) and 6-8 basic skills on the basic server, here are some features that are working:
- Other "interesting" features
Packet structures
Since packet structures change every revision for OSRS, I implemented a way to load the structures externally so that developers have the convenience of changing structures in just one file instead of 20-40 different files.
An example of a packet structure is as followed; note: it's currently being loaded using yml simply because I had the game configs loaded through yml and wanted to keep it constistent, but I do want to change the file format. Been suggested to use dsl, but still thinking about the final decision
Code:
- message: gg.rsmod.game.message.impl.LocAddChangeMessage
type: FIXED
opcode: 6
structure:
- name: tile
type: BYTE
trans: ADD
- name: settings
type: BYTE
trans: SUBTRACT
- name: id
type: SHORT
trans: ADD
The values are read from top to bottom in the structure so in this example "tile" is read first as a Byte with a transformation of "ADD" (readByteA)
Coroutines in content
Thanks to Kotlin's Coroutines, we are able to completely remove the concept of (what is known commonly in RSPS as) "Tasks" or "Events". We can pause code for as many ticks as we would like as long as we mark the code as "suspendable". I did show an example above for bookcases and for dialogs, but we will post another example:
Code:
package gg.rsmod.plugins.content
on_login {
player.queue { // We use the queue method so that we can use suspend methods such as ``wait``
player.message("Waiting to send an important message...")
wait(2) // Wait 2 game cycles (1200 ms)
player.message("Welcome to our server.")
wait(1) // Wait 1 game cycle (600 ms)
player.message("Remember to have fun!")
}
}
Attribute System We have a unique attribute system that provides type-safety & persistence (if required). Not only that, but they can be defined in a plugin itself and do not need to be defined explicitly in the game module. For example:
Code:
package gg.rsmod.plugins.content
val AN_ATTRIBUTE = AttributeKey<Int>() // Attribute will reset on logout
val A_PERSISTENT_ATTRIBUTE = AttributeKey<Boolean>("unique_attribute_name") // Attribute will save on logout
on_command("set_attribute") {
player.attr.put(AN_ATTRIBUTE, 1)
player.attr.put(A_PERSISTENT_ATTRIBUTE, true)
}
on_command("get_attribute") {
val attribValue = player.attr.get(AN_ATTRIBUTE)
val persistentAttribValue = player.attr.get(A_PERSISTENT_ATTRIBUTE)
player.message("Attribute value: $attribValue")
player.message("Persistent value: $persistentAttribValue")
}
Timer System
The timers system allows us to assign a timer to an entity, which will then invoke the respective timer plugin (if one is found) once the timer hits a time value of 0. Each time value is equal to a single game cycle (600 ms). Similar to the Attribute System, timers are defined in the plugin itself. For example:
Code:
package gg.rsmod.plugins.content
val A_TIMER = TimerKey()
on_login {
player.timers.put(A_TIMER, 1) // We need to start the timer with a time value of 1
}
on_timer(A_TIMER) {
player.message("Timer has hit a time of 0!")
}
Acknowledgements
Join our brand new discord by clicking the icon below