private static void handleReceive() { while (receiveHandleIndex < receiveIndex) { //Read header bytes if (currentMessageHeaderIndex < KMPCommon.MSG_HEADER_LENGTH) { //Determine how many header bytes can be read int bytes_to_read = Math.Min(receiveIndex - receiveHandleIndex, KMPCommon.MSG_HEADER_LENGTH - currentMessageHeaderIndex); //Read header bytes Array.Copy(receiveBuffer, receiveHandleIndex, currentMessageHeader, currentMessageHeaderIndex, bytes_to_read); //Advance buffer indices currentMessageHeaderIndex += bytes_to_read; receiveHandleIndex += bytes_to_read; //Handle header if (currentMessageHeaderIndex >= KMPCommon.MSG_HEADER_LENGTH) { int id_int = KMPCommon.intFromBytes(currentMessageHeader, 0); //Make sure the message id section of the header is a valid value if (id_int >= 0 && id_int < Enum.GetValues(typeof(KMPCommon.ServerMessageID)).Length) currentMessageID = (KMPCommon.ServerMessageID)id_int; else currentMessageID = KMPCommon.ServerMessageID.NULL; int data_length = KMPCommon.intFromBytes(currentMessageHeader, 4); if (data_length > 0) { //Init message data buffer currentMessageData = new byte[data_length]; currentMessageDataIndex = 0; } else { currentMessageData = null; //Handle received message messageReceived(currentMessageID, null); //Prepare for the next header read currentMessageHeaderIndex = 0; } } } if (currentMessageData != null) { //Read data bytes if (currentMessageDataIndex < currentMessageData.Length) { //Determine how many data bytes can be read int bytes_to_read = Math.Min(receiveIndex - receiveHandleIndex, currentMessageData.Length - currentMessageDataIndex); //Read data bytes Array.Copy(receiveBuffer, receiveHandleIndex, currentMessageData, currentMessageDataIndex, bytes_to_read); //Advance buffer indices currentMessageDataIndex += bytes_to_read; receiveHandleIndex += bytes_to_read; //Handle data if (currentMessageDataIndex >= currentMessageData.Length) { //Handle received message byte[] messageData = KMPCommon.Decompress(currentMessageData); if (messageData != null) messageReceived(currentMessageID, messageData); //Consider adding re-request here currentMessageData = null; //Prepare for the next header read currentMessageHeaderIndex = 0; } } } } //Once all receive bytes have been handled, reset buffer indices to use the whole buffer again receiveHandleIndex = 0; receiveIndex = 0; }
static void handleMessage(KMPCommon.ServerMessageID id, byte[] data) { //LogAndShare("Message ID: " + id.ToString() + " data: " + (data == null ? "0" : System.Text.Encoding.ASCII.GetString(data))); handlingMessageType = id; switch (id) { case KMPCommon.ServerMessageID.HANDSHAKE: if (handshakeCompleted) { return; } if (data != null) { if (data.Length > 4) { //Check protocol version Int32 protocol_version = KMPCommon.intFromBytes(data); if (protocol_version != KMPCommon.NET_PROTOCOL_VERSION) { //End the session if the protocol version doesn't match endSession = true; intentionalConnectionEnd = true; gameManager.disconnect("Your client is incompatible with this server"); return; } Int32 server_version_length = KMPCommon.intFromBytes(data, 4); String server_version = encoder.GetString(data, 8, server_version_length); clientID = KMPCommon.intFromBytes(data, 8 + server_version_length); gameManager.gameMode = KMPCommon.intFromBytes(data, 12 + server_version_length); gameManager.numberOfShips = KMPCommon.intFromBytes(data, 16 + server_version_length); int kmpModControl_length = KMPCommon.intFromBytes(data, 20 + server_version_length); kmpModControl_bytes = new byte[kmpModControl_length]; Array.Copy(data, 24 + server_version_length, kmpModControl_bytes, 0, kmpModControl_length); SetMessage("Handshake received. Server version: " + server_version); if (!modCheck(kmpModControl_bytes)) { endSession = true; intentionalConnectionEnd = true; gameManager.disconnect(modMismatchError); return; } sendHandshakeMessage(); //Reply to the handshake lock (udpTimestampLock) { lastUDPMessageSendTime = stopwatch.ElapsedMilliseconds; } handshakeCompleted = true; } else { //End the session if we get a bad handshake. Protects against byte[0]. endSession = true; intentionalConnectionEnd = true; gameManager.disconnect("Your client is incompatible with this server"); return; } } else { //End the session if we get a bad handshake. Protects against null. endSession = true; intentionalConnectionEnd = true; gameManager.disconnect("Your client is incompatible with this server"); return; } break; case KMPCommon.ServerMessageID.HANDSHAKE_REFUSAL: String refusal_message = encoder.GetString(data, 0, data.Length); endSession = true; intentionalConnectionEnd = true; enqueuePluginChatMessage("Server refused connection. Reason: " + refusal_message, true); break; case KMPCommon.ServerMessageID.SERVER_MESSAGE: case KMPCommon.ServerMessageID.TEXT_MESSAGE: if (data != null) { InTextMessage in_message = new InTextMessage(); in_message.fromServer = (id == KMPCommon.ServerMessageID.SERVER_MESSAGE); in_message.isMOTD = (id == KMPCommon.ServerMessageID.MOTD_MESSAGE); in_message.message = encoder.GetString(data, 0, data.Length); if (in_message.message.Contains(" has shared a screenshot.")) { int screenshotSharePlayerNameIndex = in_message.message.IndexOf(" has shared a screenshot."); string screenshotSharePlayerName = in_message.message.Substring(0, screenshotSharePlayerNameIndex); if (screenshotSharePlayerName != username) { bool listPlayerNameInScreenshotsWaiting = false; foreach (string listPlayer in screenshotsWaiting) { if (listPlayer == screenshotSharePlayerName) { listPlayerNameInScreenshotsWaiting = true; } } if (listPlayerNameInScreenshotsWaiting == false) { screenshotsWaiting.Add(screenshotSharePlayerName); } } } if (in_message.message.Contains(" has disconnected : ")) { int quitPlayerNameIndex = in_message.message.IndexOf(" has disconnected : "); string quitPlayerName = in_message.message.Substring(0, quitPlayerNameIndex); if (quitPlayerName != username) { bool listPlayerNameInScreenshotsWaiting = false; foreach (string listPlayer in screenshotsWaiting) { if (listPlayer == quitPlayerName) { listPlayerNameInScreenshotsWaiting = true; } } if (listPlayerNameInScreenshotsWaiting) { screenshotsWaiting.Remove(quitPlayerName); } } } //Queue the message enqueueTextMessage(in_message); } break; case KMPCommon.ServerMessageID.MOTD_MESSAGE: if (gameManager.gameRunning == false) { gameManager.gameStart = true; } if (data != null) { InTextMessage in_message = new InTextMessage(); in_message.fromServer = (id == KMPCommon.ServerMessageID.SERVER_MESSAGE); in_message.isMOTD = (id == KMPCommon.ServerMessageID.MOTD_MESSAGE); in_message.message = encoder.GetString(data, 0, data.Length); enqueueTextMessage(in_message); } break; case KMPCommon.ServerMessageID.PLUGIN_UPDATE: if (data != null) enqueueClientInteropMessage(KMPCommon.ClientInteropMessageID.PLUGIN_UPDATE, data); break; case KMPCommon.ServerMessageID.SCENARIO_UPDATE: if (data != null) enqueueClientInteropMessage(KMPCommon.ClientInteropMessageID.SCENARIO_UPDATE, data); break; case KMPCommon.ServerMessageID.SERVER_SETTINGS: lock (serverSettingsLock) { if (data != null && data.Length >= KMPCommon.SERVER_SETTINGS_LENGTH && handshakeCompleted) { updateInterval = KMPCommon.intFromBytes(data, 0); screenshotInterval = KMPCommon.intFromBytes(data, 4); lock (clientDataLock) { int new_screenshot_height = KMPCommon.intFromBytes(data, 8); if (screenshotSettings.maxHeight != new_screenshot_height) { screenshotSettings.maxHeight = new_screenshot_height; lastClientDataChangeTime = stopwatch.ElapsedMilliseconds; enqueueTextMessage("Screenshot Height has been set to " + screenshotSettings.maxHeight); } gameManager.safetyBubbleRadius = BitConverter.ToDouble(data, 12); if (inactiveShipsPerUpdate != data[20]) { inactiveShipsPerUpdate = data[20]; lastClientDataChangeTime = stopwatch.ElapsedMilliseconds; } gameManager.gameCheatsEnabled = Convert.ToBoolean(data[21]); gameManager.gameArrr = Convert.ToBoolean(data[22]); //partList, requiredModList, shaList, resourceList and resourceControlMode } receivedSettings = true; /* Log.Debug("Update interval: " + updateInterval); Log.Debug("Screenshot interval: " + screenshotInterval); Log.Debug("Inactive ships per update: " + inactiveShipsPerUpdate); */ } } break; case KMPCommon.ServerMessageID.SCREENSHOT_SHARE: if (data != null && data.Length > 0 && data.Length < screenshotSettings.maxNumBytes && watchPlayerName.Length > 0 && watchPlayerName != username) { enqueueClientInteropMessage(KMPCommon.ClientInteropMessageID.SCREENSHOT_RECEIVE, data); } break; case KMPCommon.ServerMessageID.CONNECTION_END: if (data != null) { String message = encoder.GetString(data, 0, data.Length); gameManager.disconnect(message); //If the reason is not a timeout, connection end is intentional intentionalConnectionEnd = message.ToLower() != "timeout"; enqueuePluginChatMessage("Server closed the connection: " + message, true); SetMessage("Disconnected from server: " + message); } else { gameManager.disconnect(); SetMessage("Disconnected from server"); } break; case KMPCommon.ServerMessageID.UDP_ACKNOWLEDGE: lock (udpTimestampLock) { lastUDPAckReceiveTime = stopwatch.ElapsedMilliseconds; } break; case KMPCommon.ServerMessageID.CRAFT_FILE: if (data != null && data.Length > 8) { //Read craft name length KMPCommon.CraftType craft_type = (KMPCommon.CraftType)KMPCommon.intFromBytes(data, 0); int craft_name_length = KMPCommon.intFromBytes(data, 4); if (craft_name_length < data.Length - 8) { //Read craft name String craft_name = encoder.GetString(data, 8, craft_name_length); //Read craft bytes byte[] craft_bytes = new byte[data.Length - craft_name_length - 8]; Array.Copy(data, 8 + craft_name_length, craft_bytes, 0, craft_bytes.Length); //Write the craft to a file String filename = getCraftFilename(craft_name, craft_type); if (filename != null) { try { //KSP.IO.File.WriteAllBytes<KMPClientMain>(craft_bytes, filename); System.IO.File.WriteAllBytes(filename, craft_bytes); enqueueTextMessage("Received craft file: " + craft_name); } catch (Exception e) { Log.Debug("Exception thrown in handleMessage(), catch 1, Exception: {0}", e.ToString()); enqueueTextMessage("Error saving received craft file: " + craft_name); } } else enqueueTextMessage("Unable to save received craft file."); } } break; case KMPCommon.ServerMessageID.PING_REPLY: long pingSendTime = BitConverter.ToInt64(data, 0); long pingReceiveTime = DateTime.UtcNow.Ticks; long pingElapsedMilliseconds = (pingReceiveTime - pingSendTime) / 10000; enqueueTextMessage("Ping Reply: " + pingElapsedMilliseconds + "ms"); break; case KMPCommon.ServerMessageID.SYNC: if (data != null) { gameManager.skewTargetTick = BitConverter.ToDouble (data, 0); gameManager.skewServerTime = BitConverter.ToInt64 (data, 8); gameManager.skewSubspaceSpeed = BitConverter.ToSingle (data, 16); gameManager.lastSubspaceLockChange = UnityEngine.Time.realtimeSinceStartup; Log.Debug ("Client time locked to server:" + gameManager.skewTargetTick + " server time: " + gameManager.skewServerTime + " frequency " + gameManager.skewSubspaceSpeed + "x."); } break; case KMPCommon.ServerMessageID.SYNC_COMPLETE: gameManager.HandleSyncCompleted(); break; case KMPCommon.ServerMessageID.SPLIT_MESSAGE: handleSplitMessage(data); break; case KMPCommon.ServerMessageID.SYNC_TIME: gameManager.HandleSyncTimeCompleted(data); break; } }
private static void ReceiveCallback(IAsyncResult ar) { try { // Retrieve the state object and the client socket // from the asynchronous state object. StateObject state = (StateObject)ar.AsyncState; TcpClient client = state.workClient; int bytesRead = client.GetStream().EndRead(ar); // Read data from the remote device directly into the message buffer. currentBytesToReceive -= bytesRead; //Decrement how many bytes we have read. if (bytesRead > 0) { //This is just a shortcut really if (!currentMessageHeaderRecieved) { //We are receiving just the header if (currentBytesToReceive == 0) { //We have recieved the full message header, lets process it. currentMessageID = (KMPCommon.ServerMessageID)BitConverter.ToInt32(currentMessage, 0); currentBytesToReceive = BitConverter.ToInt32(currentMessage, 4); if (currentBytesToReceive > KMPCommon.MAX_MESSAGE_SIZE) { throw new InvalidOperationException("Incorrect message size"); } if (currentBytesToReceive == 0) { //We received the header of a empty message, add it to the process queue, and reset the buffers. messageReceived(currentMessageID, null); currentMessageID = KMPCommon.ServerMessageID.NULL; currentBytesToReceive = KMPCommon.MSG_HEADER_LENGTH; currentMessage = new byte[currentBytesToReceive]; } else { //We received the header of a non-empty message, Let's give it a buffer and read again. currentMessage = new byte[currentBytesToReceive]; currentMessageHeaderRecieved = true; } } } else { if (currentBytesToReceive == 0) { //We have received all the message data, lets decompress and add it to the process queue, and reset the buffers. byte[] decompressedData = KMPCommon.Decompress(currentMessage); messageReceived(currentMessageID, decompressedData); currentMessageHeaderRecieved = false; currentMessageID = KMPCommon.ServerMessageID.NULL; currentBytesToReceive = KMPCommon.MSG_HEADER_LENGTH; currentMessage = new byte[currentBytesToReceive]; } } } if (currentBytesToReceive < 0) { throw new System.IO.IOException("You somehow managed to read more bytes then we asked for. Good for you. Open this up on the bugtracker now."); } if (client != null) { client.GetStream().BeginRead(currentMessage, currentMessage.Length - currentBytesToReceive, currentBytesToReceive, new AsyncCallback(ReceiveCallback), state); } } catch (Exception e) { //Basically, If anything goes wrong at all the stream is broken and there is no way to recover from it. Log.Debug("Exception thrown in ReceiveCallback(), catch 1, Exception: {0}", e.ToString()); if (gameManager.gameRunning) { //We have already been disconnected somewhere else. gameManager.disconnect("Connection error: " + e.Message.ToString()); } } }
public void queueOutgoingMessage(KMPCommon.ServerMessageID id, byte[] data) { queueOutgoingMessage(Server.buildMessageArray(id, data)); }