Skip to content

Implementation of Dedicated Server in Isolation#498

Draft
kuwacom wants to merge 26 commits intosmartcmd:mainfrom
kuwacom:feature/dedicated-server
Draft

Implementation of Dedicated Server in Isolation#498
kuwacom wants to merge 26 commits intosmartcmd:mainfrom
kuwacom:feature/dedicated-server

Conversation

@kuwacom
Copy link
Contributor

@kuwacom kuwacom commented Mar 4, 2026

Description

This PR introduces a Windows64 dedicated server executable (Minecraft.Server) and expands the dedicated-server runtime/build pipeline.

Compared to the initial pull request description, the following features have been newly added:

  • interactive server console (linenoise + command completion),
  • broader server.properties support (including LAN advertise and host options),
  • world bootstrap/load behavior based on level-id,
  • Docker (Wine) runtime support and helper scripts.

Minecraft.Client changes remain focused on dedicated-server bridge compatibility.
#65

image

Changes

Dedicated Server Executable / Startup

  • Added dedicated server entrypoint and project:
    • Minecraft.Server/Windows64/ServerMain.cpp
    • Minecraft.Server/Minecraft.Server.vcxproj (+ filters)
  • Dedicated startup flow now:
    • loads server.properties defaults,
    • applies CLI overrides,
    • bootstraps world by level-id / level-name,
    • performs initial save for newly created worlds,
    • runs periodic autosave and graceful shutdown save.
  • Dedicated launch options:
    • -port, -ip/-bind, -name, -maxplayers, -seed, -loglevel, -help
    • Since the arguments can basically override server.properties, it can also be integrated into fully autonomous deployment systems like Kubernetes.

Dedicated Runtime Modules

  • Added/expanded:
    • ServerProperties (defaulting, normalization, persistence for dedicated options)
    • WorldManager (load existing world by level-id, fallback by name, or create new world)
    • ServerLogger (debug|info|warn|error)
  • Added shared server string utility layer:
    • Minecraft.Server/Common/StringUtils
    • used by logger/properties/world/console paths to avoid duplicated conversion/normalize logic.

Interactive Server Console

  • Added console runtime:
    • ServerCli, ServerCliInput, ServerCliParser, ServerCliEngine, ServerCliRegistry
    • linenoise-based input/history/completion support.
  • Added built-in commands:
    • help (?)
    • stop
    • list
    • tp (teleport)
    • gamemode (gm)
  • Added completion behavior:
    • command/alias completion,
    • player name completion,
    • gamemode token completion (survival|creative|s|c|0|1).

Minecraft.Client Bridge Changes (Dedicated Compatibility Scope)

Only dedicated-server compatibility behavior was changed in client networking paths:

  1. Minecraft.Client/MinecraftServer.h

    • Added NetworkGameInitData::dedicatedNoLocalHostPlayer.
  2. Minecraft.Client/Common/Network/GameNetworkManager.cpp

    • Added dedicated host path to skip local host ClientConnection creation.
    • Preserved host telemetry and multiplayer mode initialization behavior.
  3. Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp

    • Dedicated host startup now uses configured bind IP/port globals.
    • LAN advertising is now controlled by dedicated config (lan-advertise).
  4. Minecraft.Client/Windows64/Network/WinsockNetLayer.h/.cpp

    • Added dedicated LAN advertise global.
    • In dedicated mode, LAN discovery thread is not started.

Build / Tooling / Docs

  • Added CMake target and wiring:
    • CMakeLists.txt: MinecraftServer target + post-build asset copy
    • cmake/CopyServerAssets.cmake: minimal dedicated runtime asset copy
  • Updated solution/docs/ignore:
    • MinecraftConsoles.sln
    • COMPILE.md
    • .gitignore
  • Added Docker runtime support (Wine):
    • docker/dedicated-server/Dockerfile
    • docker/dedicated-server/entrypoint.sh
    • docker-compose.dedicated-server.yml
    • docker-build-dedicated-server.sh
    • start-dedicated-server.sh
    • README.md dedicated Docker section

