public override int Run()
        {
            Loaded   = false;
            Stopping = false;
            Console.ForegroundColor = ConsoleColor.Cyan;
            Wrapper.InputTarget     = Wrapper.Modes.Menu;

            Wrapper.WriteLine("Preparing Minecraft server...");
            string SettingsFileName = "Settings.ini";
            string SettingsFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, SettingsFileName);

            /*=======================================
            *  Create a settings file with some default values if it doesn't exist.
            *  These default values probably won't actually work, but it'll at least create the file.
            *  =======================================*/
            if (!File.Exists(SettingsFilePath))
            {
                //ds = DefaultSettings
                IniFile ds = new IniFile(SettingsFileName);
                ds.Write("Version", "1.15.1", "Minecraft");
                ds.Write("Arguments", "nogui", "Minecraft");
                ds.Write("WorldSelectFile", "w.ini", "Minecraft");
                ds.Write("Enable", "false", "Fabric");
                ds.Write("Version", "0.4.0+build.121", "Fabric");
                ds.Write("CommandLogFile", "CommandLog.txt", "Wrapper");
                ds.Write("ServerPIDFile", "ServerPID", "Wrapper");
                ds.Write("ServerFolder", @"E:\My_Minecraft_Expansion_2\Local Server", "Windows");
                ds.Write("Executable", "java", "Java");
                ds.Write("Type", "-d64 -server", "Java");
                ds.Write("MemMax", "4G", "Java");
                ds.Write("MemMin", "4G", "Java");
                ds.Write("LogConfigFile", "log4j2.xml", "Java");
                ds.Write("Arguments", "-XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseAdaptiveGCBoundary -XX:MaxGCPauseMillis=500 -XX:-UseGCOverheadLimit -XX:SurvivorRatio=12 -XX:NewRatio=4 -Xnoclassgc -XX:UseSSE=3", "Java");
            }

            /*=======================================
            *  Read the settings file to find the server path
            *  =======================================*/
            Wrapper.WriteLine("Reading wrapper settings file...");
            IniFile s = new IniFile(SettingsFileName);

            Console.Title = "Wrapper." + Wrapper.Version + " this." + s.Read("Version", "Minecraft");
            RootPath      = s.Read("ServerFolder", "Windows") + '\\';

            //Automatically accept the EULA because screw that
            File.WriteAllText(RootPath + "eula.txt", "eula=TRUE");

            /*=======================================
            *  Build a huge string of startup arguments from the settings file
            *  =======================================*/
            //string MinecraftJar = "minecraft_server." + s.Read("Version", "Minecraft") + ".jar";
            string MinecraftJar    = s.Read("JarName", "Minecraft");
            string ArgumentsString = "-Xmx" + s.Read("MemMax", "Java") + " -Xms" + s.Read("MemMin", "Java") +
                                     " " + s.Read("Type", "Java") + " " + s.BigRead("Arguments", "Java") +
                                     " -jar \"" + RootPath;

            if (Convert.ToBoolean(s.Read("Enable", "Fabric")))
            {
                Console.Title = Console.Title + " FabricLoader." + s.Read("Version", "Fabric");
                //string FabricJar = "fabric-loader-" + s.Read("Version", "Fabric") + ".jar";
                string FabricJar = s.Read("FabricJarName", "Fabric");
                ArgumentsString += FabricJar + "\" \"" + RootPath;
                File.WriteAllText(RootPath + "fabric-server-launcher.properties", "serverJar=" + MinecraftJar);
            }


            /*=======================================
            *  Read the world select file and add stuff
            *  from that to the end of the arguments string
            *  =======================================*/
            Wrapper.WriteLine("Configuring universe...");
            string WorldSelectFilePath = RootPath + s.Read("WorldSelectFile", "Minecraft");

            if (!File.Exists(WorldSelectFilePath))
            {
                //dw = DefaultWorld
                IniFile dw = new IniFile(WorldSelectFilePath);
                dw.Write("Selected", "_default", "Universe");
                dw.Write("Selected", "world", "World");
                dw.Write("UniversesFolder", "universes", "Windows");
                dw.Write("RelativePath", "true", "Windows");
            }

            IniFile w = new IniFile(WorldSelectFilePath);

            //The folder path is processed this way since Minecraft prefers a relative path,
            //but the wrapper still needs to access the folder name itself with an absolute path.
            string UniversesFolder = w.Read("UniversesFolderName", "Windows");

            if (Convert.ToBoolean(w.Read("RelativePath", "Windows")))
            {
                UniversesFolder = ".\\" + UniversesFolder;
            }
            if (!UniversesFolder.EndsWith(@"\"))
            {
                UniversesFolder += @"\";
            }

            ArgumentsString += MinecraftJar + "\" " + s.Read("Arguments", "Minecraft") +
                               " --universe " + UniversesFolder + w.Read("Selected", "Universe") +
                               " --world " + w.Read("Selected", "World");

            /*=======================================
            *  Set up server.properties
            *  =======================================*/
            Wrapper.WriteLine("Configuring server.properties...");
            Dictionary <string, string> ServerProperties = new Dictionary <string, string>();

            //This is just here in case a property manages to
            //not get specified in any other properties file.
            Util.MergeDictionaryWithStream(ServerProperties, "BeeMovieHentai", "ServerWrapperTest.default_server.properties");

            //Loads the defaults for the whole server.
            Util.MergeDictionaryWithStream(ServerProperties, RootPath + s.Read("GlobalServerPropertiesFile", "Minecraft"));

            //Configures the generic template for
            UniversePath = RootPath + w.Read("UniversesFolderName", "Windows") + "\\" + w.Read("Selected", "Universe") + "\\";
            Util.MergeDictionaryWithStream(ServerProperties, UniversePath + "universe.properties");

            //Update the MotD to show what universe/world is loaded
            ServerProperties["motd"] += " | " + w.Read("Selected", "World");

            //Check for a world properties file and load that too if it exists
            WorldPath = UniversePath + w.Read("Selected", "World") + "\\";
            Util.MergeDictionaryWithStream(ServerProperties, WorldPath + "world.properties");

            ServerProperties["level-name"] = w.Read("Selected", "World");

            File.WriteAllLines(RootPath + "server.properties", Util.JoinDictionaryAsArray(ServerProperties, '='));

            /*=======================================
            *  Set the server icon
            *  =======================================*/
            Wrapper.WriteLine("Setting server icon...");

            //These pragma statements whats-its just make Visual Studio shut up
            //about these if statements being empty. I did that on purpose as a way
            //of short circuiting the logic involved.
            #pragma warning disable CS0642
            if (Util.SetIcon(WorldPath + "server-icon.png"))
            {
                ;
            }
            else if (Util.SetIcon(UniversePath + "server-icon.png"))
            {
                ;
            }
            else if (Util.SetIcon(RootPath + "global-icon.png"))
            {
                ;
            }
            else if (Util.CompareDefaultIcon(RootPath + "server-icon.png"))
            {
                ;
            }
            #pragma warning restore CS0642
            else
            {
                using (Stream DefaultIconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ServerWrapperTest.default-icon.png"))
                {
                    byte[] ByteBuffer = new byte[DefaultIconStream.Length];
                    using (MemoryStream memoryStream = new MemoryStream(ByteBuffer))
                    {
                        DefaultIconStream.CopyTo(memoryStream);
                        File.WriteAllBytes(RootPath + "server-icon.png", memoryStream.ToArray());
                    }
                }
            }

            ///*=======================================
            //Setup some files related to logs and crap
            //=======================================*/
            //Wrapper.WriteLine("Configuring custom logging...");
            ////this.CommandLog = new StreamWriter(RootPath + s.Read("CommandLogFile", "Wrapper"), true);
            //Wrapper.WriteLine("Checking for previous unstopped servers...");
            //PIDFile = RootPath + s.Read("PIDFile", "Minecraft");
            //if (File.Exists(PIDFile))
            //{
            //    Int32 PreviousPID = Int32.Parse(File.ReadAllText(PIDFile));
            //    try
            //    {
            //        using (Process PreviousServer = Process.GetProcessById(PreviousPID))
            //        using (StreamWriter PreviousInput = PreviousServer.StandardInput)
            //        {
            //            PreviousInput.WriteLine("stop");
            //            //Make sure the server process has stopped
            //            while (PreviousServer.HasExited == false) ;
            //        }
            //        Wrapper.WriteLine("Previous server stopped successfully");
            //    }
            //    catch (Exception)
            //    {
            //        Wrapper.WriteLine("No previous server process running");
            //    }
            //    File.Delete(PIDFile);
            //}

            /*=======================================
            *  Configure the server process before starting it
            *  =======================================*/
            Wrapper.WriteLine("Configuring server process...");
            ServerProcess = new Process();
            ServerProcess.StartInfo.FileName               = s.Read("Executable", "Java");
            ServerProcess.StartInfo.Arguments              = ArgumentsString;
            ServerProcess.StartInfo.CreateNoWindow         = false;
            ServerProcess.StartInfo.WorkingDirectory       = RootPath;
            ServerProcess.StartInfo.ErrorDialog            = false;
            ServerProcess.StartInfo.UseShellExecute        = false;
            ServerProcess.StartInfo.RedirectStandardError  = true;
            ServerProcess.StartInfo.RedirectStandardOutput = true;
            ServerProcess.StartInfo.RedirectStandardInput  = true;


            /*=======================================
            *  These are what read/print/process the server log.
            *  They'll be run whenever the server process outputs text,
            *  even while the code continues running below.
            *  =======================================*/
            Wrapper.WriteLine("Configuring console output...");
            ServerProcess.OutputDataReceived += new DataReceivedEventHandler
                                                (
                (sender, OutputText) =>
            {
                if (string.IsNullOrWhiteSpace(OutputText.Data) == false)
                {
                    Util.WriteToLog(OutputText.Data, OutputFormat);

                    if (Stopping == true)
                    {
                        Loaded = !OutputText.Data.Contains("[Server thread/INFO]: Saved the game");
                    }
                    //else if (this.Loaded)
                    //{
                    //    ProcessLog(OutputText.Data.Split(Util.RightBracketSplitter, StringSplitOptions.RemoveEmptyEntries));
                    //}
                    else if (!Loaded)
                    {
                        Loaded = OutputText.Data.Contains("[Server thread/INFO]: Done (");
                    }
                }
            }
                                                );
            ServerProcess.ErrorDataReceived += new DataReceivedEventHandler
                                               (
                (sender, ErrorText) =>
            {
                if (string.IsNullOrWhiteSpace(ErrorText.Data) == false)
                {
                    Util.WriteToLog(ErrorText.Data, ErrorFormat);
                }
            }
                                               );

            /*=======================================
            *  Finally start the dang server process
            *  =======================================*/
            Wrapper.WriteLine("Starting Minecraft server...");
            ServerProcess.Start();

            //File.WriteAllText(PIDFile, this.ServerProcess.Id.ToString());

            //Redirect the input so that the code can send text
            Input = ServerProcess.StandardInput;

            //Start checking for output
            ServerProcess.BeginOutputReadLine();
            ServerProcess.BeginErrorReadLine();

            //Don't try to do anything else until the server finishes loading
            while (Loaded == false)
            {
                ;
            }
            Wrapper.WriteLine("Minecraft server loaded!");

            Wrapper.Command("InputMode 1");
            Wrapper.WriteLine("Use \"wrapper InputMode 0\" to access Wrapper mode.");

            /*=======================================
            *  This loop monitors for user input in the console
            *  and sends it to the appropriate process
            *  =======================================*/
            string ConsoleInput = "";
            Running = true;
            do
            {
                try
                {
                    //Get user input
                    ConsoleInput = Console.In.ReadLine();

                    //If the user wasn't a squit
                    if (string.IsNullOrWhiteSpace(ConsoleInput) == false)
                    {
                        switch (Wrapper.InputTarget)
                        {
                        //Default to wrapper
                        case Wrapper.Modes.Menu:
                            Wrapper.Command(ConsoleInput);
                            break;

                        //Default to Minecraft server
                        case Wrapper.Modes.MinecraftServer:
                            //Send it to the wrapper if it's a wrapper command
                            if (ConsoleInput.StartsWith("wrapper"))
                            {
                                Wrapper.Command(ConsoleInput.Remove(0, 7));
                            }
                            //If it's a stop command, switch to the stop routine
                            else if (ConsoleInput == "stop")
                            {
                                StopRoutine();
                            }
                            else if (ConsoleInput == "stop-no-save")
                            {
                                StopRoutine(false);
                            }
                            //Send it to the Minecraft server
                            else
                            {
                                Input.WriteLine(ConsoleInput);
                            }
                            break;

                        default:
                            Wrapper.InputTarget = Wrapper.Modes.Menu;
                            throw new TrashMonkeyException("Invalid input mode! Defaulting to wrapper mode.");
                        }
                    }
                }
                catch (TrashMonkeyException e)  //Handled errors
                {
                    Wrapper.ErrorWriteLine(e.Message);
                }
                catch (Exception e)             //Something actually broke errors
                {
                    Util.PrintErrorInfo(e);
                }
                ConsoleInput = "";
            } while (Running == true);
            //Exiting this loop should return to the menu
            return(1);
        }
Example #2
0
        /*=======================================
        *  This subroutine starts up the server
        *  and then monitors the output.
        *  =======================================*/
        public static void Run(bool BridgeMode = false)
        {
            FactorioServer.IsBridge = BridgeMode;
            FactorioServer.Loaded   = false;
            FactorioServer.Stopping = false;
            Console.ForegroundColor = ConsoleColor.Cyan;
            Wrapper.InputTarget     = Wrapper.Modes.Menu;

            Wrapper.WriteLine("Preparing Factorio server...");
            string SettingsFileName = "Settings.ini";
            string SettingsFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, SettingsFileName);

            /*=======================================
            *  Create a settings file with some default values if it doesn't exist.
            *  =======================================*/
            if (!File.Exists(SettingsFilePath))
            {
                IniFile ds = new IniFile(SettingsFileName);
                ds.Write("Version", "0.18.10", "Factorio");
                ds.Write("ExePath", @"E:\Factorio\Game\steamapps\common\Factorio\bin\x64\factorio.exe", "Factorio");
                ds.Write("ServerArgs", @"--start-server E:\Factorio\Data\saves\Minecraft_Bridge_Tests\Minecraft_Bridge_Test_1 --bind 127.0.0.1:30000 --no-log-rotation", "Factorio");
            }

            /*=======================================
            *  Read the settings file to find the server path
            *  =======================================*/
            Wrapper.WriteLine("Reading wrapper settings file...");
            IniFile s = new IniFile(SettingsFileName);

            Console.Title = "Wrapper." + Wrapper.Version + " FactorioServer." + s.Read("Version", "Factorio");
            RootPath      = s.Read("ServerFolder", "Windows") + '\\';

            ScriptOutputPath = s.Read("ScriptOutputPath", "Factorio") + '\\';

            /*=======================================
            *  Setup some files related to logs and crap
            *  =======================================*/
            Wrapper.WriteLine("Checking for previous unstopped servers...");
            PIDFile = RootPath + s.Read("PIDFile", "Factorio");
            if (File.Exists(PIDFile))
            {
                Int32 PreviousPID = Int32.Parse(File.ReadAllText(PIDFile));
                try
                {
                    using (Process PreviousServer = Process.GetProcessById(PreviousPID))
                        using (StreamWriter PreviousInput = PreviousServer.StandardInput)
                        {
                            PreviousInput.WriteLine("/quit");
                            //Make sure the server process has stopped
                            while (PreviousServer.HasExited == false)
                            {
                                ;
                            }
                        }
                    Wrapper.WriteLine("Previous server stopped successfully");
                }
                catch (Exception)
                {
                    Wrapper.WriteLine("No previous server process running");
                }
                File.Delete(PIDFile);
            }

            /*=======================================
            *  Configure the server process before starting it
            *  =======================================*/
            Wrapper.WriteLine("Configuring server process...");
            FactorioServer.Process = new Process();
            FactorioServer.Process.StartInfo.FileName               = s.Read("ExePath", "Factorio");
            FactorioServer.Process.StartInfo.Arguments              = s.BigRead("ServerArgs", "Factorio");
            FactorioServer.Process.StartInfo.CreateNoWindow         = true;
            FactorioServer.Process.StartInfo.ErrorDialog            = false;
            FactorioServer.Process.StartInfo.UseShellExecute        = false;
            FactorioServer.Process.StartInfo.RedirectStandardError  = true;
            FactorioServer.Process.StartInfo.RedirectStandardOutput = true;
            FactorioServer.Process.StartInfo.RedirectStandardInput  = true;

            /*=======================================
            *  These are what read/print/process the server log.
            *  They'll be run whenever the server process outputs text,
            *  even while the code continues running below.
            *  =======================================*/
            Wrapper.WriteLine("Configuring console output...");
            FactorioServer.Process.OutputDataReceived += new DataReceivedEventHandler
                                                         (
                (sender, OutputText) => {
                if (string.IsNullOrWhiteSpace(OutputText.Data) == false)
                {
                    if (!FactorioServer.NoConsole)
                    {
                        Util.WriteToLog(OutputText.Data, FactorioServer.OutputFormat);
                    }
                    if (FactorioServer.Stopping == true)
                    {
                        FactorioServer.Loaded  = !OutputText.Data.Contains("changing state from(Disconnected) to(Closed)");
                        FactorioServer.Running = !OutputText.Data.EndsWith("Goodbye");
                    }
                    else if (!FactorioServer.Loaded)
                    {
                        FactorioServer.Loaded = OutputText.Data.Contains("changing state from(CreatingGame) to(InGame)");
                    }
                }
            }
                                                         );
            FactorioServer.Process.ErrorDataReceived += new DataReceivedEventHandler
                                                        (
                (sender, ErrorText) => {
                if (string.IsNullOrWhiteSpace(ErrorText.Data) == false)
                {
                    if (!FactorioServer.NoConsole)
                    {
                        Util.WriteToLog(ErrorText.Data, FactorioServer.ErrorFormat);
                    }
                }
            }
                                                        );

            /*=======================================
            *  Finally start the dang server process
            *  =======================================*/
            Wrapper.WriteLine("Starting Factorio server...");
            FactorioServer.NoConsole = true;
            FreeConsole();
            FactorioServer.Process.Start();

            File.WriteAllText(PIDFile, FactorioServer.Process.Id.ToString());

            //Redirect the input so that the code can send text
            FactorioServer.Input = FactorioServer.Process.StandardInput;

            //Start checking for output
            FactorioServer.Process.BeginOutputReadLine();
            FactorioServer.Process.BeginErrorReadLine();

            //Don't try to do anything else until the server finishes loading
            while (FactorioServer.Loaded == false)
            {
                ;
            }
            AllocConsole();
            FactorioServer.NoConsole = false;
            Console.Title            = "Wrapper." + Wrapper.Version + " FactorioServer." + s.Read("Version", "Factorio");
            Wrapper.WriteLine("Factorio server loaded!");

            FactorioServer.Running = true;
            if (!FactorioServer.IsBridge)
            {
                Wrapper.Command("InputMode 2");
                Wrapper.WriteLine("Use \"wrapper InputMode 0\" to access Wrapper mode.");

                /*=======================================
                *  This loop monitors for user input in the console
                *  and sends it to the appropriate process
                *  =======================================*/
                string ConsoleInput = "";
                do
                {
                    try
                    {
                        //Get user input
                        ConsoleInput = Console.In.ReadLine();

                        //If the user wasn't a squit
                        if (string.IsNullOrWhiteSpace(ConsoleInput) == false)
                        {
                            switch (Wrapper.InputTarget)
                            {
                            //Default to wrapper
                            case Wrapper.Modes.Menu:
                                Wrapper.Command(ConsoleInput);
                                break;

                            //Default to Factorio server
                            case Wrapper.Modes.FactorioServer:
                                FactorioServer.ProcessInput(ConsoleInput);
                                break;

                            default:
                                Wrapper.InputTarget = Wrapper.Modes.Menu;
                                throw new TrashMonkeyException("Invalid input mode! Defaulting to wrapper mode.");
                            }
                        }
                    }
                    catch (TrashMonkeyException e)
                    { //Handled errors
                        Wrapper.ErrorWriteLine(e.Message);
                    }
                    catch (Exception e)
                    {            //Something actually broke errors
                        Util.PrintErrorInfo(e);
                    }
                    ConsoleInput = "";
                } while (FactorioServer.Running == true);
                //Exiting this loop should return to the menu
            }
        }