NAV
shell java

Introduction

TridentSDK is a team composed of absolute best Java developers from the Bukkit API. Trident is a completely cleanroom reimplmentation of the Minecraft server, which will improve server performance, flexibility, and simplicity.

TridentSDK is the successor to the Bukkit/Spigot projects. Following the news of the “takeover” of the Bukkit project and the DMCA takedown of Bukkit, and therefore Spigot (we are quite aware of binary patches), we realized that someone needs to step up and create the best of server software for Minecraft after the decline of both projects. The result was TridentSDK, the development kit that is implemented by the server, Trident.

How to use the documentation

You may be seeing just annotations in some parts of this document. This is because there are language tabs where you can switch which language the examples are in. Unlisted means that there is no support for the particular language (which are shell and java at the moment). If you aren’t seeing the code, on mobile, open the nav and scroll up the sidebar, then press the other language. If you are on a desktop, on the top right is the language tabs, just click the other one.

Features

Feature Explanation Other servers
Developers We are top-of-the-line developers from the Bukkit API
Community We are active community members and have high-profile connections to other developers
Cleanroom Legally stable, performance-oriented, controlled, simple
Multithreaded Effectively utilizes modern CPUs
Tested Published with results, all aspects and benchmarks + comparison included
Pace Development occurs nearly everyday

Installation

Because TridentSDK is currently a high-paced work in progress, and the reimplementation of the entire Minecraft server is time-taking, there are no builds availible to use. The server does not work at the moment. You will need to download the source and compile off of eachother.

Prestiques

To install Trident, you should have the following installed on your system:

Source download

To download the server source, use:

git clone https://github.com/TridentSDK/Trident.git

To download the API source, use:

git clone https://github.com/TridentSDK/TridentSDK.git

To compile the final jar, use:

cd TridentSDK
mvn clean install

# Unix
mv target/tridentsdk-1.0-SNAPSHOT.jar ..
# Windows
move target/tridentsdk-1.0-SNAPSHOT.jar ..

cd ../Trident
javac -d .. -cp "../tridentsdk-1.0-SNAPSHOT.jar" src/main/java/ src/main/java/net/tridentsdk/server/TridentStart.java
cd ..

After completing the above steps, you should be in the starting directory, with a fully compiled jar that can be run from the command line to start your server.

Troubleshooting

Command not found

TridentSDK is developed primarily on Unix (Ubuntu, Debian) systems. If you get this, look up an alternative to your command on your OS.

If your command starts with git, mvn, or javac, then you either need to install, or reinstall the tools in Prestiques.

Another problem may be that you have not set your PATH environment variables yet. This is covered on the Presitques links.

Paths cannot be resolved

Just edit .. to your primary directory where you executed your git commands at

Where is my jar file?

It’s in the working directory of the command line. For Unix, this is often in your Home directory, and for Windows, this is in the user directory. Therefore, I recommend cd‘ing to your desktop or other place where you know exactly where all the files are going to go and how they are organized.

Updating

We are currently planning an update mechanism built into the server in the future. However, at the current moment, it has not been implemented. Open up your command line and execute the commands shown.

# Remove old files
rm -r Trident
rm -r TridentSDK

# Download again
git clone https://github.com/TridentSDK/Trident.git
git clone https://github.com/TridentSDK/TridentSDK.git

Now go to Source download and use the To compile the final jar steps.

Contributing

On the left, near the bottom, you can see a few links. Currently, there are 3 portions of the project that help would be needed in, 2 which are effective as of now.

First, we will need help with the source. The highest priority right now is player joining, which we have the protocol and world loading facitlities that are mature enough to be used for the purpose. The protocol is very elusive, and if you have used it before, help would be appreciated. Head over to our GitHub page, or tweet to us to find out how to help.

Second, we will need help with documentation. There are two places where documentation is needed - here, and on the source. Javadocs are sparingly done, as forward development and getting performance testing and finishing the server basics is the main priority. We could use some extra hands on source documentation. The documentation here is how to use the API, not what each method does. Head over to the Contribute to the Docs and submit a PR to the index.md on master, and we can recompile the site. Or, head over to the GitHub and submit a PR to add documentation comments to the methods.

Third, and our lowest priority, is commumnity administration. As we do not have a large community yet, you may help by sharing TridentSDK with your friends and users, and later on, when TridentSDK grows, you can apply for administration positions.

API

TridentSDK provides an excellent API for developing server modifications that can be used to modify game play that is determined by the server.

Prestiques

Starting

As well as what is required in the prestiques, it is imperative that you know how to use Java and are familiar with it.

