mirror of
https://github.com/ApfelTeeSaft/Perfectium.git
synced 2026-08-26 19:33:36 +00:00
InitListen
This commit is contained in:
+34
-42
@@ -48,18 +48,17 @@ FUNC_NATIVE EQU 0400h ; FUNC_Native flag
|
||||
|
||||
.const
|
||||
|
||||
; Athena terrain URL as console command
|
||||
szCmdOpenAthena DW 'o','p','e','n',' '
|
||||
; Console command for ExecuteConsoleCommand: "open Athena_Terrain?game=..."
|
||||
szOpenAthena DW 'o','p','e','n',' '
|
||||
DW 'A','t','h','e','n','a','_','T','e','r','r','a','i','n','?'
|
||||
DW 'g','a','m','e','=','/','G','a','m','e','/','A','t','h','e'
|
||||
DW 'n','a','/','A','t','h','e','n','a','_','G','a','m','e','M'
|
||||
DW 'o','d','e','.','A','t','h','e','n','a','_','G','a','m','e'
|
||||
DW 'M','o','d','e','_','C', 0
|
||||
|
||||
szInProgress DW 'I','n','P','r','o','g','r','e','s','s', 0
|
||||
|
||||
; UFunction paths
|
||||
szFn_ExecConsole DB "KismetSystemLibrary.ExecuteConsoleCommand", 0
|
||||
szFn_ExecCmd DB "KismetSystemLibrary.ExecuteConsoleCommand", 0
|
||||
szFn_ConvToName DB "KismetStringLibrary.Conv_StringToName", 0
|
||||
szFn_K2SetMatchState DB "GameMode.K2_OnSetMatchState", 0
|
||||
szFn_StartPlay DB "GameMode.StartPlay", 0
|
||||
@@ -97,7 +96,7 @@ szDbgTraveled DB "[GAME] bTraveled set to 1.", 0
|
||||
|
||||
.data?
|
||||
|
||||
pFn_ExecConsole QWORD ?
|
||||
pFn_ExecCmd QWORD ?
|
||||
pFn_ConvToName QWORD ?
|
||||
pFn_K2SetMatchState QWORD ?
|
||||
pFn_StartPlay QWORD ?
|
||||
@@ -182,12 +181,11 @@ Game__CallPE_NoParams ENDP
|
||||
; Frame: 4 pushes (rbp,rbx,rsi,rdi) + sub 58h
|
||||
; After 4 pushes RSP=8; sub 58h(88=8) -> RSP=0
|
||||
;
|
||||
; Stack layout:
|
||||
; [rsp+ 0..31] = shadow space
|
||||
; [rsp+32..39] = params.WorldContextObject (PC)
|
||||
; [rsp+40..55] = params.Command FString
|
||||
; [rsp+56..63] = params.SpecificPlayer (NULL)
|
||||
; [rsp+64..71] = saved UFunction::FunctionFlags (restored after call)
|
||||
; ExecuteConsoleCommand params struct at [rsp+32] (32 bytes total):
|
||||
; [rsp+32..39] = WorldContextObject: UObject* = PC
|
||||
; [rsp+40..55] = Command: FString (Data*8, Count4, Max4) = "open Athena_Terrain?..."
|
||||
; [rsp+56..63] = SpecificPlayer: APlayerController* = null
|
||||
; [rsp+64..67] = saved UFunction::FunctionFlags
|
||||
Game_Start PROC
|
||||
push rbp
|
||||
push rbx
|
||||
@@ -213,54 +211,48 @@ Game_Start PROC
|
||||
jmp @@done
|
||||
|
||||
@@pc_ok:
|
||||
; Load ExecuteConsoleCommand UFunction* (cached in pFn_ExecConsole).
|
||||
; Search key deliberately omits "Function Engine." prefix: GObjects stores
|
||||
; "Function /Script/Engine.KismetSystemLibrary.ExecuteConsoleCommand"
|
||||
; and strstr("Function /Script/Engine...", "Function Engine...") fails,
|
||||
; but strstr(..., "KismetSystemLibrary.ExecuteConsoleCommand") succeeds.
|
||||
lea rsi, pFn_ExecConsole
|
||||
lea rdi, szFn_ExecConsole
|
||||
; Load ExecuteConsoleCommand UFunction* (cached in pFn_ExecCmd).
|
||||
lea rsi, pFn_ExecCmd
|
||||
lea rdi, szFn_ExecCmd
|
||||
call Game__LoadFn
|
||||
test rax, rax
|
||||
jz @@ec_null
|
||||
mov rsi, rax ; rsi = fn ptr (callee-saved, survives Logger calls)
|
||||
mov rsi, rax ; rsi = fn ptr (callee-saved)
|
||||
|
||||
lea rcx, szDbgECFound
|
||||
call Logger_LogInfo
|
||||
|
||||
; Set FUNC_Native on the UFunction so ProcessEvent dispatches to the
|
||||
; native implementation rather than the Blueprint bytecode interpreter.
|
||||
; FunctionFlags is at UFunction+0x88 (first field after UStruct).
|
||||
mov eax, DWORD PTR [rsi + UFUNCTION_FLAGS_OFFSET]
|
||||
mov DWORD PTR [rsp+64], eax ; save original flags
|
||||
or DWORD PTR [rsi + UFUNCTION_FLAGS_OFFSET], FUNC_NATIVE
|
||||
; Build ExecuteConsoleCommand params at [rsp+32]:
|
||||
; [+0] WorldContextObject = PC
|
||||
; [+8] Command (FString) = "open Athena_Terrain?game=..."
|
||||
; [+24] SpecificPlayer = null
|
||||
mov QWORD PTR [rsp+32], rbx ; WorldContextObject = PC
|
||||
|
||||
; Build ExecuteConsoleCommand params struct at [rsp+32]:
|
||||
; WorldContextObject = PC (provides world context for GEngine->Exec)
|
||||
; Command = FString("open Athena_Terrain?game=...")
|
||||
; SpecificPlayer = NULL
|
||||
mov QWORD PTR [rsp+32], rbx
|
||||
lea rcx, [rsp+40]
|
||||
lea rdx, szCmdOpenAthena
|
||||
call FString_FromWideChar
|
||||
xor eax, eax
|
||||
mov QWORD PTR [rsp+56], rax
|
||||
lea rcx, [rsp+40] ; &Command FString
|
||||
lea rdx, szOpenAthena
|
||||
call FString_FromWideChar ; fills [rsp+40..55]
|
||||
|
||||
mov QWORD PTR [rsp+56], 0 ; SpecificPlayer = null
|
||||
|
||||
; Save flags, set FUNC_Native
|
||||
mov eax, DWORD PTR [rsi + UFUNCTION_FLAGS_OFFSET]
|
||||
mov DWORD PTR [rsp+64], eax
|
||||
or DWORD PTR [rsi + UFUNCTION_FLAGS_OFFSET], FUNC_NATIVE
|
||||
|
||||
lea rcx, szDbgCallingEC
|
||||
call Logger_LogInfo
|
||||
|
||||
; ProcessEvent(PC, fn, ¶ms)
|
||||
mov rcx, rbx
|
||||
; ProcessEvent(fn, fn, ¶ms) — static function; receiver is fn itself
|
||||
mov rcx, rsi
|
||||
mov rdx, rsi
|
||||
lea r8, [rsp+32]
|
||||
lea r8, [rsp+32]
|
||||
call QWORD PTR [ProcessEvent]
|
||||
|
||||
lea rcx, [rsp+40]
|
||||
call FString_Free
|
||||
|
||||
; Restore original FunctionFlags
|
||||
; Restore flags, free Command FString
|
||||
mov eax, DWORD PTR [rsp+64]
|
||||
mov DWORD PTR [rsi + UFUNCTION_FLAGS_OFFSET], eax
|
||||
lea rcx, [rsp+40]
|
||||
call FString_Free
|
||||
jmp @@set_traveled
|
||||
|
||||
@@ec_null:
|
||||
|
||||
+115
-35
@@ -15,6 +15,13 @@ VTABLE_ServerReplicateActors EQU (083h * 8) ; vtable byte offset for slot 0x53
|
||||
UWORLD_GameState EQU 0148h ; UWorld::GameState (AGameStateBase*)
|
||||
ABLDGCONT_bAlreadySearched EQU 0EA5h ; ABuildingContainer::bAlreadySearched (bit 0)
|
||||
|
||||
; Offsets for Server_Initialize network wiring
|
||||
UWORLD_LevelCollections EQU 170h ; UWorld::LevelCollections (TArray<FLevelCollection>)
|
||||
FLEVELLCOLL_NetDriver EQU 010h ; FLevelCollection::NetDriver
|
||||
FLEVELLCOLL_SIZEOF EQU 080h ; sizeof(FLevelCollection)
|
||||
AGAMEMODE_GameSession EQU 370h ; AGameMode::GameSession (AGameSession*)
|
||||
AGAMESESSION_MaxPlayers EQU 31Ch ; AGameSession::MaxPlayers (int32)
|
||||
|
||||
MAX_PEHOOKS EQU 32 ; capacity of the hook arrays
|
||||
|
||||
.data?
|
||||
@@ -1188,24 +1195,23 @@ PEHOOK_ReadyToStartMatch PROC
|
||||
ret
|
||||
PEHOOK_ReadyToStartMatch ENDP
|
||||
|
||||
; Server_Initialize - set up full listen-server infrastructure
|
||||
; Called from Hooks_TickFlush each tick until bListening is set.
|
||||
; Server_Initialize — set up full listen-server infrastructure
|
||||
; Called from Hooks_TickFlush each tick until bListening is set,
|
||||
; and from PEHOOK_ReadyToStartMatch when ReadyToStartMatch fires.
|
||||
; No arguments; no return value.
|
||||
; Returns immediately (bListening=0, no state change) if the world is
|
||||
; not yet in Athena context - Hooks_TickFlush will retry next tick.
|
||||
; not yet in Athena context — Hooks_TickFlush will retry next tick.
|
||||
;
|
||||
; Sequence:
|
||||
; 1. Guard bListening - skip if already listening
|
||||
; 2. Game_OnReadyToStartMatch()
|
||||
; 3. Spawn AFortOnlineBeaconHost -> HostBeacon
|
||||
; 4. Set ListenPort=7776 + InitHost
|
||||
; 5. Zero-init FURL on stack (port 7777)
|
||||
; 6. Resolve ServerReplicateActors from ReplicationDriver vtable slot 0x53
|
||||
; 7. PauseBeaconRequests(false), bListening=true
|
||||
;
|
||||
; Stack: 7 pushes (56) -> entry RSP=8; after 7 odd pushes RSP=0; sub128(=0) -> 0
|
||||
; Stack: 7 pushes (entry RSP=8; 7×8=56=8; 8-8=0) + sub 0B0h(176,=0) -> 0
|
||||
; [rsp+0..31] = shadow
|
||||
; [rsp+32..143] = FURL local struct (112 bytes)
|
||||
; [rsp+32..39] = arg5 slot (FString* Error for InitListen)
|
||||
; [rsp+40..55] = Error FString (16 bytes: Data*, Count, Max)
|
||||
; [rsp+56..167] = FURL local struct (112 bytes)
|
||||
;
|
||||
; Registers:
|
||||
; rbx = HostBeacon->NetDriver (0 if beacon spawn/init failed)
|
||||
; r13 = HostBeacon
|
||||
; r14 = ReplicationDriver (@@resolve_repdriver block)
|
||||
Server_Initialize PROC
|
||||
push rbp
|
||||
push rbx
|
||||
@@ -1214,7 +1220,7 @@ Server_Initialize PROC
|
||||
push r12
|
||||
push r13
|
||||
push r14
|
||||
sub rsp, 128
|
||||
sub rsp, 0B0h ; 176 bytes; RSP=0
|
||||
|
||||
; Guard: already listening?
|
||||
movzx eax, BYTE PTR [bListening]
|
||||
@@ -1226,11 +1232,11 @@ Server_Initialize PROC
|
||||
; Hooks_TickFlush retries on the next tick.
|
||||
call SDK_GetWorld
|
||||
test rax, rax
|
||||
jz @@done ; world not ready -> retry
|
||||
jz @@done
|
||||
|
||||
mov r12, QWORD PTR [rax + UWORLD_AuthorityGameMode]
|
||||
test r12, r12
|
||||
jz @@done ; GameMode not ready -> retry
|
||||
jz @@done
|
||||
|
||||
; Lazily cache AFortGameModeAthena class pointer
|
||||
mov rax, QWORD PTR [pClass_FortGameModeAthena]
|
||||
@@ -1241,16 +1247,19 @@ Server_Initialize PROC
|
||||
mov QWORD PTR [pClass_FortGameModeAthena], rax
|
||||
@@have_athena_class:
|
||||
test rax, rax
|
||||
jz @@done ; class not found -> retry
|
||||
jz @@done
|
||||
|
||||
; GameMode->ClassPrivate (+0x10) must equal AFortGameModeAthena
|
||||
cmp QWORD PTR [r12 + 010h], rax
|
||||
jne @@done ; lobby type -> retry
|
||||
jne @@done
|
||||
|
||||
; Verified Athena context - proceed
|
||||
; Verified Athena context — proceed
|
||||
; Game setup
|
||||
call Game_OnReadyToStartMatch
|
||||
|
||||
; rbx = NetDriver pointer (0 until beacon spawn succeeds)
|
||||
xor ebx, ebx
|
||||
|
||||
; Spawn AFortOnlineBeaconHost
|
||||
mov rax, QWORD PTR [pClass_FortOnlineBeaconHost]
|
||||
test rax, rax
|
||||
@@ -1267,8 +1276,8 @@ Server_Initialize PROC
|
||||
jz @@net_setup
|
||||
|
||||
mov rcx, QWORD PTR [pClass_FortOnlineBeaconHost]
|
||||
xor edx, edx ; Location = null (zero origin)
|
||||
xor r8d, r8d ; Owner = null
|
||||
xor edx, edx
|
||||
xor r8d, r8d
|
||||
call Spawners_SpawnActor
|
||||
|
||||
test rax, rax
|
||||
@@ -1283,31 +1292,102 @@ Server_Initialize PROC
|
||||
mov rcx, r13
|
||||
call QWORD PTR [Native_OnlineBeaconHost_InitHost]
|
||||
|
||||
@@net_setup:
|
||||
; Zero-init FURL on stack, set Port=7777
|
||||
lea rcx, [rsp + 32]
|
||||
xor edx, edx
|
||||
mov r8d, 112 ; sizeof(FURL) = 0x70
|
||||
call RtlZeroMemory
|
||||
mov DWORD PTR [rsp + 32 + FURL_Port], LISTEN_GAME_PORT
|
||||
; Get HostBeacon->NetDriver (AOBH_NetDriver = 0x220)
|
||||
mov rbx, QWORD PTR [r13 + AOBH_NetDriver]
|
||||
test rbx, rbx
|
||||
jz @@net_setup
|
||||
|
||||
; Resolve ServerReplicateActors from ReplicationDriver vtable[0x53]
|
||||
; NetDriver->World = GetWorld() (UNETDRIVER_World = 0x0B8h)
|
||||
call SDK_GetWorld
|
||||
test rax, rax
|
||||
jz @@final_setup
|
||||
jz @@net_setup
|
||||
mov QWORD PTR [rbx + UNETDRIVER_World], rax
|
||||
|
||||
@@net_setup:
|
||||
; Zero-init FURL at [rsp+56], set Port=7777
|
||||
lea rcx, [rsp + 56]
|
||||
mov edx, 112 ; Length = sizeof(FURL) = 0x70 bytes
|
||||
call RtlZeroMemory
|
||||
mov DWORD PTR [rsp + 56 + FURL_Port], LISTEN_GAME_PORT
|
||||
|
||||
; Call InitListen(NetDriver, World, &FURL, true, &Error)
|
||||
; Signature: bool(*)(UObject* Driver, void* InNotify, FURL& LocalURL,
|
||||
; bool bReuseAddressAndPort, FString& Error)
|
||||
test rbx, rbx
|
||||
jz @@post_init_listen
|
||||
|
||||
; Zero Error FString at [rsp+40]
|
||||
xor eax, eax
|
||||
mov QWORD PTR [rsp + 40], rax ; Error.Data = null
|
||||
mov QWORD PTR [rsp + 48], rax ; Error.Count/Max = 0
|
||||
|
||||
; arg5 (FString& Error) at [rsp+32] = pointer to Error FString
|
||||
lea rax, [rsp + 40]
|
||||
mov QWORD PTR [rsp + 32], rax
|
||||
|
||||
mov rcx, rbx ; arg1: NetDriver
|
||||
call SDK_GetWorld
|
||||
mov rdx, rax ; arg2: InNotify = World
|
||||
lea r8, [rsp + 56] ; arg3: FURL&
|
||||
mov r9d, 1 ; arg4: bReuseAddressAndPort = true
|
||||
call QWORD PTR [Native_NetDriver_InitListen]
|
||||
|
||||
@@post_init_listen:
|
||||
; Wire World->NetDriver and LevelCollections[0..1].NetDriver
|
||||
test rbx, rbx
|
||||
jz @@resolve_repdriver
|
||||
|
||||
call SDK_GetWorld
|
||||
test rax, rax
|
||||
jz @@resolve_repdriver
|
||||
|
||||
mov rsi, rax ; rsi = World
|
||||
|
||||
; World->NetDriver = NetDriver
|
||||
mov QWORD PTR [rsi + UWORLD_NetDriver], rbx
|
||||
|
||||
; LevelCollections.Data at [World+0x170], Num at [World+0x178]
|
||||
; FLevelCollection::NetDriver at +0x10, sizeof = 0x80
|
||||
mov rdi, QWORD PTR [rsi + UWORLD_LevelCollections] ; Data ptr
|
||||
test rdi, rdi
|
||||
jz @@skip_level_collections
|
||||
cmp DWORD PTR [rsi + UWORLD_LevelCollections + 8], 0 ; Num > 0?
|
||||
jle @@skip_level_collections
|
||||
mov QWORD PTR [rdi + FLEVELLCOLL_NetDriver], rbx ; [0].NetDriver
|
||||
cmp DWORD PTR [rsi + UWORLD_LevelCollections + 8], 1 ; Num > 1?
|
||||
jle @@skip_level_collections
|
||||
mov QWORD PTR [rdi + FLEVELLCOLL_SIZEOF + FLEVELLCOLL_NetDriver], rbx ; [1].NetDriver
|
||||
|
||||
@@skip_level_collections:
|
||||
; World->AuthorityGameMode->GameSession->MaxPlayers = 100
|
||||
mov rax, QWORD PTR [rsi + UWORLD_AuthorityGameMode]
|
||||
test rax, rax
|
||||
jz @@resolve_repdriver
|
||||
mov rax, QWORD PTR [rax + AGAMEMODE_GameSession]
|
||||
test rax, rax
|
||||
jz @@resolve_repdriver
|
||||
mov DWORD PTR [rax + AGAMESESSION_MaxPlayers], MAXPLAYERS
|
||||
|
||||
@@resolve_repdriver:
|
||||
; Resolve ServerReplicateActors from HostBeacon->NetDriver->ReplicationDriver
|
||||
; vtable slot 0x53 = VTABLE_ServerReplicateActors
|
||||
; World->NetDriver now points to HostBeacon->NetDriver (set above).
|
||||
call SDK_GetWorld
|
||||
test rax, rax
|
||||
jz @@pause_beacon
|
||||
mov rax, QWORD PTR [rax + UWORLD_NetDriver]
|
||||
test rax, rax
|
||||
jz @@final_setup
|
||||
jz @@pause_beacon
|
||||
mov rax, QWORD PTR [rax + UNETDRIVER_ReplDriver]
|
||||
test rax, rax
|
||||
jz @@final_setup
|
||||
jz @@pause_beacon
|
||||
|
||||
mov r14, rax
|
||||
mov rax, QWORD PTR [r14]
|
||||
mov rax, QWORD PTR [rax + VTABLE_ServerReplicateActors]
|
||||
mov QWORD PTR [Native_ReplicationDriver_ServerReplicateActors], rax
|
||||
|
||||
@@final_setup:
|
||||
@@pause_beacon:
|
||||
; PauseBeaconRequests(false), mark listening
|
||||
mov rax, QWORD PTR [HostBeacon]
|
||||
test rax, rax
|
||||
@@ -1320,7 +1400,7 @@ Server_Initialize PROC
|
||||
mov BYTE PTR [bListening], 1
|
||||
|
||||
@@done:
|
||||
add rsp, 128
|
||||
add rsp, 0B0h
|
||||
pop r14
|
||||
pop r13
|
||||
pop r12
|
||||
|
||||
Reference in New Issue
Block a user