add GUI/Input layer (zeroinput, zerogui, gui)

This commit is contained in:
ApfelTeeSaft
2026-05-06 10:18:14 +02:00
parent 7d4dc8de8e
commit 9bbf93e289
3 changed files with 1938 additions and 0 deletions
+576
View File
@@ -0,0 +1,576 @@
INCLUDE asm\include\master.inc
; SDK offsets - AFortGameStateAthena
AGSA_WarmupCountdownEndTime EQU 14F4h
AGSA_AircraftStartTime EQU 14F8h
AGSA_GamePhase EQU 1B98h ; BYTE, EAthenaGamePhase
AGSA_bWillSkipAircraft EQU 1BA8h ; BYTE
AGSA_PlayerArray_Data EQU 0330h ; TArray<APlayerState*>.Data
AGSA_PlayerArray_Num EQU 0338h ; TArray<APlayerState*>.Num
; UWorld offsets
UWORLD_GameState EQU 0148h
UWORLD_AuthorityGameMode EQU 0140h
; AGameModeBase offsets
AGMB_GameSession EQU 0370h ; AGameSession*
; AActor offsets
AACTOR_Owner EQU 0108h ; AActor* (PlayerController for PlayerState)
; EAthenaGamePhase::Aircraft = 3
EGAMEPHASE_Aircraft EQU 3
; FText size
FTEXT_SIZE EQU 18h ; 24 bytes
; Window parameters
GUI_WINDOW_W_BITS EQU 43FA0000h ; 500.0f
GUI_WINDOW_H_BITS EQU 444F0000h ; 828.0f
GUI_WINDOW_H_BITS2 EQU 44AF0000h ; 700.0f (for header)
; Position of GUI window
; Menu pos: FVector2D starting at 200, 250
F_200_0 EQU 43480000h ; 200.0f
F_250_0 EQU 437A0000h ; 250.0f
.const
; Wide string constants for GUI text
szTitle_Raider DW 'R','a','i','d','e','r', 0
szBtn_StartBus DW 'S','t','a','r','t',' ','B','u','s', 0
szBtn_Back DW '<', 0
szBtn_Kick DW 'K','i','c','k', 0
szBtnTab_Game DW 'G','a','m','e', 0
szBtnTab_Players DW 'P','l','a','y','e','r','s', 0
szText_CurPlayer DW 'C','u','r','r','e','n','t',' ','P','l','a','y','e','r',':', 0
szText_ConnPlayers DW 'C','o','n','n','e','c','t','e','d',' ','P','l','a','y','e','r','s', 0
; Console command: "startaircraft"
szConsoleCmd_W DW 's','t','a','r','t','a','i','r','c','r','a','f','t', 0
; UFunction / UClass paths for static calls
szFn_ExecConsoleCmd DB "Function Engine.KismetSystemLibrary.ExecuteConsoleCommand", 0
szClass_KismetSys DB "Class Engine.KismetSystemLibrary", 0
szFn_GetPlayerName DB "Function Engine.PlayerState.GetPlayerName", 0
szFn_ConvStrToText DB "Function Engine.KismetTextLibrary.Conv_StringToText", 0
szClass_KismetText DB "Class Engine.KismetTextLibrary", 0
; Kick reason wide string
szKickReason DW 'Y','o','u',' ','h','a','v','e',' ','b','e','e','n',' ','k','i','c','k','e','d','.', 0
; Sizebit constants as REAL4
f_25_0 REAL4 25.0
f_100_0 REAL4 100.0
f_110_0 REAL4 110.0
f_60_0 REAL4 60.0
f_90_0 REAL4 90.0
f_130_0 REAL4 130.0
f_500_0 REAL4 500.0
f_700_0 REAL4 700.0
f_0_5_gui REAL4 0.5
.data?
; Persistent menu state
GUI_menu_opened BYTE ? ; bool: menu visible (toggled by F2)
GUI_initialized BYTE ? ; one-time init done
GUI_tab DWORD ? ; active tab (0=Game, 1=Players)
GUI_currentPlayer QWORD ? ; APlayerState* selected for kick view (or 0)
GUI_pos_X REAL4 ? ; window position X (drag-persistent)
GUI_pos_Y REAL4 ? ; window position Y
; Cached UFunction* and UClass* pointers
GUI_pFn_ExecConsole QWORD ?
GUI_pClass_KismetSys QWORD ?
GUI_pFn_GetPlyName QWORD ?
GUI_pFn_ConvStr QWORD ?
GUI_pClass_TextLib QWORD ?
; Scratch buffer for player name FString (single-frame, reused per player)
GUI_nameFStr_Data QWORD ? ; FString.Data (wchar_t*)
GUI_nameFStr_Num DWORD ? ; FString.Num
GUI_nameFStr_Max DWORD ? ; FString.Max
; Scratch buffer for kick reason FString
GUI_kickFStr_Data QWORD ?
GUI_kickFStr_Num DWORD ?
GUI_kickFStr_Max DWORD ?
.code
; GUI__GetPlayerName (internal helper)
; Calls GetPlayerName UFunction on a PlayerState via ProcessEvent.
; Fills GUI_nameFStr_Data/Num/Max.
; In: RCX = APlayerState* (PlayerState object)
; Out: RAX = wchar_t* (Data pointer, or 0 on failure)
;
; Frame: 2 pushes (RBX=PlayerState*) + sub 28h -> RSP = 0
; GetPlayerName params at [rsp+20h..+2Fh]:
; +0x00: FString ReturnValue (16 bytes)
GUI__GetPlayerName PROC
push rbx
push rsi
sub rsp, 28h
mov rbx, rcx ; PlayerState*
; Lazy-load GetPlayerName UFunction*
mov rsi, QWORD PTR [GUI_pFn_GetPlyName]
test rsi, rsi
jnz @@fn_ready
lea rcx, [szFn_GetPlayerName]
call SDK_FindObject
mov QWORD PTR [GUI_pFn_GetPlyName], rax
mov rsi, rax
test rsi, rsi
jz @@fail
@@fn_ready:
; Zero the ReturnValue FString at [rsp+20h]
xor eax, eax
mov QWORD PTR [rsp+20h], rax ; Data = nullptr
mov QWORD PTR [rsp+28h], rax ; Num=0, Max=0 - wait this overwrites our saved RSI
mov QWORD PTR [rsp+20h], 0 ; Data = 0
mov DWORD PTR [rsp+28h], 0 ; Num = 0 (this is fine - shadow can be written)
mov DWORD PTR [rsp+2Ch], 0 ; Max = 0
; ProcessEvent(PlayerState, GetPlayerName, &params)
mov rcx, rbx
mov rdx, rsi
lea r8, [rsp+20h]
call QWORD PTR [ProcessEvent]
; Return FString.Data (the wchar_t* pointer)
mov rax, QWORD PTR [rsp+20h]
jmp @@done
@@fail:
xor rax, rax
@@done:
add rsp, 28h
pop rsi
pop rbx
ret
GUI__GetPlayerName ENDP
; GUI__KickController (internal helper)
; Calls Native_OnlineSession_KickPlayer(Session, PC, &empty_FText).
; In: RCX = APlayerController* (PC to kick)
; Out: none
;
; Frame: 2 pushes (RBX=PC) + sub 38h -> RSP = 8-16-56 = -64 = 0
; [rsp+20h..+37h] = zeroed FText (0x18 = 24 bytes)
GUI__KickController PROC
push rbx
push rsi
sub rsp, 38h
mov rbx, rcx ; PC to kick
test rbx, rbx
jz @@done
; Get GameSession: World->AuthorityGameMode->GameSession
call SDK_GetWorld
test rax, rax
jz @@done
mov rsi, QWORD PTR [rax + UWORLD_AuthorityGameMode]
test rsi, rsi
jz @@done
mov rsi, QWORD PTR [rsi + AGMB_GameSession]
test rsi, rsi
jz @@done
; Zero FText at [rsp+20h] (24 bytes)
xor eax, eax
mov QWORD PTR [rsp+20h], rax
mov QWORD PTR [rsp+28h], rax
mov QWORD PTR [rsp+30h], rax
; Native_OnlineSession_KickPlayer(Session, PC, &FText)
mov rcx, rsi ; Session
mov rdx, rbx ; PC
lea r8, [rsp+20h] ; &FText (empty)
call QWORD PTR [Native_OnlineSession_KickPlayer]
@@done:
add rsp, 38h
pop rsi
pop rbx
ret
GUI__KickController ENDP
; GUI__StartBus (internal helper)
; Sets GameState fields and fires "startaircraft" console command
; then calls GameModeLateGame_InitializeGameplay.
; In: none
; Frame: 4 pushes (RBX=GameState*,RBP=World*,RSI=KismetClass,RDI=fn)
; + sub 48h -> RSP = 8-32-72 = -96 = 0
; Params for ExecuteConsoleCommand at [rsp+20h]:
; +0x00: WorldContextObject QWORD
; +0x08: FString.Data QWORD
; +0x10: FString.Num DWORD
; +0x14: FString.Max DWORD
; +0x18: SpecificPlayer QWORD = 0
; Total = 0x20 = 32 bytes (fits in shadow + [rsp+20h..+3Fh])
GUI__StartBus PROC
push rbx
push rbp
push rsi
push rdi
sub rsp, 48h
; Get World
call SDK_GetWorld
test rax, rax
jz @@done
mov rbp, rax ; World*
; Get GameState
mov rbx, QWORD PTR [rbp + UWORLD_GameState]
test rbx, rbx
jz @@done
; GameState->bGameModeWillSkipAircraft = false
mov BYTE PTR [rbx + AGSA_bWillSkipAircraft], 0
; GameState->AircraftStartTime = 0
xor eax, eax
mov DWORD PTR [rbx + AGSA_AircraftStartTime], eax
; GameState->WarmupCountdownEndTime = 0
mov DWORD PTR [rbx + AGSA_WarmupCountdownEndTime], eax
; ExecuteConsoleCommand via KismetSystemLibrary
; Lazy-load KismetSystemLibrary class
mov rsi, QWORD PTR [GUI_pClass_KismetSys]
test rsi, rsi
jnz @@kls_ready
lea rcx, [szClass_KismetSys]
call SDK_FindObject
mov QWORD PTR [GUI_pClass_KismetSys], rax
mov rsi, rax
test rsi, rsi
jz @@done
@@kls_ready:
; Lazy-load ExecuteConsoleCommand UFunction*
mov rdi, QWORD PTR [GUI_pFn_ExecConsole]
test rdi, rdi
jnz @@fn_ready
lea rcx, [szFn_ExecConsoleCmd]
call SDK_FindObject
mov QWORD PTR [GUI_pFn_ExecConsole], rax
mov rdi, rax
test rdi, rdi
jz @@done
@@fn_ready:
; Build FString for "startaircraft" command using FString_FromWideChar
; Temporarily store the FString at [rsp+30h] (3 slots: Data*, Num, Max = 16 bytes)
lea rcx, [rsp+30h]
lea rdx, [szConsoleCmd_W]
call FString_FromWideChar
; Build params block at [rsp+20h]:
; [rsp+20h] = World* (WorldContextObject)
; [rsp+28h] = FString.Data (wchar_t*)
; [rsp+30h] = FString.Num (DWORD)
; [rsp+34h] = FString.Max (DWORD)
; [rsp+38h] = SpecificPlayer = 0
mov QWORD PTR [rsp+20h], rbp ; World*
mov rax, QWORD PTR [rsp+30h] ; FString.Data (was stored by FString_FromWideChar)
mov QWORD PTR [rsp+28h], rax
mov QWORD PTR [rsp+20h], rbp ; WorldContextObject = World*
lea rcx, [rsp+28h] ; out = &Command FString
lea rdx, [szConsoleCmd_W]
call FString_FromWideChar
xor rax, rax
mov QWORD PTR [rsp+38h], rax ; SpecificPlayer = nullptr
; ProcessEvent(KismetSysClass, fn, &params)
mov rcx, rsi ; KismetSystemLibrary class object
mov rdx, rdi ; ExecuteConsoleCommand fn
lea r8, [rsp+20h]
call QWORD PTR [ProcessEvent]
; Free the command FString
lea rcx, [rsp+28h]
call FString_Free
; Call GameModeLateGame_InitializeGameplay
call GameModeLateGame_InitializeGameplay
; Set bStartedBus = true
mov BYTE PTR [bStartedBus], 1
@@done:
add rsp, 48h
pop rdi
pop rsi
pop rbp
pop rbx
ret
GUI__StartBus ENDP
; GUI_Tick
; Called each frame from Hooks_PostRender.
; Frame: 4 pushes (RBX=World, RBP=GameState, RSI=playerState loop, RDI=spare)
; + sub 68h (104) -> RSP = 8-32-104 = -128 = 0
GUI_Tick PROC
push rbx
push rbp
push rsi
push rdi
sub rsp, 68h
; One-time initialization
movzx eax, BYTE PTR [GUI_initialized]
test al, al
jnz @@skip_init
; Set initial window position
mov DWORD PTR [GUI_pos_X], F_200_0
mov DWORD PTR [GUI_pos_Y], F_250_0
; menu_opened = true by default
mov BYTE PTR [GUI_menu_opened], 1
; tab = 0
mov DWORD PTR [GUI_tab], 0
; currentPlayer = nullptr
xor rax, rax
mov QWORD PTR [GUI_currentPlayer], rax
mov BYTE PTR [GUI_initialized], 1
@@skip_init:
; ZeroInput_Update (poll mouse state)
call ZeroInput_Update
; F2 toggle
mov ecx, 071h ; VK_F2 = 0x71
call GetAsyncKeyState
test ax, 1 ; check lowest bit (just-pressed flag)
jz @@no_toggle
movzx eax, BYTE PTR [GUI_menu_opened]
xor al, 1
mov BYTE PTR [GUI_menu_opened], al
@@no_toggle:
; ZeroGUI_Begin (Window)
lea rcx, [szTitle_Raider]
lea rdx, [GUI_pos_X] ; pos (FVector2D*) - persistent in .data?
movss xmm0, DWORD PTR [f_500_0]
movd r8d, xmm0 ; sizeX bits
movss xmm0, DWORD PTR [f_700_0]
movd r9d, xmm0 ; sizeY bits
movzx eax, BYTE PTR [GUI_menu_opened]
mov DWORD PTR [rsp+20h], eax ; isOpen (5th arg)
call ZeroGUI_Begin
test al, al
jz @@end_gui ; window closed
; Inside window: only if bListening && HostBeacon
movzx eax, BYTE PTR [bListening]
test al, al
jz @@end_gui
mov rax, QWORD PTR [HostBeacon]
test rax, rax
jz @@end_gui
; Get World and GameState (used by both tabs)
call SDK_GetWorld
test rax, rax
jz @@end_gui
mov rbx, rax ; rbx = World*
mov rbp, QWORD PTR [rbx + UWORLD_GameState]
test rbp, rbp
jz @@end_gui
; rbp = GameState*
; currentPlayer != nullptr branch
mov rax, QWORD PTR [GUI_currentPlayer]
test rax, rax
jz @@show_tabs
; Back button
lea rcx, [szBtn_Back]
movss xmm0, DWORD PTR [f_25_0]
movd edx, xmm0 ; sizeX = 25.0f
movd r8d, xmm0 ; sizeY = 25.0f
call ZeroGUI_Button
test al, al
jz @@no_back
; Back pressed: clear currentPlayer
xor rax, rax
mov QWORD PTR [GUI_currentPlayer], rax
jmp @@end_gui ; redraw next frame
@@no_back:
; NextColumn(90.0f)
movss xmm0, DWORD PTR [f_90_0]
call ZeroGUI_NextColumn
; Text("Current Player:")
lea rcx, [szText_CurPlayer]
xor dl, dl ; center=false
xor r8b, r8b ; outline=false
call ZeroGUI_Text
; Kick button
lea rcx, [szBtn_Kick]
movss xmm0, DWORD PTR [f_60_0]
movd edx, xmm0
movss xmm0, DWORD PTR [f_25_0]
movd r8d, xmm0
call ZeroGUI_Button
test al, al
jz @@no_kick
; Kick pressed: KickController(currentPlayer->Owner, ...)
mov rax, QWORD PTR [GUI_currentPlayer]
mov rcx, QWORD PTR [rax + AACTOR_Owner] ; Owner = PlayerController
call GUI__KickController
; Clear currentPlayer
xor rax, rax
mov QWORD PTR [GUI_currentPlayer], rax
jmp @@end_gui
@@no_kick:
jmp @@end_gui
; Tab bar
@@show_tabs:
; ButtonTab "Game" (110×25, active when tab==0)
lea rcx, [szBtnTab_Game]
movss xmm0, DWORD PTR [f_110_0]
movd edx, xmm0
movss xmm0, DWORD PTR [f_25_0]
movd r8d, xmm0
xor r9d, r9d
cmp DWORD PTR [GUI_tab], 0
sete r9b ; active = (tab == 0)
call ZeroGUI_ButtonTab
test al, al
jz @@no_tab0
mov DWORD PTR [GUI_tab], 0
@@no_tab0:
; ButtonTab "Players" (110×25, active when tab==1)
lea rcx, [szBtnTab_Players]
movss xmm0, DWORD PTR [f_110_0]
movd edx, xmm0
movss xmm0, DWORD PTR [f_25_0]
movd r8d, xmm0
xor r9d, r9d
cmp DWORD PTR [GUI_tab], 1
sete r9b
call ZeroGUI_ButtonTab
test al, al
jz @@no_tab1
mov DWORD PTR [GUI_tab], 1
@@no_tab1:
; NextColumn(130.0f)
movss xmm0, DWORD PTR [f_130_0]
call ZeroGUI_NextColumn
; Tab content
mov eax, DWORD PTR [GUI_tab]
cmp eax, 0
je @@tab_game
cmp eax, 1
je @@tab_players
jmp @@end_gui
; Tab 0: Game
@@tab_game:
; Only show "Start Bus" if !bStartedBus
movzx eax, BYTE PTR [bStartedBus]
test al, al
jnz @@end_gui
lea rcx, [szBtn_StartBus]
movss xmm0, DWORD PTR [f_100_0]
movd edx, xmm0
movss xmm0, DWORD PTR [f_25_0]
movd r8d, xmm0
call ZeroGUI_Button
test al, al
jz @@end_gui
; Start Bus clicked
call GUI__StartBus
jmp @@end_gui
; Tab 1: Players
@@tab_players:
; Text("Connected Players")
lea rcx, [szText_ConnPlayers]
xor dl, dl
xor r8b, r8b
call ZeroGUI_Text
; Iterate GameState->PlayerArray
mov esi, DWORD PTR [rbp + AGSA_PlayerArray_Num]
test esi, esi
jz @@end_gui
mov rdi, QWORD PTR [rbp + AGSA_PlayerArray_Data]
test rdi, rdi
jz @@end_gui
xor DWORD PTR [rsp+2Ch], 0 ; loop index = 0
mov DWORD PTR [rsp+2Ch], 0
@@player_loop:
mov eax, DWORD PTR [rsp+2Ch]
cmp eax, esi
jae @@end_gui ; done iterating
; Get PlayerState pointer = PlayerArray.Data[i] (array of pointers)
movzx rax, eax
mov rax, QWORD PTR [rdi + rax*8]
test rax, rax
jz @@player_next
mov QWORD PTR [rsp+30h], rax ; save PlayerState*
; Get player name via ProcessEvent
mov rcx, rax
call GUI__GetPlayerName ; RAX = wchar_t* (or 0)
test rax, rax
jz @@player_next
; Button with player name (100×25)
mov rcx, rax ; name = wchar_t*
movss xmm0, DWORD PTR [f_100_0]
movd edx, xmm0
movss xmm0, DWORD PTR [f_25_0]
movd r8d, xmm0
call ZeroGUI_Button
test al, al
jz @@player_next
; Button clicked: set currentPlayer = PlayerState
mov rax, QWORD PTR [rsp+30h]
mov QWORD PTR [GUI_currentPlayer], rax
jmp @@end_gui
@@player_next:
inc DWORD PTR [rsp+2Ch]
jmp @@player_loop
@@end_gui:
; ZeroGUI_End (flush drawlist)
call ZeroGUI_End
add rsp, 68h
pop rdi
pop rsi
pop rbp
pop rbx
ret
GUI_Tick ENDP
END
+1240
View File
File diff suppressed because it is too large Load Diff
+122
View File
@@ -0,0 +1,122 @@
INCLUDE asm\include\master.inc
.data?
ZI_mouseDown BYTE 5 DUP(?) ; mouseDown[5]
ZI_mouseDownAlready BYTE 256 DUP(?) ; mouseDownAlready[256]
ZI_keysDown BYTE 256 DUP(?) ; keysDown[256]
ZI_keysDownAlready BYTE 256 DUP(?) ; keysDownAlready[256]
.code
; ZeroInput_Update (= Input::Handle)
; Polls GetAsyncKeyState(0x01) and updates mouseDown[0].
;
; In: none
; Out: none
; Frame: 0 pushes + sub 28h -> RSP = 8-40 = -32 = 0
ZeroInput_Update PROC
sub rsp, 28h
mov ecx, 1 ; VK_LBUTTON = 0x01
call GetAsyncKeyState
test ax, ax
jz @@mouse_up
mov BYTE PTR [ZI_mouseDown], 1
jmp @@done
@@mouse_up:
mov BYTE PTR [ZI_mouseDown], 0
@@done:
add rsp, 28h
ret
ZeroInput_Update ENDP
; ZeroInput_IsMouseClicked
; In: ECX = button index (0-4)
; EDX = element_id (0-255)
; R8B = repeat (bool)
; Out: AL = 1 if clicked, 0 otherwise
;
; Logic:
; if mouseDown[button] is set:
; if !mouseDownAlready[element_id]: set it, return true
; if repeat: return true
; else:
; clear mouseDownAlready[element_id]
; return false
;
; Frame: 0 pushes + sub 28h -> RSP = 0
ZeroInput_IsMouseClicked PROC
sub rsp, 28h
; Check mouseDown[button]
movzx rax, ecx ; button index (zero-extend to 64-bit)
movzx eax, BYTE PTR [ZI_mouseDown + rax]
test al, al
jz @@clear_already ; not pressed -> clear + return false
; mouseDown[button] is set — check mouseDownAlready[element_id]
movzx rcx, edx ; element_id -> index (zero-extend)
movzx eax, BYTE PTR [ZI_mouseDownAlready + rcx]
test al, al
jnz @@check_repeat ; already marked -> check repeat
; First press: set mouseDownAlready[element_id] = true, return true
mov BYTE PTR [ZI_mouseDownAlready + rcx], 1
mov al, 1
jmp @@done
@@check_repeat:
; Already pressed: return repeat flag
movzx eax, r8b ; AL = repeat
jmp @@done
@@clear_already:
; Mouse is not down: clear mouseDownAlready[element_id]
movzx rcx, edx
mov BYTE PTR [ZI_mouseDownAlready + rcx], 0
xor al, al
@@done:
add rsp, 28h
ret
ZeroInput_IsMouseClicked ENDP
; ZeroInput_IsKeyPressed
; In: ECX = key code (0-255)
; DL = repeat (bool)
; Out: AL = 1 if pressed, 0 otherwise
;
; Frame: 0 pushes + sub 28h -> RSP = 0
ZeroInput_IsKeyPressed PROC
sub rsp, 28h
movzx rax, ecx ; key index
movzx eax, BYTE PTR [ZI_keysDown + rax]
test al, al
jz @@clear_already
movzx rcx, ecx
movzx eax, BYTE PTR [ZI_keysDownAlready + rcx]
test al, al
jnz @@check_repeat
mov BYTE PTR [ZI_keysDownAlready + rcx], 1
mov al, 1
jmp @@done
@@check_repeat:
movzx eax, dl ; AL = repeat
jmp @@done
@@clear_already:
movzx rcx, ecx
mov BYTE PTR [ZI_keysDownAlready + rcx], 0
xor al, al
@@done:
add rsp, 28h
ret
ZeroInput_IsKeyPressed ENDP
END