Documentation · python-like scripting

AdvancedScriptsMod

A full Python-like scripting language embedded directly into your Minecraft Forge 1.20.1. Write scripts with variables, loops, functions, imports, and deep access to live server data, note: this is a BETA so all the features here may not work or not yet implemented.

📄Overview

Scripts are plain-text .script files placed inside the scripts/ folder at the root of your server directory. Each script runs on its own background thread, so blocking calls like wait() never stall the server. Subdirectories are supported and are used for import paths.

Script Folder

server/scripts/
Subfolders map to import paths. e.g. utils/math.scriptimport utils.math

Threading Model

Every script runs in its own daemon thread. Commands and queries are dispatched to the server thread via a CountDownLatch, keeping both threads safe.

🏷Annotations

Annotations must appear as the very first non-blank, non-comment line of the file.

AnnotationBehavior
@PREPAREAuto-executed at server start and on /scripts reload.
@LIBRARYWhen imported, only def definitions and top-level variable assignments are exported. Commands and print() calls are skipped.
@RUNWhen imported, the entire top-level code runs. Overrides the @LIBRARY default.
@PREPARE
# This script runs automatically at server start
print("Server ready!")
global_var = 42
Note: A file with no annotation behaves like @LIBRARY.

🔣Types & Variables

TypeLiteral ExampleNotes
int42, -7Java Integer or Long
float3.14Java Double
str"hello", triple-quotedEscape sequences \n \t \r \\ \" \'
boolTrue, FalseCase-sensitive
NoneNoneNull value
list[1, "a", True]Mixed types, mutable, 0-indexed, negative indexing
dict{"key": value}String keys only
x = 10
x += 5
items = [1, 2, 3]
items[0] = 99
data = {"hp": 20}
data["hp"] = 18

Operators

CategoryOperatorsNotes
Arithmetic+ - * / // % **// floor div, ** power. + concatenates lists and strings.
Comparison== != < > <= >=Double precision
Logicaland or notShort-circuit evaluation
Membershipin not inlist, dict (by key), str
Unary- +

🔀Control Flow

if / elif / else

if hp < 5:
    print("critical!")
elif hp < 10:
    print("low health")
else:
    print("fine")

for

for i in range(5):
    print(i)
for name, score in [["Alice", 10], ["Bob", 5]]:
    print(name, score)

while / break / continue / pass

count = 0
while count < 3:
    count += 1
    wait(20)

📦Functions

def greet(player, msg="Welcome!"):
    /say {player}: {msg}

def add(a, b):
    return a + b

result = add(3, 4)   # 7
First-pass scan: All def statements are registered before any code executes.

🪄Macros (@macro)

A macro runs inside the caller's scope. Variables assigned inside a macro are set directly on the caller.

@macro
def GIVE_DIAMOND(player):
    /give {player} minecraft:diamond 64
    gave_diamond = True

GIVE_DIAMOND("Steve")
print(gave_diamond)   # True

📥Import System

SyntaxResult
import utilsBind module to utils. Call via utils.greet()
import utils as uAlias as u
import path.to.libLoads scripts/path/to/lib.script
from utils import *Inject all into current scope
from utils import greet as hi, MAX as LIMITNamed aliases

