player connect-disconnect
This commit is contained in:
1 parent
3f68a8ba07
commit
2e6b93f3ac
16 files changed
+305
-62
No files matched your search
@@ -135,6 +135,18 @@ int main()
|
||||
|
||||
FRG::PlayerStateMap noPlayers;
|
||||
state.update(50, noPlayers, false);
|
||||
auto disabledPlayer = std::make_shared<FRG::CharacterState>();
|
||||
disabledPlayer->configureAsPlayer();
|
||||
disabledPlayer->enabled = false;
|
||||
state.location->targetInteractiveObjectId = "object";
|
||||
state.location->targetInteractNpcId = "npc";
|
||||
FRG::PlayerStateMap disabledPlayers{{"player", disabledPlayer}};
|
||||
const FRG::LocationSimulationEvents disabledEvents =
|
||||
state.update(50, disabledPlayers, false);
|
||||
if (!disabledEvents.interactiveObjects.empty() ||
|
||||
!disabledEvents.npcInteractionId.empty()) {
|
||||
return fail("Disabled player interacted with location entities");
|
||||
}
|
||||
|
||||
FRG::CampaignConfig battleZone;
|
||||
battleZone.id = "battle_zone";
|
||||
@@ -165,6 +177,8 @@ int main()
|
||||
return fail("Network campaign shared player was not created");
|
||||
if (networkSnapshot->players.count("player2") != 0)
|
||||
return fail("Network campaign unexpectedly created the test bot");
|
||||
if (networkSnapshot->players.at("player").state.enabled)
|
||||
return fail("Network campaign player slot was enabled before connection");
|
||||
|
||||
nlohmann::json loadedNetworkState;
|
||||
networkServer.save(loadedNetworkState);
|
||||
@@ -186,6 +200,35 @@ int main()
|
||||
Eigen::Vector3f(4.f, 0.f, 5.f))) {
|
||||
return fail("Loaded multiplayer slot position was overwritten");
|
||||
}
|
||||
if (loadedNetworkBot->second.state.enabled)
|
||||
return fail("Loaded multiplayer slot was enabled before connection");
|
||||
|
||||
const std::string locationId = loadedNetworkSnapshot->currentLocationId;
|
||||
networkServer.submit(FRG::SetCharacterPositionCommand{
|
||||
locationId, "player2", Eigen::Vector3f(7.f, 0.f, 8.f), true});
|
||||
networkServer.submit(FRG::SetCharacterFacingAngleCommand{
|
||||
locationId, "player2", 1.5f, true});
|
||||
networkServer.submit(FRG::SetCharacterFaceTargetCommand{
|
||||
locationId, "player2", "player"});
|
||||
networkServer.submit(FRG::SetCharacterEnabledCommand{
|
||||
locationId, "player2", false});
|
||||
const auto commandSnapshot = networkServer.snapshot();
|
||||
const auto commandCharacter = commandSnapshot->players.at("player2").state;
|
||||
if (!commandCharacter.position.isApprox(Eigen::Vector3f(7.f, 0.f, 8.f)) ||
|
||||
!commandCharacter.homePosition.isApprox(Eigen::Vector3f(7.f, 0.f, 8.f)) ||
|
||||
commandCharacter.facingAngle != 1.5f ||
|
||||
commandCharacter.targetFacingAngle != 1.5f ||
|
||||
!commandCharacter.faceTargetId.empty() ||
|
||||
commandCharacter.enabled) {
|
||||
return fail("Semantic character commands were not applied");
|
||||
}
|
||||
networkServer.submit(FRG::SetCharacterEnabledCommand{
|
||||
locationId, "player2", true});
|
||||
const auto enabledPlayer =
|
||||
networkServer.snapshot()->players.at("player2").state;
|
||||
if (!enabledPlayer.enabled || !enabledPlayer.attackTargetId.empty()) {
|
||||
return fail("Enabled player acquired an autonomous attack target");
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -62,7 +62,10 @@ int main()
|
||||
|
||||
FRG::TickMessage tick;
|
||||
tick.tickId = 43;
|
||||
tick.commands.push_back({1, "player", parsedInput.target});
|
||||
tick.commands.push_back(FRG::ScheduledDestinationCommand{
|
||||
1, "player", parsedInput.target});
|
||||
tick.commands.push_back(FRG::ScheduledCharacterEnabledCommand{
|
||||
2, "uni_exterior", "player2", true});
|
||||
tick.stateHash = "0123456789abcdef";
|
||||
FRG::TickMessage parsedTick;
|
||||
require(
|
||||
@@ -70,7 +73,11 @@ int main()
|
||||
FRG::makeTickMessage(tick), parsedTick, error),
|
||||
"tick message did not round trip");
|
||||
require(
|
||||
parsedTick.tickId == 43 && parsedTick.commands.size() == 1,
|
||||
parsedTick.tickId == 43 && parsedTick.commands.size() == 2 &&
|
||||
std::holds_alternative<FRG::ScheduledCharacterEnabledCommand>(
|
||||
parsedTick.commands[1]) &&
|
||||
std::get<FRG::ScheduledCharacterEnabledCommand>(
|
||||
parsedTick.commands[1]).enabled,
|
||||
"tick contents did not round trip");
|
||||
|
||||
require(
|
||||
@@ -120,9 +127,19 @@ int main()
|
||||
const FRG::TickMessage commandTick = timeline.advance();
|
||||
require(
|
||||
commandTick.tickId == 2 && commandTick.commands.size() == 2 &&
|
||||
commandTick.commands[0].commandSequence == firstSequence &&
|
||||
commandTick.commands[1].commandSequence == secondSequence,
|
||||
std::get<FRG::ScheduledDestinationCommand>(
|
||||
commandTick.commands[0]).commandSequence == firstSequence &&
|
||||
std::get<FRG::ScheduledDestinationCommand>(
|
||||
commandTick.commands[1]).commandSequence == secondSequence,
|
||||
"scheduled commands were not finalized in stable order");
|
||||
|
||||
timeline.scheduleCharacterEnabled("uni_exterior", "player2", true);
|
||||
const FRG::TickMessage presenceTick = timeline.advance();
|
||||
require(
|
||||
presenceTick.commands.size() == 1 &&
|
||||
std::holds_alternative<FRG::ScheduledCharacterEnabledCommand>(
|
||||
presenceTick.commands[0]),
|
||||
"character enabled command was not scheduled for the next tick");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -34,11 +34,16 @@ int main()
|
||||
{
|
||||
FRG::CampaignConfig campaign;
|
||||
campaign.id = "test_lockstep";
|
||||
campaign.networkPlayerSlots = {"player", "player2"};
|
||||
|
||||
FRG::InternalGameServer authoritative;
|
||||
authoritative.startCampaign(campaign, {}, true, false);
|
||||
nlohmann::json initialState;
|
||||
authoritative.save(initialState);
|
||||
initialState["players"]["player2"] = initialState["players"]["player"];
|
||||
authoritative.load(initialState);
|
||||
FRG::WebsocketGameServer server(
|
||||
authoritative, {"player"}, "lockstep-v2:test_lockstep",
|
||||
authoritative, {"player", "player2"}, "lockstep-v2:test_lockstep",
|
||||
"127.0.0.1", 0);
|
||||
auto client = std::make_unique<FRG::WebsocketGameClient>(
|
||||
"127.0.0.1", std::to_string(server.port()), "/");
|
||||
@@ -54,6 +59,18 @@ int main()
|
||||
"client did not receive initial state");
|
||||
require(client->localPlayerId() == "player", "wrong player slot assigned");
|
||||
|
||||
auto secondClient = std::make_unique<FRG::WebsocketGameClient>(
|
||||
"127.0.0.1", std::to_string(server.port()), "/");
|
||||
require(
|
||||
waitFor([&]() {
|
||||
secondClient->update(0);
|
||||
return secondClient->initialized();
|
||||
}),
|
||||
"second client did not receive initial state");
|
||||
require(
|
||||
secondClient->localPlayerId() == "player2",
|
||||
"second client received the wrong player slot");
|
||||
|
||||
FRG::WebsocketGameClient overflow(
|
||||
"127.0.0.1", std::to_string(server.port()), "/");
|
||||
require(
|
||||
@@ -63,6 +80,18 @@ int main()
|
||||
}),
|
||||
"connection beyond configured slots was not rejected");
|
||||
|
||||
server.update();
|
||||
require(
|
||||
waitFor([&]() {
|
||||
client->update(0);
|
||||
secondClient->update(0);
|
||||
return client->snapshot()->players.at("player2").state.enabled &&
|
||||
secondClient->snapshot()->players.at("player").state.enabled;
|
||||
}) &&
|
||||
authoritative.snapshot()->players.at("player").state.enabled &&
|
||||
authoritative.snapshot()->players.at("player2").state.enabled,
|
||||
"connection presence was not broadcast to all replicas");
|
||||
|
||||
client->submit(FRG::SetCharacterDestinationCommand{
|
||||
{}, "spoofed", Eigen::Vector3f(1.234f, 0.f, 2.345f), "ignored"});
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
@@ -75,8 +104,17 @@ int main()
|
||||
}),
|
||||
"client did not consume finalized lockstep ticks");
|
||||
|
||||
client.reset();
|
||||
secondClient.reset();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
server.update();
|
||||
require(
|
||||
waitFor([&]() {
|
||||
client->update(0);
|
||||
return !client->snapshot()->players.at("player2").state.enabled;
|
||||
}) &&
|
||||
!authoritative.snapshot()->players.at("player2").state.enabled,
|
||||
"disconnect presence was not propagated");
|
||||
|
||||
FRG::WebsocketGameClient replacement(
|
||||
"127.0.0.1", std::to_string(server.port()), "/");
|
||||
require(
|
||||
@@ -85,6 +123,15 @@ int main()
|
||||
return replacement.initialized();
|
||||
}),
|
||||
"disconnected player slot was not reusable");
|
||||
server.update();
|
||||
require(
|
||||
waitFor([&]() {
|
||||
client->update(0);
|
||||
replacement.update(0);
|
||||
return client->snapshot()->players.at("player2").state.enabled;
|
||||
}) &&
|
||||
authoritative.snapshot()->players.at("player2").state.enabled,
|
||||
"reconnected player was not enabled");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Reference in new issue
Block a user