After you have sufficient familiarity with Java, you may checkout our javadocs from the link on the left, but you may want to continue further with the documentation (there are lots of packages!) before starting.

Setup

Getting TridentSDK

git clone https://github.com/TridentSDK/TridentSDK.git
cd TridentSDK
mvn clean install

First, you would want to pull from our TridentSDK repo, and compile it. The dependency can be found in the target folder after running mvn clean install.

Then, start up your IDE, which you should learn how to use from the distributor’s website. If you do not know how to use your IDE (which is used when practicing Java), go back to learning Java. Otherwise, create a new project, and add the TridentSDK dependency to your workspace.

Creating the main class

Extend TridentPlugin

package net.tridentsdk.project;

import net.tridentsdk.plugin.TridentPlugin;

public class Project extends TridentPlugin {

}

Annotate with PluginDescription

package net.tridentsdk.project;

import net.tridentsdk.plugin.TridentPlugin;
import net.tridentsdk.plugin.annotation.PluginDescription;

@PluginDescription(name = "Project", author = "Pierre C", version = "6.9")
public class Project extends TridentPlugin {

}

Use lifecycle methods

package net.tridentsdk.project;

import net.tridentsdk.plugin.TridentPlugin;
import net.tridentsdk.plugin.annotation.PluginDescription;

@PluginDescription(name = "Project", author = "Pierre C", version = "6.9")
public class Project extends TridentPlugin {
    @Override public void onEnable() {
    }

    @Override public void onLoad() {
    }

    @Override public void onDisable() {
    }
}

The first step in development of a Trident plugin is to create the main class. This is where all of your plugin’s functional aspects are initialized and started. Create a new package titled with your domain, backwards. For example, if you own example.com, then your package would be com.example.project, and project can be changed to your project name. This does not affect functionality. If you do not own a domain, you can use your email. For example, if you owned someone@random.com, then use com.random.someone. If you have a - character, use an underscore (_) instead.

Then, create a new Java class, which is named your project name. The class name does not affect functionality. After creating it, you would extend TridentPlugin, where TridentPlugin is net.tridentsdk.plugin.TridentPlugin.


Then, you would add a PluginDescription annotation to your class to mark the load parameters.

Of course, replace the name with the name of the project (does not affect functionality), the author with your name/username/online name/alias (does not affect functionality), and the version with the plugin version (does not affect functionality).

In reality, if this is a quick test plugin, only the name field is required to be filled.


TridentPlugin provides several methods that are executed during various events in a plugin lifecycle. They are called in this order: onLoad, onEnable, onDisable. onLoad is called after the Plugin object is created. onEnable is called when the plugin is enabled and starts to be provided with runtime dependecies and after class load and initialization. Finally, onDisable is called when the plugin is disabled, because of an error or because of a server shutdown.

These methods are overridden to execute the given actions during the lifecycle events. They are not required, and if you don’t need them, you should not override them, as that would not make sense.

Commands

Implementing a proper command

package net.tridentsdk.project;

import net.tridentsdk.api.entity.living.Player;
import net.tridentsdk.plugin.cmd.Command;
import net.tridentsdk.plugin.cmd.CommandIssuer;
import net.tridentsdk.plugin.cmd.ConsoleSender;

public class Project extends Command {
    @Override public void handlePlayer(Player player, String arguments, String alias) {
    }

    @Override public void handleConsole(ConsoleSender sender, String arguments, String alias) {
    }

    @Override public void handle(CommandIssuer sender, String arguments, String alias) {
    }
}

Commands are implemented by subclassing the Command class, at net.tridentsdk.plugin.cmd.Command. In order to handle commands, there are 3 provided methods to check for commands. They are handle, handlePlayer, and handleConsole. At the time of writing, TridentSDK does not support Command Blocks. The handle method handles all commands that are passed to it. handlePlayer only handles player executed commands, and handleConsole only handles console commands.

The first parameter passed to the handle method is the issuer of the command. This can be a player or console in handle, which is checked by using instanceof for the Player or ConsoleSender classes. The second parameters is args, which is unused at the time. Finally, the alias is the command executed, the first word after / in the command box. Each has its documentation at the respective classes.

Events

This is a proper event listener

package net.tridentsdk.project;

import net.tridentsdk.api.event.entity.EntityDeathEvent;
import net.tridentsdk.api.event.Listener;

public class Project implements Listener {
    public void onEvent(EntityDeathEvent event) {
    }
}

This is a proper way to prioritize events:

package net.tridentsdk.project;

import net.tridentsdk.api.event.entity.EntityDeathEvent;
import net.tridentsdk.api.event.Call;
import net.tridentsdk.api.event.Importance;
import net.tridentsdk.api.event.Listener;

