mirror of
https://github.com/Awakenchan/LanternEngine.git
synced 2026-11-10 09:21:42 -10:00
160 lines
6.9 KiB
C++
160 lines
6.9 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
#include <nlohmann/json.hpp>
|
|
|
|
#include "gpu/bbnet/room/room_crypto.h"
|
|
#include "gpu/bbnet/room/room_host.h"
|
|
#include "gpu/bbnet/room/room_link.h"
|
|
#include "gpu/bbnet/room/room_ws.h"
|
|
|
|
using namespace BbRoom;
|
|
|
|
namespace {
|
|
|
|
int failures = 0;
|
|
void Check(bool ok, const char* what) {
|
|
if (!ok) {
|
|
std::printf("FAIL: %s\n", what);
|
|
++failures;
|
|
}
|
|
}
|
|
|
|
bool Await(Link& link, Event::Kind kind, Event* out = nullptr, int timeout_ms = 8000) {
|
|
const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
|
|
while (std::chrono::steady_clock::now() < end) {
|
|
link.WaitEvent(50);
|
|
for (auto& e : link.Drain()) {
|
|
if (e.kind == kind) {
|
|
if (out) *out = std::move(e);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AwaitData(Link& link, const std::vector<std::uint8_t>& wanted, int from) {
|
|
Event e;
|
|
return Await(link, Event::Kind::Data, &e) && e.data == wanted && e.slot == from;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
const std::string name = "direct-room", password = "secret";
|
|
const int port = 38765;
|
|
const std::string server = "ws://127.0.0.1:" + std::to_string(port);
|
|
LocalRoom room;
|
|
std::string error;
|
|
Check(room.Start(port, Derive(name, password).room_id, error), "the room opens its port");
|
|
LocalRoom second;
|
|
Check(!second.Start(port, Derive(name, password).room_id, error) && !error.empty(), "a port in use is refused with a reason");
|
|
|
|
Link host, guest, other;
|
|
host.SetHost(true);
|
|
host.Start(server, name, password);
|
|
Check(Await(host, Event::Kind::Connected), "the host is welcomed");
|
|
Check(host.Slot() == 1, "the host gets slot 1");
|
|
guest.Start(server, name, password);
|
|
Check(Await(guest, Event::Kind::Connected), "a guest is welcomed");
|
|
Check(guest.Slot() == 2, "the guest gets slot 2");
|
|
Check(Await(host, Event::Kind::Joined), "the host sees the guest join");
|
|
Check(guest.HostSlot() == 1, "the guest learns the host's slot");
|
|
other.Start(server, name, password);
|
|
Check(Await(other, Event::Kind::Connected) && other.Slot() == 3, "a third hunter is welcomed");
|
|
Await(host, Event::Kind::Joined, nullptr, 500);
|
|
Await(guest, Event::Kind::Joined, nullptr, 500);
|
|
|
|
const std::vector<std::uint8_t> small{1, 2, 3};
|
|
Check(guest.Send(host.Slot(), small.data(), small.size()), "a guest can send");
|
|
Check(AwaitData(host, small, guest.Slot()), "a frame reaches the slot it names, from the sender's slot");
|
|
Check(!Await(other, Event::Kind::Data, nullptr, 300), "a targeted frame reaches nobody else");
|
|
Check(host.Send(0, small.data(), small.size()), "the host can send to all");
|
|
Check(AwaitData(guest, small, host.Slot()) && AwaitData(other, small, host.Slot()), "a frame for all reaches everyone");
|
|
Check(!Await(host, Event::Kind::Data, nullptr, 300), "a sender does not get its own frame");
|
|
const std::vector<std::uint8_t> big(60000, 7);
|
|
Check(guest.Send(host.Slot(), big.data(), big.size()), "a large frame can be sent");
|
|
Check(AwaitData(host, big, guest.Slot()), "a large frame arrives whole");
|
|
|
|
Check(guest.SendGame(host.Slot(), small.data(), small.size()), "game packets can be sent");
|
|
Check(AwaitData(host, small, guest.Slot()), "a droppable frame still reaches the slot it names");
|
|
Check(!Await(other, Event::Kind::Data, nullptr, 300), "and nobody else");
|
|
|
|
const std::string member = std::string(32, 'a');
|
|
const std::string own_url = RoomUrl(server, Derive(name, password).room_id) + "/" + member;
|
|
std::atomic<int> first_slot{0}, next_slot{0};
|
|
WebSocket original, replacement;
|
|
Check(original.Connect(own_url, 5, error), "a member connects with its token");
|
|
std::thread original_run([&] {
|
|
original.Run([&](std::string&& message, bool binary) {
|
|
if (!binary && message.find("welcome") != std::string::npos) first_slot = nlohmann::json::parse(message).value("slot", 0);
|
|
});
|
|
});
|
|
Await(host, Event::Kind::Joined, nullptr, 1000);
|
|
Await(guest, Event::Kind::Joined, nullptr, 200);
|
|
Await(other, Event::Kind::Joined, nullptr, 200);
|
|
Check(replacement.Connect(own_url, 5, error), "the same member connects again before the room saw it drop");
|
|
std::thread replacement_run([&] {
|
|
replacement.Run([&](std::string&& message, bool binary) {
|
|
if (!binary && message.find("welcome") != std::string::npos) next_slot = nlohmann::json::parse(message).value("slot", 0);
|
|
});
|
|
});
|
|
for (int i = 0; i < 100 && !next_slot.load(); ++i) std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
|
Check(first_slot.load() && first_slot.load() == next_slot.load(), "it keeps its slot");
|
|
Check(!Await(host, Event::Kind::Joined, nullptr, 300), "and the others see no new member");
|
|
original_run.join();
|
|
const std::string leave = R"({"t":"leave"})";
|
|
replacement.Send(leave.data(), leave.size(), false);
|
|
replacement.Close();
|
|
replacement_run.join();
|
|
Check(Await(host, Event::Kind::Left, nullptr, 2000), "a clean leave after the takeover frees the slot");
|
|
Await(guest, Event::Kind::Left, nullptr, 500);
|
|
Await(other, Event::Kind::Left, nullptr, 500);
|
|
|
|
Link wrong;
|
|
wrong.Start(server, name, "not the password");
|
|
Check(!Await(wrong, Event::Kind::Connected, nullptr, 1500) && !wrong.Connected(), "a wrong password is never let in");
|
|
wrong.Stop();
|
|
|
|
Link voice_a, voice_b;
|
|
voice_a.Start(server, name, password, "voice");
|
|
voice_b.Start(server, name, password, "voice");
|
|
Check(Await(voice_a, Event::Kind::Connected) && Await(voice_b, Event::Kind::Connected), "voice members are welcomed");
|
|
Await(voice_a, Event::Kind::Joined, nullptr, 200);
|
|
Check(voice_a.SendLossy(voice_b.Slot(), small.data(), small.size(), 2048), "voice can send");
|
|
Check(AwaitData(voice_b, small, voice_a.Slot()), "voice reaches a voice member");
|
|
Check(!Await(host, Event::Kind::Data, nullptr, 200), "voice stays out of the game room");
|
|
voice_a.Stop();
|
|
voice_b.Stop();
|
|
|
|
Link fourth, fifth, sixth;
|
|
fourth.Start(server, name, password);
|
|
fifth.Start(server, name, password);
|
|
Check(Await(fourth, Event::Kind::Connected) && Await(fifth, Event::Kind::Connected), "the room holds five");
|
|
sixth.Start(server, name, password);
|
|
Check(Await(sixth, Event::Kind::Refused), "the sixth hunter is refused");
|
|
sixth.Stop();
|
|
fourth.Stop();
|
|
fifth.Stop();
|
|
|
|
other.Stop();
|
|
Check(Await(guest, Event::Kind::Left), "a clean leave reaches the others");
|
|
guest.Stop();
|
|
Check(Await(host, Event::Kind::Left), "a clean leave reaches the host");
|
|
host.Stop();
|
|
room.Stop();
|
|
LocalRoom again;
|
|
Check(again.Start(port, Derive(name, password).room_id, error), "the port opens again after the room stops");
|
|
again.Stop();
|
|
|
|
if (failures) return 1;
|
|
std::printf("room-host-test: ok\n");
|
|
return 0;
|
|
}
|