From 1073e871aa5c22cc6a92e6688307054e14a0c4ae Mon Sep 17 00:00:00 2001 From: igromanru Date: Sat, 7 Sep 2024 12:41:04 +0200 Subject: [PATCH 01/23] docs: FindFirstOf annotation, it never returns a nil --- assets/Mods/shared/Types.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/Mods/shared/Types.lua b/assets/Mods/shared/Types.lua index f8109c247..d7f720b19 100644 --- a/assets/Mods/shared/Types.lua +++ b/assets/Mods/shared/Types.lua @@ -294,7 +294,7 @@ function StaticFindObject(Class, InOuter, ObjectName, ExactClass) end ---Find the first non-default instance of the supplied class name ---@param ShortClassName string Should only contains the class name itself without path info ----@return UObject? +---@return UObject function FindFirstOf(ShortClassName) end ---Find all non-default instances of the supplied class name From 307fb37d5dcbd4cf5cac1d9236cca545f565e3f6 Mon Sep 17 00:00:00 2001 From: igromanru Date: Sat, 7 Sep 2024 12:57:47 +0200 Subject: [PATCH 02/23] feat: Add RemoteObject class that allows us to create a placeholder with a IsValid function feat: Rework UEHelpers to ensure that all functions never return nil but an object with a IsValid function feat: Rework GetPlayerController(): - PlayerControllers are never just AController, no need to use FindAllOf("Controller") if no APlayerControlle instances exist - If "PlayerControllers" is empty (no APlayerController instanced were found) we should return an object - Remove redundant "pairs(PlayerControllers or {})", we check already if PlayerControllers is nil before - ipairs is slightly faster than pairs and preferable for arrays (tables with numeric keys) - Remove redundant PlayerController IsValid check at the end. PlayerControllers from FindAllOf should all be valid, otherwise the user have to check IsValid on the return anyway feat: Add funtion GetGameEngine(), GetGameViewportClient() and GetWorld() - An instance of UGameEngine, UGameViewportClient and UWorld exists at all time, while PlayerController doesn't exist in main menu of some games feat: Rework GetWorldContextObject() to return UGameViewportClient. (In UE any UObject that has the GetWorld() function is a valid WorldContext) --- assets/Mods/shared/UEHelpers/UEHelpers.lua | 77 +++++++++++++++------- 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/assets/Mods/shared/UEHelpers/UEHelpers.lua b/assets/Mods/shared/UEHelpers/UEHelpers.lua index da209e338..50e153882 100644 --- a/assets/Mods/shared/UEHelpers/UEHelpers.lua +++ b/assets/Mods/shared/UEHelpers/UEHelpers.lua @@ -3,11 +3,26 @@ local UEHelpers = {} -- local jsb = require "jsbProfi" -- Version 1 does not exist, we start at version 2 because the original version didn't have a version at all. -local Version = 2 +local Version = 3 + +---Class that allows to create a blank RemoteObject +---@class RemoteObject +local RemoteObject = {} +RemoteObject.__index = RemoteObject + +---Creates a new instance of RemoteObject +---@return RemoteObject +function RemoteObject:new() + return setmetatable({}, RemoteObject) +end + +function RemoteObject:IsValid() + return false +end -- Functions local to this module, do not attempt to use! local CacheDefaultObject = function(ObjectFullName, VariableName, ForceInvalidateCache) - local DefaultObject + local DefaultObject = nil if not ForceInvalidateCache then DefaultObject = ModRef:GetSharedVariable(VariableName) @@ -28,46 +43,62 @@ function UEHelpers.GetUEHelpersVersion() return Version end +local PlayerControllerCache = RemoteObject:new() ---@cast PlayerControllerCache APlayerController --- Returns the first valid PlayerController that is currently controlled by a player. ---@return APlayerController -local PlayerController = nil function UEHelpers.GetPlayerController() - if PlayerController and PlayerController:IsValid() then return PlayerController end + if PlayerControllerCache:IsValid() then return PlayerControllerCache end -- local PlayerControllers = jsb.simpleBench("findallof", FindAllOf, "Controller") -- Uncomment line above and comment line below to profile this function - local PlayerControllers = FindAllOf("PlayerController") or FindAllOf("Controller") - if not PlayerControllers then return Print("No PlayerControllers found\n") end - for _, Controller in pairs(PlayerControllers or {}) do + local PlayerControllers = FindAllOf("PlayerController") ---@type APlayerController[]? + if not PlayerControllers then + print("GetPlayerController: No PlayerControllers were found\n") + return RemoteObject:new() ---@type APlayerController + end + for _, Controller in ipairs(PlayerControllers) do if Controller.Pawn:IsValid() and Controller.Pawn:IsPlayerControlled() then - PlayerController = Controller + PlayerControllerCache = Controller break - -- else - -- print("Not valid or not player controlled\n") end end - if PlayerController and PlayerController:IsValid() then - return PlayerController - end - error("No PlayerController found\n") + return PlayerControllerCache end ---- Returns the UWorld that the player is currenlty in. ----@return UWorld -function UEHelpers.GetWorld() - return UEHelpers.GetPlayerController():GetWorld() +local GameEngineCache = RemoteObject:new() ---@cast GameEngineCache UGameEngine +---Returns first valid instance of UGameEngine +---@return UGameEngine +function UEHelpers.GetGameEngine() + if GameEngineCache:IsValid() then return GameEngineCache end + + GameEngineCache = FindFirstOf("GameEngine") ---@type UGameEngine + return GameEngineCache end ---- Returns the UGameViewportClient for the player. ----@return AActor +--- Returns the main UGameViewportClient +---@return UGameViewportClient function UEHelpers.GetGameViewportClient() - return UEHelpers.GetPlayerController().Player.ViewportClient + local Engine = UEHelpers.GetGameEngine() + if Engine:IsValid() then + return Engine.GameViewport + end + return RemoteObject:new() ---@type UGameViewportClient +end + +--- Returns the main UWorld +---@return UWorld +function UEHelpers.GetWorld() + local GameViewportClient = UEHelpers.GetGameViewportClient() + if GameViewportClient:IsValid() then + return GameViewportClient.World + end + return RemoteObject:new() ---@type UWorld end --- Returns an object that's useable with UFunctions that have a WorldContextObject param. --- Prefer to use an actor that you already have access to whenever possible over this function. ----@return AActor +---@return UObject function UEHelpers.GetWorldContextObject() - return UEHelpers.GetPlayerController() + return UEHelpers.GetGameViewportClient() end function UEHelpers.GetGameplayStatics(ForceInvalidateCache) From e8a7159dd3e4f794cf499c4ed8435b203a81bf10 Mon Sep 17 00:00:00 2001 From: igromanru Date: Sat, 7 Sep 2024 13:10:19 +0200 Subject: [PATCH 03/23] feat: UEHelpers: Remove duplicate function GetKismetMathLibrary() feat: UEHelpers: Add functions GetKismetStringLibrary and GetKismetTextLibrary docs: UEHelpers: Add annotations for Lua Server --- assets/Mods/shared/UEHelpers/UEHelpers.lua | 28 +++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/assets/Mods/shared/UEHelpers/UEHelpers.lua b/assets/Mods/shared/UEHelpers/UEHelpers.lua index 50e153882..89446b196 100644 --- a/assets/Mods/shared/UEHelpers/UEHelpers.lua +++ b/assets/Mods/shared/UEHelpers/UEHelpers.lua @@ -101,23 +101,45 @@ function UEHelpers.GetWorldContextObject() return UEHelpers.GetGameViewportClient() end +---@param ForceInvalidateCache boolean # Force update the cache +---@return UGameplayStatics function UEHelpers.GetGameplayStatics(ForceInvalidateCache) + ---@type UGameplayStatics return CacheDefaultObject("/Script/Engine.Default__GameplayStatics", "UEHelpers_GameplayStatics", ForceInvalidateCache) end +---@param ForceInvalidateCache boolean # Force update the cache +---@return UKismetSystemLibrary function UEHelpers.GetKismetSystemLibrary(ForceInvalidateCache) + ---@type UKismetSystemLibrary return CacheDefaultObject("/Script/Engine.Default__KismetSystemLibrary", "UEHelpers_KismetSystemLibrary", ForceInvalidateCache) end +---@param ForceInvalidateCache boolean # Force update the cache +---@return UKismetMathLibrary function UEHelpers.GetKismetMathLibrary(ForceInvalidateCache) - return CacheDefaultObject("/Script/Engine.Default__KismetMathLibrary", "UEHelpers_KismetMathLibrary", ForceInvalidateCache) + ---@type UKismetMathLibrary + return CacheDefaultObject("/Script/Engine.Default__KismetMathLibrary", "UEHelpers_KismetMathLibrary", ForceInvalidateCache) end -function UEHelpers.GetKismetMathLibrary(ForceInvalidateCache) - return CacheDefaultObject("/Script/Engine.Default__KismetMathLibrary", "UEHelpers_KismetMathLibrary", ForceInvalidateCache) +---@param ForceInvalidateCache boolean # Force update the cache +---@return UKismetStringLibrary +function UEHelpers.GetKismetStringLibrary(ForceInvalidateCache) + ---@type UKismetStringLibrary + return CacheDefaultObject("/Script/Engine.Default__KismetStringLibrary", "UEHelpers_KismetStringLibrary", ForceInvalidateCache) +end + +---@param ForceInvalidateCache boolean # Force update the cache +---@return UKismetTextLibrary +function UEHelpers.GetKismetTextLibrary(ForceInvalidateCache) + ---@type UKismetTextLibrary + return CacheDefaultObject("/Script/Engine.Default__KismetTextLibrary", "UEHelpers_KismetTextLibrary", ForceInvalidateCache) end +---@param ForceInvalidateCache boolean # Force update the cache +---@return UGameMapsSettings function UEHelpers.GetGameMapsSettings(ForceInvalidateCache) + ---@type UGameMapsSettings return CacheDefaultObject("/Script/EngineSettings.Default__GameMapsSettings", "UEHelpers_GameMapsSettings", ForceInvalidateCache) end From ed67854c79a985603fdbfdf8cddd53c8654bffe5 Mon Sep 17 00:00:00 2001 From: igromanru Date: Sat, 7 Sep 2024 13:47:03 +0200 Subject: [PATCH 04/23] feat: UEHelpers: Aad an additional check in GetPlayerController() if PlayerControllers array is smaller than 1, in case future UE4SS versions will return only an empty array with FindAllOf --- assets/Mods/shared/UEHelpers/UEHelpers.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/Mods/shared/UEHelpers/UEHelpers.lua b/assets/Mods/shared/UEHelpers/UEHelpers.lua index 89446b196..8c2670baa 100644 --- a/assets/Mods/shared/UEHelpers/UEHelpers.lua +++ b/assets/Mods/shared/UEHelpers/UEHelpers.lua @@ -51,7 +51,7 @@ function UEHelpers.GetPlayerController() -- local PlayerControllers = jsb.simpleBench("findallof", FindAllOf, "Controller") -- Uncomment line above and comment line below to profile this function local PlayerControllers = FindAllOf("PlayerController") ---@type APlayerController[]? - if not PlayerControllers then + if not PlayerControllers or #PlayerControllers < 1 then print("GetPlayerController: No PlayerControllers were found\n") return RemoteObject:new() ---@type APlayerController end From 855f13a72f27ae176093d6fb9934f618001f24de Mon Sep 17 00:00:00 2001 From: igromanru Date: Sat, 7 Sep 2024 17:57:39 +0200 Subject: [PATCH 05/23] feat: Change GetGameEngine() to GetEngine() feat: Add a fallback to GetWorld(), if PlayerController doesn't exists it will try to get UWorld from UGameViewportClient feat: Use GetWorld() in GetWorldContextObject() instead of GetGameViewportClient() in GetWorldContextObject() --- assets/Mods/shared/UEHelpers/UEHelpers.lua | 26 ++++++++++++++-------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/assets/Mods/shared/UEHelpers/UEHelpers.lua b/assets/Mods/shared/UEHelpers/UEHelpers.lua index 8c2670baa..2c8ff9672 100644 --- a/assets/Mods/shared/UEHelpers/UEHelpers.lua +++ b/assets/Mods/shared/UEHelpers/UEHelpers.lua @@ -64,33 +64,41 @@ function UEHelpers.GetPlayerController() return PlayerControllerCache end -local GameEngineCache = RemoteObject:new() ---@cast GameEngineCache UGameEngine ----Returns first valid instance of UGameEngine ----@return UGameEngine -function UEHelpers.GetGameEngine() - if GameEngineCache:IsValid() then return GameEngineCache end +local EngineCache = RemoteObject:new() ---@cast EngineCache UEngine +---Returns first valid instance of UEngine +---@return UEngine +function UEHelpers.GetEngine() + if EngineCache:IsValid() then return EngineCache end - GameEngineCache = FindFirstOf("GameEngine") ---@type UGameEngine - return GameEngineCache + EngineCache = FindFirstOf("Engine") ---@type UEngine + return EngineCache end --- Returns the main UGameViewportClient ---@return UGameViewportClient function UEHelpers.GetGameViewportClient() - local Engine = UEHelpers.GetGameEngine() + local Engine = UEHelpers.GetEngine() if Engine:IsValid() then return Engine.GameViewport end return RemoteObject:new() ---@type UGameViewportClient end +local WorldCache = RemoteObject:new() ---@cast WorldCache UWorld --- Returns the main UWorld ---@return UWorld function UEHelpers.GetWorld() + if WorldCache:IsValid() then return WorldCache end + + local PlayerController = UEHelpers.GetPlayerController() + if PlayerController:IsValid() then + return PlayerController:GetWorld() + end local GameViewportClient = UEHelpers.GetGameViewportClient() if GameViewportClient:IsValid() then return GameViewportClient.World end + return RemoteObject:new() ---@type UWorld end @@ -98,7 +106,7 @@ end --- Prefer to use an actor that you already have access to whenever possible over this function. ---@return UObject function UEHelpers.GetWorldContextObject() - return UEHelpers.GetGameViewportClient() + return UEHelpers.GetWorld() end ---@param ForceInvalidateCache boolean # Force update the cache From 5ca63e4f4e6acdb82106f2bd047b5c6697c321ae Mon Sep 17 00:00:00 2001 From: igromanru Date: Sat, 7 Sep 2024 18:00:07 +0200 Subject: [PATCH 06/23] fix: Implemented WorldCache but forget to use it --- assets/Mods/shared/UEHelpers/UEHelpers.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/assets/Mods/shared/UEHelpers/UEHelpers.lua b/assets/Mods/shared/UEHelpers/UEHelpers.lua index 2c8ff9672..844ce2cbe 100644 --- a/assets/Mods/shared/UEHelpers/UEHelpers.lua +++ b/assets/Mods/shared/UEHelpers/UEHelpers.lua @@ -92,11 +92,13 @@ function UEHelpers.GetWorld() local PlayerController = UEHelpers.GetPlayerController() if PlayerController:IsValid() then - return PlayerController:GetWorld() + WorldCache = PlayerController:GetWorld() + return WorldCache end local GameViewportClient = UEHelpers.GetGameViewportClient() if GameViewportClient:IsValid() then - return GameViewportClient.World + WorldCache = GameViewportClient.World + return WorldCache end return RemoteObject:new() ---@type UWorld From ee09c7ca83f3ef6900fb2dced28ebf3ca927a200 Mon Sep 17 00:00:00 2001 From: igromanru Date: Sat, 7 Sep 2024 18:01:50 +0200 Subject: [PATCH 07/23] refactor: formatting --- assets/Mods/shared/UEHelpers/UEHelpers.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/Mods/shared/UEHelpers/UEHelpers.lua b/assets/Mods/shared/UEHelpers/UEHelpers.lua index 844ce2cbe..671edac10 100644 --- a/assets/Mods/shared/UEHelpers/UEHelpers.lua +++ b/assets/Mods/shared/UEHelpers/UEHelpers.lua @@ -97,10 +97,10 @@ function UEHelpers.GetWorld() end local GameViewportClient = UEHelpers.GetGameViewportClient() if GameViewportClient:IsValid() then - WorldCache = GameViewportClient.World + WorldCache = GameViewportClient.World return WorldCache end - + return RemoteObject:new() ---@type UWorld end From c824c42912c830fadce54c20e686dfed20a6d4b6 Mon Sep 17 00:00:00 2001 From: igromanru Date: Sat, 7 Sep 2024 19:54:42 +0200 Subject: [PATCH 08/23] feat: UEHelpers: Add GetGameInstance() function feat: UEHelpers: Add GetPlayer() function feat: UEHelpers: Rework GetPlayerController() to get the player controller from first/main local player --- assets/Mods/shared/UEHelpers/UEHelpers.lua | 62 ++++++++++++++-------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/assets/Mods/shared/UEHelpers/UEHelpers.lua b/assets/Mods/shared/UEHelpers/UEHelpers.lua index 671edac10..ee8dcc6d0 100644 --- a/assets/Mods/shared/UEHelpers/UEHelpers.lua +++ b/assets/Mods/shared/UEHelpers/UEHelpers.lua @@ -43,29 +43,8 @@ function UEHelpers.GetUEHelpersVersion() return Version end -local PlayerControllerCache = RemoteObject:new() ---@cast PlayerControllerCache APlayerController ---- Returns the first valid PlayerController that is currently controlled by a player. ----@return APlayerController -function UEHelpers.GetPlayerController() - if PlayerControllerCache:IsValid() then return PlayerControllerCache end - -- local PlayerControllers = jsb.simpleBench("findallof", FindAllOf, "Controller") - -- Uncomment line above and comment line below to profile this function - local PlayerControllers = FindAllOf("PlayerController") ---@type APlayerController[]? - if not PlayerControllers or #PlayerControllers < 1 then - print("GetPlayerController: No PlayerControllers were found\n") - return RemoteObject:new() ---@type APlayerController - end - for _, Controller in ipairs(PlayerControllers) do - if Controller.Pawn:IsValid() and Controller.Pawn:IsPlayerControlled() then - PlayerControllerCache = Controller - break - end - end - return PlayerControllerCache -end - local EngineCache = RemoteObject:new() ---@cast EngineCache UEngine ----Returns first valid instance of UEngine +---Returns instance of UEngine ---@return UEngine function UEHelpers.GetEngine() if EngineCache:IsValid() then return EngineCache end @@ -74,6 +53,16 @@ function UEHelpers.GetEngine() return EngineCache end +local GameInstanceCache = RemoteObject:new() ---@cast GameInstanceCache UGameInstance +---Returns instance of UGameInstance +---@return UGameInstance +function UEHelpers.GetGameInstance() + if GameInstanceCache:IsValid() then return GameInstanceCache end + + GameInstanceCache = FindFirstOf("GameInstance") ---@type UGameInstance + return GameInstanceCache +end + --- Returns the main UGameViewportClient ---@return UGameViewportClient function UEHelpers.GetGameViewportClient() @@ -84,6 +73,33 @@ function UEHelpers.GetGameViewportClient() return RemoteObject:new() ---@type UGameViewportClient end +local PlayerControllerCache = RemoteObject:new() ---@cast PlayerControllerCache APlayerController +---Returns first local player controller +---@return APlayerController +function UEHelpers.GetPlayerController() + if PlayerControllerCache:IsValid() then return PlayerControllerCache end + + local GameInstance = UEHelpers.GetGameInstance() + if GameInstance:IsValid() and #GameInstance.LocalPlayers > 0 then + local localPlayer = GameInstance.LocalPlayers[1] + if localPlayer:IsValid() then + PlayerControllerCache = localPlayer.PlayerController + end + end + + return PlayerControllerCache +end + +---Returns local player pawn +---@return APawn +function UEHelpers.GetPlayer() + local playerController = UEHelpers.GetPlayerController() + if playerController:IsValid() then + return playerController.Pawn + end + return RemoteObject:new() ---@type APawn +end + local WorldCache = RemoteObject:new() ---@cast WorldCache UWorld --- Returns the main UWorld ---@return UWorld @@ -129,7 +145,7 @@ end ---@return UKismetMathLibrary function UEHelpers.GetKismetMathLibrary(ForceInvalidateCache) ---@type UKismetMathLibrary - return CacheDefaultObject("/Script/Engine.Default__KismetMathLibrary", "UEHelpers_KismetMathLibrary", ForceInvalidateCache) + return CacheDefaultObject("/Script/Engine.Default__KismetMathLibrary", "UEHelpers_KismetMathLibrary", ForceInvalidateCache) end ---@param ForceInvalidateCache boolean # Force update the cache From a82bd5438cae2f2f7c7ac98472ee6956eceb4892 Mon Sep 17 00:00:00 2001 From: igromanru Date: Sun, 8 Sep 2024 09:59:51 +0200 Subject: [PATCH 09/23] feat: UEHelpers: Rework GetPlayerController() to search through all AController for first PlayerController feat: UEHelpers: Remove fallback to GameViewportClient in GetWorld(), since the new GetPlayerController() should always return a valid controller --- assets/Mods/shared/UEHelpers/UEHelpers.lua | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/assets/Mods/shared/UEHelpers/UEHelpers.lua b/assets/Mods/shared/UEHelpers/UEHelpers.lua index ee8dcc6d0..15afdf546 100644 --- a/assets/Mods/shared/UEHelpers/UEHelpers.lua +++ b/assets/Mods/shared/UEHelpers/UEHelpers.lua @@ -79,11 +79,13 @@ local PlayerControllerCache = RemoteObject:new() ---@cast PlayerControllerCache function UEHelpers.GetPlayerController() if PlayerControllerCache:IsValid() then return PlayerControllerCache end - local GameInstance = UEHelpers.GetGameInstance() - if GameInstance:IsValid() and #GameInstance.LocalPlayers > 0 then - local localPlayer = GameInstance.LocalPlayers[1] - if localPlayer:IsValid() then - PlayerControllerCache = localPlayer.PlayerController + local Controllers = FindAllOf("Controller") ---@type AController[]? + if Controllers then + for _, Controller in ipairs(Controllers) do + if Controller:IsValid() and Controller:IsPlayerController() then + PlayerControllerCache = Controller + break + end end end @@ -111,13 +113,8 @@ function UEHelpers.GetWorld() WorldCache = PlayerController:GetWorld() return WorldCache end - local GameViewportClient = UEHelpers.GetGameViewportClient() - if GameViewportClient:IsValid() then - WorldCache = GameViewportClient.World - return WorldCache - end - return RemoteObject:new() ---@type UWorld + return WorldCache end --- Returns an object that's useable with UFunctions that have a WorldContextObject param. From 6a23b680e96a98df378ddb425cf98d006dbe00e9 Mon Sep 17 00:00:00 2001 From: igromanru Date: Sun, 8 Sep 2024 11:23:51 +0200 Subject: [PATCH 10/23] feat: UEHelpers: - Revert GetWorldContextObject() back to use PlayerController, since now it should always exists - Add GetPersistentLevel() and GetWorldSettings() functions - Add GetActorFromHitResult(HitResult) function --- assets/Mods/shared/UEHelpers/UEHelpers.lua | 36 +++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/assets/Mods/shared/UEHelpers/UEHelpers.lua b/assets/Mods/shared/UEHelpers/UEHelpers.lua index 15afdf546..acdb74971 100644 --- a/assets/Mods/shared/UEHelpers/UEHelpers.lua +++ b/assets/Mods/shared/UEHelpers/UEHelpers.lua @@ -117,11 +117,45 @@ function UEHelpers.GetWorld() return WorldCache end +---Returns UWorld->PersistentLevel +---@return ULevel +function GetPersistentLevel() + local World = UEHelpers.GetWorld() + if World:IsValid() and World.PersistentLevel:IsValid() then + return World.PersistentLevel + end + return RemoteObject:new() ---@type ULevel +end + +---Returns PersistentLevel->WorldSettings +---@return AWorldSettings +function GetWorldSettings() + local PersistentLevel = UEHelpers.GetPersistentLevel() + if PersistentLevel:IsValid() and PersistentLevel.WorldSettings:IsValid() then + return PersistentLevel.WorldSettings + end + return RemoteObject:new() ---@type AWorldSettings +end + --- Returns an object that's useable with UFunctions that have a WorldContextObject param. --- Prefer to use an actor that you already have access to whenever possible over this function. ---@return UObject function UEHelpers.GetWorldContextObject() - return UEHelpers.GetWorld() + return UEHelpers.GetPlayerController() +end + +---Returns hit actor from FHitResult, it handles the struct differance between UE4 and UE5 +---@param HitResult FHitResult +---@return AActor +function UEHelpers.GetActorFromHitResult(HitResult) + if not HitResult or not HitResult:IsValid() then + return RemoteObject:new() ---@type AActor + end + + if UnrealVersion:IsBelow(5, 0) then + return HitResult.Actor:Get() + end + return HitResult.HitObjectHandle.Actor:Get() end ---@param ForceInvalidateCache boolean # Force update the cache From b9e1825dc55b62825d612264f10d2028425d2e82 Mon Sep 17 00:00:00 2001 From: igromanru Date: Sun, 8 Sep 2024 13:26:50 +0200 Subject: [PATCH 11/23] fix: GetPersistentLevel() and GetWorldSettings() weren't UEHelpers functions --- assets/Mods/shared/UEHelpers/UEHelpers.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/Mods/shared/UEHelpers/UEHelpers.lua b/assets/Mods/shared/UEHelpers/UEHelpers.lua index acdb74971..fe89297a3 100644 --- a/assets/Mods/shared/UEHelpers/UEHelpers.lua +++ b/assets/Mods/shared/UEHelpers/UEHelpers.lua @@ -119,7 +119,7 @@ end ---Returns UWorld->PersistentLevel ---@return ULevel -function GetPersistentLevel() +function UEHelpers.GetPersistentLevel() local World = UEHelpers.GetWorld() if World:IsValid() and World.PersistentLevel:IsValid() then return World.PersistentLevel @@ -129,7 +129,7 @@ end ---Returns PersistentLevel->WorldSettings ---@return AWorldSettings -function GetWorldSettings() +function UEHelpers.GetWorldSettings() local PersistentLevel = UEHelpers.GetPersistentLevel() if PersistentLevel:IsValid() and PersistentLevel.WorldSettings:IsValid() then return PersistentLevel.WorldSettings From 1379728e72fe7ca5e8dbae18bffa1c390a76b84e Mon Sep 17 00:00:00 2001 From: igromanru Date: Sun, 8 Sep 2024 14:48:51 +0200 Subject: [PATCH 12/23] feat: UEHelpers: Add and annotate FName utility functions to Find, Add a FName or get a "None" FName --- assets/Mods/shared/UEHelpers/UEHelpers.lua | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/assets/Mods/shared/UEHelpers/UEHelpers.lua b/assets/Mods/shared/UEHelpers/UEHelpers.lua index fe89297a3..33e6104da 100644 --- a/assets/Mods/shared/UEHelpers/UEHelpers.lua +++ b/assets/Mods/shared/UEHelpers/UEHelpers.lua @@ -200,6 +200,29 @@ function UEHelpers.GetGameMapsSettings(ForceInvalidateCache) return CacheDefaultObject("/Script/EngineSettings.Default__GameMapsSettings", "UEHelpers_GameMapsSettings", ForceInvalidateCache) end +---Returns Empty FName aka. "None" +---@return FName +function UEHelpers.FName_None() + return NAME_None +end + +---Returns found FName or "None" FName if the operation faled +---@param Name string +---@return FName +function UEHelpers.FindFName(Name) + return FName(Name, EFindName.FNAME_Find) +end + +---Returns added FName or "None" FName if the operation faled +---@param Name string +---@return FName +function UEHelpers.AddFName(Name) + return FName(Name, EFindName.FNAME_Add) +end + +---Tries to find existing FName, if it doesn't exist a new FName will be added to the pool +---@param Name string +---@return FName # Returns found or added FName, "None" FName if both operation failed function UEHelpers.FindOrAddFName(Name) local NameFound = FName(Name, EFindName.FNAME_Find) if NameFound == NAME_None then From 37965143ba8094672bc029a3ddeb311df639cf5a Mon Sep 17 00:00:00 2001 From: igromanru Date: Sun, 8 Sep 2024 17:45:43 +0200 Subject: [PATCH 13/23] docs: UEHelpers: Added annoations to CacheDefaultObject function and annotated ForceInvalidateCache parameter as optional feat: UEHelpers: Added functions GetGameModeBase() and GetGameStateBase() docs: Added changes from GH-650 to the Changelog --- assets/Changelog.md | 21 +++++++++++ assets/Mods/shared/UEHelpers/UEHelpers.lua | 43 ++++++++++++++++++---- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/assets/Changelog.md b/assets/Changelog.md index 21a3f6524..2d3c6a1a4 100644 --- a/assets/Changelog.md +++ b/assets/Changelog.md @@ -21,6 +21,17 @@ Added search filter: `IncludeClassNames`. ([UE4SS #472](https://github.com/UE4SS ### UHT Dumper ### Lua API +#### UEHelpers [PR #650](https://github.com/UE4SS-RE/RE-UE4SS/pull/650) +- Added local class `RemoteObject` with method `IsValid`. A new instance of the class should be used as return value in all UEHelpers functions instead of nil +- Added function `GetPlayer` which is just a fast way to get player controlled Pawn (the majority of the time it will be the player character) +- Added functions: `GetEngine`, `GetGameInstance`, `GetGameViewportClient`, `GetGameModeBase`, `GetGameStateBase`,`GetPersistentLevel` and `GetWorldSettings` +- Added functions to get static objects: `GetKismetStringLibrary`, `GetKismetTextLibrary` +- Added function `GetActorFromHitResult` which extracts the hit actor from a `FHitResult` struct based on UE's version +- Added FName utility functions: + - `FName_None`: returns a `None` FName (FName with ComparisonIndex = 0) + - `FindFName`: wrapper for `FName(Name, EFindName.FNAME_Find)` + - `AddFName`: wrapper for `FName(Name, EFindName.FNAME_Add)` +- Added [Lua Server Annotations](https://luals.github.io/wiki/annotations/) to all UEHelpers functions ### C++ API Key binds created with `UE4SSProgram::register_keydown_event` end up being duplicated upon mod hot-reload. @@ -63,6 +74,14 @@ The following search filters now allow multiple values, with each value separate The callback of `NotifyOnNewObject` can now optionally return `true` to unregister itself ([UE4SS #432](https://github.com/UE4SS-RE/RE-UE4SS/pull/432)) - Lyrth +#### UEHelpers [UE4SS #650](https://github.com/UE4SS-RE/RE-UE4SS/pull/650) +- Increased version to 3 +- Reworked all UEHelpers functions to ensure that they always return an object which can be checked with the function `IsValid` for validation +- Reworked `UEHelpers.GetPlayerController` to return first valid player controller (It will now return a player controller even if it doesn't control a pawn at the time) +- Reworked `UEHelpers.GetWorld` function to use UWorld cache (UWorld usually never changes) +- Change `UEHelpers.GetWorldContextObject` function annotation to return `UObject`. (Any UObject with a GetWorld() function is a valid WorldContext) +- Removed duplicate function `UEHelpers.GetKismetMathLibrary` + ### C++ API ### BPModLoader @@ -110,6 +129,8 @@ Fixed crash when calling UFunctions that take one or more 'out' params of type T Fixed `RegisterProcessConsoleExecPostHook`. ([UE4SS #631](https://github.com/UE4SS-RE/RE-UE4SS/pull/631)) +Fixed `FindFirstOf` return type annotation in `Types.lua` to signal that the return value will never be nil. ([UE4SS #650](https://github.com/UE4SS-RE/RE-UE4SS/pull/650)) + ### C++ API Fixed a crash caused by a race condition enabled by C++ mods using `UE4SS_ENABLE_IMGUI` in their constructor ([UE4SS #481](https://github.com/UE4SS-RE/RE-UE4SS/pull/481)) diff --git a/assets/Mods/shared/UEHelpers/UEHelpers.lua b/assets/Mods/shared/UEHelpers/UEHelpers.lua index 33e6104da..aa3cc7e9d 100644 --- a/assets/Mods/shared/UEHelpers/UEHelpers.lua +++ b/assets/Mods/shared/UEHelpers/UEHelpers.lua @@ -21,8 +21,13 @@ function RemoteObject:IsValid() end -- Functions local to this module, do not attempt to use! -local CacheDefaultObject = function(ObjectFullName, VariableName, ForceInvalidateCache) - local DefaultObject = nil + +---@param ObjectFullName string +---@param VariableName string +---@param ForceInvalidateCache boolean? +---@return UObject +local function CacheDefaultObject(ObjectFullName, VariableName, ForceInvalidateCache) + local DefaultObject = RemoteObject:new() if not ForceInvalidateCache then DefaultObject = ModRef:GetSharedVariable(VariableName) @@ -127,6 +132,28 @@ function UEHelpers.GetPersistentLevel() return RemoteObject:new() ---@type ULevel end +---Returns UWorld->AuthorityGameMode
+---The function doesn't guarantee to be an AGameMode, as many games derive their own game states directly from AGameModeBase! +---@return AGameModeBase +function UEHelpers.GetGameModeBase() + local World = UEHelpers.GetWorld() + if World:IsValid() and World.AuthorityGameMode:IsValid() then + return World.AuthorityGameMode + end + return RemoteObject:new() ---@type AGameModeBase +end + +---Returns UWorld->GameState
+---The function doesn't guarantee to be an AGameState, as many games derive their own game states directly from AGameStateBase! +---@return AGameStateBase +function UEHelpers.GetGameStateBase() + local World = UEHelpers.GetWorld() + if World:IsValid() and World.GameState:IsValid() then + return World.GameState + end + return RemoteObject:new() ---@type AGameStateBase +end + ---Returns PersistentLevel->WorldSettings ---@return AWorldSettings function UEHelpers.GetWorldSettings() @@ -158,42 +185,42 @@ function UEHelpers.GetActorFromHitResult(HitResult) return HitResult.HitObjectHandle.Actor:Get() end ----@param ForceInvalidateCache boolean # Force update the cache +---@param ForceInvalidateCache boolean? # Force update the cache ---@return UGameplayStatics function UEHelpers.GetGameplayStatics(ForceInvalidateCache) ---@type UGameplayStatics return CacheDefaultObject("/Script/Engine.Default__GameplayStatics", "UEHelpers_GameplayStatics", ForceInvalidateCache) end ----@param ForceInvalidateCache boolean # Force update the cache +---@param ForceInvalidateCache boolean? # Force update the cache ---@return UKismetSystemLibrary function UEHelpers.GetKismetSystemLibrary(ForceInvalidateCache) ---@type UKismetSystemLibrary return CacheDefaultObject("/Script/Engine.Default__KismetSystemLibrary", "UEHelpers_KismetSystemLibrary", ForceInvalidateCache) end ----@param ForceInvalidateCache boolean # Force update the cache +---@param ForceInvalidateCache boolean? # Force update the cache ---@return UKismetMathLibrary function UEHelpers.GetKismetMathLibrary(ForceInvalidateCache) ---@type UKismetMathLibrary return CacheDefaultObject("/Script/Engine.Default__KismetMathLibrary", "UEHelpers_KismetMathLibrary", ForceInvalidateCache) end ----@param ForceInvalidateCache boolean # Force update the cache +---@param ForceInvalidateCache boolean? # Force update the cache ---@return UKismetStringLibrary function UEHelpers.GetKismetStringLibrary(ForceInvalidateCache) ---@type UKismetStringLibrary return CacheDefaultObject("/Script/Engine.Default__KismetStringLibrary", "UEHelpers_KismetStringLibrary", ForceInvalidateCache) end ----@param ForceInvalidateCache boolean # Force update the cache +---@param ForceInvalidateCache boolean? # Force update the cache ---@return UKismetTextLibrary function UEHelpers.GetKismetTextLibrary(ForceInvalidateCache) ---@type UKismetTextLibrary return CacheDefaultObject("/Script/Engine.Default__KismetTextLibrary", "UEHelpers_KismetTextLibrary", ForceInvalidateCache) end ----@param ForceInvalidateCache boolean # Force update the cache +---@param ForceInvalidateCache boolean? # Force update the cache ---@return UGameMapsSettings function UEHelpers.GetGameMapsSettings(ForceInvalidateCache) ---@type UGameMapsSettings From 32ca09395bd47933d3b547c7e9582b447de6d5de Mon Sep 17 00:00:00 2001 From: igromanru Date: Mon, 9 Sep 2024 08:38:09 +0200 Subject: [PATCH 14/23] docs: UEHelpers: Fix and improve comments --- assets/Mods/shared/UEHelpers/UEHelpers.lua | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/assets/Mods/shared/UEHelpers/UEHelpers.lua b/assets/Mods/shared/UEHelpers/UEHelpers.lua index aa3cc7e9d..a0aa0913e 100644 --- a/assets/Mods/shared/UEHelpers/UEHelpers.lua +++ b/assets/Mods/shared/UEHelpers/UEHelpers.lua @@ -5,6 +5,8 @@ local UEHelpers = {} -- Version 1 does not exist, we start at version 2 because the original version didn't have a version at all. local Version = 3 +-- Functions and classes local to this module, do not attempt to use! + ---Class that allows to create a blank RemoteObject ---@class RemoteObject local RemoteObject = {} @@ -20,8 +22,6 @@ function RemoteObject:IsValid() return false end --- Functions local to this module, do not attempt to use! - ---@param ObjectFullName string ---@param VariableName string ---@param ForceInvalidateCache boolean? @@ -68,7 +68,7 @@ function UEHelpers.GetGameInstance() return GameInstanceCache end ---- Returns the main UGameViewportClient +---Returns the main UGameViewportClient ---@return UGameViewportClient function UEHelpers.GetGameViewportClient() local Engine = UEHelpers.GetEngine() @@ -79,7 +79,7 @@ function UEHelpers.GetGameViewportClient() end local PlayerControllerCache = RemoteObject:new() ---@cast PlayerControllerCache APlayerController ----Returns first local player controller +---Returns first player controller ---@return APlayerController function UEHelpers.GetPlayerController() if PlayerControllerCache:IsValid() then return PlayerControllerCache end @@ -133,7 +133,7 @@ function UEHelpers.GetPersistentLevel() end ---Returns UWorld->AuthorityGameMode
----The function doesn't guarantee to be an AGameMode, as many games derive their own game states directly from AGameModeBase! +---The function doesn't guarantee it to be an AGameMode, as many games derive their own game modes directly from AGameModeBase! ---@return AGameModeBase function UEHelpers.GetGameModeBase() local World = UEHelpers.GetWorld() @@ -144,7 +144,7 @@ function UEHelpers.GetGameModeBase() end ---Returns UWorld->GameState
----The function doesn't guarantee to be an AGameState, as many games derive their own game states directly from AGameStateBase! +---The function doesn't guarantee it to be an AGameState, as many games derive their own game states directly from AGameStateBase! ---@return AGameStateBase function UEHelpers.GetGameStateBase() local World = UEHelpers.GetWorld() @@ -164,14 +164,16 @@ function UEHelpers.GetWorldSettings() return RemoteObject:new() ---@type AWorldSettings end ---- Returns an object that's useable with UFunctions that have a WorldContextObject param. +--- Returns an object that's useable with UFunctions that have a WorldContext parameter.
--- Prefer to use an actor that you already have access to whenever possible over this function. +--- Any UObject that has a GetWorld() function can be used as WorldContext. ---@return UObject function UEHelpers.GetWorldContextObject() return UEHelpers.GetPlayerController() end ----Returns hit actor from FHitResult, it handles the struct differance between UE4 and UE5 +---Returns hit actor from FHitResult.
+---The function handles the struct differance between UE4 and UE5 ---@param HitResult FHitResult ---@return AActor function UEHelpers.GetActorFromHitResult(HitResult) @@ -249,7 +251,7 @@ end ---Tries to find existing FName, if it doesn't exist a new FName will be added to the pool ---@param Name string ----@return FName # Returns found or added FName, "None" FName if both operation failed +---@return FName # Returns found or added FName, “None” FName if both operations fail function UEHelpers.FindOrAddFName(Name) local NameFound = FName(Name, EFindName.FNAME_Find) if NameFound == NAME_None then From 8eea5421ccfec58a514e5dede163f82ed01665f5 Mon Sep 17 00:00:00 2001 From: igromanru Date: Sat, 21 Sep 2024 08:36:35 +0200 Subject: [PATCH 15/23] docs: Add NAME_None as alias to Types.lua feat: Remove UEHelpers.FName_None() --- assets/Mods/shared/Types.lua | 2 ++ assets/Mods/shared/UEHelpers/UEHelpers.lua | 6 ------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/assets/Mods/shared/Types.lua b/assets/Mods/shared/Types.lua index d7f720b19..00781ed94 100644 --- a/assets/Mods/shared/Types.lua +++ b/assets/Mods/shared/Types.lua @@ -345,6 +345,8 @@ function UnregisterHook(UFunctionName, PreId, PostId) end ---@param Callback fun() function ExecuteInGameThread(Callback) end +---@alias NAME_None FName "None" FName + ---Returns the FName for this string/ComparisonIndex or the FName for "None" if the name doesn't exist ---@param Name string ---@return FName diff --git a/assets/Mods/shared/UEHelpers/UEHelpers.lua b/assets/Mods/shared/UEHelpers/UEHelpers.lua index a0aa0913e..0ea4d511b 100644 --- a/assets/Mods/shared/UEHelpers/UEHelpers.lua +++ b/assets/Mods/shared/UEHelpers/UEHelpers.lua @@ -229,12 +229,6 @@ function UEHelpers.GetGameMapsSettings(ForceInvalidateCache) return CacheDefaultObject("/Script/EngineSettings.Default__GameMapsSettings", "UEHelpers_GameMapsSettings", ForceInvalidateCache) end ----Returns Empty FName aka. "None" ----@return FName -function UEHelpers.FName_None() - return NAME_None -end - ---Returns found FName or "None" FName if the operation faled ---@param Name string ---@return FName From 846312a1b81985bfab2980c6871861f4e510549f Mon Sep 17 00:00:00 2001 From: igromanru Date: Sat, 21 Sep 2024 08:45:37 +0200 Subject: [PATCH 16/23] docs: Updated Changelog to match last commit --- assets/Changelog.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/Changelog.md b/assets/Changelog.md index 2d3c6a1a4..5351578eb 100644 --- a/assets/Changelog.md +++ b/assets/Changelog.md @@ -21,6 +21,9 @@ Added search filter: `IncludeClassNames`. ([UE4SS #472](https://github.com/UE4SS ### UHT Dumper ### Lua API + +Added `NAME_None` as alias in `Types.lua`. ([UE4SS #650](https://github.com/UE4SS-RE/RE-UE4SS/pull/650)) + #### UEHelpers [PR #650](https://github.com/UE4SS-RE/RE-UE4SS/pull/650) - Added local class `RemoteObject` with method `IsValid`. A new instance of the class should be used as return value in all UEHelpers functions instead of nil - Added function `GetPlayer` which is just a fast way to get player controlled Pawn (the majority of the time it will be the player character) @@ -28,7 +31,6 @@ Added search filter: `IncludeClassNames`. ([UE4SS #472](https://github.com/UE4SS - Added functions to get static objects: `GetKismetStringLibrary`, `GetKismetTextLibrary` - Added function `GetActorFromHitResult` which extracts the hit actor from a `FHitResult` struct based on UE's version - Added FName utility functions: - - `FName_None`: returns a `None` FName (FName with ComparisonIndex = 0) - `FindFName`: wrapper for `FName(Name, EFindName.FNAME_Find)` - `AddFName`: wrapper for `FName(Name, EFindName.FNAME_Add)` - Added [Lua Server Annotations](https://luals.github.io/wiki/annotations/) to all UEHelpers functions From dd4bc4bca6b3ab875cdd43ae9ddf2067b10c5259 Mon Sep 17 00:00:00 2001 From: igromanru Date: Sat, 21 Sep 2024 09:10:11 +0200 Subject: [PATCH 17/23] docs: Add NAME_None, EFindName and FName overloads with FindType parameters to Types.lua docs: Updated the Changelog.md --- assets/Changelog.md | 5 ++++- assets/Mods/shared/Types.lua | 26 ++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/assets/Changelog.md b/assets/Changelog.md index 5351578eb..64ab3073f 100644 --- a/assets/Changelog.md +++ b/assets/Changelog.md @@ -22,7 +22,10 @@ Added search filter: `IncludeClassNames`. ([UE4SS #472](https://github.com/UE4SS ### Lua API -Added `NAME_None` as alias in `Types.lua`. ([UE4SS #650](https://github.com/UE4SS-RE/RE-UE4SS/pull/650)) +#### Types.lua [PR #650](https://github.com/UE4SS-RE/RE-UE4SS/pull/650) +- Added `NAME_None` definition +- Added `EFindName` enum definition +- Added `FName` function overloads with FindType parameter #### UEHelpers [PR #650](https://github.com/UE4SS-RE/RE-UE4SS/pull/650) - Added local class `RemoteObject` with method `IsValid`. A new instance of the class should be used as return value in all UEHelpers functions instead of nil diff --git a/assets/Mods/shared/Types.lua b/assets/Mods/shared/Types.lua index 00781ed94..3f7410a06 100644 --- a/assets/Mods/shared/Types.lua +++ b/assets/Mods/shared/Types.lua @@ -277,7 +277,6 @@ EInternalObjectFlags = { ---@alias float number ---@alias double number - -- # Global Functions ---@param ObjectName string @@ -345,18 +344,37 @@ function UnregisterHook(UFunctionName, PreId, PostId) end ---@param Callback fun() function ExecuteInGameThread(Callback) end ----@alias NAME_None FName "None" FName +---FName with "None" as value +NAME_None = FName(0) + +---@enum EFindName +EFindName = { + FNAME_Find = 0, + FNAME_Add = 1 +} ----Returns the FName for this string/ComparisonIndex or the FName for "None" if the name doesn't exist +---Returns the FName for this string or the FName for "None" if the name doesn't exist ---@param Name string ---@return FName function FName(Name) end ----Returns the FName for this string/ComparisonIndex or the FName for "None" if the name doesn't exist +---Returns the FName for this ComparisonIndex or the FName for "None" if the name doesn't exist ---@param ComparisonIndex integer ---@return FName function FName(ComparisonIndex) end +---Finds or adds FName for the string, depending on FindType +---@param Name string +---@param FindType EFindName|integer # Find = 0, Add = 1 +---@return FName +function FName(Name, FindType) end + +---Finds or adds FName for the ComparisonIndex, depending on FindType +---@param ComparisonIndex integer +---@param FindType EFindName|integer # Find = 0, Add = 1 +---@return FName +function FName(ComparisonIndex, FindType) end + ---Attempts to construct a UObject of the passed UClass ---(>=4.26) Maps to https://docs.unrealengine.com/4.27/en-US/API/Runtime/CoreUObject/UObject/StaticConstructObject_Internal/1/ ---(<4.25) Maps to https://docs.unrealengine.com/4.27/en-US/API/Runtime/CoreUObject/UObject/StaticConstructObject_Internal/2/ From 23679f859f2cd668ae795dac26a6ddd528787581 Mon Sep 17 00:00:00 2001 From: igromanru Date: Sat, 21 Sep 2024 09:37:15 +0200 Subject: [PATCH 18/23] docs: Add "How to use your mod's directory as workspace" to "Using Custom Lua Bindings" documentation --- docs/guides/using-custom-lua-bindings.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/guides/using-custom-lua-bindings.md b/docs/guides/using-custom-lua-bindings.md index a8fc28b3f..eed4de954 100644 --- a/docs/guides/using-custom-lua-bindings.md +++ b/docs/guides/using-custom-lua-bindings.md @@ -28,8 +28,22 @@ When developing your Lua mods, the language server should automatically parse al } ``` -To get context sensitive information about the custom game types, you need to [annotate your code](https://emmylua.github.io/annotation.html). This is done by adding a comment above the function/class/object that you want to annotate. +### How to use your mod's directory as workspace +As alternative you can open just your mod's root directory as workspace. +In this case you need to add a `.luarc.json` with `"workspace.library"` entries containing a path to the "shared" folder and the "Scripts" directory of your mod. +Both paths can be relative. +**Example .luarc.json:** +```json +{ + "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json", + "workspace.maxPreload": 50000, + "workspace.preloadFileSize": 5000, + "workspace.library": ["../shared", "Scripts"] +} +``` +## Annotating +To get context sensitive information about the custom game types, you need to [annotate your code](https://emmylua.github.io/annotation.html) ([alternative documentation](https://luals.github.io/wiki/annotations/)). This is done by adding a comment above the function/class/object that you want to annotate. ## Example ```lua From 7c9a7382c817d47f45c15e9d6e113bcfc429d8b8 Mon Sep 17 00:00:00 2001 From: igromanru Date: Fri, 27 Sep 2024 14:47:25 +0200 Subject: [PATCH 19/23] Revert FindFirstOf changes, it will be done through PR #666 --- assets/Changelog.md | 2 -- assets/Mods/shared/Types.lua | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/assets/Changelog.md b/assets/Changelog.md index 64ab3073f..55d40a9bd 100644 --- a/assets/Changelog.md +++ b/assets/Changelog.md @@ -134,8 +134,6 @@ Fixed crash when calling UFunctions that take one or more 'out' params of type T Fixed `RegisterProcessConsoleExecPostHook`. ([UE4SS #631](https://github.com/UE4SS-RE/RE-UE4SS/pull/631)) -Fixed `FindFirstOf` return type annotation in `Types.lua` to signal that the return value will never be nil. ([UE4SS #650](https://github.com/UE4SS-RE/RE-UE4SS/pull/650)) - ### C++ API Fixed a crash caused by a race condition enabled by C++ mods using `UE4SS_ENABLE_IMGUI` in their constructor ([UE4SS #481](https://github.com/UE4SS-RE/RE-UE4SS/pull/481)) diff --git a/assets/Mods/shared/Types.lua b/assets/Mods/shared/Types.lua index 3f7410a06..ae95a8f4b 100644 --- a/assets/Mods/shared/Types.lua +++ b/assets/Mods/shared/Types.lua @@ -293,7 +293,7 @@ function StaticFindObject(Class, InOuter, ObjectName, ExactClass) end ---Find the first non-default instance of the supplied class name ---@param ShortClassName string Should only contains the class name itself without path info ----@return UObject +---@return UObject? function FindFirstOf(ShortClassName) end ---Find all non-default instances of the supplied class name From d491a337cc08a1a321df334fc83407324386f2db Mon Sep 17 00:00:00 2001 From: igromanru Date: Sat, 28 Sep 2024 11:00:10 +0200 Subject: [PATCH 20/23] feat: Replace RemoteObject:new() with CreateBlankObject() --- assets/Mods/shared/UEHelpers/UEHelpers.lua | 39 +++++++--------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/assets/Mods/shared/UEHelpers/UEHelpers.lua b/assets/Mods/shared/UEHelpers/UEHelpers.lua index 0ea4d511b..af3bc3292 100644 --- a/assets/Mods/shared/UEHelpers/UEHelpers.lua +++ b/assets/Mods/shared/UEHelpers/UEHelpers.lua @@ -7,27 +7,12 @@ local Version = 3 -- Functions and classes local to this module, do not attempt to use! ----Class that allows to create a blank RemoteObject ----@class RemoteObject -local RemoteObject = {} -RemoteObject.__index = RemoteObject - ----Creates a new instance of RemoteObject ----@return RemoteObject -function RemoteObject:new() - return setmetatable({}, RemoteObject) -end - -function RemoteObject:IsValid() - return false -end - ---@param ObjectFullName string ---@param VariableName string ---@param ForceInvalidateCache boolean? ---@return UObject local function CacheDefaultObject(ObjectFullName, VariableName, ForceInvalidateCache) - local DefaultObject = RemoteObject:new() + local DefaultObject = CreateBlankObject() if not ForceInvalidateCache then DefaultObject = ModRef:GetSharedVariable(VariableName) @@ -48,7 +33,7 @@ function UEHelpers.GetUEHelpersVersion() return Version end -local EngineCache = RemoteObject:new() ---@cast EngineCache UEngine +local EngineCache = CreateBlankObject() ---@cast EngineCache UEngine ---Returns instance of UEngine ---@return UEngine function UEHelpers.GetEngine() @@ -58,7 +43,7 @@ function UEHelpers.GetEngine() return EngineCache end -local GameInstanceCache = RemoteObject:new() ---@cast GameInstanceCache UGameInstance +local GameInstanceCache = CreateBlankObject() ---@cast GameInstanceCache UGameInstance ---Returns instance of UGameInstance ---@return UGameInstance function UEHelpers.GetGameInstance() @@ -75,10 +60,10 @@ function UEHelpers.GetGameViewportClient() if Engine:IsValid() then return Engine.GameViewport end - return RemoteObject:new() ---@type UGameViewportClient + return CreateBlankObject() ---@type UGameViewportClient end -local PlayerControllerCache = RemoteObject:new() ---@cast PlayerControllerCache APlayerController +local PlayerControllerCache = CreateBlankObject() ---@cast PlayerControllerCache APlayerController ---Returns first player controller ---@return APlayerController function UEHelpers.GetPlayerController() @@ -104,10 +89,10 @@ function UEHelpers.GetPlayer() if playerController:IsValid() then return playerController.Pawn end - return RemoteObject:new() ---@type APawn + return CreateBlankObject() ---@type APawn end -local WorldCache = RemoteObject:new() ---@cast WorldCache UWorld +local WorldCache = CreateBlankObject() ---@cast WorldCache UWorld --- Returns the main UWorld ---@return UWorld function UEHelpers.GetWorld() @@ -129,7 +114,7 @@ function UEHelpers.GetPersistentLevel() if World:IsValid() and World.PersistentLevel:IsValid() then return World.PersistentLevel end - return RemoteObject:new() ---@type ULevel + return CreateBlankObject() ---@type ULevel end ---Returns UWorld->AuthorityGameMode
@@ -140,7 +125,7 @@ function UEHelpers.GetGameModeBase() if World:IsValid() and World.AuthorityGameMode:IsValid() then return World.AuthorityGameMode end - return RemoteObject:new() ---@type AGameModeBase + return CreateBlankObject() ---@type AGameModeBase end ---Returns UWorld->GameState
@@ -151,7 +136,7 @@ function UEHelpers.GetGameStateBase() if World:IsValid() and World.GameState:IsValid() then return World.GameState end - return RemoteObject:new() ---@type AGameStateBase + return CreateBlankObject() ---@type AGameStateBase end ---Returns PersistentLevel->WorldSettings @@ -161,7 +146,7 @@ function UEHelpers.GetWorldSettings() if PersistentLevel:IsValid() and PersistentLevel.WorldSettings:IsValid() then return PersistentLevel.WorldSettings end - return RemoteObject:new() ---@type AWorldSettings + return CreateBlankObject() ---@type AWorldSettings end --- Returns an object that's useable with UFunctions that have a WorldContext parameter.
@@ -178,7 +163,7 @@ end ---@return AActor function UEHelpers.GetActorFromHitResult(HitResult) if not HitResult or not HitResult:IsValid() then - return RemoteObject:new() ---@type AActor + return CreateBlankObject() ---@type AActor end if UnrealVersion:IsBelow(5, 0) then From 058ed3ff48b109da9ab10a537e28b29996f59eb3 Mon Sep 17 00:00:00 2001 From: igromanru Date: Sat, 28 Sep 2024 11:02:19 +0200 Subject: [PATCH 21/23] docs: Remove UEHelpers->RemoteObject changes from Changelog.md since it was replaced by CreateBlankObject() --- assets/Changelog.md | 1 - 1 file changed, 1 deletion(-) diff --git a/assets/Changelog.md b/assets/Changelog.md index 55d40a9bd..3ded412d0 100644 --- a/assets/Changelog.md +++ b/assets/Changelog.md @@ -28,7 +28,6 @@ Added search filter: `IncludeClassNames`. ([UE4SS #472](https://github.com/UE4SS - Added `FName` function overloads with FindType parameter #### UEHelpers [PR #650](https://github.com/UE4SS-RE/RE-UE4SS/pull/650) -- Added local class `RemoteObject` with method `IsValid`. A new instance of the class should be used as return value in all UEHelpers functions instead of nil - Added function `GetPlayer` which is just a fast way to get player controlled Pawn (the majority of the time it will be the player character) - Added functions: `GetEngine`, `GetGameInstance`, `GetGameViewportClient`, `GetGameModeBase`, `GetGameStateBase`,`GetPersistentLevel` and `GetWorldSettings` - Added functions to get static objects: `GetKismetStringLibrary`, `GetKismetTextLibrary` From ef474af2a9269b8ec448c7b6a0446651d454cbb8 Mon Sep 17 00:00:00 2001 From: igromanru Date: Sun, 29 Sep 2024 00:37:06 +0200 Subject: [PATCH 22/23] feat: Update GetActorFromHitResult for UE v5.4 --- assets/Mods/shared/UEHelpers/UEHelpers.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/Mods/shared/UEHelpers/UEHelpers.lua b/assets/Mods/shared/UEHelpers/UEHelpers.lua index af3bc3292..8ec644e63 100644 --- a/assets/Mods/shared/UEHelpers/UEHelpers.lua +++ b/assets/Mods/shared/UEHelpers/UEHelpers.lua @@ -168,8 +168,10 @@ function UEHelpers.GetActorFromHitResult(HitResult) if UnrealVersion:IsBelow(5, 0) then return HitResult.Actor:Get() + elseif UnrealVersion:IsBelow(5, 4) then + return HitResult.HitObjectHandle.Actor:Get() end - return HitResult.HitObjectHandle.Actor:Get() + return HitResult.HitObjectHandle.ReferenceObject:Get() end ---@param ForceInvalidateCache boolean? # Force update the cache From 84afc760a5b4b52178016ea761299113c993dd55 Mon Sep 17 00:00:00 2001 From: igromanru Date: Tue, 1 Oct 2024 09:36:40 +0200 Subject: [PATCH 23/23] refactor: Replace CreateBlankObject() with CreateInvalidObject() --- assets/Mods/shared/UEHelpers/UEHelpers.lua | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/assets/Mods/shared/UEHelpers/UEHelpers.lua b/assets/Mods/shared/UEHelpers/UEHelpers.lua index 8ec644e63..c033c4858 100644 --- a/assets/Mods/shared/UEHelpers/UEHelpers.lua +++ b/assets/Mods/shared/UEHelpers/UEHelpers.lua @@ -12,7 +12,7 @@ local Version = 3 ---@param ForceInvalidateCache boolean? ---@return UObject local function CacheDefaultObject(ObjectFullName, VariableName, ForceInvalidateCache) - local DefaultObject = CreateBlankObject() + local DefaultObject = CreateInvalidObject() if not ForceInvalidateCache then DefaultObject = ModRef:GetSharedVariable(VariableName) @@ -33,7 +33,7 @@ function UEHelpers.GetUEHelpersVersion() return Version end -local EngineCache = CreateBlankObject() ---@cast EngineCache UEngine +local EngineCache = CreateInvalidObject() ---@cast EngineCache UEngine ---Returns instance of UEngine ---@return UEngine function UEHelpers.GetEngine() @@ -43,7 +43,7 @@ function UEHelpers.GetEngine() return EngineCache end -local GameInstanceCache = CreateBlankObject() ---@cast GameInstanceCache UGameInstance +local GameInstanceCache = CreateInvalidObject() ---@cast GameInstanceCache UGameInstance ---Returns instance of UGameInstance ---@return UGameInstance function UEHelpers.GetGameInstance() @@ -60,10 +60,10 @@ function UEHelpers.GetGameViewportClient() if Engine:IsValid() then return Engine.GameViewport end - return CreateBlankObject() ---@type UGameViewportClient + return CreateInvalidObject() ---@type UGameViewportClient end -local PlayerControllerCache = CreateBlankObject() ---@cast PlayerControllerCache APlayerController +local PlayerControllerCache = CreateInvalidObject() ---@cast PlayerControllerCache APlayerController ---Returns first player controller ---@return APlayerController function UEHelpers.GetPlayerController() @@ -89,10 +89,10 @@ function UEHelpers.GetPlayer() if playerController:IsValid() then return playerController.Pawn end - return CreateBlankObject() ---@type APawn + return CreateInvalidObject() ---@type APawn end -local WorldCache = CreateBlankObject() ---@cast WorldCache UWorld +local WorldCache = CreateInvalidObject() ---@cast WorldCache UWorld --- Returns the main UWorld ---@return UWorld function UEHelpers.GetWorld() @@ -114,7 +114,7 @@ function UEHelpers.GetPersistentLevel() if World:IsValid() and World.PersistentLevel:IsValid() then return World.PersistentLevel end - return CreateBlankObject() ---@type ULevel + return CreateInvalidObject() ---@type ULevel end ---Returns UWorld->AuthorityGameMode
@@ -125,7 +125,7 @@ function UEHelpers.GetGameModeBase() if World:IsValid() and World.AuthorityGameMode:IsValid() then return World.AuthorityGameMode end - return CreateBlankObject() ---@type AGameModeBase + return CreateInvalidObject() ---@type AGameModeBase end ---Returns UWorld->GameState
@@ -136,7 +136,7 @@ function UEHelpers.GetGameStateBase() if World:IsValid() and World.GameState:IsValid() then return World.GameState end - return CreateBlankObject() ---@type AGameStateBase + return CreateInvalidObject() ---@type AGameStateBase end ---Returns PersistentLevel->WorldSettings @@ -146,7 +146,7 @@ function UEHelpers.GetWorldSettings() if PersistentLevel:IsValid() and PersistentLevel.WorldSettings:IsValid() then return PersistentLevel.WorldSettings end - return CreateBlankObject() ---@type AWorldSettings + return CreateInvalidObject() ---@type AWorldSettings end --- Returns an object that's useable with UFunctions that have a WorldContext parameter.
@@ -163,7 +163,7 @@ end ---@return AActor function UEHelpers.GetActorFromHitResult(HitResult) if not HitResult or not HitResult:IsValid() then - return CreateBlankObject() ---@type AActor + return CreateInvalidObject() ---@type AActor end if UnrealVersion:IsBelow(5, 0) then