16#include <sys/socket.h>
17#include <sys/select.h>
35 using Bytes = std::vector<std::uint8_t>;
36 using Clock = std::chrono::steady_clock;
37 constexpr std::size_t
MaxBody = 1024 * 1024;
40 constexpr std::uint32_t
Magic = 0x31534D4D;
42 enum class Kind : std::uint16_t { Hello = 1, Action = 2, Heartbeat = 3 };
91 std::span<const std::uint8_t>
bytes;
101 std::uint64_t result = 0;
102 for (
unsigned i = 0;
i <
count; ++
i)
103 result |= std::uint64_t{
bytes[
at++]} << (
i * 8);
109 if (!valid || size >
bytes.size() - at)
112 at +=
static_cast<std::size_t
>(
size);
125 bytes.insert(
bytes.end(), body.begin(), body.end());
133 if (message.binaryData.size() >
MaxBody ||
134 message.binaryDataA.size() >
MaxBody - message.binaryData.size() ||
135 message.binaryData.size() + message.binaryDataA.size() >
MaxBody - 372)
138 put(body,
static_cast<unsigned>(message.action), 2);
139 put(body, message.fromPlayer, 1);
140 put(body, message.toPlayer, 1);
141 for (
auto value : {message.numberA, message.numberB, message.numberC,
142 message.numberD, message.numberE})
143 put(body,
static_cast<std::uint64_t
>(
value), 8);
144 std::array<std::uint32_t, 80>
text{};
146 for (std::size_t
i = 0;
i < message.stringA.size() &&
147 message.stringA[
i] != 0 &&
out <
text.size() - 1; ++
i)
149 auto value =
static_cast<std::uint32_t
>(message.stringA[
i]);
150 if constexpr (
sizeof(
wchar_t) == 2)
154 if (++
i >= message.stringA.size())
return {};
155 const auto low =
static_cast<std::uint32_t
>(message.stringA[
i]);
165 for (
const auto*
blob : {&message.binaryData, &message.binaryDataA})
168 body.insert(body.end(),
blob->begin(),
blob->end());
170 return frame(Kind::Action, body);
173 bool decode(std::span<const std::uint8_t> body, actions::Message& message)
180 for (
auto*
number : {&message.numberA, &message.numberB, &message.numberC,
181 &message.numberD, &message.numberE})
182 *
number = std::bit_cast<std::int64_t>(reader.get(8));
185 for (
unsigned i = 0;
i < 80; ++
i)
187 const auto value =
static_cast<std::uint32_t
>(reader.get(4));
188 if (
value == 0) { ended =
true;
continue; }
191 if constexpr (
sizeof(
wchar_t) == 2)
193 if (
value >= 0x10000)
195 if (
out + 2 >= message.stringA.size())
return false;
196 message.stringA[
out++] =
static_cast<wchar_t>(0xD800 + ((
value - 0x10000) >> 10));
197 message.stringA[
out++] =
static_cast<wchar_t>(0xDC00 + ((
value - 0x10000) & 1023));
201 if (
out + 1 >= message.stringA.size())
return false;
202 message.stringA[
out++] =
static_cast<wchar_t>(
value);
204 return ended && reader.blob(message.binaryData) &&
205 reader.blob(message.binaryDataA) && reader.valid && reader.at == body.size();
227 class TcpTransport
final :
public Transport
230 explicit TcpTransport(
bool host,
TcpSessionMode mode) : host_(host), mode_(mode) {}
240 bool open(std::string_view
address, std::uint16_t
port)
245 return fail(
"WSAStartup failed");
252 return fail(
"TCP endpoint requires a numeric IPv4 address and port 1..65535");
258 return fail(
"TCP nonblocking setup failed");
266 return fail(
"TCP bind/listen failed");
277 return fail(
"TCP connect failed");
285 peers_.push_back(std::move(
peer));
292 std::uint32_t localSourceId()
const noexcept override {
return localSourceId_; }
297 bool sourceConnected(std::uint32_t
source)
const noexcept override
299 if (host_ &&
source == 0)
return true;
300 return std::any_of(peers_.begin(), peers_.end(), [&](
const Peer&
peer) {
301 return peer.ready && !peer.dead && (host_ ? peer.id == source : source == 0);
304 bool admitSource(std::uint32_t
source)
override
306 if (!host_)
return false;
307 for (
auto&
peer : peers_)
310 peer.admitted =
true;
315 bool receiveDisconnectedSource(std::uint32_t&
source)
override
317 if (disconnected_.empty())
return false;
318 source = disconnected_.front();
319 disconnected_.pop_front();
322 bool sendToSource(
const actions::Message& message, std::uint32_t
source)
override
326 if (
bytes.empty())
return false;
327 for (
auto&
peer : peers_)
332 drop(
peer,
"TCP peer outgoing queue exceeded its bound");
339 if (host_ && gameplayEnabled() && listener_ !=
InvalidSocket)
return true;
340 return std::any_of(peers_.begin(), peers_.end(),
341 [](
const Peer&
peer) { return peer.ready && !peer.dead; });
346 std::size_t
count = 0;
347 for (
const auto&
peer : peers_)
352 bool receive(actions::Message& message)
override
354 if (incoming_.empty())
return false;
355 message = std::move(incoming_.front());
356 incoming_.pop_front();
360 bool send(
const actions::Message& message)
override
364 if (!active())
return host_;
374 for (
auto&
peer : peers_)
379 for (
auto&
peer : peers_)
381 if (!
peer.ready ||
peer.dead || (host_ && !
peer.admitted))
continue;
384 owners_[message.toPlayer] !=
peer.id)
continue;
386 drop(
peer,
"TCP peer outgoing queue exceeded its bound");
391 return host_ ||
sent;
409 peers_.push_back(std::move(
peer));
415 for (
auto&
peer : peers_)
424 actions::Message stop;
429 stop.sourceId =
peer.id;
431 incoming_.push_back(std::move(stop));
434 disconnected_.push_back(
peer.id);
438 std::erase_if(peers_, [&](Peer&
peer)
440 if (!
peer.dead || (host_ &&
peer.ready))
return false;
447 bool fail(std::string
text) { error_ = std::move(
text);
return false; }
463 if (
peer.output.empty())
peer.lastProgress = Clock::now();
468 bool validVoice(
const actions::Message& message)
const
474 message.numberC == 0 && message.numberD == 0 && message.numberE == 0 &&
475 message.stringA[0] == 0 && message.binaryData.empty() &&
481 const auto action =
static_cast<unsigned>(message.action);
494 const auto action =
static_cast<unsigned>(message.action);
499 message.numberA == message.fromPlayer;
500 if (action == 0 || action > 50 || action == 2 ||
501 action == 3 || action == 12 || (action >= 19 && action <= 22))
511 message.numberB == 0 && message.numberC == 0 &&
512 message.numberD == 0 && message.numberE == 0 &&
513 message.stringA[0] == 0 && message.binaryData.empty() &&
518 bool consume(Peer&
peer, Kind
kind, std::span<const std::uint8_t> body)
522 if (
kind != Kind::Hello || (body.size() != 4 && body.size() != 8))
return false;
524 const auto id =
static_cast<std::uint32_t
>(reader.get(4));
525 const auto capability = body.size() == 8 ? reader.get(4) : 0;
529 if (
id != 0 || (
capability == 1 && !gameplayEnabled()))
return false;
531 peer.admitted = !gameplayEnabled();
538 actions::Message resync;
543 resync.sourceId =
peer.id;
544 incoming_.push_back(std::move(resync));
548 if (
id == 0 || (
capability == 1) != gameplayEnabled())
return false;
556 if (
kind == Kind::Heartbeat)
return body.empty();
557 if (
kind != Kind::Action)
return false;
558 actions::Message message;
559 if (!
decode(body, message))
return false;
563 message.sourceId =
peer.id;
569 (message.numberD < 0 || message.numberD > std::numeric_limits<std::uint32_t>::max() ||
573 incoming_.push_back(std::move(message));
579 const auto now = Clock::now();
580 if (
now -
peer.lastReceive > std::chrono::seconds(
peer.ready ? 30 : 10) ||
597 if (selected < 0) {
drop(
peer,
"TCP connection polling failed");
return; }
598 if (selected == 0)
return;
601 int length =
sizeof(status);
606 reinterpret_cast<char*
>(&status), &
length) != 0 || status != 0)
607 {
drop(
peer,
"TCP connection failed");
return; }
608 peer.connecting =
false;
610 if (
peer.ready &&
peer.output.empty() &&
now -
peer.lastSend > std::chrono::seconds(5))
612 if (!
peer.output.empty())
619 const auto sent = ::send(
peer.socket,
620 reinterpret_cast<const char*
>(
bytes.data() +
peer.offset),
621 static_cast<int>(std::min<std::size_t>(
bytes.size() -
peer.offset, 64 * 1024)), flags);
625 peer.offset +=
static_cast<std::size_t
>(
sent);
626 peer.buffered -=
static_cast<std::size_t
>(
sent);
633 std::array<std::uint8_t, 64 * 1024>
buffer{};
638 static_cast<int>(std::min(
room,
buffer.size())), 0);
647 std::size_t consumed = 0;
652 const std::span<const std::uint8_t>
input(
peer.input);
655 {
drop(
peer,
"TCP protocol signature/version mismatch");
return; }
656 const auto kind =
static_cast<Kind
>(
header.get(2));
657 const auto size =
static_cast<std::size_t
>(
header.get(4));
658 if (size >
MaxBody) {
drop(
peer,
"TCP frame exceeds size limit");
return; }
662 if (host_ && gameplayEnabled() &&
peer.ready &&
663 !
peer.admitted &&
kind == Kind::Action)
break;
666 {
drop(
peer,
"TCP frame rejected");
return; }
670 peer.input.erase(
peer.input.begin(),
peer.input.begin() + consumed);
671 if (
peer.readClosed && (
peer.input.empty() ||
672 ((!host_ || !gameplayEnabled() ||
peer.admitted) && consumed == 0)))
673 drop(
peer,
"TCP peer disconnected");
678 std::uint32_t localSourceId_{};
679 std::array<std::uint32_t, rules::MaxPlayers> owners_{};
684 std::uint32_t nextId_{1};
685 std::vector<Peer> peers_;
686 std::deque<actions::Message> incoming_;
687 std::deque<std::uint32_t> disconnected_;
695 auto transport = std::make_unique<TcpTransport>(host, mode);
697 return std::unexpected(std::string(
transport->error()));
698 return std::unique_ptr<Transport>(std::move(
transport));
std::vector< std::uint8_t > data
std::vector< std::byte > bytes
std::shared_ptr< const data::HmdTextureImage > source
Clock::time_point lastReceive
Clock::time_point lastSend
std::deque< Bytes > output
Clock::time_point lastProgress
bool encode(const State &state, std::vector< std::uint8_t > &result) noexcept
bool decode(std::span< const std::uint8_t > data, State &result) noexcept
constexpr std::size_t MessageQueueCapacity
std::expected< std::unique_ptr< Transport >, std::string > openTcpTransport(bool host, std::string_view address, std::uint16_t port, TcpSessionMode mode)
void setPlayerOwner(rules::PlayerNumber player, std::uint32_t source)
void send(const GameState &state, const actions::Message &message, std::int64_t error)
constexpr PlayerNumber BankPlayer
std::uint8_t PlayerNumber
constexpr PlayerNumber SpectatorPlayer
constexpr PlayerNumber MaxPlayers
constexpr PlayerNumber AllPlayers
bool receive(Message &message)
ParseStatus parse(std::span< const std::uint8_t > bytes, std::uint32_t sourceId, EventSink sink=nullptr) noexcept
bool makeStopPacket(std::vector< std::uint8_t > &packet)