public static IEnumerator RemoveItemsFromClusterio(string bodyName, Dictionary <string, int> clusterioInventory, string itemName, int count, Action <bool> onFinished = null)
        {
            if (!BodyHasServer(bodyName))
            {
                Debug.Log(String.Format("XenoIndustrySignpost: Sending a RemoveItemsFromClusterio request to a celestial {0} which has no assigned server!", bodyName));
                yield break;
            }

            XenoIndustrySignpostBodyAddress address = bodyAddresses[bodyName];

            // Connect to the server if we aren't connected yet
            if (!ClusterioConnector.IsConnected(address.masterIP, address.masterPort))
            {
                ClusterioConnector.ConntectToMaster(address.masterIP, address.masterPort, address.masterAuthToken);
            }

            // First, we need to see what items are stored in the Clusterio server, and wait for that request to be finished
            yield return(XenoIndustryCore.instance.StartCoroutine(XenoIndustrySignpost.GetClusterioInventory(bodyName, clusterioInventory)));

            // Don't do a remove request if there's not enough items, or if the item isn't there at all
            if (!clusterioInventory.ContainsKey(itemName) || clusterioInventory[itemName] < count)
            {
                yield break;
            }

            Dictionary <string, string> sendValues = new Dictionary <string, string>();

            sendValues["name"]  = itemName;
            sendValues["count"] = count.ToString();

            Debug.Log(String.Format("ClusterioUtil: removing {0} of {1} from item repository", count, itemName));

            string apiCommand = "/api/remove";

            ClusterioMessage resultMessage = new ClusterioMessage();

            yield return(ClusterioConnector.SendPostRequest(address.masterIP, address.masterPort, apiCommand, resultMessage, sendValues));

            if (resultMessage.result == ClusterioMessageResult.SUCCESS)
            {
                if (onFinished != null)
                {
                    onFinished(true);
                }
            }
            else
            {
                if (onFinished != null)
                {
                    onFinished(false);
                }
            }
        }
        public static IEnumerator GetClusterioInventory(string bodyName, Dictionary <string, int> clusterioInventory)
        {
            if (!BodyHasServer(bodyName))
            {
                Debug.Log(String.Format("XenoIndustrySignpost: Sending a GetClusterioInventory request to a celestial {0} which has no assigned server!", bodyName));
                yield break;
            }

            XenoIndustrySignpostBodyAddress address = bodyAddresses[bodyName];

            // Connect to the server if we aren't connected yet
            if (!ClusterioConnector.IsConnected(address.masterIP, address.masterPort))
            {
                ClusterioConnector.ConntectToMaster(address.masterIP, address.masterPort, address.masterAuthToken);
            }

            string apiRequest = "/api/inventory";

            ClusterioMessage resultMessage = new ClusterioMessage();

            yield return(ClusterioConnector.SendGetRequest(address.masterIP, address.masterPort, apiRequest, resultMessage));

            if (resultMessage.result == ClusterioMessageResult.SUCCESS)
            {
                // Inventory request successful, refresh the inventory
                clusterioInventory.Clear();

                JSONNode rootNode = JSON.Parse(resultMessage.text);

                foreach (JSONNode childNode in rootNode.Children)
                {
                    if (childNode["name"] == null)
                    {
                        Debug.Log("GetClusterioInventory: an item is missing its name!");
                        continue;
                    }

                    if (childNode["count"] == null)
                    {
                        Debug.Log(String.Format("GetClusterioInventory: item {0} is missing its count number!", childNode["name"]));
                        continue;
                    }

                    clusterioInventory[childNode["name"]] = childNode["count"].AsInt;
                }
            }
        }
        public static IEnumerator AddItemsToClusterio(string bodyName, string itemName, int count, Action <bool> onFinished = null)
        {
            if (!BodyHasServer(bodyName))
            {
                Debug.Log(String.Format("XenoIndustrySignpost: Sending a AddItemsToClusterio request to a celestial {0} which has no assigned server!", bodyName));
                yield break;
            }

            XenoIndustrySignpostBodyAddress address = bodyAddresses[bodyName];

            // Connect to the server if we aren't connected yet
            if (!ClusterioConnector.IsConnected(address.masterIP, address.masterPort))
            {
                ClusterioConnector.ConntectToMaster(address.masterIP, address.masterPort, address.masterAuthToken);
            }

            Dictionary <string, string> sendValues = new Dictionary <string, string>();

            sendValues["name"]  = itemName;
            sendValues["count"] = count.ToString();

            Debug.Log(String.Format("ClusterioUtil: adding {0} of {1} to item repository", count, itemName));

            string apiCommand = "/api/place";

            ClusterioMessage resultMessage = new ClusterioMessage();

            yield return(ClusterioConnector.SendPostRequest(address.masterIP, address.masterPort, apiCommand, resultMessage, sendValues));

            if (resultMessage.result == ClusterioMessageResult.SUCCESS)
            {
                if (onFinished != null)
                {
                    onFinished(true);
                }
            }
            else
            {
                if (onFinished != null)
                {
                    onFinished(false);
                }
            }
        }
        IEnumerator TransferScienceToClusterio()
        {
            int scienceTransferAmount     = (int)(ResearchAndDevelopment.Instance.Science - (ResearchAndDevelopment.Instance.Science % sciencePerSciencePack));
            int sciencePackTransferAmount = (int)(scienceTransferAmount / sciencePerSciencePack);

            ClusterioMessage resultMessage = new ClusterioMessage();

            string bodyName = "Kerbin"; // Science packs can only be converted at Kerbin for now

            yield return(StartCoroutine(XenoIndustrySignpost.AddItemsToClusterio(bodyName, "space-science-pack", sciencePackTransferAmount, (success) =>
            {
                if (success)
                {
                    // Show results as text
                    Debug.Log("TransferScienceToClusterio: science sent successfully");

                    // Only subtract science in KSP if the request successfuly reached the Clusterio master server
                    ResearchAndDevelopment.Instance.AddScience(-scienceTransferAmount, TransactionReasons.ScienceTransmission);
                }
            }
                                                                                 )));
        }
        public static IEnumerator SendGetRequest(string masterIP, string masterPort, string apiCall, ClusterioMessage resultMessage)
        {
            ClusterioConnection connection = GetRequestClusterioConnection(masterIP, masterPort, resultMessage);

            if (connection == null)
            {
                yield break;
            }

            UnityWebRequest webRequest = UnityWebRequest.Get(masterIP + ":" + masterPort + "/" + apiCall);

            if (connection.masterAuthToken != null)
            {
                webRequest.SetRequestHeader("x-access-token", connection.masterAuthToken);
            }

            yield return(webRequest.Send());

            ReturnRequestMessage(connection, webRequest, resultMessage);
        }
        private static void ReturnRequestMessage(ClusterioConnection connection, UnityWebRequest webRequest, ClusterioMessage resultMessage)
        {
            if (webRequest.isHttpError || webRequest.responseCode == 0) // Response code is 0 when server cannot be reached
            {
                resultMessage.result = ClusterioMessageResult.ERROR;
                resultMessage.text   = webRequest.error;

                Debug.Log("ClusterioConnector: request failed! Error: " + webRequest.error);

                XenoIndustryCore.instance.StartCoroutine(RefreshConnection(connection));
            }
            else
            {
                resultMessage.result = ClusterioMessageResult.SUCCESS;
                resultMessage.text   = webRequest.downloadHandler.text;
            }
        }
        private static ClusterioConnection GetRequestClusterioConnection(string masterIP, string masterPort, ClusterioMessage resultMessage)
        {
            string masterAddress = masterIP + ":" + masterPort;

            if (!connections.ContainsKey(masterAddress))
            {
                resultMessage.result = ClusterioMessageResult.ERROR;
                resultMessage.text   = "Master server connection not set";

                Debug.Log("ClusterioConnector: cannot send requests when master IP or port aren't set!");
                return(null);
            }

            if (!IsConnected(masterIP, masterPort))
            {
                resultMessage.result = ClusterioMessageResult.ERROR;
                resultMessage.text   = "No master server connection";

                Debug.Log("ClusterioConnector: cannot send requests, not connected to master server");
                return(null);
            }

            ClusterioConnection connection = connections[masterAddress];

            return(connection);
        }