private static void SetupClient(TcpClient newClientConnection) { ClientObject newClientObject = new ClientObject(); newClientObject.subspace = Messages.WarpControl.GetLatestSubspace(); newClientObject.playerStatus = new PlayerStatus(); newClientObject.connectionStatus = ConnectionStatus.CONNECTED; newClientObject.endpoint = newClientConnection.Client.RemoteEndPoint.ToString(); newClientObject.ipAddress = (newClientConnection.Client.RemoteEndPoint as IPEndPoint).Address; //Keep the connection reference newClientObject.connection = newClientConnection; newClientObject.connection.NoDelay = true; StartReceivingIncomingMessages(newClientObject); StartSendingOutgoingMessages(newClientObject); DMPPluginHandler.FireOnClientConnect(newClientObject); Messages.Handshake.SendHandshakeChallange(newClientObject); lock (clients) { List <ClientObject> newList = new List <ClientObject>(clients); newList.Add(newClientObject); clients = newList.AsReadOnly(); Server.playerCount = GetActiveClientCount(); Server.players = GetActivePlayerNames(); DarkLog.Debug("Online players is now: " + Server.playerCount + ", connected: " + clients.Count); } }
internal static void DisconnectClient(ClientObject client) { lock (client.disconnectLock) { //Remove clients from list if (clients.Contains(client)) { List<ClientObject> newList = new List<ClientObject>(clients); newList.Remove(client); clients = newList.AsReadOnly(); Server.playerCount = GetActiveClientCount(); Server.players = GetActivePlayerNames(); DarkLog.Debug("Online players is now: " + Server.playerCount + ", connected: " + clients.Count); if (!Settings.settingsStore.keepTickingWhileOffline && clients.Count == 0) { Messages.WarpControl.HoldSubspace(); } Messages.WarpControl.DisconnectPlayer(client.playerName); } //Disconnect if (client.connectionStatus != ConnectionStatus.DISCONNECTED) { DMPPluginHandler.FireOnClientDisconnect(client); if (client.playerName != null) { Messages.Chat.RemovePlayer(client.playerName); } client.connectionStatus = ConnectionStatus.DISCONNECTED; if (client.authenticated) { ServerMessage newMessage = new ServerMessage(); newMessage.type = ServerMessageType.PLAYER_DISCONNECT; using (MessageWriter mw = new MessageWriter()) { mw.Write<string>(client.playerName); newMessage.data = mw.GetMessageBytes(); } SendToAll(client, newMessage, true); LockSystem.fetch.ReleasePlayerLocks(client.playerName); } try { if (client.connection != null) { client.connection.GetStream().Close(); client.connection.Close(); } } catch (Exception e) { DarkLog.Debug("Error closing client connection: " + e.Message); } Server.lastPlayerActivity = Server.serverClock.ElapsedMilliseconds; } } }
private void DisconnectClient(ClientObject client, string aReason) { if (client.connectionStatus != ConnectionStatus.DISCONNECTED) { DMPPluginHandler.FireOnClientDisconnect(client); if (client.playerName != null) { if (playerChatChannels.ContainsKey(client.playerName)) { playerChatChannels.Remove(client.playerName); } if (playerDownloadedScreenshotIndex.ContainsKey(client.playerName)) { playerDownloadedScreenshotIndex.Remove(client.playerName); } if (playerUploadedScreenshotIndex.ContainsKey(client.playerName)) { playerUploadedScreenshotIndex.Remove(client.playerName); } if (playerWatchScreenshot.ContainsKey(client.playerName)) { playerWatchScreenshot.Remove(client.playerName); } } client.connectionStatus = ConnectionStatus.DISCONNECTED; if (client.authenticated) { Messages.ServerClient_PlayerLeaveSend msg = new Messages.ServerClient_PlayerLeaveSend(); msg.name = client.playerName; BroadcastBut(client, msg); lockSystem.ReleasePlayerLocks(client.playerName); } m_clients.Remove(client); try { m_server.Kick(client.Id, aReason); } catch (Exception e) { DarkLog.Debug("Error closing client connection: " + e.Message); } Server.lastPlayerActivity = Server.serverClock.ElapsedMilliseconds; } }
private void HandleMessage(IMessage aMessage) { if (DMPPluginHandler.FireOnMessageReceived(aMessage)) { return; } try { Trigger(aMessage); } catch { //DarkLog.Debug("Error handling " + typeof(aMessage).ToString() + " from " + client.playerName + ", exception: " + e); m_server.Kick(aMessage.ConnectionId, "Server failed to process " + aMessage.GetType().ToString() + " message"); } }
public void Update() { try { m_clients.Flush(); // TODO: Save subspace if player leaves and count == 0 m_server.Run(); // Dispatch events Run(); //Check timers NukeKSC.CheckTimer(); Dekessler.CheckTimer(); //Run plugin update DMPPluginHandler.FireOnUpdate(); } catch (Exception e) { DarkLog.Error("Fatal error thrown, exception: " + e); Server.ShutDown("Crashed!"); } }
public static void Main() { #if !DEBUG try { #endif //Start the server clock serverClock = new Stopwatch(); serverClock.Start(); Settings.Reset(); //Set the last player activity time to server start lastPlayerActivity = serverClock.ElapsedMilliseconds; //Periodic garbage collection long lastGarbageCollect = 0; //Periodic screenshot check long lastScreenshotExpiredCheck = 0; //Periodic log check long lastLogExpiredCheck = 0; //Periodic day check long lastDayCheck = 0; //Set universe directory and modfile path universeDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Universe"); modFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DMPModControl.txt"); if (!Directory.Exists(configDirectory)) { Directory.CreateDirectory(configDirectory); } string oldSettingsFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DMPServerSettings.txt"); string newSettingsFile = Path.Combine(Server.configDirectory, "Settings.txt"); string oldGameplayFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DMPGameplaySettings.txt"); string newGameplayFile = Path.Combine(Server.configDirectory, "GameplaySettings.txt"); // Run the conversion BackwardsCompatibility.ConvertSettings(oldSettingsFile, newSettingsFile); if (File.Exists(oldGameplayFile)) { if (!File.Exists(newGameplayFile)) { File.Move(oldGameplayFile, newGameplayFile); } File.Delete(oldGameplayFile); } //Register the server commands CommandHandler.RegisterCommand("exit", Server.ShutDown, "Shuts down the server"); CommandHandler.RegisterCommand("quit", Server.ShutDown, "Shuts down the server"); CommandHandler.RegisterCommand("shutdown", Server.ShutDown, "Shuts down the server"); CommandHandler.RegisterCommand("restart", Server.Restart, "Restarts the server"); CommandHandler.RegisterCommand("kick", KickCommand.KickPlayer, "Kicks a player from the server"); CommandHandler.RegisterCommand("ban", BanSystem.fetch.BanPlayer, "Bans a player from the server"); CommandHandler.RegisterCommand("banip", BanSystem.fetch.BanIP, "Bans an IP Address from the server"); CommandHandler.RegisterCommand("bankey", BanSystem.fetch.BanPublicKey, "Bans a Guid from the server"); CommandHandler.RegisterCommand("pm", PMCommand.HandleCommand, "Sends a message to a player"); CommandHandler.RegisterCommand("admin", AdminCommand.HandleCommand, "Sets a player as admin/removes admin from the player"); CommandHandler.RegisterCommand("whitelist", WhitelistCommand.HandleCommand, "Change the server whitelist"); //Register the ctrl+c event Console.CancelKeyPress += new ConsoleCancelEventHandler(CatchExit); serverStarting = true; //Fix kerbals from 0.23.5 to 0.24 (Now indexed by string, thanks Squad! BackwardsCompatibility.FixKerbals(); //Remove player tokens BackwardsCompatibility.RemoveOldPlayerTokens(); //Add new stock parts BackwardsCompatibility.UpdateModcontrolPartList(); if (System.Net.Sockets.Socket.OSSupportsIPv6) { Settings.settingsStore.address = "::"; } DarkLog.Debug("Loading settings..."); Settings.Load(); if (Settings.settingsStore.gameDifficulty == GameDifficulty.CUSTOM) { GameplaySettings.Reset(); GameplaySettings.Load(); } //Test compression if (Settings.settingsStore.compressionEnabled) { long testTime = Compression.TestSysIOCompression(); Compression.compressionEnabled = true; DarkLog.Debug("System.IO compression works: " + Compression.sysIOCompressionWorks + ", test time: " + testTime + " ms."); } //Set day for log change day = DateTime.Now.Day; //Load plugins DMPPluginHandler.LoadPlugins(); Console.Title = "DMPServer " + Common.PROGRAM_VERSION + ", protocol " + Common.PROTOCOL_VERSION; while (serverStarting || serverRestarting) { if (serverRestarting) { DarkLog.Debug("Reloading settings..."); Settings.Reset(); Settings.Load(); if (Settings.settingsStore.gameDifficulty == GameDifficulty.CUSTOM) { DarkLog.Debug("Reloading gameplay settings..."); GameplaySettings.Reset(); GameplaySettings.Load(); } } serverRestarting = false; DarkLog.Normal("Starting DMPServer " + Common.PROGRAM_VERSION + ", protocol " + Common.PROTOCOL_VERSION); if (Settings.settingsStore.gameDifficulty == GameDifficulty.CUSTOM) { //Generate the config file by accessing the object. DarkLog.Debug("Loading gameplay settings..."); GameplaySettings.Load(); } //Load universe DarkLog.Normal("Loading universe... "); CheckUniverse(); DarkLog.Normal("Starting " + Settings.settingsStore.warpMode + " server on port " + Settings.settingsStore.port + "... "); serverRunning = true; Thread commandThread = new Thread(new ThreadStart(CommandHandler.ThreadMain)); Thread clientThread = new Thread(new ThreadStart(ClientHandler.ThreadMain)); commandThread.Start(); clientThread.Start(); while (serverStarting) { Thread.Sleep(500); } StartHTTPServer(); DarkLog.Normal("Ready!"); DMPPluginHandler.FireOnServerStart(); while (serverRunning) { //Run a garbage collection every 30 seconds. if ((serverClock.ElapsedMilliseconds - lastGarbageCollect) > 30000) { lastGarbageCollect = serverClock.ElapsedMilliseconds; GC.Collect(); } //Run the screenshot expire function every 10 minutes if ((serverClock.ElapsedMilliseconds - lastScreenshotExpiredCheck) > 600000) { lastScreenshotExpiredCheck = serverClock.ElapsedMilliseconds; ScreenshotExpire.ExpireScreenshots(); } //Run the log expire function every 10 minutes if ((serverClock.ElapsedMilliseconds - lastLogExpiredCheck) > 600000) { lastLogExpiredCheck = serverClock.ElapsedMilliseconds; LogExpire.ExpireLogs(); } // Check if the day has changed, every minute if ((serverClock.ElapsedMilliseconds - lastDayCheck) > 60000) { lastDayCheck = serverClock.ElapsedMilliseconds; if (day != DateTime.Now.Day) { DarkLog.LogFilename = Path.Combine(DarkLog.LogFolder, "dmpserver " + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + ".log"); DarkLog.WriteToLog("Continued from logfile " + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss") + ".log"); day = DateTime.Now.Day; } } Thread.Sleep(500); } DMPPluginHandler.FireOnServerStop(); commandThread.Abort(); clientThread.Join(); } DarkLog.Normal("Goodbye!"); Environment.Exit(0); #if !DEBUG } catch (Exception e) { DarkLog.Fatal("Error in main server thread, Exception: " + e); throw; } #endif }
internal static void HandleMessage(ClientObject client, ClientMessage message) { //Prevent plugins from dodging SPLIT_MESSAGE. If they are modified, every split message will be broken. if (message.type != ClientMessageType.SPLIT_MESSAGE) { DMPPluginHandler.FireOnMessageReceived(client, message); if (message.handled) { //a plugin has handled this message and requested suppression of the default DMP behavior return; } } //Clients can only send HEARTBEATS, HANDSHAKE_REQUEST or CONNECTION_END's until they are authenticated. if (!client.authenticated && !(message.type == ClientMessageType.HEARTBEAT || message.type == ClientMessageType.HANDSHAKE_RESPONSE || message.type == ClientMessageType.CONNECTION_END)) { Messages.ConnectionEnd.SendConnectionEnd(client, "You must authenticate before attempting to send a " + message.type.ToString() + " message"); return; } #if !DEBUG try { #endif switch (message.type) { case ClientMessageType.HEARTBEAT: //Don't do anything for heartbeats, they just keep the connection alive break; case ClientMessageType.HANDSHAKE_RESPONSE: Messages.Handshake.HandleHandshakeResponse(client, message.data); break; case ClientMessageType.CHAT_MESSAGE: Messages.Chat.HandleChatMessage(client, message.data); break; case ClientMessageType.PLAYER_STATUS: Messages.PlayerStatus.HandlePlayerStatus(client, message.data); break; case ClientMessageType.PLAYER_COLOR: Messages.PlayerColor.HandlePlayerColor(client, message.data); break; case ClientMessageType.GROUP: Messages.GroupMessage.HandleMessage(client, message.data); break; case ClientMessageType.SCENARIO_DATA: Messages.ScenarioData.HandleScenarioModuleData(client, message.data); break; case ClientMessageType.SYNC_TIME_REQUEST: Messages.SyncTimeRequest.HandleSyncTimeRequest(client, message.data); break; case ClientMessageType.KERBALS_REQUEST: Messages.KerbalsRequest.HandleKerbalsRequest(client); break; case ClientMessageType.KERBAL_PROTO: Messages.KerbalProto.HandleKerbalProto(client, message.data); break; case ClientMessageType.VESSELS_REQUEST: Messages.VesselRequest.HandleVesselsRequest(client, message.data); break; case ClientMessageType.VESSEL_PROTO: Messages.VesselProto.HandleVesselProto(client, message.data); break; case ClientMessageType.VESSEL_UPDATE: Messages.VesselUpdate.HandleVesselUpdate(client, message.data); break; case ClientMessageType.VESSEL_REMOVE: Messages.VesselRemove.HandleVesselRemoval(client, message.data); break; case ClientMessageType.PERMISSION: Messages.PermissionMessage.HandleMessage(client, message.data); break; case ClientMessageType.CRAFT_LIBRARY: Messages.CraftLibrary.HandleCraftLibrary(client, message.data); break; case ClientMessageType.SCREENSHOT_LIBRARY: Messages.ScreenshotLibrary.HandleScreenshotLibrary(client, message.data); break; case ClientMessageType.FLAG_SYNC: Messages.FlagSync.HandleFlagSync(client, message.data); break; case ClientMessageType.PING_REQUEST: Messages.PingRequest.HandlePingRequest(client, message.data); break; case ClientMessageType.MOTD_REQUEST: Messages.MotdRequest.HandleMotdRequest(client); break; case ClientMessageType.WARP_CONTROL: Messages.WarpControl.HandleWarpControl(client, message.data); break; case ClientMessageType.LOCK_SYSTEM: Messages.LockSystem.HandleLockSystemMessage(client, message.data); break; case ClientMessageType.MOD_DATA: Messages.ModData.HandleModDataMessage(client, message.data); break; case ClientMessageType.KERBAL_REMOVE: Messages.VesselRemove.HandleKerbalRemoval(client, message.data); break; case ClientMessageType.SPLIT_MESSAGE: Messages.SplitMessage.HandleSplitMessage(client, message.data); break; case ClientMessageType.CONNECTION_END: Messages.ConnectionEnd.HandleConnectionEnd(client, message.data); break; case ClientMessageType.MODPACK_DATA: Messages.Modpack.HandleModpackMessage(client, message.data); break; default: DarkLog.Debug("Unhandled message type " + message.type); Messages.ConnectionEnd.SendConnectionEnd(client, "Unhandled message type " + message.type); #if DEBUG throw new NotImplementedException("Message type not implemented"); #else break; #endif } #if !DEBUG } catch (Exception e) { DarkLog.Debug("Error handling " + message.type + " from " + client.playerName + ", exception: " + e); Messages.ConnectionEnd.SendConnectionEnd(client, "Server failed to process " + message.type + " message"); } #endif }
private static void SendNetworkMessage(ClientObject client, ServerMessage message) { //Write the send times down in SYNC_TIME_REPLY packets if (message.type == ServerMessageType.SYNC_TIME_REPLY) { try { using (MessageWriter mw = new MessageWriter()) { using (MessageReader mr = new MessageReader(message.data)) { client.bytesQueuedOut += 8; //Client send time mw.Write <long>(mr.Read <long>()); //Server receive time mw.Write <long>(mr.Read <long>()); //Server send time mw.Write <long>(DateTime.UtcNow.Ticks); message.data = mw.GetMessageBytes(); } } } catch (Exception e) { DarkLog.Debug("Error rewriting SYNC_TIME packet, Exception " + e); } } //Continue sending byte[] messageBytes = Common.PrependNetworkFrame((int)message.type, message.data); client.lastSendTime = Server.serverClock.ElapsedMilliseconds; client.bytesQueuedOut -= messageBytes.Length; client.bytesSent += messageBytes.Length; if (client.connectionStatus == ConnectionStatus.CONNECTED) { try { client.connection.GetStream().Write(messageBytes, 0, messageBytes.Length); } catch (Exception e) { HandleDisconnectException("Send Network Message", client, e); return; } } DMPPluginHandler.FireOnMessageSent(client, message); if (message.type == ServerMessageType.CONNECTION_END) { using (MessageReader mr = new MessageReader(message.data)) { string reason = mr.Read <string>(); DarkLog.Normal("Disconnecting client " + client.playerName + ", sent CONNECTION_END (" + reason + ") to endpoint " + client.endpoint); client.disconnectClient = true; DisconnectClient(client); } } if (message.type == ServerMessageType.HANDSHAKE_REPLY) { using (MessageReader mr = new MessageReader(message.data)) { int response = mr.Read <int>(); string reason = mr.Read <string>(); if (response != 0) { DarkLog.Normal("Disconnecting client " + client.playerName + ", sent HANDSHAKE_REPLY (" + reason + ") to endpoint " + client.endpoint); client.disconnectClient = true; DisconnectClient(client); } } } }
public static void ThreadMain() { try { clients = new List <ClientObject>().AsReadOnly(); Messages.WarpControl.Reset(); Messages.Chat.Reset(); Messages.ScreenshotLibrary.Reset(); SetupTCPServer(); while (Server.serverRunning) { //Process current clients foreach (ClientObject client in clients) { Messages.Heartbeat.CheckHeartBeat(client); } ModpackSystem.fetch.SendFilesToClients(); //Check timers NukeKSC.CheckTimer(); Dekessler.CheckTimer(); Messages.WarpControl.CheckTimer(); //Run plugin update DMPPluginHandler.FireOnUpdate(); Thread.Sleep(10); } } catch (Exception e) { DarkLog.Error("Fatal error thrown, exception: " + e); Server.ShutDown("Crashed!"); } try { long disconnectTime = DateTime.UtcNow.Ticks; bool sendingHighPriotityMessages = true; while (sendingHighPriotityMessages) { if ((DateTime.UtcNow.Ticks - disconnectTime) > 50000000) { DarkLog.Debug("Shutting down with " + Server.playerCount + " players, " + clients.Count + " connected clients"); break; } sendingHighPriotityMessages = false; foreach (ClientObject client in clients) { if (client.authenticated && (client.sendMessageQueueHigh.Count > 0)) { sendingHighPriotityMessages = true; } } Thread.Sleep(10); } ShutdownTCPServer(); } catch (Exception e) { DarkLog.Fatal("Fatal error thrown during shutdown, exception: " + e); throw; } }
public static void Main() { try { //Start the server clock serverClock = new Stopwatch(); serverClock.Start(); //Set the last player activity time to server start lastPlayerActivity = serverClock.ElapsedMilliseconds; //Periodic garbage collection long lastGarbageCollect = 0; //Periodic screenshot check long lastScreenshotExpiredCheck = 0; //Set universe directory and modfile path universeDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Universe"); modFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DMPModControl.txt"); CommandHandler commandHandler = new CommandHandler(); //Register the server commands commandHandler.RegisterCommand("exit", Server.ShutDown, "Shuts down the server"); commandHandler.RegisterCommand("quit", Server.ShutDown, "Shuts down the server"); commandHandler.RegisterCommand("shutdown", Server.ShutDown, "Shuts down the server"); commandHandler.RegisterCommand("restart", Server.Restart, "Restarts the server"); commandHandler.RegisterCommand("kick", x => WorldManager.Instance.KickPlayer(x), "Kicks a player from the server"); commandHandler.RegisterCommand("ban", x => WorldManager.Instance.BanPlayer(x), "Bans a player from the server"); commandHandler.RegisterCommand("banip", x => WorldManager.Instance.BanIP(x), "Bans an IP Address from the server"); commandHandler.RegisterCommand("bankey", x => WorldManager.Instance.BanPublicKey(x), "Bans a Guid from the server"); commandHandler.RegisterCommand("pm", x => WorldManager.Instance.PMCommand(x), "Sends a message to a player"); commandHandler.RegisterCommand("admin", x => WorldManager.Instance.AdminCommand(x), "Sets a player as admin/removes admin from the player"); commandHandler.RegisterCommand("whitelist", x => WorldManager.Instance.WhitelistCommand(x), "Change the server whitelist"); //Register the ctrl+c event Console.CancelKeyPress += new ConsoleCancelEventHandler(CatchExit); serverStarting = true; //Fix kerbals from 0.23.5 to 0.24 (Now indexed by string, thanks Squad! BackwardsCompatibility.FixKerbals(); //Remove player tokens BackwardsCompatibility.RemoveOldPlayerTokens(); //Load plugins DMPPluginHandler.LoadPlugins(); Console.Title = "DMPServer " + Common.PROGRAM_VERSION + ", protocol " + Common.PROTOCOL_VERSION; while (serverStarting || serverRestarting) { serverRestarting = false; DarkLog.Normal("Starting DMPServer " + Common.PROGRAM_VERSION + ", protocol " + Common.PROTOCOL_VERSION); //Load settings DarkLog.Normal("Loading universe... "); CheckUniverse(); DarkLog.Normal("Done!"); DarkLog.Normal("Loading settings... "); Settings.Load(); DarkLog.Normal("Done!"); DarkLog.Normal("Starting " + Settings.settingsStore.warpMode + " server on port " + Settings.settingsStore.port + "... "); serverRunning = true; DarkLog.Normal("Done!"); StartHTTPServer(); DarkLog.Normal("Done!"); DMPPluginHandler.FireOnServerStart(); while (serverRunning) { WorldManager.Instance.Update(); commandHandler.Run(); //Run a garbage collection every 2 minutes. if ((serverClock.ElapsedMilliseconds - lastGarbageCollect) > 12000) { lastGarbageCollect = serverClock.ElapsedMilliseconds; GC.Collect(); } //Run the screenshot expire function every 10 minutes if ((serverClock.ElapsedMilliseconds - lastScreenshotExpiredCheck) > 600000) { lastScreenshotExpiredCheck = serverClock.ElapsedMilliseconds; ScreenshotExpire.ExpireCache(); } Thread.Sleep(10); } DMPPluginHandler.FireOnServerStop(); } DarkLog.Normal("Goodbye!"); Environment.Exit(0); } catch (Exception e) { DarkLog.Fatal("Error in main server thread, Exception: " + e); throw; } }