Monopoly — Portage moderne 0.1.0
Portage moderne C/C++23 du Monopoly de 1999
Loading...
Searching...
No Matches
VoiceChatRuntime.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "Actions.hpp"
4#include "Messaging.hpp"
5#include "VoiceChatPacket.hpp"
6
7#include <cstddef>
8#include <cstdint>
9#include <optional>
10#include <span>
11#include <unordered_map>
12
13namespace monopoly::voicechat
14{
15 inline constexpr std::size_t MaxReceiveSessions = 10;
16
17 using ReceiveSink = bool (*)(
18 std::span<const std::uint8_t> payload,
19 std::uint32_t sourceId);
21
23 {
24 bool active{};
26 std::uint32_t dimensions{};
27 std::uint32_t volume{100};
28 std::uint64_t dataChunks{};
29 std::uint64_t restartDataChunks{};
30 std::uint64_t positionChunks{};
31 };
32
34 {
35 std::uint64_t receivedPackets{};
36 std::uint64_t deliveredPackets{};
37 std::uint64_t droppedPackets{};
38 std::uint64_t malformedPackets{};
39 std::uint64_t sentPackets{};
40 std::uint64_t startedSessions{};
41 std::uint64_t stoppedSessions{};
42 std::uint64_t dataChunks{};
43 std::uint64_t restartDataChunks{};
44 };
45
46 namespace detail
47 {
51 inline std::unordered_map<std::uint32_t, SessionState> sessions{};
52
54 std::uint32_t sourceId) noexcept
55 {
56 const auto found = sessions.find(sourceId);
57 if (found == sessions.end() || !found->second.active)
58 return nullptr;
59 return &found->second;
60 }
61
62 [[nodiscard]] inline bool consumeEvent(
63 const packet::Event& event,
64 std::uint32_t sourceId)
65 {
66 if (event.kind == packet::EventKind::Start)
67 {
68 const bool existing = sessions.contains(sourceId);
69 if (!existing && sessions.size() >= MaxReceiveSessions)
70 return true;
71
73 state.active = true;
74 state.format = event.format;
75 sessions[sourceId] = state;
77
78 if (receiveEventSink == nullptr ||
79 receiveEventSink(event, sourceId))
80 return true;
81 sessions.erase(sourceId);
82 return false;
83 }
84
85 auto* session = activeSession(sourceId);
86 if (session == nullptr)
87 return true;
88
89 bool forward = true;
90 switch (event.kind)
91 {
93 session->dimensions = event.value;
94 break;
96 session->volume = event.value;
97 break;
99 ++session->dataChunks;
101 break;
103 ++session->restartDataChunks;
105 break;
107 ++session->positionChunks;
108 break;
111 break;
113 forward = false;
114 break;
115 }
116
117 const bool accepted = !forward || receiveEventSink == nullptr ||
118 receiveEventSink(event, sourceId);
119 if (event.kind == packet::EventKind::Stop)
120 sessions.erase(sourceId);
121 return accepted;
122 }
123 }
124
125 [[nodiscard]] inline bool validPacket(
126 std::span<const std::uint8_t> payload) noexcept
127 {
128 return packet::parse(payload, 0u) == packet::ParseStatus::Ok;
129 }
130
131 inline void setReceiveSink(ReceiveSink sink) noexcept
132 {
134 }
135
140
141 inline void resetSession() noexcept
142 {
144 detail::sessions.clear();
145 }
146
148 {
149 return detail::statistics;
150 }
151
152 [[nodiscard]] inline std::optional<SessionState> sessionState(
153 std::uint32_t sourceId)
154 {
155 const auto found = detail::sessions.find(sourceId);
156 if (found == detail::sessions.end())
157 return std::nullopt;
158 return found->second;
159 }
160
162 const actions::Message& message)
163 {
165 return false;
166
168 const auto sourceId = static_cast<std::uint32_t>(message.numberD);
169 if (packet::parse(message.binaryDataA, sourceId) !=
171 {
174 return true;
175 }
176
177 const auto parsed = packet::parse(
178 message.binaryDataA, sourceId, detail::consumeEvent);
180 {
182 return true;
183 }
184
185 if (detail::receiveEventSink != nullptr)
186 {
188 return true;
189 }
190 if (detail::receiveSink == nullptr)
191 {
193 return true;
194 }
195
196 if (detail::receiveSink(message.binaryDataA, sourceId))
198 else
200 return true;
201 }
202
203 [[nodiscard]] inline bool sendPacket(
204 std::span<const std::uint8_t> payload,
206 {
207 if (!validPacket(payload))
208 return false;
209
210 // SendVoiceChatCallback() drops non-critical voice data once the
211 // outgoing queue grows past 50 entries; CHAT/STOP packets bypass it.
213 return false;
214
215 actions::Message message{};
217 message.fromPlayer = rules::SpectatorPlayer;
218 message.toPlayer = rules::BankPlayer;
219 message.numberA = rules::AllPlayers;
220 message.binaryDataA.assign(payload.begin(), payload.end());
221
222 if (!messaging::sendAction(message))
223 return false;
225 return true;
226 }
227}
unsigned state
unsigned index
std::size_t queuedActionCount()
bool sendAction(const actions::Message &message)
constexpr PlayerNumber BankPlayer
Definition RuleTypes.hpp:15
constexpr PlayerNumber SpectatorPlayer
Definition RuleTypes.hpp:22
constexpr PlayerNumber AllPlayers
Definition RuleTypes.hpp:17
std::unordered_map< std::uint32_t, SessionState > sessions
bool consumeEvent(const packet::Event &event, std::uint32_t sourceId)
SessionState * activeSession(std::uint32_t sourceId) noexcept
ParseStatus parse(std::span< const std::uint8_t > bytes, std::uint32_t sourceId, EventSink sink=nullptr) noexcept
bool(*)(const Event &, std::uint32_t sourceId) EventSink
void setReceiveEventSink(ReceiveEventSink sink) noexcept
constexpr std::size_t MaxReceiveSessions
bool(*)(std::span< const std::uint8_t > payload, std::uint32_t sourceId) ReceiveSink
void setReceiveSink(ReceiveSink sink) noexcept
bool sendPacket(std::span< const std::uint8_t > payload, bool importantDataDoNotDiscard)
packet::EventSink ReceiveEventSink
std::optional< SessionState > sessionState(std::uint32_t sourceId)
bool processRuleMessage(const actions::Message &message)
bool validPacket(std::span< const std::uint8_t > payload) noexcept
void resetSession() noexcept
const Statistics & statistics() noexcept
std::vector< std::uint8_t > binaryDataA
Definition Actions.hpp:143