test: cover testNetSession
This commit is contained in:
1 parent
0fce9668ff
commit
5a665a3dd9
1 file changed
+157
@@ -153,3 +153,160 @@ VF_TEST(aPlayPacketArrivingBeforeLoginEndsTheSessionWithAReasonInsteadOfCrashing
|
|||||||
TransportConfig transportConfig;
|
TransportConfig transportConfig;
|
||||||
Pair pair(quality, transportConfig, quietConfig());
|
Pair pair(quality, transportConfig, quietConfig());
|
||||||
|
|
||||||
|
// A well-formed PlayerState sent straight at a server still in handshake.
|
||||||
|
PlayerState early;
|
||||||
|
early.x = 12.0f;
|
||||||
|
early.y = 40.0f;
|
||||||
|
early.z = -3.0f;
|
||||||
|
early.health = 20.0f;
|
||||||
|
PacketWriter body;
|
||||||
|
early.encode(body);
|
||||||
|
ByteWriter stream;
|
||||||
|
writeFramed(stream, PacketId::PlayerState, body);
|
||||||
|
pair.clientTransport.sendReliable(stream.bytes());
|
||||||
|
|
||||||
|
for (uint64_t tick = 0; tick < 10; ++tick) pair.step(tick);
|
||||||
|
|
||||||
|
CHECK(pair.server.phase() == SessionPhase::Disconnected);
|
||||||
|
CHECK(pair.server.disconnectReason() == "packet out of phase");
|
||||||
|
CHECK_EQ(pair.server.stats().packetsRejected, static_cast<uint64_t>(1));
|
||||||
|
CHECK(!pair.server.timedOut());
|
||||||
|
CHECK_EQ(pair.server.view().position.x, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(aJoinGameArrivingAfterTheClientIsAlreadyPlayingIsRejected) {
|
||||||
|
LinkQuality quality;
|
||||||
|
quality.latencyTicks = 1;
|
||||||
|
TransportConfig transportConfig;
|
||||||
|
Pair pair(quality, transportConfig, quietConfig());
|
||||||
|
pair.client.beginHandshake("vulp", 1, 2);
|
||||||
|
for (uint64_t tick = 0; tick < 10; ++tick) pair.step(tick);
|
||||||
|
CHECK(pair.client.phase() == SessionPhase::Play);
|
||||||
|
|
||||||
|
JoinGame again;
|
||||||
|
again.entityId = 99;
|
||||||
|
again.levelType = "default";
|
||||||
|
PacketWriter body;
|
||||||
|
again.encode(body);
|
||||||
|
ByteWriter stream;
|
||||||
|
writeFramed(stream, PacketId::JoinGame, body);
|
||||||
|
pair.serverTransport.sendReliable(stream.bytes());
|
||||||
|
|
||||||
|
for (uint64_t tick = 10; tick < 20; ++tick) pair.step(tick);
|
||||||
|
CHECK(pair.client.phase() == SessionPhase::Disconnected);
|
||||||
|
CHECK(pair.client.disconnectReason() == "packet out of phase");
|
||||||
|
CHECK_EQ(pair.client.entityId(), 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(theServerDisconnectsAClientThatStopsAnsweringKeepAlive) {
|
||||||
|
LinkQuality quality;
|
||||||
|
quality.latencyTicks = 1;
|
||||||
|
TransportConfig transportConfig;
|
||||||
|
SessionConfig config = quietConfig();
|
||||||
|
config.keepAliveIntervalTicks = 5;
|
||||||
|
config.keepAliveTimeoutTicks = 30;
|
||||||
|
Pair pair(quality, transportConfig, config);
|
||||||
|
|
||||||
|
pair.client.beginHandshake("vulp", 1, 2);
|
||||||
|
for (uint64_t tick = 0; tick < 60; ++tick) pair.step(tick);
|
||||||
|
CHECK(pair.server.phase() == SessionPhase::Play);
|
||||||
|
CHECK(pair.server.stats().keepAlivesSent > 5u);
|
||||||
|
CHECK(!pair.server.timedOut());
|
||||||
|
|
||||||
|
// The client goes silent: the server keeps ticking on its own.
|
||||||
|
for (uint64_t tick = 60; tick < 130; ++tick) {
|
||||||
|
pair.server.receive(tick);
|
||||||
|
pair.server.send(tick);
|
||||||
|
}
|
||||||
|
CHECK(pair.server.phase() == SessionPhase::Disconnected);
|
||||||
|
CHECK(pair.server.timedOut());
|
||||||
|
CHECK(pair.server.disconnectReason() == "keep alive timeout");
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(chunksAreStreamedNearestFirstAndNeverMoreThanTheTickBudget) {
|
||||||
|
LinkQuality quality;
|
||||||
|
quality.latencyTicks = 1;
|
||||||
|
TransportConfig transportConfig;
|
||||||
|
SessionConfig config = quietConfig();
|
||||||
|
config.chunksPerTick = 2;
|
||||||
|
Pair pair(quality, transportConfig, config);
|
||||||
|
buildTerrain(pair.serverWorld, 3);
|
||||||
|
pair.server.view().position = Vec3{8.0f, 8.0f, 8.0f};
|
||||||
|
|
||||||
|
pair.client.beginHandshake("vulp", 1, 2);
|
||||||
|
for (uint64_t tick = 0; tick < 6; ++tick) pair.step(tick);
|
||||||
|
|
||||||
|
CHECK(pair.server.phase() == SessionPhase::Play);
|
||||||
|
uint64_t afterFirstTicks = pair.server.stats().chunksSent;
|
||||||
|
CHECK(afterFirstTicks > 0u);
|
||||||
|
CHECK(afterFirstTicks <= 2u * 6u);
|
||||||
|
|
||||||
|
// The chunk the player stands in must be among the first handful sent.
|
||||||
|
CHECK(pair.server.view().hasChunk(Vec3i{0, 0, 0}));
|
||||||
|
CHECK(!pair.server.view().hasChunk(Vec3i{2, 0, 2}));
|
||||||
|
|
||||||
|
for (uint64_t tick = 6; tick < 60; ++tick) pair.step(tick);
|
||||||
|
CHECK_EQ(pair.server.view().pendingCount(), static_cast<std::size_t>(0));
|
||||||
|
// The sphere of radius 2 holds 33 chunk slots, only 13 of which exist in
|
||||||
|
// the y = 0 plane, so exactly those get snapshots.
|
||||||
|
CHECK_EQ(pair.server.stats().chunksSent, static_cast<uint64_t>(13));
|
||||||
|
CHECK_EQ(pair.client.stats().chunksApplied, static_cast<uint64_t>(13));
|
||||||
|
CHECK_EQ(pair.clientWorld.chunkCount(), static_cast<std::size_t>(13));
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(manyChangesInOneChunkBecomeAMultiBlockChangeAndFewBecomeSingleBlockChanges) {
|
||||||
|
LinkQuality quality;
|
||||||
|
quality.latencyTicks = 1;
|
||||||
|
TransportConfig transportConfig;
|
||||||
|
SessionConfig config = quietConfig();
|
||||||
|
config.multiBlockThreshold = 4;
|
||||||
|
Pair pair(quality, transportConfig, config);
|
||||||
|
buildTerrain(pair.serverWorld, 1);
|
||||||
|
|
||||||
|
pair.client.beginHandshake("vulp", 1, 2);
|
||||||
|
for (uint64_t tick = 0; tick < 40; ++tick) pair.step(tick);
|
||||||
|
CHECK(pair.server.view().hasChunk(Vec3i{0, 0, 0}));
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; ++i) pair.edit(i, 14, 0, Block::Glowstone);
|
||||||
|
pair.step(40);
|
||||||
|
pair.clearDirty();
|
||||||
|
for (uint64_t tick = 41; tick < 50; ++tick) pair.step(tick);
|
||||||
|
CHECK_EQ(pair.server.stats().blockChangesSent, static_cast<uint64_t>(3));
|
||||||
|
CHECK_EQ(pair.server.stats().multiBlockChangesSent, static_cast<uint64_t>(0));
|
||||||
|
CHECK_EQ(pair.client.stats().blockChangesApplied, static_cast<uint64_t>(3));
|
||||||
|
|
||||||
|
for (int i = 0; i < 9; ++i) pair.edit(i, 15, 0, Block::Obsidian);
|
||||||
|
pair.step(50);
|
||||||
|
pair.clearDirty();
|
||||||
|
for (uint64_t tick = 51; tick < 60; ++tick) pair.step(tick);
|
||||||
|
CHECK_EQ(pair.server.stats().blockChangesSent, static_cast<uint64_t>(3));
|
||||||
|
CHECK_EQ(pair.server.stats().multiBlockChangesSent, static_cast<uint64_t>(1));
|
||||||
|
CHECK_EQ(pair.client.stats().multiBlockChangesApplied, static_cast<uint64_t>(1));
|
||||||
|
|
||||||
|
for (int i = 0; i < 9; ++i) CHECK(pair.clientWorld.blockAt(i, 15, 0) == Block::Obsidian);
|
||||||
|
for (int i = 0; i < 3; ++i) CHECK(pair.clientWorld.blockAt(i, 14, 0) == Block::Glowstone);
|
||||||
|
}
|
||||||
|
|
||||||
|
VF_TEST(theClientWorldMirrorsTheServerByteForByteAcrossAnUnreliableLink) {
|
||||||
|
LinkQuality quality;
|
||||||
|
quality.lossPercent = 30;
|
||||||
|
quality.duplicatePercent = 10;
|
||||||
|
quality.reorderPercent = 25;
|
||||||
|
quality.reorderExtraTicks = 4;
|
||||||
|
quality.latencyTicks = 2;
|
||||||
|
quality.seed = 0xabcdef0123456789ull;
|
||||||
|
|
||||||
|
TransportConfig transportConfig;
|
||||||
|
transportConfig.mtu = 512;
|
||||||
|
transportConfig.retransmitTimeoutTicks = 10;
|
||||||
|
transportConfig.sendWindow = 24;
|
||||||
|
|
||||||
|
SessionConfig config = quietConfig();
|
||||||
|
config.chunksPerTick = 2;
|
||||||
|
config.multiBlockThreshold = 6;
|
||||||
|
Pair pair(quality, transportConfig, config);
|
||||||
|
buildTerrain(pair.serverWorld, 2);
|
||||||
|
pair.server.view().position = Vec3{8.0f, 8.0f, 8.0f};
|
||||||
|
|
||||||
|
pair.client.beginHandshake("vulp", 1, 2);
|
||||||
|
|
||||||
Reference in new issue
Block a user