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());
                }
            }
        }
Exemple #4
0
 public void queueOutgoingMessage(KMPCommon.ServerMessageID id, byte[] data)
 {
     queueOutgoingMessage(Server.buildMessageArray(id, data));
 }