/* * Original work Copyright (C) 2026 gwog :3 (SyST3MDeV/Undaunted) * Modified work Copyright (C) 2026 MysticFox / Pranav Karande (pranav158/Mystic-Paradox) * * Licensed under the GNU Affero General Public License v3.0. * You may obtain a copy of the License at the root of this repository. * * SPDX-License-Identifier: AGPL-3.0-only * Additional terms under AGPLv3 Section 7 apply. See ADDITIONAL_TERMS.md. */ #pragma once #include #include "SDK.hpp" using namespace SDK; namespace Networking { extern UNetDriver* NetDriver; // Returns true only after InitListen succeeds and, in player-hosted mode, the // launcher has accepted the structured readiness message over its private pipe. bool Listen(UEngine* Engine, int Port); // [1.14.7 2026-10-03] Emit the exact MYSTICPARADOX_GAMESERVER_READY marker the DeployServer waits for, // for the case where the ENGINE created the listener itself (map URL carries ?listen) instead of // this module creating the NetDriver by hand. The manual path leaves the world context invalid on // 1.14.7 ("WorldContext requested with invalid context object" x28 per run), so the engine path is // preferred; readiness is still announced only once a listener actually exists. void AnnounceEngineReady(int Port); void TickNetworking(); // [2026-07-25 PLAYER-ROLE PUSH DIAGNOSIS] // UActorChannel::ReplicateActor (1.12 RVA 0x03B7B470) can report "wrote nothing" for two very // different reasons, and the old bare wroteData=0 log could not tell them apart: // (a) it never got to try - it returns immediately, before touching any property, when // bPendingDormancy is set and the connection is not replaying, or when it is already // replicating this same actor further up the stack; // (b) it ran fully and neither ReplicateProperties nor ReplicateSubobjects had dirty state. // (a) means "retry later", (b) means "nothing to send". Treating them alike is why the // post-activation refresh burned its 3-attempt budget in 4.5s and declared EXHAUSTED. // // Offsets read off the 1.12 binary in Ghidra (ReplicateActor prologue): // UActorChannel + 0x30 bit 0x100 = bPendingDormancy (`*(uint*)(param_1+6) >> 8 & 1`) // UActorChannel + 0x90 bit 0x004 = bIsReplicatingActor (`*(uint*)(param_1+0x12) & 4`) // UChannel + 0x28 = Connection (`param_1[5]`) // UNetConnection+ 0x1510 = ResendAllDataState (`*(char*)(param_1[5]+0x1510)`) struct ActorChannelPushDiag { void* Channel = nullptr; bool ExistingChannel = false; bool WroteData = false; bool PendingDormancy = false; bool ReplicatingReentrant = false; int ResendAllDataState = -1; bool Valid = false; // The channel state made a write impossible this frame, independent of dirty properties. bool Blocked() const { return Valid && (PendingDormancy || ReplicatingReentrant); } }; // Creates and emits the initial actor channel for a single actor/connection pair. This is a // narrowly-scoped bootstrap for dynamically-created owner equipment that has already been routed // into the native RepGraph but did not receive an initial channel. Ongoing replication remains // owned by the graph. OutDiag is optional and purely observational. int BootstrapActorChannel(AActor* Actor, UNetConnection* Connection, ActorChannelPushDiag* OutDiag = nullptr); // [2026-07-25 OMNICELL CHANNEL CHURN] Read-only probe: does an open UActorChannel currently // exist for this actor on this connection, and if so what is its ChIndex? // // Repro 10 established that the server's AArchonPlayerRole actors are STABLE - two pointers // (PR_ICEBORNE 1823233038128, PR_TEMPEST 1825136038496) for the entire match - while both // clients minted a brand new role UObject every few seconds: // 21:03:33.570 role=2152964842720 21:03:36.180 role=2152969074400 // 21:03:57.635 role=2153085740288 21:04:00.402 role=2152938156720 // every one of them arriving with gameplayEquipped=0 roleActive=0 bpEquipCalled=0. A client // minting a new object for a stable server actor means the actor channel closed and reopened - // each reopen is a fresh NetGUID->object resolve, and the HUD binds/unbinds with it. That is // the omnicell flicker. // // Polling channel presence per (role, connection) and logging only the transitions tells us // exactly when the channel drops, which is the half we have never been able to see. Returns the // ChIndex when a channel exists, -1 when it does not, -2 when the inputs are unusable. int GetActorChannelIndexForConnection(AActor* Actor, UNetConnection* Connection); }