using IgniteEngine; using IgniteEngine.IO; using IgniteEngine.Networking; using IgniteEngine.Protocols; using LoginServer.Handlers; using System; using System.Collections.Generic; using System.Data.Odbc; using System.IO; using System.Runtime; using System.Threading; namespace LoginServer { internal class Program { internal static Dictionary CachedAccounts = new Dictionary(); internal static INIFile Config; internal static NetworkServer ClientServer = new NetworkServer(NetworkConnectionType.NCT_CLIENT); internal static OdbcConnection ODBC; internal static List Transfers = new List(); internal static List Worlds = new List(); internal static NetworkServer WorldServer = new NetworkServer(NetworkConnectionType.NCT_WORLDMANAGER); internal static NetworkConnection GameLogServer = new NetworkConnection(NetworkConnectionType.NCT_DB_GAMELOG); 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."); } 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 WorldServer.Listen(Config.GetString("world_server_ip"), (ushort) Config.GetInt16("world_server_port")); ClientServer.Listen(Config.GetString("client_server_ip"), (ushort) Config.GetInt16("client_server_port")); GameLogServer.Connect(Config.GetString("gamelog_server_ip"), (ushort) Config.GetInt16("gamelog_server_port")); stopwatch.Stop(); Debug.Log($"Time taken to start: {stopwatch.ElapsedMilliseconds}ms\n"); // Starts the main server loop. new Thread(() => { while (true) { Update(Time.Milliseconds); Thread.Sleep(10); } }).Start(); // 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($"World Clients: {WorldServer.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_REQ, MiscHandlers.NC_MISC_S2SCONNECTION_REQ); NetworkMessageHandler.Store(NetworkCommand.NC_USER_CLIENT_VERSION_CHECK_REQ, UserHandlers.NC_USER_CLIENT_VERSION_CHECK_REQ); NetworkMessageHandler.Store(NetworkCommand.NC_USER_US_LOGIN_REQ, UserHandlers.NC_USER_US_LOGIN_REQ); NetworkMessageHandler.Store(NetworkCommand.NC_USER_XTRAP_REQ, UserHandlers.NC_USER_XTRAP_REQ); NetworkMessageHandler.Store(NetworkCommand.NC_USER_WORLD_STATUS_REQ, UserHandlers.NC_USER_WORLD_STATUS_REQ); NetworkMessageHandler.Store(NetworkCommand.NC_USER_WORLDSELECT_REQ, UserHandlers.NC_USER_WORLDSELECT_REQ); NetworkMessageHandler.Store(NetworkCommand.NC_USER_NORMALLOGOUT_CMD, UserHandlers.NC_USER_NORMALLOGOUT_CMD); NetworkMessageHandler.Store(NetworkCommand.NC_USER_LOGIN_WITH_OTP_REQ, UserHandlers.NC_USER_LOGIN_WITH_OTP_REQ); NetworkMessageHandler.Store(NetworkCommand.NC_USER_WILLLOGIN_ACK, UserHandlers.NC_USER_WILLLOGIN_ACK); } private static void Update(long now) { Worlds.FilteredAction(world => !world.Connection.IsConnected, world => { world.Status = 0x00; }); // Remove transfers that have been waiting for more that 5 seconds. var timeouts = Transfers.Filter(t => now - t.CreateTime >= 5000); timeouts.ForBackwards(transfer => { new PROTO_NC_USER_LOGINFAIL_ACK(0x49).Send(transfer.Connection); Transfers.RemoveSafe(transfer); }); } } }