// News

Loading…

// Server Instances
Whitelist When enabled, only listed players can join. Others see "Coming Soon!"
Server List MOTD
Line 2 there is automatic — the private server's current minigame or map name.

Loading…

// Instance History

Select this tab to load…

// Users

Loading…

// Your Profile

Loading…

// Minigames

Loading…

+ Add Minigame
// NolensiaScript Docs

NolensiaScript (.ns) is the DSL for writing minigames without Java — parsed once, tree-walked, no compile step, editable straight from Minigames → Add/Edit (type ns). Source of truth: JavaNolensia/NolensiaScriptRuntime/ and nolensiascript/README.md in the repo. Live examples: nolensiascript/*.ns (ForkSpleef, Duels, TowerWars, KingOfTheTowers).

Top-level structure
game <Name> {
    settings { ... }

    on [phase] <event>(<params>) { ... }
    every <n> tick { ... }
    fn <name>(<params>) { ... }    // parsed but not yet callable — see Known limitations
}
Settings
settings {
    difficulty: hard        // peaceful | easy | normal | hard (default: normal)
    nat_regen: false        // natural health regen (default: true)
    mob_spawning: false     // default: true
    gamemode: survival      // survival | adventure (default: adventure)
    block_breaking: false   // default: true
    block_placing: false    // default: true
    saturation: 20          // permanent ambient SATURATION effect, amplifier as int
    min_players: 2          // default: 1
    max_players: 8          // default: 100
    team_mode: false        // default: false
    debug: true             // logs every dispatched event + cancel state (default: false)
}

Unquoted words in a settings value (hard, survival, ...) are read as plain strings — don't quote them.

Handlers — on [phase] event(params) { }

phase is optional and, when present, must be followed by another identifier (the event name) — otherwise the word is parsed as the event name itself. Three phases exist, matching the Java GameConfigurations split:

Phase (default: always)Active windowLifecycle hook on entry
(omitted) → alwaysSTARTING → CLOSINGon start()
prepareSTARTING → ACTIVE
activeACTIVE → CLOSINGon active()

A handler for the same event name can be declared once per phase — e.g. on prepare player_death(player) and on active player_death(player) are independent handlers that never both fire for the same death. Call cancel (bare keyword, or cancel()) anywhere in a handler body to cancel the underlying Bukkit event — it does not stop executing the rest of the handler body, it just flags the result.

Events
EventParamsNotes
startLifecycle hook, fires once when the always adapter activates (STARTING).
activeLifecycle hook, fires once when the active adapter activates (ACTIVE).
player_quitplayer
player_damageplayer, causecause is a string, e.g. "LAVA", "FALL", "ENTITY_ATTACK".
block_breakplayer
food_changeplayer, levellevel is a number 0–20.
item_dropplayer
item_pickupplayer
player_moveplayerFires every tick the player moves — keep handlers cheap.
player_deathplayer, killerkiller is null if not killed by another player (fall, void, environment, ...).
player_interactplayer, is_right_click, item_material, face_locationitem_material is the main-hand item's material name string. face_location is null unless a block was clicked.
player_burnplayer, fire_duration
trident_shootplayer
trident_hittrident, hit_playerhit_player is null if the trident hit a block, not a player.
Timers — every <n> tick { }
every 20 tick {
    game.message("tick!")
}

Runs every n ticks for the lifetime of the match (registered once, cancelled automatically when the match ends). Each timer body's exceptions are isolated — one broken every block logs an error but never stops other timers or itself on future ticks. tick/ticks are interchangeable and optional after the number, but the block itself is required.

Statements
let x = 5
if x > 3 { game.message("big") } else { game.message("small") }
for p in active_players { p.heal() }
cancel
return
return some_value

let declares a variable in the current block scope (shadows outer scopes; for/if bodies get their own child scope). for x in <list-expr> requires the iterable to evaluate to a list (players, active_players, or game.locations(prefix)) — anything else throws. There's no numeric range loop and no list literals — bulk operations like game.trident_rain or game.fill_disk exist precisely because that plumbing doesn't exist at the script level; reach for a game.* bulk helper before trying to hand-roll a loop.

Operators & comments

Arithmetic: + - * / (numbers only, except + also does string concatenation whenever either side is a string). Comparison: == != < > <= >=. Logical: and or not (short-circuiting). Unary minus: -x. Comments are // line comment only — no block comments.

Built-in globals

Rebuilt before every single event dispatch, so they're always fresh: game (the running match), spawn (a Location), players (everyone currently in the match, including eliminated/spectating), active_players (players still alive/active).

game.* methods
MethodReturnsDescription
message(text)Broadcasts to everyone in the match.
sound(name) / sound(name, pitch, volume)Plays a sound to everyone.
remove(player)Removes a player from the active set (eliminates them).
set_winner(player)Marks the winner and ends the match.
location(name)Location or nullA named location from the map config.
locations(prefix)list of LocationAll map-config locations whose key starts with prefix.
quitted_player(player)Marks a player as having quit (vs. eliminated).
is_active(player)boolWhether a player is still in the active set.
apply_kit(player, index)Applies the map's assigned kit at index to the player.
spawn_entity(player, location, type, opts)Spawns an entity from a spawn-egg-style config.
player_team(player)string or nullTeam name, only meaningful when team_mode: true.
delay_item(player, material, ticks)Gives one material item after a delay.
place_block_if_air(location, material)boolPlaces a block only if the target is air.
set_block(location, material)Unconditionally sets a block.
active_count()numberAlso available as the game.active_count property.
state()stringAlso available as the game.state property ("starting", "active", ...).
delay_respawn(player, ticks)Teleports the player back to spawn and sets ADVENTURE mode after a delay.
fill_disk(location, radius, y_offset, material)Fills a flat disk.
clear_disk(location, radius, y_offset, material)Clears (air) a flat disk.
int_conf(key) / int_conf(key, default)numberReads a per-map custom int property.
string_conf(key)string or nullReads a per-map custom string property.
bool_conf(key)boolReads a per-map custom bool property.
destroy_area(location, radius)Destroys blocks in a radius.
elapsed_seconds()numberSeconds since the match went ACTIVE.
add_stat(player, name, amount)Adds to a per-player numeric counter (e.g. a kill count). Same accumulator the orchestrator reads for custom EXP bonuses.
get_stat(player, name)numberReads a per-player counter, defaulting to 0.
has_flag(name) / set_flag(name)bool / —One-shot boolean flags — the idiomatic way to gate a "fire once" block inside a timer (see ForkSpleef's sudden-death warning).
spawn_trident(location, seek)Drops a falling trident; homes in on location if seek is true.
chance(percent)boolpercent in [0, 100]; game.chance(50) is a coin flip.
random_active_player()Player or null
trident_rain(count)Drops count tridents above random active players (skips liquid tiles, each independently seeking or not).
player.* methods
MethodReturnsDescription
heal()
set_velocity(x, y, z)
clear_inventory()
damage(amount)
set_saturation(level)
throw_tnt(fuseTicks)
reduce_hand()Reduces the main-hand item stack by one.
add_item(material) / add_item(material, amount)
add_enchanted_item(material, amount, unbreakable, "NAME:LEVEL,NAME:LEVEL")Enchant string is comma-separated NAME:LEVEL pairs.
teleport(location)
set_gamemode(mode)"survival" | "creative" | "spectator", anything else → adventure.
set_food(level)Alias of set_saturation.
send(text)Sends a chat message to just this player.
effect(type, ticks, amplifier)Applies an ambient potion effect.
is_on_ground()bool
is_flying()bool
name()string
location()Location
block()stringMaterial name of the block the player is standing in.

player also exposes .name, .on_ground, .flying as plain properties (no parentheses) — mostly equivalent to the method forms above.

Location.* and trident.* methods

Location: x(), y(), z(), block() (material name of the block there), and offset(dx, dy, dz) (returns a new Location shifted by a relative block offset — the offset location's x/y/z mirror the origin, only block() reflects the offset position). .x, .y, .z are also available as properties. Trident (inside trident_hit only): location() and remove() — also exposed as the .location property.

Known limitations
  • fn is parsed but not wired up. Declarations are collected by the interpreter but nothing ever looks them up — there's also no call syntax for a bare identifier (myFn(x)) in the grammar, only <object>.<method>(...). Don't rely on fn yet; repeat logic inline or push it into a new built-in if it needs to be shared.
  • No numeric ranges or list literals. Bulk operations are built-in game.* helpers rather than expressible as a script-level loop.
  • return outside of a handler is a no-op that just unwinds the current block — there is no user-callable function to return a value to.
  • Comparing a number to anything other than an identical number/string/boolean via ==/!= always returns false — no numeric-vs-string coercion.
  • A script error thrown inside a handler is logged (with a stack trace if debug: true) and the event resolves as not cancelled — it does not crash the match.
Full example — ForkSpleef.ns
game ForkSpleef {

    settings {
        debug: true
    }

    on start() {
        // Nothing to set up during prepare phase
    }

    on active() {
        for p in active_players {
            p.add_item("TRIDENT")
        }
    }

    every 20 tick {
        if game.elapsed_seconds() >= 150 and not game.has_flag("sudden_death_warned") {
            game.set_flag("sudden_death_warned")
            game.message("Sudden death in 30 seconds!")
        }
        if game.elapsed_seconds() >= 180 {
            if not game.has_flag("sudden_death_started") {
                game.set_flag("sudden_death_started")
                game.message("SUDDEN DEATH!")
                game.sound("entity.lightning_bolt.thunder", 1.0, 1.0)
            }
            game.trident_rain(2)
        }
    }

    on prepare player_death(player) {
        cancel
        game.delay_respawn(player, 1)
    }

    on active player_death(player) {
        cancel
        player.set_gamemode("spectator")
        if game.is_active(player) {
            game.message(player.name() + " has been ELIMINATED!")
            game.remove(player)
            game.sound("entity.player.attack.crit", 1.0, 1.0)
        }
    }

    on active trident_shoot(player) {
        game.delay_item(player, "TRIDENT", 20)
    }

    on active item_pickup(player) { cancel }

    on active trident_hit(trident, hit_player) {
        if hit_player != null {
            cancel
            game.destroy_area(hit_player.location(), 1)
        } else {
            game.destroy_area(trident.location(), 1)
        }
        trident.remove()
    }

    on player_move(player) {
        let block = player.block()
        if block == "WATER" or block == "BUBBLE_COLUMN" {
            player.damage(9999)
        }
    }

    on player_damage(player, cause) {
        if cause == "LAVA" { player.damage(9999); cancel }
        if cause == "FALL"          { cancel }
        if cause == "ENTITY_ATTACK" { cancel }
    }

    on item_drop(player)        { cancel }
    on block_break(player)      { cancel }
    on item_pickup(player)      { cancel }
    on player_burn(player, dur) { cancel }

    on food_change(player, level) {
        player.set_saturation(0)
        cancel
    }

    on player_quit(player) {
        player.set_gamemode("spectator")
        game.message(player.name() + " has been removed from the game!")
        game.quitted_player(player)
    }
}
// Custom Maps
+ Add Map
Map Zip
No file chosen
Resource Pack (optional)
No file chosen
Shared Stats

Loading…

// Events

Loading…

// Punishments

Loading…

// Cosmetics Catalog

Metadata mirror of what's defined in LobbyPaper's Title/Pet/Morph code — bookkeeping only, not read by the game servers. Grant/revoke a specific player's cosmetics from Users → Edit.

Loading…

+ Add / Edit Cosmetic
// Recent Trades

Loading…

// Store Banner

Optional announcement shown at the top of the public store page (e.g. a sale, an event).

// Store Products

These drive both the public store page and checkout — no code deploy needed to add/edit/retire one.

Loading…

+ Add / Edit Product
// Store Purchases

Every completed store checkout, recorded when the Stripe webhook fires. If a purchase doesn't show up here after a successful checkout, the webhook isn't reaching the server — check /var/log/nolensia-store.log and the endpoint's config in the Stripe Dashboard.

Loading…

// Bug Reports

Loading…

// Account Settings

Loading…

Change password
Two-factor authentication

Loading…

// Panel Users

Loading…

Add panel user
// File Manager

Loading…

// Maps
World Files (.slime)

Loading…

Register World

Loading…

// Lobby
Loading…
Plugins
Config (server.properties)
// Database
Maintenance / Cleanup
Clear expired sessions
Purge old instance logs days
Delete inactive players days inactive

Loading…

Select a table.

// Performance

Loading…

// Staff 2FA

Manage in-game /verify TOTP for staff members.

Set up for player
Staff roster

Loading…