From a1748ca81fa7ebcc1c1478d4f2cf6749166f1ba6 Mon Sep 17 00:00:00 2001 From: ricardoofnl Date: Sat, 5 Sep 2026 09:04:40 +0700 Subject: [PATCH] [saco] Implement/match `UnkAB`; the trailing `GetAt` only survives DCE when its result gates a `return` --- saco/net/actorpool.h | 20 ++++++++++++++++++++ saco/net/netrpc.cpp | 26 +++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/saco/net/actorpool.h b/saco/net/actorpool.h index ba157c3..ccf6bdb 100644 --- a/saco/net/actorpool.h +++ b/saco/net/actorpool.h @@ -3,6 +3,19 @@ //---------------------------------------------------- +// 27 bytes, the block the create-actor RPC reads off the wire +typedef struct _ACTOR_INFO +{ + ACTORID ActorID; + int iModel; + VECTOR vecPos; + float fRotation; + float fHealth; + BYTE byteInvulnerable; +} ACTOR_INFO; + +//---------------------------------------------------- + class CActorPool { public: @@ -19,6 +32,13 @@ public: ~CActorPool(); // .text:100018E0 void sub_100016F0(ACTORID ActorID); // .text:100016F0 + void sub_10001900(ACTOR_INFO *pActorInfo); // .text:10001900 + + int GetAt(ACTORID ActorID) { + if(ActorID >= MAX_ACTORS) return 0; + if(!field_FA4[ActorID]) return 0; + return field_4[ActorID]; + } ACTORID FUNC_100018B0(DWORD dwPtr); // .text:100018B0 }; diff --git a/saco/net/netrpc.cpp b/saco/net/netrpc.cpp index 2ccafd3..82fdf8c 100644 --- a/saco/net/netrpc.cpp +++ b/saco/net/netrpc.cpp @@ -1,5 +1,6 @@ #include "../main.h" +#include "../game/util.h" #include "../../raknet/StringCompressor.h" extern CNetGame* pNetGame; @@ -891,7 +892,30 @@ void FUNC_10011990(BYTE byteType, DWORD dwValue, BYTE byteResult) pNetGame->GetRakClient()->RPC(RPC_ClientCheck,&bsSend,HIGH_PRIORITY,RELIABLE_ORDERED,0,FALSE); } void ClientCheck(RPCParameters *rpcParams) {} -void UnkAB(RPCParameters *rpcParams) {} +void UnkAB(RPCParameters *rpcParams) +{ + PCHAR Data = reinterpret_cast(rpcParams->input); + int iBitLength = rpcParams->numberOfBitsOfData; + + ACTOR_INFO ActorInfo; + + RakNet::BitStream bsData(Data,(iBitLength/8)+1,false); + + CActorPool *pActorPool = pNetGame->GetActorPool(); + + if(!pActorPool) return; + + memset(&ActorInfo,0,sizeof(ACTOR_INFO)); + bsData.Read((PCHAR)&ActorInfo,sizeof(ACTOR_INFO)); + + if(IsValidPedModel(ActorInfo.iModel)) + { + pActorPool->sub_10001900(&ActorInfo); + + // retail still does the lookup here even though nothing follows it + if(!pActorPool->GetAt(ActorInfo.ActorID)) return; + } +} // not registered, kept alive only by the rest of the section void FUNC_1000EBA0(RPCParameters *rpcParams) {