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

+67 -5
View File
@@ -1,15 +1,77 @@
#pragma once
#include "../external/nlohmann/json.hpp"
#include <Eigen/Dense>
#include <cstdint>
#include <map>
#include <string>
#include <vector>
namespace FRG {
inline constexpr int kGameProtocolVersion = 1;
inline constexpr int kGameProtocolVersion = 2;
inline constexpr std::int64_t kSimulationTickMs = 100;
inline constexpr std::uint32_t kDefaultInputDelayTicks = 2;
std::string makeStateMessage(const nlohmann::json& state);
bool parseStateMessage(
const std::string& message, nlohmann::json& state,
std::string& error);
struct InitialStateMessage {
nlohmann::json state;
std::string assignedPlayerId;
std::uint64_t snapshotTick = 0;
std::int64_t tickMs = kSimulationTickMs;
std::uint32_t inputDelayTicks = kDefaultInputDelayTicks;
std::string compatibilityFingerprint;
};
struct DestinationInputMessage {
std::uint64_t clientSequence = 0;
Eigen::Vector3f target = Eigen::Vector3f::Zero();
};
struct ScheduledDestinationCommand {
std::uint64_t commandSequence = 0;
std::string playerId;
Eigen::Vector3f target = Eigen::Vector3f::Zero();
};
struct TickMessage {
std::uint64_t tickId = 0;
std::vector<ScheduledDestinationCommand> commands;
std::string stateHash;
};
class LockstepTimeline {
public:
explicit LockstepTimeline(
std::uint32_t inputDelayTicks = kDefaultInputDelayTicks)
: inputDelayTicks_(inputDelayTicks) {}
std::uint64_t currentTick() const { return currentTick_; }
std::uint64_t schedule(
const std::string& playerId, const Eigen::Vector3f& target);
TickMessage advance();
private:
std::uint32_t inputDelayTicks_;
std::uint64_t currentTick_ = 0;
std::uint64_t nextCommandSequence_ = 1;
std::map<std::uint64_t, std::vector<ScheduledDestinationCommand>> pending_;
};
std::string makeInitialStateMessage(const InitialStateMessage& message);
bool parseInitialStateMessage(
const std::string& text, InitialStateMessage& message, std::string& error);
std::string makeDestinationInputMessage(const DestinationInputMessage& message);
bool parseDestinationInputMessage(
const std::string& text, DestinationInputMessage& message, std::string& error);
std::string makeTickMessage(const TickMessage& message);
bool parseTickMessage(
const std::string& text, TickMessage& message, std::string& error);
std::string makeErrorMessage(const std::string& code, const std::string& detail);
bool parseErrorMessage(
const std::string& text, std::string& code, std::string& detail);
// Stable for the same executable/content build. Persistence-only wall-clock
// fields are removed before hashing.
std::string canonicalStateHash(nlohmann::json state);
} // namespace FRG