diff --git a/COTLMP.sln b/COTLMP.sln
index f545747..29cfdec 100644
--- a/COTLMP.sln
+++ b/COTLMP.sln
@@ -1,12 +1,14 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.14.36310.24
+# Visual Studio Version 18
+VisualStudioVersion = 18.5.11723.231
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "COTLMP", "COTLMP\COTLMP.csproj", "{75CAB2D0-F823-DEFE-5EF0-A25BC117280F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "COTLMPServer", "COTLMPServer\COTLMPServer.csproj", "{9476AA40-C94F-43C7-BF81-9427F4329E8F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DedicatedServer", "DedicatedServer\DedicatedServer.csproj", "{1D6C6C2A-38F4-4574-918D-24A33F1FB8C6}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
{9476AA40-C94F-43C7-BF81-9427F4329E8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9476AA40-C94F-43C7-BF81-9427F4329E8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9476AA40-C94F-43C7-BF81-9427F4329E8F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1D6C6C2A-38F4-4574-918D-24A33F1FB8C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1D6C6C2A-38F4-4574-918D-24A33F1FB8C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1D6C6C2A-38F4-4574-918D-24A33F1FB8C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1D6C6C2A-38F4-4574-918D-24A33F1FB8C6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/COTLMP/COTLMP.csproj b/COTLMP/COTLMP.csproj
index abfb6fa..00208d6 100644
--- a/COTLMP/COTLMP.csproj
+++ b/COTLMP/COTLMP.csproj
@@ -5,6 +5,7 @@
COTLMP
Cult of the Lamb Multiplayer Mod Plug-In
0.0.0.5
+ Copyright (c) 2026 GeoB99 & Neco-Arc
true
latest
diff --git a/COTLMPServer/COTLMPServer.csproj b/COTLMPServer/COTLMPServer.csproj
index 939a2ed..220779e 100644
--- a/COTLMPServer/COTLMPServer.csproj
+++ b/COTLMPServer/COTLMPServer.csproj
@@ -2,6 +2,10 @@
netstandard2.0
+ Cult of the Lamb Core Server Library
+ 0.0.0.5
+ Copyright (c) 2026 GeoB99 & Neco-Arc
+ COTLMPServer
diff --git a/DedicatedServer/Cli.cs b/DedicatedServer/Cli.cs
new file mode 100644
index 0000000..08d8443
--- /dev/null
+++ b/DedicatedServer/Cli.cs
@@ -0,0 +1,329 @@
+/*
+ * PROJECT: Cult of the Lamb Multiplayer Mod
+ * LICENSE: MIT (https://spdx.org/licenses/MIT)
+ * PURPOSE: Core dedicated server CLI parser
+ * COPYRIGHT: Copyright 2026 GeoB99
+ */
+
+/* IMPORTS ********************************************************************/
+
+using System;
+using System.IO;
+using System.Reflection;
+using CommandLine;
+using Newtonsoft.Json;
+
+/* CLASSES & CODE *************************************************************/
+
+namespace DedicatedServer
+{
+ internal static class Cli
+ {
+ internal static ConsoleLogger Logger;
+ internal static bool HasOptions = true;
+ internal static bool HasConfigFile = true;
+ internal const string ConfigFile = "ServerSettings.json";
+ internal static string PathLocation = Directory.GetCurrentDirectory();
+ internal static Version ServerVersion = Assembly.GetExecutingAssembly().GetName().Version;
+
+ ///
+ /// Enumeration class for the supported game mode values.
+ ///
+ private enum GameModes
+ {
+ Standard = 0,
+ Deathmatch,
+ BossFight,
+ Zombies
+ }
+
+ ///
+ /// Translates the game mode enum value to a readable string.
+ ///
+ /// The game mode enum value to be passed.
+ /// Returns the name string of the game mode.
+ private static string TranslateGameModeToString(GameModes Modes)
+ {
+ string Mode;
+
+ switch (Modes)
+ {
+ case GameModes.Standard:
+ {
+ Mode = "Standard";
+ break;
+ }
+
+ case GameModes.Deathmatch:
+ {
+ Mode = "Deathmatch";
+ break;
+ }
+
+ case GameModes.BossFight:
+ {
+ Mode = "Boss Fight";
+ break;
+ }
+
+ case GameModes.Zombies:
+ {
+ Mode = "Zombies";
+ break;
+ }
+
+ default:
+ {
+ Mode = null;
+ break;
+ }
+ }
+
+ return Mode;
+ }
+
+ ///
+ /// Initializes the server configuration options to defaults.
+ /// The dedicated server writes the defaults only if the user hasn't provided any other server option.
+ ///
+ /// Returns the default server config data to the caller.
+ private static ServerConfig InitializeConfigDefaults()
+ {
+ ServerConfig Config;
+
+ Config = new ServerConfig
+ {
+ PortNumber = 36963,
+ ServerName = "Cult of The Lamb Server",
+ MaxPlayers = 12,
+ Password = null,
+ GameMode = 0
+ };
+
+ return Config;
+ }
+
+ ///
+ /// Validates the server argument options passed by the parser.
+ ///
+ /// The dedicated server options class of which option parameters are to be validated.
+ /// Returns true if the options are valid, false otherwise.
+ private static bool ValidateParams(DedicatedServerOptions Options)
+ {
+ int CharIndex;
+
+ /* There's no options passed to validate */
+ if (!HasOptions)
+ {
+ return true;
+ }
+
+ /* Validate the required options */
+ if ((Options.PortNumber == 0 || Options.PortNumber > 65535) ||
+ string.IsNullOrEmpty(Options.ServerName) ||
+ (Options.MaxPlayers == 0 || Options.MaxPlayers > 12))
+ {
+ return false;
+ }
+
+ /*
+ * Validate the optional options (game mode must be one of the
+ * valid mode values and the password mustn't have white spaces).
+ */
+ if (Options.GameMode > (uint)GameModes.Zombies)
+ {
+ return false;
+ }
+
+ if (!string.IsNullOrWhiteSpace(Options.Password))
+ {
+ for (CharIndex = 0; CharIndex < Options.Password.Length; CharIndex++)
+ {
+ if (Char.IsWhiteSpace(Options.Password[CharIndex]))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ /// Starts the standalone server.
+ ///
+ /// An object to a server configuration class that contains server data used to start the server.
+ private static void StartServer(ServerConfig Config)
+ {
+ // TODO: Implement this when the Server class interface is implemented
+ // FIXME: Log to the console the IP address (from COTLMPServer) of the server being started
+ Logger.LogInfo("The server has been started! Type \u001b[94mquit\x1b[97m into the console to gracefully shutdown the server.");
+ }
+
+ ///
+ /// Creates (or loads) the server configuration file upon server startup.
+ ///
+ /// The dedicated server options needed to start the create the server config file.
+ private static void SetupConfigServer(DedicatedServerOptions Options)
+ {
+ ServerConfig Config;
+ string AbsolutePath, JsonData;
+
+ /* Bail out if the server options are not valid */
+ if (!ValidateParams(Options))
+ {
+ Logger.LogFatal("Failed to create the server, at least one of the option parameters aren't valid:\n" +
+ $"\n---> Port number expected between 1 and 65535 range, got {Options.PortNumber}" +
+ "\n---> Server name expected to be non-null" +
+ $"\n---> Maximum players expected to be up to 12, got {Options.MaxPlayers}" +
+ $"\n---> Game mode expected to be within supported game modes, got {Options.GameMode}" +
+ "\n---> Password expected to not have white spaces\n");
+ return;
+ }
+
+ /*
+ * The server config file never existed, create one based on the provided
+ * server options. Or write down our defaults in case no options were provided.
+ */
+ AbsolutePath = Path.Combine(PathLocation, ConfigFile);
+ if (!HasConfigFile)
+ {
+ if (!HasOptions)
+ {
+ Config = InitializeConfigDefaults();
+ }
+ else
+ {
+ Logger.LogInfo($"No \x1b[94m{ConfigFile}\x1b[97m file could be found, creating server configuration file with provided options on first run...");
+
+ /*
+ * HACK: Always hardcode the game mode to Standard if other mode was submitted.
+ * Because we don't support any other game modes other than the standard one....
+ */
+ if (Options.GameMode != (uint)GameModes.Standard)
+ {
+ Logger.LogWarning($"{TranslateGameModeToString((GameModes)Options.GameMode)} is currently not supported as a game mode, defaulting to Standard...");
+ Options.GameMode = (uint)GameModes.Standard;
+ }
+
+ Config = new ServerConfig
+ {
+ PortNumber = Options.PortNumber,
+ ServerName = Options.ServerName,
+ MaxPlayers = Options.MaxPlayers,
+ Password = Options.Password,
+ GameMode = Options.GameMode
+ };
+ }
+
+ JsonData = JsonConvert.SerializeObject(Config, Formatting.Indented);
+ File.WriteAllText(AbsolutePath, JsonData);
+
+ Logger.LogInfo($"Starting server with name \x1b[92m{Config.ServerName}\x1b[97m (Version: \x1b[92m{ServerVersion}\x1b[97m)");
+ StartServer(Config);
+ return;
+ }
+
+ /* The server config file exists, deserealize the data from it */
+ Config = JsonConvert.DeserializeObject(File.ReadAllText(AbsolutePath));
+ if (Config == null)
+ {
+ Logger.LogFatal("Failed to create the server, couldn't read the server configuration file (might be corrupt)." +
+ $"Please delete the {ConfigFile} file and start the server with new server options!");
+ return;
+ }
+
+ /*
+ * The general rule is to always start the server using the data from the loaded
+ * config file but the parsed options (if the user ever passed them) might diverge
+ * from that of the ones from the file. So overwrite the config file with whatever
+ * has been parsed and use the newly overwritten data.
+ */
+ if (HasOptions)
+ {
+ if (Config.PortNumber != Options.PortNumber)
+ {
+ Config.PortNumber = Options.PortNumber;
+ }
+
+ if (Config.ServerName != Options.ServerName)
+ {
+ Config.ServerName = Options.ServerName;
+ }
+
+ if (Config.MaxPlayers != Options.MaxPlayers)
+ {
+ Config.MaxPlayers = Options.MaxPlayers;
+ }
+
+ if (Config.Password != Options.Password)
+ {
+ Config.Password = Options.Password;
+ }
+
+ if (Config.GameMode != Options.GameMode)
+ {
+ /*
+ * HACK: Always hardcode the game mode to Standard if other mode was submitted.
+ * Because we don't support any other game modes other than the standard one....
+ */
+ if (Options.GameMode != (uint)GameModes.Standard)
+ {
+ Logger.LogWarning($"{TranslateGameModeToString((GameModes)Options.GameMode)} is currently not supported as a game mode, defaulting to Standard...");
+ Options.GameMode = (uint)GameModes.Standard;
+ }
+
+ Config.GameMode = Options.GameMode;
+ }
+
+ JsonData = JsonConvert.SerializeObject(Config, Formatting.Indented);
+ File.WriteAllText(AbsolutePath, JsonData);
+ }
+
+ Logger.LogInfo($"Starting server with name \x1b[92m{Config.ServerName}\x1b[97m (Version: \x1b[92m{ServerVersion}\x1b[97m)");
+ StartServer(Config);
+ }
+
+ ///
+ /// Parses the server argument options and initializes the server based on the passed options.
+ ///
+ /// The command line arguments passed from the main entry point.
+ public static void Initialize(string[] Arguments)
+ {
+ Logger = new ConsoleLogger();
+
+ /*
+ * The server config file doesn't exist and the user did not pass server options.
+ * In this case create the config file using the defaults we provide.
+ * Otherwise use the server options passed by the user to write down the config file.
+ */
+ if (!File.Exists(Path.Combine(PathLocation, ConfigFile)))
+ {
+ HasConfigFile = false;
+
+ if (Arguments.Length == 0)
+ {
+ Logger.LogInfo($"No \x1b[94m{ConfigFile}\x1b[97m file could be found, creating server configuration file with defaults on first run...");
+ HasOptions = false;
+ }
+ }
+ else
+ {
+ /*
+ * The user has passed no arguments but the server config file is present.
+ * Load the said file and start the server based on that.
+ */
+ if (Arguments.Length == 0)
+ {
+ HasOptions = false;
+ }
+ }
+
+ /* Pass down the option arguments to the parser and call the parser callback */
+ CommandLine.Parser.Default.ParseArguments(Arguments)
+ .WithParsed(SetupConfigServer);
+ }
+ }
+}
+
+/* EOF */
diff --git a/DedicatedServer/ConsoleLogger.cs b/DedicatedServer/ConsoleLogger.cs
new file mode 100644
index 0000000..16d89f6
--- /dev/null
+++ b/DedicatedServer/ConsoleLogger.cs
@@ -0,0 +1,64 @@
+/*
+ * PROJECT: Cult of the Lamb Multiplayer Mod
+ * LICENSE: MIT (https://spdx.org/licenses/MIT)
+ * PURPOSE: Console logger methods
+ * COPYRIGHT: Copyright 2026 GeoB99
+ */
+
+/* IMPORTS ********************************************************************/
+
+using System;
+
+/* CLASSES & CODE *************************************************************/
+
+namespace DedicatedServer
+{
+ public class ConsoleLogger : COTLMPServer.ILogger
+ {
+ ///
+ /// Displays a fatal message to the console.
+ ///
+ /// The message string to be passed to this method.
+ public void LogFatal(string Message)
+ {
+ Console.ForegroundColor = ConsoleColor.DarkRed;
+ Console.WriteLine("Fatal: " + Message);
+ Console.ResetColor();
+ }
+
+ ///
+ /// Displays a normal message to the console.
+ ///
+ /// The message string to be passed to this method.
+ public void LogError(string Message)
+ {
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.WriteLine("Error: " + Message);
+ Console.ResetColor();
+ }
+
+ ///
+ /// Displays a warning message to the console.
+ ///
+ /// The message string to be passed to this method.
+ public void LogWarning(string Message)
+ {
+ Console.ForegroundColor = ConsoleColor.Yellow;
+ Console.WriteLine("Warning: " + Message);
+ Console.ResetColor();
+ }
+
+ ///
+ /// Displays an informational message to the console.
+ ///
+ /// The message string to be passed to this method.
+ public void LogInfo(string Message)
+ {
+ Console.ForegroundColor = ConsoleColor.White;
+ Console.WriteLine(Message);
+ Console.ResetColor();
+ }
+ }
+}
+
+/* EOF */
diff --git a/DedicatedServer/DedicatedServer.csproj b/DedicatedServer/DedicatedServer.csproj
new file mode 100644
index 0000000..df4d032
--- /dev/null
+++ b/DedicatedServer/DedicatedServer.csproj
@@ -0,0 +1,22 @@
+
+
+
+ Exe
+ net10.0
+ CultOfTheLambServer
+ CultOfTheLambServer
+ Cult of the Lamb Standalone Dedicated Server
+ 0.0.0.5
+ Copyright (c) 2026 GeoB99 & Neco-Arc
+ DedicatedServer
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DedicatedServer/DedicatedServerOptions.cs b/DedicatedServer/DedicatedServerOptions.cs
new file mode 100644
index 0000000..9dcd444
--- /dev/null
+++ b/DedicatedServer/DedicatedServerOptions.cs
@@ -0,0 +1,40 @@
+/*
+ * PROJECT: Cult of the Lamb Multiplayer Mod
+ * LICENSE: MIT (https://spdx.org/licenses/MIT)
+ * PURPOSE: Dedicated server command line options
+ * COPYRIGHT: Copyright 2026 GeoB99
+ */
+
+/* IMPORTS ********************************************************************/
+
+using System;
+using CommandLine;
+
+/* CLASSES & CODE *************************************************************/
+
+namespace DedicatedServer
+{
+ ///
+ /// Options class that contains the dedicated server command line options to be
+ /// passed to the dedicated server command line interface.
+ ///
+ internal class DedicatedServerOptions
+ {
+ [Value(0, Required = false, HelpText = "Port number used to create and estabilish server connection.")]
+ public ushort PortNumber { get; set; }
+
+ [Option('n', "Name", Required = false, HelpText = "The name of the server used to setup the dedicated server.")]
+ public string ServerName { get; set; }
+
+ [Option('c', "Count", Required = false, HelpText = "Number of allowed players to join the server.")]
+ public uint MaxPlayers { get; set; }
+
+ [Option('p', "Password", Required = false, HelpText = "(Optional) Setup a password to protect the server from players joining your server.")]
+ public string Password { get; set; }
+
+ [Option('g', "Game", Required = false, HelpText = "(Optional) The game-mode to use when creating the server. This option accepts values. The following supported game modes are: Standard (0), Deathmatch (1), Boss Fight (2) and Zombies (3) (only Standard is the supported game mode at the moment). Default = Standard.")]
+ public uint GameMode { get; set; }
+ }
+}
+
+/* EOF */
diff --git a/DedicatedServer/Launcher.cs b/DedicatedServer/Launcher.cs
new file mode 100644
index 0000000..31b3a2c
--- /dev/null
+++ b/DedicatedServer/Launcher.cs
@@ -0,0 +1,29 @@
+/*
+ * PROJECT: Cult of the Lamb Multiplayer Mod
+ * LICENSE: MIT (https://spdx.org/licenses/MIT)
+ * PURPOSE: Main entry point dedicated server launcher
+ * COPYRIGHT: Copyright 2026 GeoB99
+ */
+
+/* IMPORTS ********************************************************************/
+
+using System;
+
+/* CLASSES & CODE *************************************************************/
+
+namespace DedicatedServer
+{
+ internal static class Launcher
+ {
+ ///
+ /// Main entry point for COTLMP Standalone Dedicated Server.
+ ///
+ /// Command line arguments for the server.
+ public static void Main(string[] args)
+ {
+ DedicatedServer.Cli.Initialize(args);
+ }
+ }
+}
+
+/* EOF */
diff --git a/DedicatedServer/ServerConfig.cs b/DedicatedServer/ServerConfig.cs
new file mode 100644
index 0000000..913fe67
--- /dev/null
+++ b/DedicatedServer/ServerConfig.cs
@@ -0,0 +1,39 @@
+/*
+ * PROJECT: Cult of the Lamb Multiplayer Mod
+ * LICENSE: MIT (https://spdx.org/licenses/MIT)
+ * PURPOSE: Main entry point dedicated server launcher
+ * COPYRIGHT: Copyright 2026 GeoB99
+ */
+
+/* IMPORTS ********************************************************************/
+
+using System;
+using Newtonsoft.Json;
+
+/* CLASSES & CODE *************************************************************/
+
+namespace DedicatedServer
+{
+ ///
+ /// Server configuration class used to denote a JSON object containing server data.
+ ///
+ internal class ServerConfig
+ {
+ [JsonProperty]
+ internal ushort PortNumber { get; set; }
+
+ [JsonProperty]
+ internal string ServerName { get; set; }
+
+ [JsonProperty]
+ internal uint MaxPlayers { get; set; }
+
+ [JsonProperty]
+ internal string Password { get; set; }
+
+ [JsonProperty]
+ internal uint GameMode { get; set; }
+ }
+}
+
+/* EOF */