#include "Server/server_activity.h" #include "Engine/Game/Multiplayer/chat_rate.h" #include "Extension/Throwdowns/throwdown_wire.h" #include #include #include using namespace dingosdk; using namespace dingosdk::server; using multiplayer::ThrowdownMessage; using Kind = ThrowdownMessage::Kind; namespace { int failures = 0; void check(bool condition, const char *what) { if (condition) return; ++failures; std::fprintf(stderr, "FAILED: %s\n", what); } constexpr std::uint64_t zee = 76561198000000001ULL, kush = 76561198000000002ULL, tally = 76561198000000003ULL; std::vector lines; std::string name_of(std::uint64_t id) { return id == zee ? "Zee" : id == kush ? "Kush" : id == tally ? "Tally" : std::string{}; } std::vector wire(Kind kind, std::string series = {}) { ThrowdownMessage m; m.kind = kind; m.leader = zee; m.id = 7; m.series = std::move(series); if (kind == Kind::offer) m.placement = {1, 2, 3}; return multiplayer::encode_throwdown(m); } std::vector wire(ThrowdownMessage m) { m.leader = zee; m.id = 7; return multiplayer::encode_throwdown(m); } bool said(std::string_view text) { for (const auto &line : lines) if (line == text) return true; return false; } } // namespace int main() { ActivityLog log([](const std::string &line) { lines.push_back(line); }, name_of); const std::array at{606.2f, 198.9f, 1075.5f}; // Spot Battle: a drop, a queue, a start, a turn, and the result once it goes quiet. log.throwdown(zee, wire(Kind::offer, "SpotBattle"), &at, 1'000'000); log.throwdown(zee, wire(Kind::offer, "SpotBattle"), &at, 11'000'000); // repeated offer: no new line check(lines.size() == 1 && lines[0] == "[throwdown] Zee placed a Spot Battle drop near (606, 199, 1076)", "a placed drop is logged once with where it is"); log.throwdown(kush, wire(Kind::join), nullptr, 12'000'000); log.throwdown(tally, wire(Kind::join), nullptr, 13'000'000); check(said("[throwdown] Tally joined Zee's Spot Battle (3 in the queue)"), "joins are logged with the queue size"); ThrowdownMessage start; start.kind = Kind::start; start.order = {zee, kush, tally}; log.throwdown(zee, wire(start), nullptr, 14'000'000); check(said("[throwdown] Zee's Spot Battle started with 3 players: Zee, Kush, Tally"), "the start lists the players"); for (const int line : {1200, 3300}) { ThrowdownMessage row; row.kind = Kind::row; row.board = 1; row.add = true; row.value = line; log.throwdown(kush, wire(row), nullptr, 20'000'000); } ThrowdownMessage best; best.kind = Kind::row; best.value = 4500; log.throwdown(kush, wire(best), nullptr, 20'000'000); ThrowdownMessage turn; turn.kind = Kind::turn_end; turn.value = 1; log.throwdown(kush, wire(turn), nullptr, 21'000'000); check(said("[throwdown] Zee's Spot Battle: Kush scored 4,500 in round 1 (best 4,500)"), "a turn's points are logged"); log.throwdown(tally, wire(Kind::leave), nullptr, 22'000'000); check(said("[throwdown] Tally quit Zee's Spot Battle"), "quitting a running throwdown is logged"); log.tick(60'000'000); check(!said("[throwdown] Zee's Spot Battle has finished: 1. Kush 4,500, 2. Zee 0, 3. Tally 0 (quit)"), "a throwdown is not over while turns are still due"); log.tick(200'000'000); check(said("[throwdown] Zee's Spot Battle has finished: 1. Kush 4,500, 2. Zee 0, 3. Tally 0 (quit)"), "the result is logged once it goes quiet"); // S.K.A.T.E.: attempts, and a cancelled queue. lines.clear(); log.throwdown(zee, wire(Kind::offer, "ThrowdownSkate"), nullptr, 300'000'000); check(lines.size() == 1 && lines[0] == "[throwdown] Zee placed a S.K.A.T.E. drop", "a drop without a position"); start.order = {zee, kush}; log.throwdown(zee, wire(start), nullptr, 300'500'000); ThrowdownMessage attempt; attempt.kind = Kind::attempt; attempt.value = 2; attempt.add = true; log.throwdown(kush, wire(attempt), nullptr, 301'000'000); check(said("[throwdown] Zee's S.K.A.T.E.: Kush landed (turn 2)"), "attempts are logged"); lines.clear(); log.throwdown(zee, wire(Kind::close), nullptr, 302'000'000); check(lines.empty(), "a running throwdown ignores a close"); // Objects: a few are listed, many are summed up, moves say nothing. lines.clear(); std::vector before, after; after.push_back({1, "bk_rail_01", {10.f, 2.f, 30.f}}); log.objects(kush, before, after); check(lines.size() == 1 && lines[0] == "[objects] Kush placed bk_rail_01 at (10, 2, 30)", "a placed object is logged"); before = after; after[0].position[0] = 12.f; lines.clear(); log.objects(kush, before, after); check(lines.empty(), "moving an object is not logged"); for (std::uint64_t id = 2; id < 12; ++id) after.push_back({id, "bk_box", {}}); log.objects(kush, before, after); check(lines.size() == 1 && lines[0] == "[objects] Kush placed 10 objects (11 in total)", "many objects are summed up"); // Chat pace: four at once, then one per 1.5 s; no repeats within 15 s. ChatRate rate; using V = ChatRate::Verdict; for (int i = 0; i < 4; ++i) check(rate.accept(1'000'000, "line " + std::to_string(i)) == V::accepted, "a short burst of chat is allowed"); check(rate.accept(1'000'000, "line 4") == V::too_fast, "a fifth line at once is refused"); check(rate.accept(2'600'000, "line 5") == V::accepted, "one more line is allowed after 1.5 s"); check(rate.accept(2'700'000, "line 5") == V::repeated, "the same line straight after is refused"); check(rate.accept(30'000'000, "line 5") == V::accepted, "the same line later is allowed"); ChatRate relayed; for (int i = 0; i < 5; ++i) check(relayed.accept(1'000'000, std::to_string(i), 1) == V::accepted, "receivers allow one line of slack"); if (failures) std::fprintf(stderr, "%d check(s) failed\n", failures); else std::printf("server activity: all checks passed\n"); return failures ? 1 : 0; }