peer_transport.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. #include <Windows.h>
  2. #include <array>
  3. #include "../../../middleware/gameplay/external/common_state.h"
  4. #include "../../../middleware/gameplay/peer/established_packet.h"
  5. #include "../association/association_host.h"
  6. #include "../dtls/dtls_host.h"
  7. #include "../entity_identities.h"
  8. #include "peer_transport_internal.h"
  9. namespace sunrise::server::gameplay::peer {
  10. namespace {
  11. namespace gp = state::gameplay;
  12. namespace wire = middleware::gameplay::peer;
  13. /** Bit position of the payload marker inside its first byte. */
  14. constexpr unsigned kMarkerShift = 7;
  15. /**
  16. * Resolves the session a message that does not name one belongs to.
  17. * A link carrying more than one session leaves it unresolved rather than guessing.
  18. * @param peer Link the message arrived on.
  19. * @return The session id, or zero when the link carries none or several.
  20. */
  21. [[nodiscard]] std::uint64_t sole_session_locked(const gp::PeerLink& peer) noexcept {
  22. std::uint64_t only = 0;
  23. for (const std::uint64_t held : peer.sessions) {
  24. if (held == 0) {
  25. continue;
  26. }
  27. if (only != 0) {
  28. return 0;
  29. }
  30. only = held;
  31. }
  32. return only;
  33. }
  34. } // namespace
  35. SRWLOCK g_lock{SRWLOCK_INIT};
  36. std::array<gp::PeerLink, gp::kAssociationCapacity> g_peers;
  37. std::uint64_t g_peerGeneration{};
  38. middleware::gameplay::external::Lane0Codec g_lane0Codec{};
  39. void (*g_lane0Outcome)(const void*, std::uint64_t, wire::AckOutcome) noexcept {};
  40. Lane0Transport g_lane0Transport{};
  41. middleware::gameplay::external::TypePayloadCodec g_entityCodec{};
  42. bool (*g_entityAccepted)(const void*,
  43. std::uint64_t,
  44. const middleware::gameplay::external::EntityBatch&) noexcept {};
  45. const void* g_entityAcceptedContext{};
  46. EntityTransport g_entityTransport{};
  47. std::uint32_t g_channelId{0};
  48. /** Sends one payload over whichever transport the peer arrived on. */
  49. bool send_transport(const gp::Endpoint& to, std::span<const std::byte> payload) noexcept {
  50. return dtls::send_payload(to, payload) || association::send_payload(to, payload);
  51. }
  52. /** Copies out the contributions a link is about to drop, so their owner can be told. */
  53. std::size_t collect_displaced_locked(const gp::PeerLink& peer,
  54. DisplacedExternals& displaced) noexcept {
  55. std::size_t count = 0;
  56. for (const auto& contribution : peer.externalContributions) {
  57. if (contribution.occupied) {
  58. displaced[count++] = {contribution.groupSessionId,
  59. contribution.transmissionId,
  60. wire::AckOutcome::unresolved};
  61. }
  62. }
  63. return count;
  64. }
  65. /** Reports one batch of external outcomes to whichever channel-0 sink is installed. */
  66. void notify_external_outcomes(const DisplacedExternals& completed, std::size_t count) noexcept {
  67. if (g_lane0Transport.outcome != nullptr) {
  68. for (std::size_t index = 0; index < count; ++index) {
  69. g_lane0Transport.outcome(g_lane0Transport.context,
  70. completed[index].groupSessionId,
  71. completed[index].transmissionId,
  72. completed[index].outcome);
  73. }
  74. }
  75. if (g_lane0Outcome != nullptr) {
  76. for (std::size_t index = 0; index < count; ++index) {
  77. g_lane0Outcome(
  78. g_lane0Codec.context, completed[index].transmissionId, completed[index].outcome);
  79. }
  80. }
  81. }
  82. /** Tells the lane-0 transport that a group session lost its channel state. */
  83. void reset_transports(const std::uint64_t* sessions, std::size_t count) noexcept {
  84. if (g_lane0Transport.reset != nullptr) {
  85. for (std::size_t index = 0; index < count; ++index) {
  86. g_lane0Transport.reset(g_lane0Transport.context, sessions[index]);
  87. }
  88. }
  89. }
  90. /** Tells the lane-0 transport that one group session lost its channel state. */
  91. void reset_transports(std::uint64_t sessionId) noexcept {
  92. reset_transports(&sessionId, 1);
  93. }
  94. /** Source retirement follows peer-to-identity lock order and waits for publication leases. */
  95. void invalidate_entity_identity_locked(const gp::entity_identity::Source& source) noexcept {
  96. if (source.groupSessionId != 0) entity_identities::reset_source(source);
  97. }
  98. /** Retires only the captured source, never a replacement sharing its group. */
  99. void reset_entity_source(const gp::entity_identity::Source& source) noexcept {
  100. if (source.groupSessionId == 0) return;
  101. AcquireSRWLockShared(&g_lock);
  102. if (g_entityTransport.reset != nullptr) {
  103. g_entityTransport.reset(g_entityTransport.context, source);
  104. }
  105. ReleaseSRWLockShared(&g_lock);
  106. }
  107. /**
  108. * Copies the admitted source while its peer is locked.
  109. * @param peer Peer whose lifecycle is
  110. * being read or changed.
  111. * @return Exact current entity source.
  112. */
  113. gp::entity_identity::Source entity_source(const gp::PeerLink& peer) noexcept {
  114. gp::entity_identity::Source source{};
  115. source.activitySessionId = peer.activityBinding.sessionId;
  116. source.activityRevision = peer.activityBinding.createdRevision;
  117. source.activityClientGeneration = peer.commonReconciler.owner_generation();
  118. source.groupSessionId = peer.externalGroupSessionId;
  119. source.peerGeneration = peer.peerGeneration;
  120. source.channelGeneration = peer.channelGeneration;
  121. source.viewGeneration = peer.viewGeneration;
  122. source.address = peer.endpoint.address;
  123. source.port = peer.endpoint.port;
  124. source.localPort = peer.endpoint.localPort;
  125. source.localConnectionSequence = peer.localConnectionSequence;
  126. source.remoteConnectionSequence = peer.remoteConnectionSequence;
  127. return source;
  128. }
  129. /** @return Peer for one endpoint, or null. Callers already hold the lock. */
  130. gp::PeerLink* find_locked(const gp::Endpoint& from) noexcept {
  131. for (gp::PeerLink& peer : g_peers) {
  132. if (peer.stage != gp::PeerStage::absent && peer.endpoint == from) {
  133. return &peer;
  134. }
  135. }
  136. return nullptr;
  137. }
  138. /** @return True when the link carries one group session. Callers hold the lock. */
  139. bool carries_locked(const gp::PeerLink& peer, std::uint64_t sessionId) noexcept {
  140. for (const std::uint64_t held : peer.sessions) {
  141. if (held == sessionId) {
  142. return true;
  143. }
  144. }
  145. return false;
  146. }
  147. /** @return Link carrying one group session, or null. Callers hold the lock. */
  148. gp::PeerLink* find_session_locked(std::uint64_t sessionId) noexcept {
  149. if (sessionId == 0) {
  150. return nullptr;
  151. }
  152. for (gp::PeerLink& peer : g_peers) {
  153. if (peer.stage != gp::PeerStage::absent && carries_locked(peer, sessionId)) {
  154. return &peer;
  155. }
  156. }
  157. return nullptr;
  158. }
  159. /** @return Link whose authenticated external view names one group, or null. */
  160. gp::PeerLink* find_external_group_locked(std::uint64_t groupSessionId) noexcept {
  161. if (groupSessionId == 0) {
  162. return nullptr;
  163. }
  164. for (gp::PeerLink& peer : g_peers) {
  165. if (peer.stage != gp::PeerStage::absent && peer.externalGroupSessionId == groupSessionId) {
  166. return &peer;
  167. }
  168. }
  169. return nullptr;
  170. }
  171. /** Resolves the session an out-of-band message at one endpoint belongs to. */
  172. std::uint64_t session_for_endpoint(const gp::Endpoint& from) noexcept {
  173. AcquireSRWLockShared(&g_lock);
  174. const gp::PeerLink* const peer = find_locked(from);
  175. const std::uint64_t only = peer == nullptr ? 0 : sole_session_locked(*peer);
  176. ReleaseSRWLockShared(&g_lock);
  177. return only;
  178. }
  179. /** @return A free peer slot, or null. Callers already hold the lock. */
  180. gp::PeerLink* allocate_locked() noexcept {
  181. for (gp::PeerLink& peer : g_peers) {
  182. if (peer.stage == gp::PeerStage::absent) {
  183. return &peer;
  184. }
  185. }
  186. return nullptr;
  187. }
  188. /** Installs the process-lifetime channel-0 codec. */
  189. void install_lane0_codec(const middleware::gameplay::external::Lane0Codec& codec,
  190. void (*outcome)(const void*,
  191. std::uint64_t,
  192. wire::AckOutcome) noexcept) noexcept {
  193. AcquireSRWLockExclusive(&g_lock);
  194. g_lane0Codec = codec;
  195. g_lane0Outcome = outcome;
  196. ReleaseSRWLockExclusive(&g_lock);
  197. }
  198. /** Installs one process-lifetime, session-aware channel-0 transport. */
  199. void install_lane0_transport(const Lane0Transport& transport) noexcept {
  200. AcquireSRWLockExclusive(&g_lock);
  201. g_lane0Transport = transport;
  202. ReleaseSRWLockExclusive(&g_lock);
  203. }
  204. /** Installs the process-lifetime session-aware channel-2 boundary. */
  205. void install_entity_transport(const EntityTransport& transport) noexcept {
  206. AcquireSRWLockExclusive(&g_lock);
  207. g_entityTransport = transport;
  208. ReleaseSRWLockExclusive(&g_lock);
  209. }
  210. /** Retirement is serialized with packet decode before entering the codec's own lock. */
  211. std::size_t retire_entity_baselines(
  212. const state::gameplay::entity_identity::Source& source,
  213. std::span<const state::gameplay::entity_identity::RetiredLifetime> lifetimes) noexcept {
  214. AcquireSRWLockExclusive(&g_lock);
  215. const auto retired =
  216. g_entityTransport.retire == nullptr
  217. ? 0
  218. : g_entityTransport.retire(g_entityTransport.context, source, lifetimes);
  219. ReleaseSRWLockExclusive(&g_lock);
  220. return retired;
  221. }
  222. /** Installs the process-lifetime channel-2 codec and accepted-record sink. */
  223. void install_entity_codec(
  224. const middleware::gameplay::external::TypePayloadCodec& codec,
  225. bool (*accepted)(const void*,
  226. std::uint64_t,
  227. const middleware::gameplay::external::EntityBatch&) noexcept,
  228. const void* acceptedContext) noexcept {
  229. AcquireSRWLockExclusive(&g_lock);
  230. g_entityCodec = codec;
  231. g_entityAccepted = accepted;
  232. g_entityAcceptedContext = acceptedContext;
  233. ReleaseSRWLockExclusive(&g_lock);
  234. }
  235. /** Consumes one decrypted transport payload. */
  236. void deliver(const gp::Endpoint& from,
  237. std::span<const std::byte> payload,
  238. std::uint64_t now) noexcept {
  239. if (payload.empty()) {
  240. return;
  241. }
  242. if ((std::to_integer<unsigned>(payload[0]) >> kMarkerShift) != 0) {
  243. consume_container(from, payload, now);
  244. return;
  245. }
  246. consume_established(from, payload, now);
  247. }
  248. /** Queues one reliable message for a peer. */
  249. bool enqueue_reliable(std::uint64_t sessionId,
  250. std::uint8_t id,
  251. std::uint32_t declaredSize,
  252. std::span<const std::byte> body,
  253. std::size_t bodyBits) noexcept {
  254. AcquireSRWLockExclusive(&g_lock);
  255. gp::PeerLink* peer = find_session_locked(sessionId);
  256. const bool queued =
  257. peer != nullptr && wire::enqueue_message(peer->outbound, id, declaredSize, body, bodyBits);
  258. if (queued) {
  259. // The next service slice carries it, so the acknowledgement path also flushes sends.
  260. peer->acknowledgementOwed = true;
  261. // The queue changed, so the packet it was stamped against no longer carries all of it.
  262. peer->outbound.awaitingAcknowledgement = false;
  263. }
  264. ReleaseSRWLockExclusive(&g_lock);
  265. return queued;
  266. }
  267. /** Queues one sessionless reliable message on the exact peer endpoint. */
  268. bool enqueue_reliable(const gp::Endpoint& endpoint,
  269. std::uint8_t id,
  270. std::uint32_t declaredSize,
  271. std::span<const std::byte> body,
  272. std::size_t bodyBits) noexcept {
  273. AcquireSRWLockExclusive(&g_lock);
  274. gp::PeerLink* peer = find_locked(endpoint);
  275. const bool queued =
  276. peer != nullptr && wire::enqueue_message(peer->outbound, id, declaredSize, body, bodyBits);
  277. if (queued) {
  278. peer->acknowledgementOwed = true;
  279. peer->outbound.awaitingAcknowledgement = false;
  280. }
  281. ReleaseSRWLockExclusive(&g_lock);
  282. return queued;
  283. }
  284. /** Reports the NetAddr one peer sent in its own connect request. */
  285. bool remote_address(std::uint64_t sessionId,
  286. std::array<std::byte, gp::kNetAddrBlobSize>& output) noexcept {
  287. AcquireSRWLockShared(&g_lock);
  288. const gp::PeerLink* peer = find_session_locked(sessionId);
  289. const bool present = peer != nullptr && peer->remoteAddressPresent;
  290. if (present) {
  291. output = peer->remoteAddress;
  292. }
  293. ReleaseSRWLockShared(&g_lock);
  294. return present;
  295. }
  296. /** Binds one peer's view signature. */
  297. void bind_view(const gp::Endpoint& from, const gp::ViewSignature& signature) noexcept {
  298. AcquireSRWLockExclusive(&g_lock);
  299. // Keyed by endpoint, not by session. The view body carries no session id, and a link holding
  300. // both a current and a target region resolves no sole session to key it by.
  301. gp::PeerLink* peer = find_locked(from);
  302. if (peer != nullptr) {
  303. peer->view = signature;
  304. }
  305. ReleaseSRWLockExclusive(&g_lock);
  306. }
  307. /** Retains one inbound view stage and exposes its pending response. */
  308. ViewStageResult receive_view_stage(const gp::Endpoint& from,
  309. const middleware::gameplay::group::ViewEstablishment& input,
  310. middleware::gameplay::group::ViewEstablishment& response,
  311. std::uint64_t& generation) noexcept {
  312. response = {};
  313. generation = 0;
  314. DisplacedExternals displaced{};
  315. std::size_t displacedCount = 0;
  316. std::uint64_t resetSessionId = 0;
  317. gp::entity_identity::Source resetSource{};
  318. AcquireSRWLockExclusive(&g_lock);
  319. gp::PeerLink* const peer = find_locked(from);
  320. if (peer == nullptr) {
  321. ReleaseSRWLockExclusive(&g_lock);
  322. return ViewStageResult::noPeer;
  323. }
  324. auto& receptor = peer->viewReceptor;
  325. bool accepted = false;
  326. if (receptor.phase() == gp::external::view_receptor::Phase::closed) {
  327. resetSource = entity_source(*peer);
  328. invalidate_entity_identity_locked(resetSource);
  329. ++peer->viewGeneration;
  330. if (peer->viewGeneration == 0) {
  331. ++peer->viewGeneration;
  332. }
  333. accepted = receptor.open_from_stage_one(input, peer->viewGeneration);
  334. if (accepted) {
  335. resetSessionId = peer->externalGroupSessionId;
  336. displacedCount = collect_displaced_locked(*peer, displaced);
  337. peer->view = {};
  338. peer->commonReconciler.reset();
  339. peer->commonCommitted = false;
  340. peer->externalGroupSessionId = 0;
  341. peer->activityBinding = {};
  342. peer->externalShadow = {};
  343. peer->externalContributions = {};
  344. }
  345. } else {
  346. const auto result = receptor.receive(input);
  347. accepted = result == gp::external::view_receptor::ReceiveResult::accepted
  348. || result == gp::external::view_receptor::ReceiveResult::responsePending;
  349. }
  350. if (accepted && input.kind == 2) {
  351. peer->view.token = input.sessionToken;
  352. peer->view.kind = input.kind;
  353. peer->view.optionalValue = input.optionalValue;
  354. peer->view.hasOptionalValue = input.hasOptionalValue;
  355. peer->view.list = input.list;
  356. peer->view.listCount = input.listCount;
  357. peer->view.hasList = input.hasList;
  358. }
  359. accepted = accepted && receptor.pending(response);
  360. if (accepted && response.kind == 5
  361. && peer->commonReconciler.phase() != gp::external::common_reconciler::Phase::ready) {
  362. accepted = false;
  363. response = {};
  364. }
  365. if (accepted) {
  366. generation = receptor.generation();
  367. }
  368. ReleaseSRWLockExclusive(&g_lock);
  369. notify_external_outcomes(displaced, displacedCount);
  370. if (resetSessionId != 0) {
  371. reset_transports(resetSessionId);
  372. }
  373. reset_entity_source(resetSource);
  374. return accepted ? ViewStageResult::accepted : ViewStageResult::refused;
  375. }
  376. /** Commits one response and opens the flat accepted-view gate at stage 5. */
  377. bool commit_view_response(const gp::Endpoint& from, std::uint64_t generation) noexcept {
  378. AcquireSRWLockExclusive(&g_lock);
  379. gp::PeerLink* const peer = find_locked(from);
  380. middleware::gameplay::group::ViewEstablishment pending{};
  381. bool committed =
  382. peer != nullptr && generation != 0 && peer->viewReceptor.generation() == generation
  383. && peer->viewReceptor.pending(pending)
  384. && (pending.kind != 5
  385. || peer->commonReconciler.phase() == gp::external::common_reconciler::Phase::ready)
  386. && peer->viewReceptor.commit_pending();
  387. if (committed && peer->viewReceptor.phase() == gp::external::view_receptor::Phase::accepted) {
  388. peer->view.bound = true;
  389. peer->view.kind = peer->viewReceptor.local_stage();
  390. }
  391. ReleaseSRWLockExclusive(&g_lock);
  392. return committed;
  393. }
  394. /** Opens common reconciliation for one exact ActivityClient generation. */
  395. bool open_external_common(
  396. std::uint64_t groupSessionId,
  397. const state::activity::SessionBinding& activity,
  398. const middleware::bap::activity_message::patch_epoch::PatchEpoch& patchEpoch,
  399. std::uint64_t activityClientGeneration,
  400. std::uint8_t replicationEpoch) noexcept {
  401. gp::Endpoint endpoint{};
  402. AcquireSRWLockShared(&g_lock);
  403. const gp::PeerLink* const peer = find_session_locked(groupSessionId);
  404. const bool present = peer != nullptr;
  405. if (present) {
  406. endpoint = peer->endpoint;
  407. }
  408. ReleaseSRWLockShared(&g_lock);
  409. return present
  410. && open_external_common(endpoint,
  411. groupSessionId,
  412. activity,
  413. patchEpoch,
  414. activityClientGeneration,
  415. replicationEpoch);
  416. }
  417. /** Opens common reconciliation before the group join binds its session to the endpoint. */
  418. bool open_external_common(
  419. const gp::Endpoint& endpoint,
  420. std::uint64_t groupSessionId,
  421. const state::activity::SessionBinding& activity,
  422. const middleware::bap::activity_message::patch_epoch::PatchEpoch& patchEpoch,
  423. std::uint64_t activityClientGeneration,
  424. std::uint8_t replicationEpoch) noexcept {
  425. if (groupSessionId == 0) {
  426. return false;
  427. }
  428. DisplacedExternals displaced{};
  429. std::size_t displacedCount = 0;
  430. std::uint64_t resetSessionId = 0;
  431. gp::entity_identity::Source resetSource{};
  432. AcquireSRWLockExclusive(&g_lock);
  433. gp::PeerLink* const peer = find_locked(endpoint);
  434. const auto previousSource =
  435. peer != nullptr ? entity_source(*peer) : gp::entity_identity::Source{};
  436. const bool same =
  437. peer != nullptr && peer->externalGroupSessionId == groupSessionId
  438. && peer->activityBinding.sessionId == activity.sessionId
  439. && peer->activityBinding.createdRevision == activity.createdRevision
  440. && peer->commonReconciler.owner_generation() == activityClientGeneration
  441. && peer->commonReconciler.phase() != gp::external::common_reconciler::Phase::closed
  442. && peer->commonReconciler.phase() != gp::external::common_reconciler::Phase::failed;
  443. auto nextReconciler =
  444. peer != nullptr ? peer->commonReconciler : gp::external::common_reconciler::Reconciler{};
  445. const bool opened =
  446. same
  447. || (peer != nullptr && peer->viewGeneration != 0
  448. && nextReconciler.open_known(
  449. activity.sessionId, patchEpoch, activityClientGeneration, replicationEpoch));
  450. if (opened) {
  451. auto nextSource = previousSource;
  452. nextSource.activitySessionId = activity.sessionId;
  453. nextSource.activityRevision = activity.createdRevision;
  454. nextSource.activityClientGeneration = nextReconciler.owner_generation();
  455. nextSource.groupSessionId = groupSessionId;
  456. if (nextSource != previousSource) {
  457. resetSource = previousSource;
  458. invalidate_entity_identity_locked(resetSource);
  459. }
  460. peer->commonReconciler = nextReconciler;
  461. peer->activityBinding = activity;
  462. if (!same) {
  463. resetSessionId = peer->externalGroupSessionId;
  464. displacedCount = collect_displaced_locked(*peer, displaced);
  465. peer->externalGroupSessionId = groupSessionId;
  466. peer->commonCommitted = false;
  467. peer->externalContributions = {};
  468. }
  469. }
  470. ReleaseSRWLockExclusive(&g_lock);
  471. notify_external_outcomes(displaced, displacedCount);
  472. if (resetSessionId != 0) {
  473. reset_transports(resetSessionId);
  474. }
  475. reset_entity_source(resetSource);
  476. return opened;
  477. }
  478. /**
  479. * Advances matching views without replacing their entity source or baseline store.
  480. * @param
  481. * activity Exact admitted activity binding.
  482. * @param activityClientGeneration Owner of the
  483. * committed host operation.
  484. * @param expectedEpoch Previously authored epoch.
  485. * @param nextEpoch
  486. * Epoch carried by the committed operation.
  487. * @return Number of views whose epoch advanced.
  488. */
  489. std::size_t commit_replication_epoch(const state::activity::SessionBinding& activity,
  490. std::uint64_t activityClientGeneration,
  491. std::uint8_t expectedEpoch,
  492. std::uint8_t nextEpoch) noexcept {
  493. if (activity.sessionId == 0 || activity.createdRevision == 0 || activityClientGeneration == 0)
  494. return 0;
  495. std::size_t advanced = 0;
  496. AcquireSRWLockExclusive(&g_lock);
  497. for (gp::PeerLink& peer : g_peers) {
  498. if (peer.stage == gp::PeerStage::absent || peer.externalGroupSessionId == 0
  499. || peer.activityBinding.sessionId != activity.sessionId
  500. || peer.activityBinding.createdRevision != activity.createdRevision
  501. || peer.commonReconciler.owner_generation() != activityClientGeneration
  502. || !peer.commonReconciler.advance_host_epoch(expectedEpoch, nextEpoch))
  503. continue;
  504. const auto source = entity_source(peer);
  505. const auto domain = peer.commonReconciler.allocation_domain();
  506. if (g_entityTransport.advanceEpoch != nullptr)
  507. g_entityTransport.advanceEpoch(
  508. g_entityTransport.context, source, expectedEpoch, nextEpoch, domain);
  509. static_cast<void>(
  510. entity_identities::advance_epoch(source, expectedEpoch, nextEpoch, domain));
  511. peer.commonCommitted = false;
  512. for (auto& contribution : peer.externalContributions) {
  513. contribution.commonPresent = false;
  514. }
  515. peer.acknowledgementOwed = true;
  516. ++advanced;
  517. }
  518. ReleaseSRWLockExclusive(&g_lock);
  519. return advanced;
  520. }
  521. /** Reports whether every native outbound gate is open. */
  522. bool external_outbound_eligible(std::uint64_t groupSessionId) noexcept {
  523. AcquireSRWLockShared(&g_lock);
  524. const gp::PeerLink* const peer = find_session_locked(groupSessionId);
  525. middleware::gameplay::external::CommonState common{};
  526. const auto viewPhase =
  527. peer == nullptr ? gp::external::view_receptor::Phase::closed : peer->viewReceptor.phase();
  528. const bool eligible = peer != nullptr && peer->stage == gp::PeerStage::connected
  529. && peer->externalGroupSessionId == groupSessionId
  530. && (viewPhase == gp::external::view_receptor::Phase::provisional
  531. || viewPhase == gp::external::view_receptor::Phase::accepted)
  532. && peer->commonReconciler.outbound_common(common)
  533. && (g_lane0Transport.write != nullptr || g_lane0Codec.write != nullptr);
  534. ReleaseSRWLockShared(&g_lock);
  535. return eligible;
  536. }
  537. /** Reports whether the link carrying one session holds a bound view and is established. */
  538. bool view_bound(std::uint64_t sessionId) noexcept {
  539. AcquireSRWLockShared(&g_lock);
  540. const gp::PeerLink* peer = find_session_locked(sessionId);
  541. // A bound body alone is not readiness. The link also has to be past its connect exchange, or
  542. // the view belongs to a channel the peer has already rebuilt.
  543. const bool ready =
  544. peer != nullptr && peer->view.bound && peer->stage == gp::PeerStage::connected;
  545. ReleaseSRWLockShared(&g_lock);
  546. return ready;
  547. }
  548. /** Reports how far the link carrying one group session has got. */
  549. bool link_stage(std::uint64_t sessionId, gp::PeerStage& stage) noexcept {
  550. stage = gp::PeerStage::absent;
  551. AcquireSRWLockShared(&g_lock);
  552. const gp::PeerLink* peer = find_session_locked(sessionId);
  553. const bool present = peer != nullptr;
  554. if (present) {
  555. stage = peer->stage;
  556. }
  557. ReleaseSRWLockShared(&g_lock);
  558. return present;
  559. }
  560. /** Copies the connect sequences of the link carrying one joined or external group. */
  561. bool link_identity(std::uint64_t sessionId, LinkIdentity& output) noexcept {
  562. output = {};
  563. AcquireSRWLockShared(&g_lock);
  564. const gp::PeerLink* peer = find_session_locked(sessionId);
  565. if (peer == nullptr) {
  566. peer = find_external_group_locked(sessionId);
  567. }
  568. const bool present = peer != nullptr;
  569. if (present) {
  570. output.localConnectionSequence = peer->localConnectionSequence;
  571. output.remoteConnectionSequence = peer->remoteConnectionSequence;
  572. output.viewGeneration = peer->viewGeneration;
  573. }
  574. ReleaseSRWLockShared(&g_lock);
  575. return present;
  576. }
  577. /** Drops one group session, leaving the link and its other sessions alone. */
  578. void drop(std::uint64_t sessionId) noexcept {
  579. DisplacedExternals displaced{};
  580. std::size_t displacedCount = 0;
  581. gp::entity_identity::Source resetSource{};
  582. AcquireSRWLockExclusive(&g_lock);
  583. gp::PeerLink* const peer = find_session_locked(sessionId);
  584. if (peer != nullptr) {
  585. if (peer->externalGroupSessionId == sessionId) {
  586. resetSource = entity_source(*peer);
  587. invalidate_entity_identity_locked(resetSource);
  588. }
  589. // The channel outlives the session. A leave names one region, and the client keeps playing
  590. // the other over the same channel.
  591. for (std::uint64_t& slot : peer->sessions) {
  592. if (slot == sessionId) {
  593. slot = 0;
  594. }
  595. }
  596. if (peer->externalGroupSessionId == sessionId) {
  597. resetSource = entity_source(*peer);
  598. displacedCount = collect_displaced_locked(*peer, displaced);
  599. peer->externalGroupSessionId = 0;
  600. peer->activityBinding = {};
  601. peer->commonReconciler = {};
  602. peer->commonCommitted = false;
  603. peer->externalContributions = {};
  604. }
  605. }
  606. ReleaseSRWLockExclusive(&g_lock);
  607. notify_external_outcomes(displaced, displacedCount);
  608. reset_transports(sessionId);
  609. reset_entity_source(resetSource);
  610. }
  611. /** Drops every link at one endpoint, which is what a connect-closed names. */
  612. void drop_endpoint(const gp::Endpoint& endpoint) noexcept {
  613. std::array<std::uint64_t, gp::kAssociationCapacity * gp::kSessionsPerLink> sessions{};
  614. std::size_t sessionCount = 0;
  615. std::array<gp::entity_identity::Source, gp::kAssociationCapacity> sources{};
  616. std::size_t sourceCount = 0;
  617. AcquireSRWLockExclusive(&g_lock);
  618. for (gp::PeerLink& peer : g_peers) {
  619. if (peer.stage != gp::PeerStage::absent && peer.endpoint == endpoint) {
  620. sources[sourceCount++] = entity_source(peer);
  621. invalidate_entity_identity_locked(sources[sourceCount - 1]);
  622. for (const std::uint64_t sessionId : peer.sessions) {
  623. if (sessionId != 0) {
  624. sessions[sessionCount++] = sessionId;
  625. }
  626. }
  627. peer = {};
  628. }
  629. }
  630. ReleaseSRWLockExclusive(&g_lock);
  631. reset_transports(sessions.data(), sessionCount);
  632. for (std::size_t index = 0; index < sourceCount; ++index)
  633. reset_entity_source(sources[index]);
  634. }
  635. /** Drops every peer. */
  636. void reset() noexcept {
  637. std::array<std::uint64_t, gp::kAssociationCapacity * gp::kSessionsPerLink> sessions{};
  638. std::size_t sessionCount = 0;
  639. std::array<gp::entity_identity::Source, gp::kAssociationCapacity> sources{};
  640. std::size_t sourceCount = 0;
  641. AcquireSRWLockExclusive(&g_lock);
  642. for (gp::PeerLink& peer : g_peers) {
  643. sources[sourceCount++] = entity_source(peer);
  644. invalidate_entity_identity_locked(sources[sourceCount - 1]);
  645. for (const std::uint64_t sessionId : peer.sessions) {
  646. if (sessionId != 0) {
  647. sessions[sessionCount++] = sessionId;
  648. }
  649. }
  650. peer = {};
  651. }
  652. ReleaseSRWLockExclusive(&g_lock);
  653. reset_transports(sessions.data(), sessionCount);
  654. for (std::size_t index = 0; index < sourceCount; ++index)
  655. reset_entity_source(sources[index]);
  656. }
  657. } // namespace sunrise::server::gameplay::peer