#pragma once #include #include #include #include #include #include "RemoteClient.hpp" #include "Server/Abstract.hpp" #include #include #include #include #include #include "ContentEventController.hpp" #include "WorldDefManager.hpp" #include "BinaryResourceManager.hpp" #include "World.hpp" #include "SaveBackend.hpp" #include "boost/asio/ip/address.hpp" namespace LV::Server { namespace fs = std::filesystem; class GameServer : public AsyncObject { TOS::Logger LOG = "GameServer"; DestroyLock UseLock; std::thread RunThread; bool IsAlive = true, IsGoingShutdown = false; std::string ShutdownReason; static constexpr float PerTickDuration = 1/30.f, // Минимальная и стартовая длина такта PerTickAdjustment = 1/60.f; // Подгонка длительности такта в случае провисаний float CurrentTickDuration = PerTickDuration, // Текущая длительность такта GlobalTickLagTime = 0; // На сколько глобально запаздываем по симуляции struct { Lockable> ConnectedPlayersSet; Lockable>> NewConnectedPlayers; } External; struct ContentObj { public: // WorldDefManager WorldDM; // VoxelDefManager VoxelDM; // NodeDefManager NodeDM; BinaryResourceManager TextureM; BinaryResourceManager ModelM; BinaryResourceManager SoundM; ContentObj(asio::io_context &ioc, std::shared_ptr zeroTexture, std::shared_ptr zeroModel, std::shared_ptr zeroSound) : TextureM(ioc, zeroTexture), ModelM(ioc, zeroModel), SoundM(ioc, zeroSound) { } } Content; struct { std::vector> CECs; // Индекс игрока, у которого в следующем такте будет пересмотрен ContentEventController->ContentViewCircles uint16_t CEC_NextRebuildViewCircles = 0, CEC_NextCheckRegions = 0; ServerTime AfterStartTime = {0, 0}; } Game; struct Expanse_t { std::unordered_map ContentBridges; // Вычисляет окружности обозримой области // depth ограничивает глубину входа в ContentBridges std::vector accumulateContentViewCircles(ContentViewCircle circle, int depth = 2); // Вынести в отдельный поток static ContentViewGlobal makeContentViewGlobal(const std::vector &views); ContentViewGlobal makeContentViewGlobal(ContentViewCircle circle, int depth = 2) { return makeContentViewGlobal(accumulateContentViewCircles(circle, depth)); } // std::unordered_map> remapCVCsByWorld(const std::vector &list); // std::unordered_map> calcAndRemapCVC(ContentViewCircle circle, int depth = 2) { // return remapCVCsByWorld(calcCVCs(circle, depth)); // } std::unordered_map> Worlds; /* Регистрация миров по строке */ private: void _accumulateContentViewCircles(ContentViewCircle circle, int depth); } Expanse; struct { std::unique_ptr World; std::unique_ptr Player; std::unique_ptr Auth; std::unique_ptr ModStorage; } SaveBackend; public: GameServer(asio::io_context &ioc, fs::path worldPath) : AsyncObject(ioc), Content(ioc, nullptr, nullptr, nullptr) { init(worldPath); } virtual ~GameServer(); void shutdown(const std::string reason) { if(ShutdownReason.empty()) ShutdownReason = reason; IsGoingShutdown = true; } bool isAlive() { return IsAlive; } void waitShutdown() { UseLock.wait_no_use(); } // Подключение tcp сокета coro<> pushSocketConnect(tcp::socket socket); // Сокет, прошедший авторизацию (onSocketConnect() передаёт его в onSocketAuthorized()) coro<> pushSocketAuthorized(tcp::socket socket, const std::string username); // Инициализация игрового протокола для сокета (onSocketAuthorized() может передать сокет в onSocketGame()) coro<> pushSocketGameProtocol(tcp::socket socket, const std::string username); /* Загрузит, сгенерирует или просто выдаст регион из мира, который должен существовать */ Region* forceGetRegion(WorldId_t worldId, Pos::GlobalRegion pos); private: void init(fs::path worldPath); void prerun(); void run(); void stepContent(); /* Дождаться и получить необходимые данные с бд или диска Получить несрочные данные */ void stepSyncWithAsync(); void stepPlayers(); void stepWorlds(); /* Пересмотр наблюдаемых зон (чанки, регионы, миры) Добавить требуемые регионы в список на предзагрузку с приоритетом TODO: нужен механизм асинхронной загрузки регионов с бд В начале следующего такта обязательное дожидание прогрузки активной зоны и оповещение миров об изменениях в наблюдаемых регионах */ void stepViewContent(); void stepSendPlayersPackets(); void stepLoadRegions(); void stepGlobal(); void stepSave(); void save(); }; }