major bug: when connecting to the server a second time, it gets softlocked and keeps repeating ticking connection A\n in debug logs until it times out.
11 KiB
Minecraft.Server Developer Guide (English)
This document is for contributors who are new to Minecraft.Server and need a practical map for adding or modifying features safely.
1. What This Server Does
Minecraft.Server is the dedicated-server executable entry for this codebase.
Core responsibilities:
- Switch the process working directory to the executable folder before relative file I/O
- Load, normalize, and repair
server.properties - Initialize dedicated runtime systems, connection logging, and access control
- Load or create the target world and keep
level-idaligned with the actual save destination - Run the dedicated main loop (network tick, XUI actions, autosave, CLI input)
- Maintain operator-facing access files such as
banned-players.jsonandbanned-ips.json - Perform an initial save for newly created worlds and then shut down safely
2. Important Files
Startup and Runtime
Windows64/ServerMain.cppPrintUsage()andParseCommandLine()SetExeWorkingDirectory()- Runtime setup and shutdown flow
- Initial save path for newly created worlds
- Main loop, autosave scheduler, and CLI polling
World Selection and Save Load
WorldManager.hWorldManager.cpp- Finds matching save by
level-idfirst, then world-name fallback - Applies storage title + save ID consistently
- Wait helpers for async storage/server action completion
- Finds matching save by
Server Properties
ServerProperties.hServerProperties.cpp- Default values and normalization ranges
- Parse/repair/write
server.properties - Exposes
ServerPropertiesConfig SaveServerPropertiesConfig()rewriteslevel-name,level-id, andwhite-list
Access Control, Ban, and Whitelist Storage
Access/Access.hAccess/Access.cpp- Process-wide access-control facade
- Published snapshot model used by console commands and login checks
Access/BanManager.hAccess/BanManager.cpp- Reads/writes
banned-players.jsonandbanned-ips.json - Normalizes identifiers and filters expired entries from snapshots
- Reads/writes
Access/WhitelistManager.hAccess/WhitelistManager.cpp- Reads/writes
whitelist.json - Normalizes XUID-based whitelist entries used by login validation and CLI commands
- Reads/writes
Logging and Connection Audit
ServerLogger.hServerLogger.cpp- Log level parsing
- Colored/timestamped console logs
- General categories such as
startup,world-io,console,access,network, andshutdown
ServerLogManager.hServerLogManager.cpp- Accepted/rejected TCP connection logs
- Login/disconnect audit logs
- Remote-IP cache used by
ban-ip <player>
Console Command System
Console/ServerCli.cpp(facade)Console/ServerCliInput.cpp(linenoise input thread + completion bridge)Console/ServerCliParser.cpp(tokenization, quoted args, completion context)Console/ServerCliEngine.cpp(dispatch, completion, helpers)Console/ServerCliRegistry.cpp(command registration + lookup)Console/commands/*(individual commands)
3. End-to-End Startup Flow
Main flow in Windows64/ServerMain.cpp:
SetExeWorkingDirectory()switches the current directory to the executable folder.- Load and normalize
server.propertiesviaLoadServerPropertiesConfig(). - Copy config into
DedicatedServerConfig, then apply CLI overrides (-port,-ip/-bind,-name,-maxplayers,-seed,-loglevel,-help/--help/-h). - Initialize process state,
ServerLogManager, andAccess::Initialize("."). - Initialize window/device/profile/network/thread-local systems.
- Set host/game options from
ServerPropertiesConfig. - Bootstrap world with
BootstrapWorldForServer(...). - If world bootstrap resolves a different normalized save ID, persist it with
SaveServerPropertiesConfig(). - Start hosted game thread (
RunNetworkGameThreadProc). - If a brand-new world was created, explicitly request one initial save.
- Enter the main loop:
TickCoreSystems()HandleXuiActions()serverCli.Poll()- autosave scheduling
- On shutdown:
- stop CLI input
- request save-on-exit / halt server
- wait for network shutdown completion
- terminate log, access, network, and device systems
4. Current Operator Surface
4.1 Launch Arguments
-port <1-65535>-ip <addr>or-bind <addr>-name <name>(runtime max 16 chars)-maxplayers <1-8>-seed <int64>-loglevel <debug|info|warn|error>-help,--help,-h
Notes:
- CLI overrides affect only the current process.
- The only values currently written back by the server are
level-nameandlevel-id, and that happens when world bootstrap resolves identity changes.
4.2 Built-in Console Commands
help/?stoplistban <player> [reason ...]- currently requires the target player to be online
ban-ip <address|player> [reason ...]- accepts a literal IPv4/IPv6 address or an online player's current remote IP
pardon <player>pardon-ip <address>- only accepts a literal address
banlisttp <player> <target>/teleportgamemode <survival|creative|0|1> [player]/gm
CLI behavior notes:
- Command parsing accepts both
cmdand/cmd. - Quoted arguments are supported by
ServerCliParser. - Completion is implemented per command via
Complete(...).
4.3 Files Written Next to the Executable
server.propertiesbanned-players.jsonbanned-ips.json
This follows from SetExeWorkingDirectory(), so these files are resolved relative to Minecraft.Server.exe, not the shell directory you launched from.
5. Common Development Tasks
5.1 Add a New CLI Command
Use this pattern when adding commands like /kick, /time, etc.
- Add files under
Console/commands/CliCommandYourCommand.hCliCommandYourCommand.cpp
- Implement
IServerCliCommandName(),Usage(),Description(),Execute(...)- optional:
Aliases()andComplete(...)
- Register the command in
ServerCliEngine::RegisterDefaultCommands(). - Add source/header to build definitions:
CMakeLists.txt(MINECRAFT_SERVER_SOURCES)Minecraft.Server/Minecraft.Server.vcxproj(<ClCompile>/<ClInclude>)
- Manual verify:
- command appears in
help - command executes correctly
- completion works for both
cmdand/cmd - quoted arguments behave as expected
- command appears in
Implementation references:
CliCommandHelp.cppfor a simple no-arg commandCliCommandTp.cppfor multi-arg + completion + runtime checksCliCommandGamemode.cppfor argument parsing and aliasesCliCommandBanIp.cppfor access-backed behavior with connection metadata
5.2 Add or Change a server.properties Key
- Add/update the field in
ServerPropertiesConfig(ServerProperties.h). - Add a default entry to
kServerPropertyDefaults(ServerProperties.cpp). - Load and normalize the value in
LoadServerPropertiesConfig().- Use existing helpers for bool/int/string/int64/log level/level type.
- If this value should be written back, update
SaveServerPropertiesConfig().- Note: today that function intentionally only persists world identity.
- Apply it to runtime where needed:
ApplyServerPropertiesToDedicatedConfig(...)- host options in
ServerMain.cpp(app.SetGameHostOption(...)) PrintUsage()/ParseCommandLine()if the key also gets a CLI override
- Manual verify:
- missing key regeneration
- invalid value normalization
- clamped ranges still make sense
- runtime behavior reflects the new value
Normalization details worth remembering:
level-idis normalized to a safe save ID and length-limited.server-nameis capped to 16 runtime chars.max-playersis clamped to1..8.autosave-intervalis clamped to5..3600.level-typenormalizes todefaultorflat.
5.3 Change Ban / Access Behavior
Primary code lives in Access/Access.cpp, Access/BanManager.cpp, and ServerLogManager.cpp.
When changing this area:
- Keep
BanManagerresponsible for storage/caching, not live-network policy. - Keep the clone-and-publish snapshot pattern in
Access.cppso readers never block on disk I/O. - Remember that
ban-ip <player>depends onServerLogManager::TryGetConnectionRemoteIp(...). - Keep expired entries out of
SnapshotBannedPlayers()/SnapshotBannedIps()output. - Verify:
- clean boot creates empty ban files when missing
ban,ban-ip,pardon,pardon-ip, andbanliststill work- online bans disconnect live targets immediately
- manual edits still reload safely if you later add or extend reload paths
5.4 Change World Load/Create Behavior
Primary code is in WorldManager.cpp.
Current matching policy:
- Match by
level-id(UTF8SaveFilename) first. - Fall back to world-name match on title/file name.
When changing this logic:
- Keep
ApplyWorldStorageTarget(...)usage consistent (title + save ID together). - Preserve periodic ticking in wait loops (
tickProc) to avoid async deadlocks. - Keep timeout/error logs specific enough for diagnosis.
- Verify:
- existing world is reused correctly
- no accidental new save directory creation
- shutdown save still succeeds
- newly created worlds still get the explicit initial save from
ServerMain.cpp
5.5 Add Logging for New Feature Work
Use ServerLogger helpers:
LogDebug,LogInfo,LogWarn,LogError- formatted variants
LogDebugf,LogInfof, etc.
Use ServerLogManager when the event is specifically part of the transport/login/disconnect lifecycle.
Recommended categories:
startupfor init/shutdown lifecycleworld-iofor save/world operationsconsolefor CLI command handlingaccessfor ban/access control statenetworkfor connection/login audit
6. Build and Run
From repository root:
cmake -S . -B build -G "Visual Studio 17 2022" -A x64
cmake --build build --config Debug --target MinecraftServer
cd .\build\Debug
.\Minecraft.Server.exe -port 25565 -bind 0.0.0.0 -maxplayers 8 -name DedicatedServer
Notes:
- The process switches its working directory to the executable directory at startup.
server.properties,banned-players.json, andbanned-ips.jsonare therefore read/written next to the executable.- For Visual Studio workflow, see root
COMPILE.md.
7. Safety Checklist Before Commit
- the server starts without crash when
server.propertiesis missing or sparse - missing access files are recreated on a clean boot
- existing world loads by expected
level-id - new world creation still performs the explicit initial save
- CLI input and completion remain responsive
banlistoutput stays sane after adding/removing bans- no busy-wait path removed from async wait loops
- both CMake and
.vcxprojinclude newly added source files
8. Quick Troubleshooting
- Unknown command:
- check
RegisterDefaultCommands()and build-file entries
- check
server.propertiesor ban files seem to load from the wrong folder:- remember
SetExeWorkingDirectory()moves the working directory to the executable folder
- remember
- Autosave or shutdown save timing out:
- confirm wait loops still call
TickCoreSystems()andHandleXuiActions()where required
- confirm wait loops still call
- World not reused on restart:
- inspect
level-idnormalization and matching logic inWorldManager.cpp
- inspect
ban-ip <player>cannot resolve an address:- confirm the player is currently online and
ServerLogManagerhas a cached remote IP for that connection
- confirm the player is currently online and
- Settings not applied:
- confirm the value is loaded into
ServerPropertiesConfig, optionally copied intoDedicatedServerConfig, and then applied inServerMain.cpp
- confirm the value is loaded into