Implemented the real server

This commit is contained in:
Vladislav Khorev committed 2026-10-30 16:37:30 +06:00
1 parent 931236bec0
commit 075750442f
22 files changed
+1130 -44

No files matched your search

+48 -24
View File
@@ -85,8 +85,10 @@ namespace FRG
Game::Game()
: audioPlayer(std::make_unique<AudioPlayerAsync>())
, internalGameServer()
, gameServerHost(internalGameServer)
, menuManager(renderer, internalGameServer, *audioPlayer)
, localGameServerHost(internalGameServer)
, gameServer(internalGameServer)
, gameServerHost(localGameServerHost)
, menuManager(renderer, gameServer, *audioPlayer)
, newTickCount(0)
, lastTickCount(0)
{
@@ -400,7 +402,7 @@ namespace FRG
performResetToInitialState();
applyTutorialSetting();
const CampaignConfig& campaign = activeCampaign();
internalGameServer.submit(
server().submit(
SetCurrentLocationCommand{campaign.startLocation});
menuManager.showGameplayHud(); // first HUD frame drawn from the fresh state
if (currentLocation()) currentLocation()->scriptEngine.callLocationEnterCallback();
@@ -549,12 +551,27 @@ namespace FRG
const CampaignConfig* campaign =
CampaignRegistry::instance().find(campaignId);
if (!campaign) return;
#if defined(NETWORK)
remoteCampaignActive = campaignId == "battle_zone";
if (remoteCampaignActive) {
websocketGameClient = std::make_unique<WebsocketGameClient>();
remoteGameServerHost =
std::make_unique<RemoteGameServerHost>(*websocketGameClient);
gameServer.use(*websocketGameClient);
gameServerHost.use(*remoteGameServerHost);
} else {
gameServer.use(internalGameServer);
gameServerHost.use(localGameServerHost);
}
#else
remoteCampaignActive = false;
#endif
gameServerHost.initializeCampaign(
campaignId, campaign->questsPath, CONST_ZIP_FILE);
// The player stays loaded for the whole campaign; locations come and go around him.
player = Location::createPlayer(
renderer, audioPlayer.get(), internalGameServer,
internalGameServer.localPlayerId());
renderer, audioPlayer.get(), server(),
server().localPlayerId());
if (player)
player->onDeathAnimComplete = [this]() { startDarklandsTransition(); };
}
@@ -564,6 +581,13 @@ namespace FRG
player.reset();
loadedCampaignId.clear();
gameServerHost.clearCampaign();
#if defined(NETWORK)
gameServer.use(internalGameServer);
gameServerHost.use(localGameServerHost);
remoteGameServerHost.reset();
websocketGameClient.reset();
#endif
remoteCampaignActive = false;
renderer.textureManager.UnloadUnused(); // the player's textures
}
@@ -609,7 +633,7 @@ namespace FRG
const auto state = stateSnapshot();
if (!state) return;
enterLocationThen(state->campaignId, destName, [this, destName, destPos, destRotY]() {
internalGameServer.submit(SetCurrentLocationCommand{destName});
server().submit(SetCurrentLocationCommand{destName});
Location* loc = currentLocation();
if (!loc) return;
if (loc->player) {
@@ -624,13 +648,13 @@ namespace FRG
const float inclination = locationState
? locationState->cameraInclination
: 1.1036f;
internalGameServer.submit(SetLocationCameraCommand{
server().submit(SetLocationCameraCommand{
destName, destRotY, inclination});
loc->scriptEngine.callLocationEnterCallback();
//Reset taxi
internalGameServer.submit(ResetTaxiCommand{destName});
internalGameServer.submit(SetTaxiCalledCommand{false});
server().submit(ResetTaxiCommand{destName});
server().submit(SetTaxiCalledCommand{false});
menuManager.onLocationChanged(destName);
});
@@ -693,7 +717,7 @@ namespace FRG
// (if the campaign has one); a disabled tutorial is simply one that is already completed.
void Game::applyTutorialSetting() {
if (!activeCampaign().tutorial || !menuManager.tutorialEnabledSetting)
internalGameServer.submit(
server().submit(
SetTutorialStepCommand{TutorialStep::Completed});
}
@@ -731,20 +755,20 @@ namespace FRG
params.player = player;
auto loc = std::make_shared<Location>(
renderer, internalGameServer, config.id);
renderer, server(), config.id);
gameServerHost.setupRuntimeLocation(
config.id, loc, std::move(params), audioPlayer.get());
loc->requestNightDayTransition = [this](bool isNight, bool isDawn) {
const auto state = stateSnapshot();
if (!state) return;
internalGameServer.submit(SetWorldModeCommand{
server().submit(SetWorldModeCommand{
state->isDarklands, isNight, isDawn});
};
loc->requestDarklandsTransition = [this]() { return startDarklandsTransition(); };
loc->requestAdvanceDarklandsHud = [this]() { menuManager.advanceUniIntDarklandsHud(); };
loc->requestClosePhone = [this]() { menuManager.closePhoneEntirely(); };
loc->requestReturnToMainMenu = [this]() {
internalGameServer.submit(SetCurrentLocationCommand{std::string()});
server().submit(SetCurrentLocationCommand{std::string()});
menuManager.showMenuAfterGameCompleted();
};
loc->requestSetChatUnread = [this](int idx, bool unread, const std::string& msg) {
@@ -783,11 +807,11 @@ namespace FRG
// completeTutorialStep() only acts on the current step, so sharing is safe.
{
loc->dialogueSystem.setOnDialogueAdvanced([this]() {
internalGameServer.submit(
server().submit(
CompleteTutorialStepCommand{TutorialStep::AdvanceDialogue});
});
loc->onPlayerFloorWalk = [this]() {
internalGameServer.submit(
server().submit(
CompleteTutorialStepCommand{TutorialStep::Walk});
menuManager.onPlayerStartedWalking();
};
@@ -840,9 +864,9 @@ namespace FRG
loc->player->isMoving() == false
&&
loc->player->getHp() > 0) {
internalGameServer.submit(SetCharacterTargetCommand{
server().submit(SetCharacterTargetCommand{
loc->locationId(),
internalGameServer.localPlayerId(), attackerId});
server().localPlayerId(), attackerId});
}
}
@@ -1134,7 +1158,7 @@ namespace FRG
if (!menuManager.isMainMenuOpen())
menuManager.update(static_cast<float>(delta));
if (loadingCompleted && currentLocation())
if (loadingCompleted)
{
// The authoritative implementation advances simulation; a future
// network client can use the same call to poll/apply server updates.
@@ -1147,7 +1171,7 @@ namespace FRG
void Game::processServerEvents()
{
for (GameServerEvent& event : internalGameServer.drainEvents()) {
for (GameServerEvent& event : server().drainEvents()) {
std::visit([this](const auto& value) {
using T = std::decay_t<decltype(value)>;
if constexpr (std::is_same_v<T, InventoryItemAddedEvent>) {
@@ -1380,7 +1404,7 @@ namespace FRG
}
//logger() << "Current zoom: " << Environment::zoom << std::endl;
// Tutorial: any mouse-wheel scroll counts as the "zoom gesture".
internalGameServer.submit(
server().submit(
CompleteTutorialStepCommand{TutorialStep::Zoom});
}
@@ -1564,7 +1588,7 @@ namespace FRG
// Tutorial: detect a significant pinch-zoom (≥ 2 zoom units).
if (std::abs(Environment::zoom - pinchStartZoom) >= 2.0f) {
internalGameServer.submit(
server().submit(
CompleteTutorialStepCommand{TutorialStep::Zoom});
}
}
@@ -1717,7 +1741,7 @@ namespace FRG
float deltaAz = std::abs(state->cameraAzimuth - dragStartAzimuth);
float deltaInc = std::abs(state->cameraInclination - dragStartInclination);
if (deltaAz >= TUTORIAL_ROTATION_THRESHOLD && deltaInc >= TUTORIAL_ROTATION_THRESHOLD) {
internalGameServer.submit(
server().submit(
CompleteTutorialStepCommand{TutorialStep::RotateCamera});
}
}
@@ -1770,7 +1794,7 @@ namespace FRG
const auto before = stateSnapshot();
if (!before) return;
if (isNightTransition) {
internalGameServer.submit(SetWorldModeCommand{
server().submit(SetWorldModeCommand{
before->isDarklands, !before->isNight, before->isDawn});
if (currentLocation())
{
@@ -1781,7 +1805,7 @@ namespace FRG
menuManager.lastMusicTrack = "music/bishkek night.ogg";
} else {
internalGameServer.submit(SetWorldModeCommand{
server().submit(SetWorldModeCommand{
!before->isDarklands, before->isNight, before->isDawn});
const auto after = stateSnapshot();
if (!after) return;