kuwacom added 4 commits March 4, 2026 18:12
- Introduced `ServerMain.cpp` for the dedicated server logic, handling command-line arguments, server initialization, and network management.
- Created `postbuild_server.ps1` script for post-build tasks, including copying necessary resources and DLLs for the dedicated server.
- Added `CopyServerAssets.cmake` to manage the copying of server assets during the build process, ensuring required files are available for the dedicated server.
- Defined project filters in `Minecraft.Server.vcxproj.filters` for better organization of server-related files.
- Introduced ServerLogger for logging startup steps and world I/O operations.
- Implemented ServerProperties for loading and saving server configuration from `server.properties`.
- Added WorldManager to handle world loading and creation based on server properties.
- Updated ServerMain to integrate server properties loading and world management.
- Enhanced project files to include new source and header files for the server components.
@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 4, 2026

I hadn’t been merging upstream changes while implementing, so I’m in the middle of merging them now...

# Conflicts:
#	Minecraft.Client/Common/Network/PlatformNetworkManagerStub.cpp
#	Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp
#	Minecraft.Client/Windows64/Network/WinsockNetLayer.h
@gizmogoat
Copy link

gizmogoat commented Mar 4, 2026

Some things have changed in the past day-ish, watch out for incompatibilities/redundant re-implementations of these:

@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 4, 2026

So -server has been added, I see.
From a quick look at the issues, it seems there are still a few world-related bugs remaining.

servers.txt is a nice addition as well.
It’s more user-friendly than -target.

kuwacom added 2 commits March 5, 2026 09:38
Since 31881af56936aeef38ff322b975fd0 , `skinHud.swf` for 720 is not included in `MediaWindows64.arc`,
the app crashes unless the virtual screen is set to HD.
@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 5, 2026

It was partially affected by #495, but I was able to merge it without any issues.

@haychk
Copy link

haychk commented Mar 5, 2026

is it still only LAN multiplayer only? or can worlds be opened up to more people?

@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 5, 2026

It can be accessed from anywhere if you have a suitable network that can be accessed from the global internet (a network line that can open the specified TCP port).

In short, it's a regular server app.

@haychk
Copy link

haychk commented Mar 5, 2026

oh damn. so i could run this on a vps?

@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 5, 2026

Yes, but since it still uses a lot of Windows API internally (to maintain compatibility), it requires a Windows system or a system that includes Windows API to run.

I would like to eventually release a Linux version and put it into Docker.

@haychk
Copy link

haychk commented Mar 5, 2026

i mean, i've tried it on linux and it works fine. i could try and create a docker image if you'd like

@gizmogoat
Copy link

I would like to eventually release a Linux version and put it into Docker.

You can run it fine on Linux without a GUI launcher via umu

@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 5, 2026

i mean, i've tried it on linux and it works fine. i could try and create a docker image if you'd like

seriously!
That's nice.

@haychk
Copy link

haychk commented Mar 5, 2026

wine also works really well. tried lutris and has similar results to umu

@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 5, 2026

Which one has a smaller image size?
wine or umu

@haychk
Copy link

haychk commented Mar 5, 2026

wine iirc

@void2012 void2012 marked this pull request as draft March 5, 2026 07:45
@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 5, 2026

Currently there are two approaches: the separated version (this pull request) and the -server option version (merged yesterday) that starts the client directly as a server. Which one should be prioritized?

The separated version modifies the world-saving logic and changes the startup logic, so integrating it into the client side would be a bit tricky.

Alternatively, it would also be possible to keep both.

@codeHusky
Copy link
Collaborator

Realistically speaking a completely separate Server executable should be our preferred way for this to work. The -server command line option is moreso jank and this is far cleaner.

@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 5, 2026

If we're planning to merge the separated server in the future, I was considering removing the -server option and the unnecessary client-side logic as well. Would it be okay to leave them as they are for now?

@smartcmd
Copy link
Owner

smartcmd commented Mar 5, 2026

If we're planning to merge the separated server in the future, I was considering removing the -server option and the unnecessary client-side logic as well. Would it be okay to leave them as they are for now?

Yeah

@haychk
Copy link

haychk commented Mar 6, 2026

seems to be a bug where i cannot change the game's difficulty and enable some settings (trust players, pvp etc)
image
image
doing this all on a vps too

@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 6, 2026

