#pragma once #include #include #include #include #include #include #include struct subprocess_s; struct MinecraftServerOptions { std::string argv0 = ""; int view_distance = 5; }; /// @brief Singleton wrapper around a Minecraft server subprocess instance. /// Built to be used in a single-threaded way (to follow catch2 requirements). class MinecraftServer { private: MinecraftServer(); ~MinecraftServer(); MinecraftServer(const MinecraftServer&) = delete; MinecraftServer(MinecraftServer&&) = delete; MinecraftServer& operator=(const MinecraftServer&) = delete; public: static MinecraftServerOptions options; static MinecraftServer& GetInstance(); void Initialize(); /// @brief Wait for the subprocess to write a line to stdout or stderr. Thread safe /// @param timeout_ms If != 0 and no line received after timeout_ms, will throw an exception /// @return The received line content std::string WaitLine(const long long int timeout_ms = 0); /// @brief Wait for the subprocess to write a line matching regex to stdout or stderr. /// @param regex A regex pattern of the line to match /// @param timeout_ms If != 0 and no matching line received after timeout_ms, will throw an exception /// @return First element is the full line, other are all the captured groups from the regex std::vector WaitLine(const std::string& regex, const long long int timeout_ms = 0); /// @brief Write a line to subprocess stdin. Not thread-safe. /// @param input Line to write void SendLine(const std::string& input); /// @brief Path to the folder holding the structure files /// @return The path in which the server loads the structures std::filesystem::path GetStructurePath() const; private: /// @brief Terminate the subprocess void Kill(); /// @brief Internal function used to retrieve stdout and stderr void InternalThreadRead(); /// @brief Prepare a folder to host a server void InitServerFolder(const std::filesystem::path& path); /// @brief Set a gamerule value on the server void SetGamerule(const std::string& gamerule, const std::string& value); /// @brief Set all non default gamerules on the server void InitServerGamerules(); private: /// @brief Thread running the stdout reading loop std::thread read_thread; /// @brief Internal mutex for the std::queue lines std::mutex internal_read_mutex; /// @brief Queue of lines generated by the subprocess std::queue lines; /// @brief Server subprocess handle std::unique_ptr subprocess; bool running; std::filesystem::path server_path; };