Quote:
Originally Posted by nick4ever
You're cool, bro. One question, can I ask for any hint for writing a plugin? You made me start being curious, hiz...
|
The concept and core idea of this server is to make the development of plugins easy for developers who either may not have enough experience to mess with the core of the server, or simply those who'd rather focus on content rather than getting involved with the packets and server infrastructure, and most features in the core of the server uses the producer/consumer pattern. This means that plugins can listen for most events in game, and be notified and called when they're executed.
The bootstrap script ideally should never be touched. This script is basically the API wrapper for all plugins, and make them very easy to write. All incoming messages (decoded packet data structures) are stored in the "genesis.messages.impl.incoming" package. For example when a user goes to create a character, the packet is decoded and constructed as a message called CharacterCreationRequestMessage, which contains the creation data, such as the hair, face, height, class, mode, name and so on. Ruby plugins are designed to be able to listen to these messages easily.
We can see the message is called CharacterCreationRequestMessage, so to listen to it, we'd call the on_message procedure, pass the message name in snake_case_form, and then supply the procedure block to execute, such as:
Code:
on :message, :character_creation_request do | account, message |
puts "Character created with the name #{message.get_character_name}"
end
The call "on :message" refers to the "on" function in the bootstrap script, and the type of ":message". The function then passes the parameters to the "on_message" function, this is just to make it nicer and easier to use. The message system is built around the concept of a message chain. That is, multiple plugins can listen for the same message, and process it. This can be stopped by calling "message.terminate" in your plugin, which stops other plugins from processing it, by stopping the message being passed further down the chain.
Character actions are put in an action queue and processed every 30 milliseconds in the game world thread pool (the tick time might be changed later on after further testing). So if we wanted to listen for an npc interaction message and then execute an action based on that, we could do something like this:
Code:
class WarehouseAction < Action
# Override the internal start function, which is called when the action is ticked through and processed
def start
character.open_warehouse
end
end
# Listen for the npc interaction message
on :message, :npc_interact do | account, message |
# If the npc id is that of a Warehouse npc
if message.get_npc_id == 2
# Get the active character for the account
character = account.get_active_character
# Queue the warehouse action
character.do_action(WarehouseAction.new)
end
end
A command is also similar to a message, except rather than listener for a specific message type, we call the command dispatcher instance and tell it to listen for a command identifier. The function accepts 2 parameters, the command name, and, optionally, the minimum privilege level to execute it (for now, these are PLAYER, GAME_SAGE, GAME_MASTER, ADMIN).For example, if we wanted to write a command that listens for "/notice "Hello, world" with a minimum privilege level of game master required, we would do it like this:
Code:
on :command, :notice, PRIVILEGE_GAME_MASTER do | character, command |
# The command arguments (space delimited, except when using quotes, they are grouped into 1 argument) 0 refers to the first element in the command argument array, which would be "Hello, world" (without the quotes)
message = command.get_argument[0]
# Call the sendNotice function in the Scala game world instance (Scala based instance variables are prefixed with a $)
$world.send_notice(message)
end
Remember that messages are passed down a chain, so if we wanted to stop other plugins from listening for the same message after this is execute, we should add "message.terminate" is that block. Does this help clarify how to write plugins? This is all subject to change as it's still largely in a design phase, but it should give you a rough idea. I'll release an in-depth tutorial when the server is released.