Sorry, that’s not written in the description, but it’s still a dummy for now.
It’s only included in the default server.properties generation ahead of time (so it can be implemented later).

@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 6, 2026

doing this all on a vps too

Would it be possible to send the Dockerfile and docker-compose.yml?

@haychk
Copy link

haychk commented Mar 6, 2026

doing this all on a vps too

Would it be possible to send the Dockerfile and docker-compose.yml?

not using a docker image, which is surprising. i took your pr build and ran it on my vps. works REALLY well, minus the default server settings being awful (peaceful mode, no pvp etc)

Like most command line tools, it highlights predictions in gray.
@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 7, 2026

How close is this? What's left before this is out of draft? no rush, just curious :)

I’ve basically implemented all the features I had in mind!
Next up is enhancing the commands and merging with the latest changes.

Also, there are some messy cases where reusable utility functions like WideToUtf8 and Utf8ToWide were implemented on the spot and then wrapped elsewhere. I’ll unify them.

@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 7, 2026

update pullrequest description newer

kuwacom added 2 commits March 7, 2026 17:39
…down race

Before this change, server/host shutdown closed sockets directly in
ServerConnection::stop(), which bypassed the normal disconnect flow.
As a result, clients could be dropped without receiving a proper
DisconnectPacket during stop/kill/world-close paths.

Also, WinsockNetLayer::Shutdown() could destroy synchronization objects
while host-side recv threads were still exiting, causing a crash in
RecvThreadProc (access violation on world close in host mode).
@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 7, 2026

I fixed an implementation that I hadn’t really questioned before, and no issue had been filed about it, but it was a bit awkward.
This affects not only the server but also hosts on the client side.

Until now, when a host ended the session on either the server or client side, the other clients would remain connected.
Looking at the implementation, there is a dedicated feature called DisconnectPacket for showing a disconnect message, but the shutdown path had been bypassing it.

image

- Add client-side host disconnect handling in CPlatformNetworkManagerStub::DoWork() for _WINDOWS64.
- When in QNET_STATE_GAME_PLAY as a non-host and WinsockNetLayer::IsConnected() becomes false, trigger g_NetworkManager.HandleDisconnect(false) to enter the normal disconnect/UI flow.
- Use m_bLeaveGameOnTick as a one-shot guard to prevent repeated disconnect handling while the link remains down.
- Reset m_bLeaveGameOnTick on LeaveGame(), HostGame(), and JoinGame() to avoid stale state across sessions.
@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 7, 2026

I fixed another similar issue.

Previously, if the host process was killed and the clients lost the TCP connection, they would somehow keep playing instead of returning to the menu.
I fixed this so that after the disconnect UI is shown, clients are returned to the menu.
The processing from the Xbox version was reused.

image

@codeHusky
Copy link
Collaborator

Could you implement an IP ban feature too? We’ve had a number of servers struggling with griefers and trolls so having the ability to 1) identify who’s connected from what IP and 2) IP Ban them would be super helpful for them

@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 7, 2026

Sounds good!
Having at least some restrictions is better than having none.

@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 7, 2026

I noticed a new issue.

When the client opens a world in host mode, LAN advertising doesn’t seem to work.

Even with logs added throughout for debugging, it seems to be working, so it might be an environment issue rather than the code.
It would be helpful if everyone could test it in their environments as well.

@void2012
Copy link
Collaborator

void2012 commented Mar 7, 2026

@kuwacom when you will be ready to merge, let me know, I will review the code

@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 7, 2026

@kuwacom when you will be ready to merge, let me know, I will review the code

Would it be better to merge once we reach a certain point?
Since new features and improvements keep coming up, that’s why.

@void2012
Copy link
Collaborator

void2012 commented Mar 7, 2026

Personally, I would like to see a very stable support for a feature like this. So if you feel this PR is enough for your needs, decide it yourself.

Conflict resolution summary:

- .gitignore: kept dedicated-server-specific ignore entries (tmp*/, _server_asset_probe/, server-data/) and also kept main-side entries (*.user, /out).

- Minecraft.Client/Common/Network/GameNetworkManager.cpp: kept int64_t seed from main and preserved dedicatedNoLocalHostPlayer logic from dedicated-server.