public class Project implements Listener {
    @Call(Importance.LOWEST)
    public void onEvent(EntityDeathEvent event) {
    }
}

Implementing ignorance

Event event = new EntityDeathEvent(/* Your values */);
EventManager.call(event);
if (event.isIgnored()) {
    // Do what you need to do if the event is cancelled
} else {
    // Proceed
}

Ignoring events

package net.tridentsdk.project;

import net.tridentsdk.api.event.entity.EntityDeathEvent;
import net.tridentsdk.api.event.Listener;

public class Project implements Listener {
    public void onEvent(EntityDeathEvent event) {
        event.cancel(true);
    }
}

Unlike the Bukkit event system, the Trident events are automatically registered. You should not explicity register your events.

In order to use this, the requirements for an event method is to have a public void method in a class implementing Listener, where Listener is net.tridentsdk.api.event.Listener, and the method must have a single parameter that is superclassed by Event.

Events are usually self-descriptive by class name, for example, EntityDeathEvent occurs when an entity dies, of course, it must be a LivingEntity.

Events can be removed from the event caller list using EventManager#unregister(Listener), where Listener represents an instance of the listener that was registered into the list.


Event priorities are annotated by a @Call annotation, with an Importance parameter.

Importance events are called in this order: LOWEST, LOW, MEDIUM, HIGH, HIGHEST. This occurs because the most important events must edit what the other plugins have set to the event. If 2 events have the same priority, then they are registered by which plugin loads first. If the event does not have a @Call annotation, then the importance is automatically set to Importance.MEDIUM.

Importance is valuable in APIs, where you’d want to check in with events, but not edit them, and allow other plugins to view event modifications made by the API, as well as providing higher level plugins to have control over the event result.


Events that implement Cancellable where Cancellable is net.tridentsdk.api.event.Cancellable can be used to prevent and implement cancellation functionality. To provide cancellation functionality for your event, simply implement Cancellable, overriding the two new methods to modify a boolean field, which is checked when the event is called. Events are called using EventManager#call(Event). After calling, check the event object using isIgnored(), which returns true if the event should be cancelled.

For the API user, you can choose to have the event do it’s functionality specified in the cancellation policy by calling Cancellable#cancel(boolean), where the boolean is true if the event should be cancelled, or false to prevent the event from being cancelled by lower priority listeners. You can check if the event has been cancelled before it reached the current listener using Cancellable#isIgnored(), which returns true if the event should be ignored.

Configuration

Correctly creating a configuration file, or multiple.

import net.tridentsdk.api.config.JsonConfig;
import net.tridentsdk.api.factory.Factories;

public class Project extends TridentPlugin {
    private final JsonConfig config =
            Factories
            .configs()
            .createConfig(this.getConfigDirectory() + "/configuration.json");
    private final JsonConfig anotherConfig =
            Factories
            .configs()
            .createConfig(this.getConfigDirectory() + "/players.json")
}

Deleting your file

try {
    Files.delete(config.getPath());
} catch (IOException e) {
    e.printStackTrace();
}

Reloading the configuration

config.reload();

Proper JSON format for TridentSDK

{
    // Normally you can't have comments
    // but we parse without them
    // use // comments in your configs

    "here is a key": "this is a string value",
    "append , after key-value": 10,
    "you can have doubles too": 0.6,

    "this is a section": {
        "it has more key-values": 10,
        "you can also have lists": 10
    },

    "this is a list": [
        "there are values in lists",
        "they have brackets instead of braces",
        "and you still use commas to separate entries"
    ],

    "usually you want no spaces in your keys": 10,
    "ThisIsAKey": 10,
    "your last entry doesn't have a comma": "enjoy using json"
}

The TridentSDK configuration is similar to that of the Bukkit configuration, however, we use JSON instead of YAML. JSON has a slightly more complex syntax than YAML, however, it does not have the same constraints (such as anti-tabs, spacing, etc).

Each TridentPlugin has a configuration directory to hold files the plugin may have. This can be accessed using TridentPlugin#getgetConfigDirectory(), returned as a File. This file is actually a folder.

A configuration file can be created, or retrieved using the ConfigFactory#createConfig(...) method, from Factories#configs(). More information about Factories can be found in the section below. If this config exists, the data will be loaded into the returned JsonConfig object. Else, it will be created for you. The path is preferred to have a .json file type.

If you do not want your configuration, it can be deleted at the path specified in the JsonConfig.

