using IgniteEngine; using IgniteEngine.Content.GameObjects; using IgniteEngine.Definitions.SHN; using IgniteEngine.IO; using IgniteEngine.Networking; using System; using System.Collections.Generic; using System.Data.Odbc; using System.IO; using System.Runtime; using System.Threading; using WorldManagerServer.Handlers; namespace WorldManagerServer { internal class Program { internal static INIFile Config; internal static NetworkServer ClientServer = new NetworkServer(NetworkConnectionType.NCT_CLIENT); internal static NetworkConnection LoginServer = new NetworkConnection(NetworkConnectionType.NCT_LOGIN); internal static NetworkConnection GameLogServer = new NetworkConnection(NetworkConnectionType.NCT_DB_GAMELOG); internal static OdbcConnection ODBC; internal static string ShinePath => Config.GetString("shine_data"); internal static List Transfers = new List(); internal static NetworkServer ZoneServer = new NetworkServer(NetworkConnectionType.NCT_ZONE); /* Shine Data */ internal static Dictionary DefaultCharacterData = new Dictionary(); internal static ObjectCollection FaceInfo = new ObjectCollection(); internal static ObjectCollection HairInfo = new ObjectCollection(); internal static ObjectCollection HairColorInfo = new ObjectCollection(); internal static ObjectCollection SingleData = new ObjectCollection(); private static readonly string configFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{Assembly.Name}.ini"); internal static void Main() { var stopwatch = new System.Diagnostics.Stopwatch(); stopwatch.Start(); Console.Title = $"{Assembly.Name} {Assembly.Version}{(System.Diagnostics.Debugger.IsAttached ? " (Debugging)" : "")} "; Debug.Log($"Local Time: {DateTime.Now}"); Debug.Log($"System (UTC) Time: {DateTime.UtcNow}\n"); // Check application recommendations if (!Environment.Is64BitProcess) { Debug.LogWarning("The process is currently not in 64-bit mode. Performance issues may occur. Press any key to continue code execution."); Console.ReadKey(); } if (!GCSettings.IsServerGC) { Debug.LogWarning("Garbage collection is not configured to server mode.\n"); } Debug.Log($"Loading configuration file '{Path.GetFileName(configFilePath)}'."); // Load configuration if (!File.Exists(configFilePath)) { Debug.LogError("Configuration file was not found, please make sure it exists and has the correct properties. Press any key to quit."); Console.ReadKey(); return; } Config = new INIFile(configFilePath, "Config"); Debug.Log("Configuration file loaded successfully."); Debug.Log("Launching the server...\n"); // Connect to the database InitDBConnection(); // Store handlers StoreHandlers(); // Setup networking // Connect to the login server before actually listening for anything. GameLogServer.Connect(Config.GetString("gamelog_server_ip"), (ushort)Config.GetInt16("gamelog_server_port")); LoginServer.Connect(Config.GetString("login_server_ip"), (ushort) Config.GetInt16("login_server_port")); ZoneServer.Listen(Config.GetString("zone_server_ip"), (ushort) Config.GetInt16("zone_server_port")); ClientServer.Listen(Config.GetString("client_server_ip"), (ushort) Config.GetInt16("client_server_port")); new Thread(() => { while (true) { Update(Time.Milliseconds); Thread.Sleep(10); } }).Start(); stopwatch.Stop(); Debug.Log($"Time taken to start: {stopwatch.ElapsedMilliseconds}ms\n"); // Handle console commands while (true) { if (Console.ReadKey(true).Key == ConsoleKey.Enter) { Console.Write("$ "); var input = Console.ReadLine(); if (input?.Length > 0) { var parts = input.Split(new[] { ' ' }, 2); if (parts.Length > 1) { HandleConsoleCommand(parts[0], parts[1].Split(' ')); continue; } HandleConsoleCommand(parts[0], null); } } } } private static void HandleConsoleCommand(string command, string[] args) { switch (command.ToLower()) { case "clear": Console.Clear(); break; case "performance": Debug.Log($"CPU: {Profiler.CPUUsage}% RAM: {Profiler.RAMUsage}MB"); break; case "clients": Debug.Log($"Zone Clients: {ZoneServer.Connections.Count}"); Debug.Log($"Game Clients: {ClientServer.Connections.Count}"); break; } } private static void InitDBConnection() { try { ODBC = new OdbcConnection(Config.GetString("odbc_connection_string")); ODBC.Open(); var startupCommand = ODBC.CreateCommand(); startupCommand.CommandText = Config.GetString("odbc_startup_command"); startupCommand.ExecuteNonQuery(); } catch (Exception e) { Debug.LogException($"Could not initialize the ODBC database connection: {e.Message}"); } } private static void StoreHandlers() { NetworkMessageHandler.Store(NetworkCommand.NC_MISC_SEED_ACK, MiscHandlers.NC_MISC_SEED_ACK); NetworkMessageHandler.Store(NetworkCommand.NC_MISC_S2SCONNECTION_RDY, MiscHandlers.NC_MISC_S2SCONNECTION_RDY); NetworkMessageHandler.Store(NetworkCommand.NC_MISC_S2SCONNECTION_ACK, MiscHandlers.NC_MISC_S2SCONNECTION_ACK); NetworkMessageHandler.Store(NetworkCommand.NC_MISC_GAMETIME_REQ, MiscHandlers.NC_MISC_GAMETIME_REQ); NetworkMessageHandler.Store(NetworkCommand.NC_USER_WILLLOGIN_REQ, UserHandlers.NC_USER_WILLLOGIN_REQ); NetworkMessageHandler.Store(NetworkCommand.NC_USER_LOGINWORLD_REQ, UserHandlers.NC_USER_LOGINWORLD_REQ); NetworkMessageHandler.Store(NetworkCommand.NC_USER_NORMALLOGOUT_CMD, UserHandlers.NC_USER_NORMALLOGOUT_CMD); NetworkMessageHandler.Store(NetworkCommand.NC_AVATAR_CREATE_REQ, AvatarHandlers.NC_AVATAR_CREATE_REQ); NetworkMessageHandler.Store(NetworkCommand.NC_AVATAR_ERASE_REQ, AvatarHandlers.NC_AVATAR_ERASE_REQ); } private static void Update(long now) { // Remove transfers that have been waiting for more that 5 seconds. var timeouts = Transfers.Filter(t => now - t.CreateTime >= 5000); timeouts.ForBackwards(Transfers.RemoveSafe); } } }