Set up basic deterministic lockstep
This commit is contained in:
1 parent
28c6e83eda
commit
23fef6f349
26 files changed
+1172
-239
No files matched your search
@@ -26,31 +26,103 @@ int main()
|
||||
{"players", {{"player", {{"hp", 200.0}}}}},
|
||||
{"locations", {{"uni_exterior", {{"state", savedLocationState}}}}}
|
||||
};
|
||||
const std::string message = FRG::makeStateMessage(original);
|
||||
nlohmann::json parsed;
|
||||
FRG::InitialStateMessage initial;
|
||||
initial.state = original;
|
||||
initial.assignedPlayerId = "player";
|
||||
initial.snapshotTick = 42;
|
||||
initial.compatibilityFingerprint = "lockstep-v2:battle_zone_multiplayer";
|
||||
const std::string message = FRG::makeInitialStateMessage(initial);
|
||||
FRG::InitialStateMessage parsed;
|
||||
std::string error;
|
||||
require(
|
||||
FRG::parseStateMessage(message, parsed, error),
|
||||
"valid state envelope was rejected");
|
||||
require(parsed == original, "state payload did not round trip");
|
||||
FRG::parseInitialStateMessage(message, parsed, error),
|
||||
"valid initial-state envelope was rejected");
|
||||
require(parsed.state == original, "state payload did not round trip");
|
||||
require(parsed.snapshotTick == 42, "snapshot tick did not round trip");
|
||||
require(
|
||||
!parsed["locations"]["uni_exterior"]["state"].contains("cameraAzimuth") &&
|
||||
!parsed["locations"]["uni_exterior"]["state"].contains("cameraInclination"),
|
||||
parsed.assignedPlayerId == "player",
|
||||
"assigned player did not round trip");
|
||||
require(
|
||||
!parsed.state["locations"]["uni_exterior"]["state"].contains("cameraAzimuth") &&
|
||||
!parsed.state["locations"]["uni_exterior"]["state"].contains("cameraInclination"),
|
||||
"local camera state leaked into the network payload");
|
||||
|
||||
FRG::DestinationInputMessage input;
|
||||
input.clientSequence = 7;
|
||||
input.target = Eigen::Vector3f(1.234f, 0.f, -5.678f);
|
||||
FRG::DestinationInputMessage parsedInput;
|
||||
require(
|
||||
!FRG::parseStateMessage(
|
||||
R"({"protocolVersion":2,"type":"state","payload":{}})",
|
||||
FRG::parseDestinationInputMessage(
|
||||
FRG::makeDestinationInputMessage(input), parsedInput, error),
|
||||
"destination input did not round trip");
|
||||
require(
|
||||
parsedInput.clientSequence == 7 &&
|
||||
parsedInput.target.isApprox(Eigen::Vector3f(1.23f, 0.f, -5.68f)),
|
||||
"destination input was not normalized");
|
||||
|
||||
FRG::TickMessage tick;
|
||||
tick.tickId = 43;
|
||||
tick.commands.push_back({1, "player", parsedInput.target});
|
||||
tick.stateHash = "0123456789abcdef";
|
||||
FRG::TickMessage parsedTick;
|
||||
require(
|
||||
FRG::parseTickMessage(
|
||||
FRG::makeTickMessage(tick), parsedTick, error),
|
||||
"tick message did not round trip");
|
||||
require(
|
||||
parsedTick.tickId == 43 && parsedTick.commands.size() == 1,
|
||||
"tick contents did not round trip");
|
||||
|
||||
require(
|
||||
!FRG::parseInitialStateMessage(
|
||||
R"({"protocolVersion":1,"type":"initial_state","payload":{}})",
|
||||
parsed, error),
|
||||
"unknown protocol version was accepted");
|
||||
require(
|
||||
!FRG::parseStateMessage(
|
||||
R"({"protocolVersion":1,"type":"command","payload":{}})",
|
||||
parsed, error),
|
||||
!FRG::parseDestinationInputMessage(
|
||||
R"({"protocolVersion":2,"type":"command","payload":{}})",
|
||||
parsedInput, error),
|
||||
"unknown message type was accepted");
|
||||
require(
|
||||
!FRG::parseStateMessage("not json", parsed, error),
|
||||
!FRG::parseInitialStateMessage("not json", parsed, error),
|
||||
"malformed JSON was accepted");
|
||||
require(
|
||||
!FRG::parseDestinationInputMessage(
|
||||
R"({"protocolVersion":2,"type":"destination_input","payload":{"clientSequence":1,"target":[1e999,0,0]}})",
|
||||
parsedInput, error),
|
||||
"non-finite destination was accepted");
|
||||
std::string errorCode;
|
||||
std::string errorDetail;
|
||||
require(
|
||||
FRG::parseErrorMessage(
|
||||
FRG::makeErrorMessage("server_full", "No slot"),
|
||||
errorCode, errorDetail) &&
|
||||
errorCode == "server_full" && errorDetail == "No slot",
|
||||
"error message did not round trip");
|
||||
|
||||
nlohmann::json hashState = original;
|
||||
hashState["savedAt"] = "first";
|
||||
const std::string firstHash = FRG::canonicalStateHash(hashState);
|
||||
hashState["savedAt"] = "second";
|
||||
require(
|
||||
firstHash == FRG::canonicalStateHash(hashState),
|
||||
"persistence timestamp changed deterministic hash");
|
||||
|
||||
FRG::LockstepTimeline timeline(2);
|
||||
const std::uint64_t firstSequence =
|
||||
timeline.schedule("player", Eigen::Vector3f(1.f, 0.f, 1.f));
|
||||
const std::uint64_t secondSequence =
|
||||
timeline.schedule("player_2", Eigen::Vector3f(2.f, 0.f, 2.f));
|
||||
const FRG::TickMessage emptyTick = timeline.advance();
|
||||
require(
|
||||
emptyTick.tickId == 1 && emptyTick.commands.empty(),
|
||||
"input-delay tick was not empty");
|
||||
const FRG::TickMessage commandTick = timeline.advance();
|
||||
require(
|
||||
commandTick.tickId == 2 && commandTick.commands.size() == 2 &&
|
||||
commandTick.commands[0].commandSequence == firstSequence &&
|
||||
commandTick.commands[1].commandSequence == secondSequence,
|
||||
"scheduled commands were not finalized in stable order");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in new issue
Block a user