Once the JsonConfig constructor is invoked, the object will automatically reload and store the configuration to memory. If the config is changed externally, such as by opening with a text editor, then you must reload yourself.

Getting and setting

Config manipulation is handled at ConfigSection, where get/set for all primitives, lists, objects, BigInteger, and BigDecimals in the config is supported. There is also contains(), parent/child section access, and programatic access to the JSON object form of the config, and saving capability. Trident parses without comments, so you can have // comments without a problem. The technical documentation for JSON can be found at the Engineering Task Force website. However, most developers can get by with the example provided on the right.

Factories

The TridentSDK factories API provides a creational access to the major components of TridentSDK, especially implemetation-specific facilities. Classes that relate to collections, configuration, executors, tasks, threads, and reflection can be accessed from Factories. Factories replaces the important methods from the original Bukkit class with a modular, easy to understand and access API.

Factories is thread-safe. Initialization occurs before any access is done to the Factories class, as well as guarding by HeldValueLatch, which blocks until the main thread initializes all accessors inside of Factories.

Here is a list of all facilities provided by Factories:

Benchmarks

TridentSDK is heavily benchmarked. Some of the most performance critical classes, such as the reflection API, scheduling, events, and caching are benchmarked inside the Trident test package.

This is a list of the currently benchmarked classes:

All benchmarks have a baseline benchmark at 0, called control. The traditional benchmark, before the update, used straight pounding on the data structure that was being benchmarked. In a realistic situation, this does not happen. Thankfully, we used JMH, which is feature packed with everything we needed to performance test our classes.

One of the features was the Blackhole class, which is used in the benchmarking infrastructure to prevent optimization of the harness. Then, there was Blackhole.consumeCPU(int). This is used to amoritize the test pounding on the harness, which was used in the tests to improve realism and accuracy of the calcuations.

The graphs were a result of 4 hours of work to experiment with Google Charts’s excellent API. However, there were a couple things that may seem to be off at first. The primary thing you’d see is that there are negative values. How is this possible? Let’s begin by examining how the charts are generated from the data.

The charts are generated by running Benchmarks.chart(Benchmarks.parse(Collection<RunResult>, String). The parse method iterates through the results to find the times and label of the benchmarks. All benchmarks are run with a control group, which measures the CPU cycles taken up by the amoritization method discussed above. Since each harness runs the consumption method, we have the actual benchmark PLUS the cost of running the amoritization. Therefore, we plot the data by subtracting the amoritization cost from the total benchmark. HOWEVER, this is not totally accurate. The amoritization harness runs independently of the other harnesses, therefore, it may be larger or smaller than the benchmarked value of the particular harness.

Secondly, you may see that if you run the method several times, you’d get different colors (yay). This WILL NOT affect the data produced by the graph. The colors are randomized when generated, so that will not comprimise the integrity or the accuracy of the data gathered during the test.

Third, you might also see that control is a straight line… Yes, it is a straight line if you look at the URL data. Control, again, is the amoritization cost, which is subtracted by the amoritization cost when plotted… So the amoritization cost minus the amoritization cost is 0.


If you’d like to use Benchmarks for your own benchmarks, please note the Apache License, and BenchmarkTemplate can be used as a starter for your benchmarks. Benchmarking requires a large amount of knowledge on how the Java technologies (GC, JIT, JVM, etc) work. I am not liable for any damages caused by the results I have posted or generated from my class. They are meant for reference use only.

Reference article: Nanotrusting the Nanotime, by Aleksey Shipilёv

How to read the charts

The charts are in nanoseconds / operation on the Y axis, which corresponds with the amoritization cycles for that harness point. 1024 cycles represents light server load, while 1 represents heavy server load. The data for the selected item should fit the load capacity of the server, and the majority of the fastest option. TridentSDK maintains responsiveness by allowing slow registry trade for fast runtime. Lower ns/op is better.

Why benchmarking matters

The past generation server software, and designs based on them, such as Sponge/Forge/NMS based servers are unable to multithread their servers because NMS limits the ability to exploit multiple processors. Servers are plagued with “Cannot keep up” warnings, which result in too many tasks being executed for the given time in a server heartbeat. In order to reduce these warnings, TridentSDK is implemented cleanroom, breaking free of the multithreading bonds to fully use the processor efficiently, and reduce the task execution time to fit more in a single tick.

These tests compare performance of a particular class to previous generation software or library classes (that may or may not have the same semantic functionality as the benchmark comparison). They also help debug classes, as performance issues relate to bugs in the code, or inefficient/over-defensive programming.

Benchmarking also helps make decisions with which option to use, and how well it performs compared to other pieces of software. TridentSDK, is of course, performance-focused.