Set up basic deterministic lockstep

This commit is contained in:
Vladislav Khorev committed 2026-11-05 23:02:53 +06:00
1 parent 28c6e83eda
commit 23fef6f349
26 files changed
+1172 -239

No files matched your search

+26 -5
View File
@@ -9,7 +9,7 @@ namespace FRG {
namespace {
constexpr const char* kBattleZoneCampaignId = "battle_zone";
constexpr const char* kBattleZoneTestBotId = "battle_zone_test_bot";
constexpr const char* kBattleZonePlayer2Id = "player2";
constexpr std::int64_t kBattleZoneTestBotIntervalMs = 5000;
void configurePlayerAnimations(
@@ -44,11 +44,11 @@ void InternalGameServer::ensureBattleZoneTestBot()
{
if (state_.campaignId != kBattleZoneCampaignId) return;
if (!battleZoneTestBotEnabled_) {
state_.players.erase(kBattleZoneTestBotId);
state_.players.erase(kBattleZonePlayer2Id);
return;
}
auto& bot = state_.players[kBattleZoneTestBotId];
auto& bot = state_.players[kBattleZonePlayer2Id];
if (!bot) bot = std::make_shared<CharacterState>();
bot->configureAsPlayer();
configurePlayerAnimations(*bot, resourceZipFile_);
@@ -60,7 +60,7 @@ void InternalGameServer::updateBattleZoneTestBot(std::int64_t deltaMs)
if (!battleZoneTestBotEnabled_ ||
state_.campaignId != kBattleZoneCampaignId ||
state_.currentLocationName.empty() ||
state_.players.find(kBattleZoneTestBotId) == state_.players.end()) {
state_.players.find(kBattleZonePlayer2Id) == state_.players.end()) {
return;
}
@@ -72,7 +72,7 @@ void InternalGameServer::updateBattleZoneTestBot(std::int64_t deltaMs)
static std::uniform_real_distribution<float> coordinate(-10.0f, 10.0f);
submit(SetCharacterDestinationCommand{
state_.currentLocationName,
kBattleZoneTestBotId,
kBattleZonePlayer2Id,
Eigen::Vector3f(coordinate(randomEngine), 0.f, coordinate(randomEngine)),
std::string()});
}
@@ -92,6 +92,7 @@ void InternalGameServer::startCampaign(
bool createLocalPlayer, bool enableTestBot)
{
resourceZipFile_ = zipFile;
networkPlayerSlots_ = campaign.networkPlayerSlots;
battleZoneTestBotEnabled_ = enableTestBot;
state_.campaignId = campaign.id;
state_.currentLocationName.clear();
@@ -125,6 +126,7 @@ void InternalGameServer::clearCampaign()
state_.currentLocationName.clear();
state_.players.clear();
state_.locations.clear();
networkPlayerSlots_.clear();
battleZoneTestBotElapsedMs_ = 0;
publishMutation();
}
@@ -487,6 +489,19 @@ void InternalGameServer::update(std::int64_t deltaMs)
pendingSimulationLocationId_ = state_.currentLocationName;
pendingSimulationEvents_ =
stored->second.update(deltaMs, state_.players, interactiveObjectsLocked);
// Navigation recovery is a simulation consequence, not presentation input.
// Applying it here makes every lockstep replica produce the same follow-up.
for (const CharacterSimulationEvent& event :
pendingSimulationEvents_.characters) {
if (!event.events.navigationRequested) continue;
CharacterState* character = state_.findCharacter(
state_.currentLocationName, event.characterId);
if (character && stored->second.location) {
character->setTarget(
event.events.navigationTarget, {},
*stored->second.location, event.characterId);
}
}
publishMutation();
}
@@ -499,6 +514,12 @@ void InternalGameServer::load(const nlohmann::json& in)
{
pendingEvents_.clear();
state_.load(in);
for (const std::string& playerId : networkPlayerSlots_) {
auto player = state_.players.find(playerId);
if (player == state_.players.end() || !player->second) continue;
player->second->configureAsPlayer();
configurePlayerAnimations(*player->second, resourceZipFile_);
}
battleZoneTestBotElapsedMs_ = 0;
ensureBattleZoneTestBot();
publishMutation();