🔧Preprocessor (#define)

#define MAX_HP 20
#define SPAWN_MSG "Spawning entity now"
hp = MAX_HP        # becomes: hp = 20
print(SPAWN_MSG)

💬F-Strings

name = "Steve"
hp   = 18
msg  = f"Player {name} has {hp} HP ({hp / 20 * 100}%)"
/say Hello, {name}!
/give {name} minecraft:apple {hp * 2}

🌐Scope & global

counter = 0
def increment():
    global counter
    counter += 1
increment()
print(counter)   # 1

🔩Built-in Functions

FunctionSignatureReturns
printprint(*args)Logs to server console with [Script] prefix
waitwait(ticks)Pauses script. 20 ticks = 1 second. Interruptible.
lenlen(obj)int
strstr(val)Convert to string
intint(val)Convert to integer
floatfloat(val)Convert to float
boolbool(val)Truthiness as boolean
absabs(n)Absolute value
minmin(*args) or min(list)Minimum
maxmax(*args) or max(list)Maximum
roundround(n, digits=0)Rounded number
sumsum(list)Sum of all numeric items
rangerange(stop) / range(start, stop, step)list of integers
listlist(iterable?)New list
dictdict()New empty dict
typetype(val)"int", "float", "bool", "str", "list", "dict", "module"
sortedsorted(list)New sorted list
reversedreversed(list)New reversed list
enumerateenumerate(list, start=0)List of [index, item]
zipzip(list1, list2)List of [a, b] pairs
hasattrhasattr(module, name)bool
getattrgetattr(module, name, default)Module member or default
inputinput()Always ""

📋List Methods

MethodDescription
list.append(val)Add item to end
list.extend(other)Add all items from another list
list.insert(i, val)Insert at index
list.remove(val)Remove first occurrence
list.pop(i=-1)Remove and return item at index
list.clear()Remove all items
list.sort()In-place numeric sort
list.reverse()In-place reverse
list.copy()Shallow copy
list.count(val)Count occurrences
list.index(val)Index of first occurrence or -1

📂Dict Methods

MethodDescription
dict.get(key, default)Get value or default
dict.keys()List of all keys
dict.values()List of all values
dict.items()List of [key, value] pairs
dict.pop(key)Remove and return value
dict.update(other)Merge another dict
dict.clear()Remove all entries
dict.copy()Shallow copy
dict.setdefault(key, val)Set and return if key absent
dict.has_key(key)Boolean key existence check

🔤String Methods

MethodDescription
str.upper() / str.lower()Case conversion
str.strip() / str.lstrip() / str.rstrip()Trim whitespace
str.split(sep)Split on separator. Returns list.
str.join(list)Join list items with separator
str.replace(old, new)Replace all occurrences
str.startswith(prefix)bool
str.endswith(suffix)bool
str.contains(sub)bool
str.find(sub)First index of substring, or -1
str.count(sub)Number of non-overlapping occurrences
str.format(*args)Replace {} placeholders in order
str.zfill(width)Zero-pad to width
str.isdigit()bool
str.isalpha()bool
str.isalnum()bool

🗃Database Functions

The script engine includes a lightweight JSON-based key-value store. Each table is a separate file saved at scripts/data/<table>.json. No SQL, no setup — just read and write values by key.

Persistence: Data survives server restarts. Files are created automatically on first write. Supported value types: int, float, str, bool, dict, list, None.
FunctionSignatureReturns
db_setdb_set(table, key, value)None — write or overwrite a key
db_getdb_get(table, key, default?)Stored value, or default (None) if key absent
db_existdb_exist(table)bool — table file exists on disk
db_hasdb_has(table, key)bool — key exists in table
db_keysdb_keys(table)list[str] — all keys in the table
db_alldb_all(table)dict — full table as a dict
db_deletedb_delete(table, key)None — remove a single key
db_cleardb_clear(table)None — empty the table (file kept)
db_tablesdb_tables()list[str] — names of all existing tables
db_dropdb_drop(table)bool — delete the table file from disk

Basic usage

# Write values
db_set("players", "Steve_kills", 42)
db_set("players", "Steve_coins", 100)

# Read values (with optional default)
kills = db_get("players", "Steve_kills")          # 42
coins = db_get("players", "Steve_money", 0)       # 0  (default)

# Existence checks
db_exist("players")                                  # True
db_has("players", "Steve_kills")                    # True

# Enumerate
print(db_keys("players"))                            # ["Steve_kills", "Steve_coins"]
print(db_all("players"))                             # {"Steve_kills": 42, "Steve_coins": 100}
print(db_tables())                                    # ["players"]

# Cleanup
db_delete("players", "Steve_kills")
db_clear("players")                                   # empties the table
db_drop("players")                                    # deletes the file

Example — coin economy

@PREPARE
while True:
    for name in player_list():
        if not db_has("economy", name + "_coins"):
            db_set("economy", name + "_coins", 0)
        coins = db_get("economy", name + "_coins", 0)
        db_set("economy", name + "_coins", coins + 1)
        /tell {name} §6+1 coin! Total: {coins + 1}
    wait(6000)   # 5 minutes
Concurrency note: Each db_set / db_get reads and writes the JSON file synchronously. If two scripts write the same table simultaneously, last write wins. Keep writes coarse-grained for high-frequency updates.

Server Commands

Any line starting with / is executed as a server command (permission level 4). Expressions inside {} are evaluated before execution.

player = "Steve"
/gamemode creative {player}
/give {player} minecraft:diamond_sword 1
/tp {player} 0 64 0
Thread-safe: Commands are dispatched to the server thread. Timeout is 10 seconds.

🧍Player Functions

Presence & Count

FunctionReturns
player_online(name)bool
player_list()list[str] — all online player names
player_count()int

Stats

FunctionReturns
player_health(name)float
player_max_health(name)float
player_hunger(name)int (0–20)
player_saturation(name)float
player_level(name)int
player_xp(name)float (0.0–1.0)
player_total_xp(name)int
player_gamemode(name)"survival" / "creative" / "adventure" / "spectator"

Position & Rotation

FunctionReturns
player_x(name) / player_y(name) / player_z(name)float
player_pos(name)[x, y, z]
player_yaw(name) / player_pitch(name)float
player_dimension(name)"overworld" / "the_nether" / "the_end"

State Flags

FunctionReturns
player_flying(name)bool
player_sneaking(name)bool
player_sprinting(name)bool
player_on_ground(name)bool
player_in_water(name)bool
player_is_op(name)bool

Inventory

FunctionReturns
player_inventory(name)List of {"slot", "id", "count", "name"} dicts
player_hand(name){"id", "count", "name"}

🌍World Functions

FunctionReturns
world_time()int — total game ticks
world_day_time()int — ticks within day (0–23999)
world_day()int
world_weather()"clear" / "rain" / "thunder"
world_difficulty()"peaceful" / "easy" / "normal" / "hard"
world_spawn()[x, y, z]

🖥Server Functions

FunctionReturns
server_max_players()int
server_tps()float (max 20.0)
server_mspt()float

🧱Block Functions

FunctionReturns
block_at(x, y, z)str block ID, e.g. "minecraft:stone"
block_at(x, y, z, dim)Same in specified dimension
block_is_air(x, y, z)bool
block_light(x, y, z)int 0–15

🐉Entity Functions

All entity functions accept a standard Minecraft entity selector: "@e", "@e[type=zombie]", "@e[limit=1,sort=nearest]", etc.

FunctionReturns
entity_count(sel)int
entity_list(sel)List of entity dicts
entity_type(sel)str e.g. "minecraft:zombie"
entity_name(sel)str
entity_uuid(sel)str
entity_x/y/z(sel)float
entity_pos(sel)[x, y, z]
entity_yaw/pitch(sel)float
entity_dimension(sel)str
entity_velocity(sel)[vx, vy, vz]
entity_health(sel) / entity_max_health(sel)float
entity_alive(sel)bool
entity_on_fire/on_ground/in_water/silent/no_gravity/is_baby(sel)bool
entity_tags(sel)list[str]
entity_has_tag(sel, tag)bool
entity_nbt(sel)dict
entity_nbt_all(sel)list[dict]
entity_nbt_tag(sel, tag)any

🗄NBT Snapshots

player_nbt(name) fields

KeyTypeDescription
Health / MaxHealthfloatHP
FoodLevelint0–20
XpLevel / XpProgress / TotalXpint/float/int
GameModestr
PosX / PosY / PosZfloatPosition
Yaw / PitchfloatRotation
Dimensionstr
Flying / Sneaking / Sprinting / OnGround / InWater / IsOpbool
Name / UUIDstr
nbt = player_nbt("Steve")
print(nbt["Health"], nbt["XpLevel"])
tag = player_nbt_tag("Steve", "Dimension")

🏆Scoreboard Functions

These functions read and write Minecraft scoreboard objectives and scores directly, without needing to dispatch /scoreboard commands. All target parameters accept either a player name ("Steve") or any entity selector ("@a[tag=active]").

One display slot at a time: Minecraft supports only one objective per display slot. Calling score_display("sidebar", obj) replaces whatever is currently shown. Pass an empty string as the objective to hide the display completely.

Read & Write

FunctionSignatureReturnsDescription
score_get score_get(target, objective) int Returns the score of the first entity matching target. Returns 0 if the player is not tracked in this objective.
score_set score_set(target, objective, value) None Set score to an exact integer. Applies to all entities matching the selector.
score_add score_add(target, objective, amount) None Add amount to the current score. Use a negative value to subtract. Applies to all matches.
score_reset score_reset(target, objective) None Remove the score entry entirely — the player will no longer appear in score_get_all until a new score is set.
score_test score_test(target, objective, min, max) bool Returns True if the score of the first matching entity is within [min, max] inclusive. Ideal for exact milestone detection without external state.
score_get_all score_get_all(objective) dict Returns a {"playerName": score} dict of every tracked holder for this objective, including offline players. Useful for leaderboard logic.

Objective Management

FunctionSignatureReturnsDescription
score_exists score_exists(objective) bool Check whether an objective with this name is registered. Use before score_create to avoid errors on reload.
score_objectives score_objectives() list[str] Returns the names of all currently registered objectives on the scoreboard.
score_create score_create(name, criteria?, displayName?) None Register a new objective. criteria defaults to "dummy" if omitted. displayName defaults to name. No-op if the objective already exists — safe to call on every server start.
score_remove score_remove(name) None Permanently delete an objective and all associated scores from the scoreboard.
score_display score_display(slot, objective) None Assign an objective to a display slot. Pass "" as objective to clear the slot. Valid slots: "sidebar", "list", "belowName".

Criteria reference

Criteria stringIncrements when…Manual control
dummyNever — fully manualYes — use score_set / score_add
playerKillCountPlayer kills another playerAlso writable
deathCountPlayer diesAlso writable
totalKillCountPlayer kills any entityAlso writable
healthMirrors current HP (live, read-only in practice)No
levelMirrors XP level (live)No
foodMirrors hunger bar (live)No
xpMirrors total XP points (live)No

Example — Setup on server start

@PREPARE
if not score_exists("kills"):
    score_create("kills", "playerKillCount", "☠ Kill Tracker")
score_display("sidebar", "kills")

Example — Milestone title with score_test

while True:
    for p in player_list():
        if score_test(p, "kills", 5, 5):
            /title {p} actionbar [{"text":"5 Kill Streak!","color":"gold","bold":true}]
        if score_test(p, "kills", 25, 25):
            /title {p} title [{"text":"CHAMPION","color":"red","bold":true}]
            /say {p} ha raggiunto 25 kill!
    wait(60)

Example — Leaderboard announce with score_get_all

scores = score_get_all("kills")
best  = None
top   = -1
for p in scores:
    if scores[p] > top:
        top  = scores[p]
        best = p
if best != None:
    /say Leader: {best} with {top} kill!

Example — Reset all scores

for p in player_list():
    score_set(p, "kills", 0)
/say All scores resetted!

Example — Switch sidebar objective

# Hide current sidebar, show deaths instead
score_display("sidebar", "")       # clear
score_display("sidebar", "deaths") # show deaths
Auto-criteria: Objectives with criteria like playerKillCount or deathCount are incremented automatically by the server — you don't need to call score_add. Use dummy for scores you manage entirely from scripts.

🧵Threading & Lifecycle

BehaviorDetails
Thread interrupt/scripts stop sets the interrupt flag; the interpreter checks it at every statement boundary and inside wait().
TimeoutEvery server query and command has a 10-second timeout.
Duplicate preventionCannot start a script that is already running by name.
Daemon threadsDo not prevent JVM shutdown.
# /scripts reload  — clears module cache and runs all @PREPARE scripts
# /scripts run events/greet
# /scripts stop events/greet
# /scripts stopall

Module Cache

Imported modules are parsed once and stored in a thread-safe cache. Subsequent import statements reuse the cached module without re-reading the file.

Hot-reload: Run /scripts reload after editing a library file to clear the cache.

📚Examples

Auto-broadcast & loop

@PREPARE
messages = ["Welcome!", "Visit our Discord!", "Type /help"]
idx = 0
while True:
    /say {messages[idx]}
    idx = (idx + 1) % len(messages)
    wait(1200)

Low-health warning system

def check_players():
    for name in player_list():
        hp = player_health(name)
        if hp < 4:
            /title {name} actionbar {"§cCritical HP: " + str(int(hp)) + "♥"}

while True:
    check_players()
    wait(10)

Entity janitor

MAX_ZOMBIES = 20
while True:
    cnt = entity_count("@e[type=zombie]")
    if cnt > MAX_ZOMBIES:
        /kill @e[type=zombie,sort=furthest,limit={cnt - MAX_ZOMBIES}]
        /say Cleared {cnt - MAX_ZOMBIES} excess zombies.
    wait(600)

Library + importer pattern

# lib/rewards.script
@LIBRARY
def give_starter_kit(player):
    /give {player} minecraft:iron_sword 1
    /give {player} minecraft:bread 16

# welcome.script
from lib.rewards import give_starter_kit
for name in player_list():
    give_starter_kit(name)
Note: List comprehensions are not supported. Use a for loop and list.append() instead.