- Minecraft.Client/Windows64/Network/WinsockNetLayer.cpp: preserved dedicated-server shutdown flow and also included main-side cleanup for s_smallIdToSocketLock.
@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 7, 2026

It looks like the new feature is working properly.
image

@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 7, 2026

I noticed a new issue.

When the client opens a world in host mode, LAN advertising doesn’t seem to work.

Even with logs added throughout for debugging, it seems to be working, so it might be an environment issue rather than the code. It would be helpful if everyone could test it in their environments as well.

In the end, there’s a 99% chance this issue is specific to my environment.
I got the same result even when building the main branch.

@void2012
Copy link
Collaborator

void2012 commented Mar 7, 2026

Saw this claim on discord

ive seen hackers that uses speedhacks to lag the server with blocks destroying not sure if that counts

Could the protection against this be implemented?

@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 7, 2026

Saw this claim on discord

ive seen hackers that uses speedhacks to lag the server with blocks destroying not sure if that counts

Could the protection against this be implemented?

After looking through the code, it seems that the moved too quickly check is intentionally disabled by 4J.
It might be for fast movement during debugging.

// 4J-PB - removing this one for now

		/*if (dist > 100.0f)
		{
		//            logger.warning(player->name + " moved too quickly!");
		disconnect(DisconnectPacket::eDisconnect_MovedTooQuickly);
		//                System.out.println("Moved too quickly at " + xt + ", " + yt + ", " + zt);
		//                teleport(player->x, player->y, player->z, player->yRot, player->xRot);
		return;
		}
		*/

@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 7, 2026

The same applies to the instant break.

It seems that the server-side block breaking timer check was intentionally commented out by 4J.
According to the comment, it was removed because network instability on the console versions could cause timing desync, making blocks harder to break and negatively affecting gameplay.

// MGH - removed checking for the destroy progress here, it has already been checked on the client before it sent the packet.

			Tile *tile = Tile::tiles[t];

			// MGH -	removed checking for the destroy progress here, it has already been checked on the client before it sent the packet.
			//			fixes issues with this failing to destroy because of packets bunching up
			//             float destroyProgress = tile->getDestroyProgress(player, player->level, x, y, z) * (ticksSpentDestroying + 1);
			//             if (destroyProgress >= .7f || bIgnoreDestroyProgress)
			{
				isDestroyingBlock = false;
				level->destroyTileProgress(player->entityId, x, y, z, -1);
				destroyBlock(x, y, z);
			}
			// 			else if (!hasDelayedDestroy)
			// 			{
			// 				isDestroyingBlock = false;
			//                 hasDelayedDestroy = true;
			//                 delayedDestroyX = x;
			//                 delayedDestroyY = y;
			//                 delayedDestroyZ = z;
			//                 delayedTickStart = destroyProgressStart;
			//             }

@goshavindtburg
Copy link

Are we able to change the autosave interval? Every minute gets kind of annoying and intrusive to gameplay having that screen pop up every minute.

@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 7, 2026

Are we able to change the autosave interval? Every minute gets kind of annoying and intrusive to gameplay having that screen pop up every minute.

Please check server.properties

@goshavindtburg
Copy link

Are we able to change the autosave interval? Every minute gets kind of annoying and intrusive to gameplay having that screen pop up every minute.

Please check server.properties

Very nice, thanks a bunch, hooefully ban ip and kick player commands next :)

@codeHusky
Copy link
Collaborator

codeHusky commented Mar 7, 2026

Considering we’re not in 2012 anymore, I think we can ignore the network instability thing and just prefer something safer that avoids as much cheating. Also pretty classic console game dev to write something like “well we already validated this on the client side so why would we do it server side” lmao

I’d imagine the moving too fast checks might not work 100% properly with creative mode but they’d probably work fine with survival.

Regarding block breaking, there’s definitely some sort of maximum speed you can break blocks at in LCE creative. If there isn’t it wouldn’t be too hard to implement a same clicks-per-second limit before we start rejecting their block breaks

@kuwacom
Copy link
Contributor Author

kuwacom commented Mar 7, 2026

I plan to try restoring these implementations later and see how they behave.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants