Skip to content

Add ConnectionClass and SendQueueType - #75

Open
11EJDE11 wants to merge 18 commits into
Phobos-developers:phobos-devfrom
11EJDE11:add-connectionclass
Open

Add ConnectionClass and SendQueueType#75
11EJDE11 wants to merge 18 commits into
Phobos-developers:phobos-devfrom
11EJDE11:add-connectionclass

Conversation

@11EJDE11

@11EJDE11 11EJDE11 commented Aug 4, 2026

Copy link
Copy Markdown

No description provided.

Comment thread TheirSync.h
Comment on lines +5 to +15
struct TheirSync
{
DEFINE_ARRAY_REFERENCE(TheirSync, [8], Array, 0xAFA358)

int frame;
int __send;
int __recv;
int timing_C;
int __router_resp;
int timing_14;
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how we mapped it in TS.

struct FrameSyncStruct {
	int frame;				/// other players' frame #
	unsigned int sent;		// # cmds other player claims to have sent
	unsigned int recv;		// # cmds actually received from others
	unsigned int timing;

	FrameSyncStruct(void);
};

FrameSyncStruct::FrameSyncStruct(void)
{
	frame = -1;
	sent = 0;
	recv = 0;
	timing = 0;
}

Evidently, there is something new in YR, but please take time to clean up field names and not just dump IDA WIP stuff in.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread IPXGlobalConnClass.h Outdated
Comment on lines +12 to +13
COMMAND_AND_CONQUER = 0xaa01,
COMMAND_AND_CONQUER0 = 0xaa00

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
COMMAND_AND_CONQUER = 0xaa01,
COMMAND_AND_CONQUER0 = 0xaa00
COMMAND_AND_CONQUER4 = 0xaa04, /// YR
COMMAND_AND_CONQUER3 = 0xaa03, /// RA2
COMMAND_AND_CONQUER2 = 0xaa02, /// TS
COMMAND_AND_CONQUER1 = 0xaa01,
COMMAND_AND_CONQUER0 = 0xaa00

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread IPXGlobalConnClass.h Outdated
{ JMP_THIS(0x5403F0); }

public:
__int16 ProductID;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
__int16 ProductID;
unsigned short ProductID;

Don't use __ types.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread IPXConnClass.h Outdated
Comment on lines +48 to +49
DWORD Immed_Set;
DWORD ID;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
DWORD Immed_Set;
DWORD ID;
int Immed_Set;
int ID;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread IPXConnClass.h Outdated
public:
IPXAddressClass Address;
NetNodeType ImmediateAddress;
PROTECTED_PROPERTY(BYTE, align_5E[0x2]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a need for all this alignment to excplicitly be here? Applies to everywhere.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread IPXConnClass.h Outdated
CONN_NAME_MAX = 40
};

DEFINE_REFERENCE(WORD, Socket, 0xAA0568)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
DEFINE_REFERENCE(WORD, Socket, 0xAA0568)
DEFINE_REFERENCE(unsigned short, Socket, 0xAA0568)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread ConnectionClass.h Outdated
Comment on lines +108 to +112
int __resends;
int __numlost;
int __percentlost;
int __missedoverall;
int __missedmagic;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
int __resends;
int __numlost;
int __percentlost;
int __missedoverall;
int __missedmagic;
int NumResends;
int NumLost;
int PercentLost;
int MissedOverall;
int MissedMagic;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread ConnectionClass.h Outdated
Comment on lines +51 to +55
struct GlobalHeaderType
{
CommHeaderType Header;
__int16 ProductID;
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is technically not a thing, the functions actually pass void* and then cast as necessary

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread CommBufferClass.h Outdated
Comment on lines +99 to +111
DWORD DelaySum;
DWORD NumDelay;
DWORD MeanDelay;
DWORD MaxDelay;

SendQueueType* SendQueue;
int SendCount; // number of entries currently queued
DWORD SendTotal; // total ever added, used as the outgoing packet ID
int* SendIndex;

ReceiveQueueType* ReceiveQueue;
int ReceiveCount;
DWORD ReceiveTotal;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not use DWORD please

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread CommBufferClass.h Outdated
CommHeaderType* Buffer;
int ExtraLen; // size of the extra data (an IPXAddressClass, for global conns)
void* ExtraBuffer;
__int16 Port; // destination port this entry was queued for

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ZivDero ZivDero left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went over this against TS's combuf/connect/ipxconn/ipxgconn and against gamemd.exe in IDA — checked every vtable slot, every offset and every static_assert. The structural work is solid, and all five new headers compile standalone at /std:c++20. Five things are wrong.

Bugs

1. IPXConnClass::Broadcast passes the wrong register — JMP_THIS on a 2-arg static __fastcall (IPXConnClass.h:33)

EPILOG_THISCALL's pop ecx only restores ECX by accident, when MSVC happens to allocate the frame with push ecx. With two register params it doesn't. Compiling the exact pattern (MSVC 14.51, x86, /O2):

; static int __fastcall Broadcast(void*, int) { JMP_THIS(0x53F830); }
push ebp / mov ebp,esp / sub esp,8
mov DWORD PTR _l$[ebp], edx     ; [ebp-8] = buflen
mov DWORD PTR _b$[ebp], ecx     ; [ebp-4] = buf
pop  ecx                        ; <-- ecx = [ebp-8] = buflen, NOT buf
mov esp,ebp / pop ebp / jmp 0x53F830

Broadcast at 0x53F830 opens with mov eax, ecx and hands that straight to PacketTransport->Broadcast as the buffer pointer, so this would send from the address buflen. Use JMP_STD — same as VoxClass::PlayTaunt/SilenceIndex in this PR, whose codegen leaves ECX/EDX untouched.

Open_Socket/Close_Socket (lines 23, 25) survive only because with a single register arg MSVC emits push ecx for the frame, so pop ecx restores the original — please switch them too rather than relying on that.

2. IPXAddressClass::field_A comment is backwards (IPX.h:25-26)

Widening to 12 bytes is right — Queue_Send(…, &addr, 12, port) at 0x53FC99, PacketTransport->Write(…, &addr, 12, …) at 0x53F812, and IPXConnClass::Address spans 0x4C0x58. But the trailing two bytes are not the IP endpoint. As_String (0x53F0E0) prints, in IP mode, "%d.%d.%d.%d ; %d" from NodeAddress[0..3] (the IPv4) and *(unsigned short*)NetworkNumberthe port lives at offset 0, and set_Port (0x53F200) writes there. The default ctor (0x53ECB0) initialises only bytes 0–9 and never touches 0xA0xB; they get copied purely because the game moves the struct as 12 bytes. So: tail padding, meaningful in neither mode.

3. TacticalClass::AI() duplicates an existing virtual (TacticalClass.h:100)

0x6D2540 is vtable slot +0x5C of TacticalLogicClass::AI dispatches it via call [eax+5Ch], and +0x60 is sub_6DBB60, which YRpp already declares as TacticalClass's first new virtual. Slot +0x5C is therefore the one YRpp models as AbstractClass::Update(). Adding a non-virtual AI() gives two names for the same slot, and a direct call bypasses anyone who replaces Update. Please declare it as virtual void Update() override JMP_THIS(0x6D2540); instead.

4. EditBeaconMessage has no return value (BeaconManagerClass.h:27)

const wchar_t* and the shouldBroadcast rename are both right — the 4th arg does gate the global-message send (is_local_action && beacon->Owner == PlayerPtr->ID && send). But voidchar isn't. Every return in 0x431450 hands back whatever is left in EAX: this->Count, a BeaconClass*, Players.ActiveCount, Try_Create_Radar_Event's bool, or beacon_id + 3*house. All four in-game callers (0x48D810, 0x55E5F3, 0x55E734, 0x55E7A9) discard it. Keep void.

5. MSTimer comment is inverted (Timer.h:27)

SystemTimer::GetTime is timeGetTime(); shr eax, 4 (0x6C8C40), so its ticks are 16 milliseconds each — a sixteenth of the resolution, not "sixteenths of one [millisecond]". Cross-checked live: 0x887348 = 0xADAEF, 0x887328 = 0xADAEF7, exactly >>4 apart.

Nits

  • GameTimers::FrameTimer (Timer.h:138) shadows the FrameTimer clock struct declared at line 13 of the same header. It matches the game's own symbol, but it reads badly next to CDTimerClass = TimerStruct<FrameTimer>.
  • Timer.h's new includes don't make it self-contained — it still fails standalone on std::convertible_to, DWORD, JMP_STD and Unsorted::CurrentFrame.
  • IsValidTauntCommand is stricter than PlayTaunt, which masks (Command & 0xF, (Command >> 4) & 0xF) rather than rejecting high bits — 0x101 plays but fails validation. Fine as a sanitiser for network input; the comment at VoxClass.h:21 just isn't accurate about the game's behaviour.
  • Worth documenting on ConnectionClass::PacketBuf: for a global connection it points at a 16-byte header (CommHeaderType + unsigned short ProductID at +14) — PacketBuf->ProductID = this->ProductID at 0x53FC3F, payload at &PacketBuf[1], Queue_Send(…, buflen + 16, …), versus buflen + 14 in the base class. Dropping GlobalHeaderType was the right call, but nobody should compute a payload offset from sizeof(CommHeaderType) on a global conn without a warning in the header.

Verified correct

Everything else checked out, including the parts that would be easiest to get wrong:

  • Vtables match declaration order exactlyConnectionClass at 0x7E51B4, IPXConnClass at 0x7EC0CC, IPXGlobalConnClass at 0x7EC10C, including Purge_Send_Queue sitting before Service_Send_Queue, Send_To_Address appended at slot 10, and all seven new IPXGlobalConnClass virtuals landing at slots 11–17 in declaration order despite the public/protected split.
  • Send(…, bool isGlobalConn, short port)retn 18h confirms six args, and 0x48C2D7 computes the 5th as this == Ipx.IPXGlobalConn2. Good call typing it bool.
  • CommHeaderType = 14 bytes with int field_8 / short field_C, written as exactly a dword and a word at 0x53FBE6/0x53FBEC and 0x53F75B/0x53F75F. ForwardTo at +3 comes from OwnerTalk.Get_Player_Flags only under ViaPacketRouter — comment is right.
  • SendQueueType::Port at +0x20 (mov [eax+esi+20h], di, retn 14h), and the 1/2/4 flag bits are manipulated as a plain int at offset 0 in Queue_Send. ReceiveQueueType::Time is genuinely what Strip_Packets ages on.
  • TheirSync[7] at 0xAFA358, stride 0x18 — the reset loop runs 0xAFA35C0xAFA404, and my_sent sits at 0xAFA400. RouterResponseTime at +0x10 is fed by Global_Response_Time3 under ViaPacketRouter; LastHeardTime at +0x14 is a timeGetTime() stamp (0x646DF0).
  • Timers0x887348 is initialised by ??__EFrameTimer, 0x887328 by init_NFTTimer; 0xAFA450 is literally CDTimerClass<FrameTimerClass> Queue_AI_Multiplayer::skip_crc.
  • Beacon flagsBitfield |= 1 under house < 8 in PlaceBeacon (0x430CB9), Bitfield |= 2 after Unselect_All in SelectBeacon (0x431050), Bitfield & 1 tested first in VisibleToPlayer, and test [eax+0Ch], 2 is exactly the scan the −1/−1 path uses. All three comments are accurate.
  • IPXGlobalConnClass tailProductID@0xB8, BridgeNet@0xBA / BridgeNode@0xBE / IsBridge@0xC4, LastAddress@0xCC + LastPacketID@0xD0 + LastCount@0xD4 (capacity) + LastRXIndex@0xD8, all confirmed in Receive_Packet/Send. Socket is written as a word (0x53F4F4), so unsigned short is right.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants