diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..3e9e8ac294 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +**/.git +uo-client \ No newline at end of file diff --git a/.gitignore b/.gitignore index 04da663518..e0e51e31d9 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,5 @@ /packages/* /Distribution/Configuration/server-access.json + +uo-client/* diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..68dfa1bc3d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +FROM ubuntu:22.04 + +WORKDIR /tmp + +RUN apt-get update \ + && apt-get install -y vim wget libicu-dev libz-dev zstd libargon2-dev tzdata + +RUN wget https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh +RUN chmod +x ./dotnet-install.sh +RUN ./dotnet-install.sh --version 7.0.400 + +RUN echo 'DOTNET_ROOT=$HOME/.dotnet' >> /root/.bashrc +RUN echo 'PATH=$PATH:$DOTNET_ROOT:$DOTNET_ROOT/tools' >> /root/.bashrc + +WORKDIR /app + +CMD [ "tail", "-f", "/dev/null" ] diff --git a/Projects/Server/Configuration/ServerConfiguration.cs b/Projects/Server/Configuration/ServerConfiguration.cs index 2cb1b6e69c..4fc74a317d 100644 --- a/Projects/Server/Configuration/ServerConfiguration.cs +++ b/Projects/Server/Configuration/ServerConfiguration.cs @@ -263,6 +263,7 @@ public static void Load(bool mocked = false) { updated = true; _settings.Listeners.AddRange(ServerConfigurationPrompts.GetListeners()); + _settings.Settings["serverListing.privateAddress"] = ServerConfigurationPrompts.GetPrivateAddress(); } // We have a known, current expansion, so we can deserialize it from Configuration diff --git a/Projects/Server/Configuration/ServerConfigurationPrompts.cs b/Projects/Server/Configuration/ServerConfigurationPrompts.cs index b1094211ab..2d0905d057 100644 --- a/Projects/Server/Configuration/ServerConfigurationPrompts.cs +++ b/Projects/Server/Configuration/ServerConfigurationPrompts.cs @@ -118,6 +118,49 @@ internal static List GetDataDirectories() return directories; } + internal static string GetPrivateAddress() + { + Console.WriteLine("If the server is running in a Docker container, specify the parent host IP:"); + Console.WriteLine("- 127.0.0.1"); + Console.WriteLine("- Or leave blank to skip"); + + string ipStr; + + do + { + // IP:Port? + Console.Write("[{0}]> ", "enter to finish"); + ipStr = Console.ReadLine(); + + IPEndPoint ip; + if (string.IsNullOrWhiteSpace(ipStr)) + { + ipStr = null; + break; + } + else + { + if (!IPEndPoint.TryParse(ipStr, out ip)) + { + Utility.PushColor(ConsoleColor.Red); + Console.Write(ipStr); + Utility.PopColor(); + Console.WriteLine(" is not a valid IP or port."); + continue; + } + } + + Console.Write("Added "); + Utility.PushColor(ConsoleColor.Green); + Console.Write(ipStr); + Utility.PopColor(); + Console.WriteLine("."); + break; + } while (true); + + return ipStr; + } + internal static List GetListeners() { Console.WriteLine("Please enter the IP and ports to listen:"); diff --git a/Projects/UOContent/Misc/ServerList.cs b/Projects/UOContent/Misc/ServerList.cs index 1b60f14d99..32b76467fd 100644 --- a/Projects/UOContent/Misc/ServerList.cs +++ b/Projects/UOContent/Misc/ServerList.cs @@ -17,6 +17,9 @@ namespace Server.Misc * "serverListing.address": null, * "serverListing.autoDetect": false * + * If you want to run MUO inside a container, you need to setup the private address so that CUO can connect, e.g. + * "serverListing.privateAddress": "127.0.0.1" + * * If you want players outside your LAN to be able to connect to your server and you are behind a router, you must also * forward TCP port 2593 to your private IP address. The procedure for doing this varies by manufacturer but generally * involves configuration of the router through your web browser. @@ -34,6 +37,7 @@ public static class ServerList private static readonly ILogger logger = LogFactory.GetLogger(typeof(ServerList)); private static IPAddress _publicAddress; + public static string PrivateAddress { get; private set; } public static string Address { get; private set; } public static string ServerName { get; private set; } @@ -41,6 +45,7 @@ public static class ServerList public static void Configure() { + PrivateAddress = ServerConfiguration.GetOrUpdateSetting("serverListing.privateAddress", null); Address = ServerConfiguration.GetOrUpdateSetting("serverListing.address", null); AutoDetect = ServerConfiguration.GetOrUpdateSetting("serverListing.autoDetect", true); ServerName = ServerConfiguration.GetOrUpdateSetting("serverListing.serverName", "ModernUO"); @@ -85,6 +90,10 @@ private static void EventSink_ServerList(ServerListEventArgs e) { localAddress = _publicAddress; } + else if (PrivateAddress != null) + { + Resolve(PrivateAddress, out localAddress); + } } e.AddServer(ServerName, new IPEndPoint(localAddress, localPort)); diff --git a/README.md b/README.md index 228c3c326d..c82c358caf 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,28 @@ apt-get install -y libicu-dev libz-dev zstd libargon2-dev tzdata - Follow the [publish](https://github.com/modernuo/ModernUO#publishing-builds) instructions - Run `ModernUO.exe` or `dotnet ModernUO.dll` from the `Distribution` directory on the +## Running with Docker + +- Download a game client: https://uo.com/client-download/ +- Install the downloaded game client and run it to download the game assets +- Copy game assets into the `uo-client` directory in the project. This folder gets mounted into the Docker container under the `/app/uo-client` path. + +Once that's done, run the following command to make a build + +``` +docker-compose run --build --rm compile [debug] +``` + + +Finally, run the command below to start the server. +If this is your first run, you'll need to specify the game assets path, which is `/app/uo-client`. +If you plan connecting from the host machine, you'll also need to specify the private IP (e.g. `127.0.0.1`) when prompted during the first run (or change it later in the `modernuo.json`). + +``` +docker-compose run --service-ports --rm server +``` + + ## Troubleshooting / FAQ - See [FAQ](./FAQ.md) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..19f0ff388c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +version: "3" +services: + + compile: + volumes: [".:/app:delegated"] + build: . + restart: always + entrypoint: ["/usr/bin/bash", "-i", "./publish.sh"] + profiles: ["build-compile"] + + server: + volumes: [".:/app:delegated"] + build: . + restart: always + ports: ["2593:2593/udp", "2593:2593/tcp"] + entrypoint: ["/root/.dotnet/dotnet", "/app/Distribution/ModernUO.dll"] + profiles: ["run-server"] + + app: + volumes: [".:/app:delegated"] + build: . + restart: always + ports: ["2593:2593/udp", "2593:2593/tcp"]