🤖 Agent: one line in docs/hosting.md (Intel Macs have had server builds since the 10-31 Nightly; install.sh already maps Darwin-x86_64); the README half is already on main via bf4657da26.
The first install on Windows now checks SHA256SUMS.sig (a Nightly's
SHA256SUMS.v2.sig, for the version its title names) against the release
key, then the archive against SHA256SUMS, as install.sh does. OpenSSL is
looked for on the PATH and in Git for Windows. Without OpenSSL 3 it says
so and keeps the GitHub SHA-256 check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
packaging/server/amp/ holds a template for CubeCoders AMP's generic module.
Its update stage runs the egg's own install script (the release asset checked
against its SHA-256), the start runs the same horizon-start.sh, and its
settings are the egg's variables with the same defaults. Tests keep both in
step and check the console patterns against the lines the server logs.
Fixes#8238
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The README and the hosting guide still said Intel Macs come "from the next
Nightly" and listed no Windows ARM64 launcher; both ship in every Nightly and
launcher release.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: a failed region already restores its originals, so blocking the server start on any prune error took a server down for one corrupt chunk record. I pushed a change that starts it when no journal is left, with a supervisor test for it. Note that delete mode now writes regions one at a time with fsync, so a large prune takes longer than the old parallel rewrite. 169 horizon-server tests pass on Linux.
What a Horizon server sends a 26.2 client (accept_older_clients) now carries 26.2's
registry ids: tags, block states, entity, block entity, block, attribute, sound and
particle ids, and items and components through the existing Way::Down writers, which
now leave out components the client lacks. A creative slot or container click from the
client is read with its ids up.
generate_registry_id_adapter.py --substitutes writes, from both versions' data
reports, a stand-in for each 26.3 entry 26.2 lacks (poplar as birch, wool and concrete
slabs and stairs as stone ones of the same shape, ...); what has none (the cushion
entity) isn't sent. v26_2_ids.rs is regenerated with it.
Part of #10404.
HORIZON_TRACE=FILE, horizon-ctl trace FILE [SECONDS] and horizon-trace on a
dedicated server's console write each span of every thread as a Chrome trace
event, so what ran beside what, and what a thread waited for, can be read off
in ui.perfetto.dev. A thread that records spans but is never taken from (a far
terrain or lighting worker while spans are on) now drops them past 16k instead
of growing for as long as spans stay on.
Only packet ids are translated for an older client so far (#10404): the
registry data, tags (registry ids) and play packets (chunks, light, login,
player_action numbering...) would reach a real 26.2 client in 26.3 ids and
layouts. With accept_older_clients on, such a client now logs in and is
told so in configuration, right after the known packs and before any of
them. NetConfig::older_clients_play (tests only, not in server.toml) keeps
the play path reachable for the id renumbering test until the later parts
translate the contents.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A server.toml option, [server] accept_older_clients (horizon-accept-older-clients), off
by default. On, a login handshake stating 776 opens an adapter::ClientSession on the
connection instead of being refused, and a 26.2 status ping is told its own protocol.
The session follows the connection's states as Session does, mirrored, and renumbers
packet ids both ways with the inverse of the generated 26.2 tables, in the blocking
login path and in the io_pool play path. Off, nothing changes.
Part of #10404.
horizon-server accepts and keeps a [combat] section (off by default, left out
of the file while off); the dedicated game reads it itself, like
[anticheat], and turns on the legacy rules a hosted world's PvP page switch
gives. A wrong section is logged and fights keep 26.3's rules.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The official SMP is mob-bound (5,400 mobs, 73 ms a tick at 60 players) but the report
only said tick_creature_entities took the time, not which mobs. Format 2 adds, without
a restart and at a measured cost:
- mob_costs: one tick in four times every mob's tick and sums it by dimension and type
(ms a tick, mobs ticked, us a mob);
- level_* spans: each dimension's share of the server tick;
- chunk_workers: streaming::job_stats while recording (load/generate/decorate/light...);
- network: wire bytes in/out per player and in all (connection counters, no new
per-packet cost);
- overhead: spans and mob samples a tick at their price measured on the machine;
- horizon-report stop / status.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A one-shot step before the game starts, switched on in server.toml: chunks
with InhabitedTime under min_inhabited_ticks (1200) are deleted from region/,
entities/ and poi/, files rewritten compacted and emptied ones deleted.
Chunks near the spawn, named by keep_files (SMP claims and homes), that can't
be read, and a ring around every kept chunk stay. mode = "dry-run" only
writes the report while the game runs; either mode turns itself off.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Route CLI moderation through file-backed lists when no game is running, pin vanilla-style replies, and document the stopped-server behavior.
Fixes#9753
Co-Authored-By: Codex (GPT-5.6) <[email protected]>
/tempban, /mute, /tempmute, /mutelist and /unmute worked on dedicated servers but were left out of the
command tree, so operators' clients showed them as unknown commands with no completions, and the
official server's operators believed only permanent bans existed. They now reach the trees of
operators of level 3 (as a native mod's commands do), where the game runs them: the mute commands
when the server filters chat, the ban ones on a dedicated server.
/mute <player> [duration] [reason] takes a duration after the name (/tempmute stays), and
/tempbanlist lists the bans that end by themselves, soonest first, with who banned, the time left,
the end and the reason.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: roadmap item server-panels. I fixed the online-mode setting's text: it said Horizon's client joins offline servers only, out of date since Microsoft sign-in. The egg is not tested on a live panel.
🤖 Agent: I made the destination follow the server's lookup order ([engine] resources, then an existing resources/, then the installer's game-files), else an import could land where the next start never looks; version from VERSION_ID.
Adds packaging/server/pterodactyl-egg.json and panel-start.sh, a docs/hosting.md section,
and tests/test_server_panels.py. Part of #8238.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Checks the folder (version stamp, derived files and checksums, asset folders), copies it through a temp folder and an atomic rename, keeps the old copy on any failure, and prints the next step.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
On a running server, `horizon-server config set` and PATCH /api/v1/config sent
only console commands, so the message of the day and the player limit were
always reported as "restart required", although the game takes both live
through the management settings (`serversettings/motd/set` and
`serversettings/max_players/set`).
`live_line` now returns the control line to send: the same management
request for these two settings, `console ...` for the ones vanilla changes
through its console, and nothing for the supervisor's own settings. The
CLI talks to the game's control endpoint the way the API already did, and
a reply with an error leaves the setting for the next start.
Fixes#8206
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The Coming paragraph said console, ban, pardon and mcp were still to come.
console and mcp are in main.rs (the commands list already shows them), and
players, kick, ban, pardon, ban-ip and pardon-ip are dispatched there too
(cli::players, cli::moderate). Drop the paragraph and list the moderation
commands with the others.
Refs #7232
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A dedicated server now times its spans every tick and writes a tick slower
than 250 ms to the log with where its time went (HORIZON_SPIKES's line;
the variable still sets another threshold). A server more than two seconds
behind, which already skipped the ticks it missed, now says so as vanilla's
MinecraftServer.runServer does ("Can't keep up! Is the server overloaded?
Running Nms or N ticks behind"), at most every 15 seconds.
The official server shows 10-13 s freezes (bursts of 'moved too quickly'
from 15+ players at once) with nothing in its log to say where they went.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Reuse the launcher native build pipeline for a headless server target,
export an independent runtime, and document manual server selection.
Keep server cache entries outside playable versions and preserve export
permissions, resources, and post-build data.
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
Co-Authored-By: OpenCode GLM 5.3 <[email protected]>
Co-Authored-By: OpenCode Nemotron 3.5 Super VL <[email protected]>
An RCON command used to run as the server console, so the operators saw nothing of it and its
`say` read `[Server]`. It now goes through its own control request (`rcon <line>`): the same
console at level 4, with the source named `Rcon` as RconConsoleSource.createCommandSourceStack
names it. Its feedback reaches the operators in game as `[Rcon: ...]` while
`broadcast-rcon-to-ops` is on (CommandSource.shouldInformAdmins), its `say` reads `[Rcon] ...`
and leaves the reply empty, and the console and an RCON client take success feedback whatever
`send_command_feedback` says (CommandSource.acceptsSuccess), as a player's source does not.
Fixes#6393
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
"Before you start" still said Horizon's client can't sign in to a Microsoft
account, so Horizon players could join offline servers only, and that it has
no chat session. Both stopped being true: the launcher signs players in
(docs/microsoft-account.md), the game joins online-mode servers, and since
#4375, #4376 and #4377 (Nightly 2026-11-01) it signs its chat.
- Online mode: every player joins with their own account, vanilla and
Horizon players alike.
- Offline servers: the same safety advice, and only players under an
offline name are limited to them.
- Secure chat: Horizon's client signs its chat too.
Refs #3332 (the website half is horizon-rust/horizon-site#38)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The supervisor serves vanilla's GS4 query as QueryThreadGs4 does: the handshake (type 9, a token
below 2^24 as text), then the basic and the full stat to a request that carries the token, each
byte for byte what a vanilla 26.3 server sends for the same settings (checked on the wire). The
players come from the game's server-status, read at most once a second; the full stat is kept
5 seconds as in vanilla. Off by default; an invalid port leaves it off with vanilla's warning.
Refs #6108
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
mimalloc backs the game's memory with transparent huge pages where Linux
allows them; many are only partly used. low_memory = true starts the game
with MIMALLOC_ALLOW_THP=0: 60 MB less idle and 190 MB less at 20 players,
for ~1.2 % more tick time (playhorizon, 20 bots, 5 rounds). Off by default.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Conflicts: lod.rs (main's wanted-tile heap from #4747; a failed tile stays out of the heap until its RETRY passes, then the heap is rebuilt so it is asked again; main's MAX_LEVEL 22) and remote.rs (send_far_terrain and main's tick_chat_session both run before ClientTickEnd).
Co-Authored-By: Claude Opus 5.5 <[email protected]>