InitListen

This commit is contained in:
ApfelTeeSaft
2026-05-07 14:36:13 +02:00
parent f4a0d0dd00
commit d0792640b5
2 changed files with 149 additions and 77 deletions
+34 -42
View File
@@ -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, &params)
mov rcx, rbx
; ProcessEvent(fn, fn, &params) — 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
View File
@@ -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