Titan is a complete Minestom-based Minecraft lobby server that provides various quality-of-life features to enhance player experience. It contains everything needed to run a fully functional Minestom server.
- Sitting System: Allows players to sit on specific blocks like stairs
- Tickle Mechanic: Players can tickle each other using feathers, with cooldown periods
- Elytra Boost: Provides boost functionality for players using elytra
- Height Teleportation: Automatically teleports players when they exceed certain height limits
- Java 24 or higher
- Download the latest release from the releases page
- Run the server using:
java -jar titan-x.x.x.jar - The server runs with every module's shipped defaults if no configuration file is present; copy
application.example.yamlfrom the distribution (next to the jar) toapplication.yamland edit it to customize (see Configuration below)
Once installed, you can:
- Start the server with additional memory:
java -Xmx2G -jar titan-x.x.x.jar - Use the console to manage the server while it's running
- Stop the server safely by typing
stopin the console
You can configure server properties like port, MOTD, and more in the generated configuration files.
Configuration lives in application.yaml in the lobby's working directory (next to the jar), one
named section per lobby feature module, keys following the <module-id>.<field> schema. The
shipped defaults live inside the jar; a commented application.example.yaml listing every section
and key with its default also ships in the distribution, next to the jar - copy it to
application.yaml and edit only the values that should differ. A missing file, a missing section
or a missing key falls back to the shipped default, listed below. The lobby never creates or writes
a configuration file itself.
A profile file application-<profile>.yaml, next to application.yaml, only needs to set the keys
that differ for that profile - everything else still comes from the base file. Activate one or more
profiles with the environment variable AVAJE_PROFILES (e.g. AVAJE_PROFILES=dev) or the system
property -Davaje.profiles=dev. Every start logs the active profiles at INFO:
Active configuration profiles: [...].
An external file can be layered in via the environment variable CONFIG_FILE or the system
property -Dconfig.file=... (e.g. for a Kubernetes ConfigMap or a CloudNet template file outside
the working directory).
Every key can also be set directly via an environment variable or a system property. Rank order, low to high:
- the shipped default,
application.yaml,- the active profile's
application-<profile>.yaml, - the external file selected via
CONFIG_FILE/config.file, - an environment variable,
- a system property (
-D...).
An environment variable's name is the dotted key, upper-cased, with . replaced by _ and -
dropped - e.g. spawn.simulationDistance becomes SPAWN_SIMULATIONDISTANCE, and the matching
system property is -Dspawn.simulationDistance=.... A list value is set as a single
comma-separated value via an environment variable or system property, e.g.
SIT_ALLOWEDBLOCKS=minecraft:oak_stairs,minecraft:spruce_stairs.
A navigator entry is a named map entry rather than a plain record field, so its keys follow the
pattern NAVIGATOR_ENTRIES_<NAME>_<FIELD>, <NAME> being the entry's map key, upper-cased - e.g.
navigator.entries.survival.destination becomes NAVIGATOR_ENTRIES_SURVIVAL_DESTINATION.
An invalid value - from application.yaml, a profile or an override - aborts startup with a
message naming the full key (<module-id>.<field>) and the reason (e.g. a negative cooldown, or
spawn.minHeight not less than spawn.maxHeight). An unknown or misspelled key is no longer
reported - it is silently ignored, and the lobby starts using the shipped default for that key.
spawn:
minHeight: -64
maxHeight: 310
simulationDistance: 2
sit:
offset:
x: 0.5
y: 0.25
z: 0.5
allowedBlocks:
- minecraft:spruce_stairs
tickle:
cooldownMillis: 4000
elytra:
burnDurationTicks: 30
cooldownTicks: 40
navigator:
title: "<yellow>Navigator"
entries:
elytrarace:
slot: 0
icon: minecraft:elytra
displayName: "<!i><gradient:#fcba03:#03fc8c>ElytraRace</gradient>"
destination: ElytraRace
survival:
slot: 4
icon: minecraft:grass_block
displayName: "<!i><green>Survival"
destination: Survival
slender:
slot: 5
icon: minecraft:enderman_spawn_egg
displayName: "<!i><gradient:#616161:#e80000c>Slender</gradient>"
destination: cygnus
feature: NAVIGATOR_SLENDER
creative:
slot: 8
icon: minecraft:wooden_axe
displayName: "<!i><rainbow>Creative</rainbow>"
destination: MemberBuildspawn.minHeight/spawn.maxHeight: height bounds a player is teleported back to spawn outside ofspawn.simulationDistance: simulation distance sent to a player on spawn - also the only key the setup server reads (see "Setup server" below)sit.offset: offset from the clicked block's position to the seat (x, y, z)sit.allowedBlocks: block keys players may sit down on, e.g.minecraft:spruce_stairstickle.cooldownMillis: duration of the tickle cooldown in millisecondselytra.burnDurationTicks: how many ticks a lit firework rocket boosts a flying player for - the boost itself is Vanilla's own client-side firework impulse (ported from Voyager'sFireworkBoostTracker/Rockets), not a server-applied velocity, so there is no multiplier to configureelytra.cooldownTicks: how many ticks after a boost starts before the player may use another rocket; must be strictly greater thanelytra.burnDurationTicks, since it is measured from the burn's startnavigator.title: the shared navigator inventory's title, as a MiniMessage stringnavigator.entries: a map of the navigator's destinations, keyed by a unique name (e.g.survival) so a profile or an override can change a single entry without repeating the others; each entry has a hotbar-chest slot (0-8), an icon material key, a MiniMessage display name and the CloudNet task name a click delivers the player tonavigator.entries.<name>.feature(optional): the name of aTitanFeaturesfeature flag this destination is gated behind, e.g.NAVIGATOR_SLENDER. Omitted, the destination is always visible. A name Togglz does not recognize aborts startup with a message namingnavigator.entriesand the unknown name. A flag missing fromflags.propertiescounts as off - Slender, for example, stays hidden untilNAVIGATOR_SLENDERis explicitly turned on. Toggling a flag takes effect the next time a player opens the navigator, with no restart.
| Key | Environment variable |
|---|---|
spawn.minHeight |
SPAWN_MINHEIGHT |
spawn.maxHeight |
SPAWN_MAXHEIGHT |
spawn.simulationDistance |
SPAWN_SIMULATIONDISTANCE |
sit.offset.x |
SIT_OFFSET_X |
sit.offset.y |
SIT_OFFSET_Y |
sit.offset.z |
SIT_OFFSET_Z |
sit.allowedBlocks (comma-separated) |
SIT_ALLOWEDBLOCKS |
tickle.cooldownMillis |
TICKLE_COOLDOWNMILLIS |
elytra.burnDurationTicks |
ELYTRA_BURNDURATIONTICKS |
elytra.cooldownTicks |
ELYTRA_COOLDOWNTICKS |
navigator.title |
NAVIGATOR_TITLE |
navigator.entries.<name>.slot |
NAVIGATOR_ENTRIES_<NAME>_SLOT |
navigator.entries.<name>.icon |
NAVIGATOR_ENTRIES_<NAME>_ICON |
navigator.entries.<name>.displayName |
NAVIGATOR_ENTRIES_<NAME>_DISPLAYNAME |
navigator.entries.<name>.destination |
NAVIGATOR_ENTRIES_<NAME>_DESTINATION |
navigator.entries.<name>.feature |
NAVIGATOR_ENTRIES_<NAME>_FEATURE |
<NAME> is the entry's map key, upper-cased - e.g. navigator.entries.survival.destination
becomes NAVIGATOR_ENTRIES_SURVIVAL_DESTINATION. The default entries are elytrarace,
survival, slender and creative.
app.json is no longer read or converted. Before upgrading a server that still has one, either
start the previous release once - it switches app.json over to application.yaml on its own, as
described in that release's docs - or transfer the values by hand into a new application.yaml
(same sections and keys as before). A leftover app.json or app.json.migrated next to the jar is
ignored and does not affect startup; once application.yaml is in place, either file can be
deleted.
The setup server no longer edits configuration - the /setup app ... commands have been removed.
It only reads spawn.simulationDistance (default 2) from the same configuration.
A CloudNet template, a Docker image or a Kubernetes deployment delivers application.yaml (or an
external file referenced via CONFIG_FILE) into the working directory and sets AVAJE_PROFILES
for the environment it runs in.
- Clone the repository
- Build using Gradle:
./gradlew clean build
Run tests using:
./gradlew test
Code coverage reports are generated using JaCoCo and can be found in build/reports/jacoco/.
A lobby feature is a self-contained package under
app/src/main/java/net/onelitefeather/titan/app/feature/<name>/, discovered automatically by
Avaje Inject - there is no central module list to edit:
- New package, copied from the template module at
app/src/test/java/net/onelitefeather/titan/app/feature/example/(ExampleModuleand friends). - The
<Name>Moduleclass implementsLobbyModuleand carries@jakarta.inject.Singletonplus a unique@io.avaje.inject.Priority(n)- ascending priority is start order, the seven existing modules use gaps of 100 (protection 100, spawn 200, respawn 300, navigator 400, sit 500, tickle 600, elytra 700). Missing either annotation fails the build (ArchUnit), not just the running lobby. - Dependencies (a platform service such as
Deliver, anInstance, aClock, ...) are requested through the constructor;@jakarta.inject.Injectis only needed on a constructor when the class has more than one. A brand-new shared platform service is added as another@Beaninapp/src/main/java/net/onelitefeather/titan/app/bootstrap/PlatformBeans.java, or, if it carries feature-spanning logic of its own rather than wrapping a platform type, as its own@Singletonclass. - Zero changed lines outside the new package - except a brand-new shared platform service, which
necessarily touches
PlatformBeans. - A dependency nothing provides fails the build or the start, naming the missing type, instead of the lobby quietly running without that module.
- The actual start order is visible at runtime in one INFO log line:
Lobby modules enabled in order: {}.
See docs/lobby-modules.md (German) for the full walkthrough - module
anatomy, ModuleContext dock points, tick-thread rules, test setup with ModuleHarness, the
ArchUnit rules, and a copyable template module with its tests.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
Developed by OneLiteFeather Network.