Last active
March 15, 2024 10:27
-
-
Save geordiemhall/5bd5ad4737262cf9433422590f7fb227 to your computer and use it in GitHub Desktop.
UE4 Cheat manager with meta-based cheat names
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "MyCheatManager.h" | |
#include "Engine/Console.h" | |
#include "AssetRegistryModule.h" | |
DEFINE_LOG_CATEGORY(LogCheatManager); | |
void UMyCheatManager::AddWorkers(int32 NumWorkers) | |
{ | |
UE_LOG(LogCheatManager, Log, TEXT("%s: AddWorkers: %i"), *GetName(), NumWorkers); | |
} | |
void UMyCheatManager::RemoveWorkers(int32 NumWorkers) | |
{ | |
UE_LOG(LogCheatManager, Log, TEXT("%s: RemoveWorkers: %i"), *GetName(), NumWorkers); | |
} | |
void UMyCheatManager::ResetWorkers() | |
{ | |
UE_LOG(LogCheatManager, Log, TEXT("%s: ResetWorkers"), *GetName()); | |
} | |
static int32 NumAutoCompletesRegistered = 0; | |
static const FString TagNamespace = TEXT("CheatFunc:"); | |
void UMyCheatManager::InitCheatManager() | |
{ | |
Super::InitCheatManager(); | |
if (NumAutoCompletesRegistered <= 0) | |
{ | |
UE_LOG(LogCheatManager, Verbose, TEXT("Registering cheat manager for autocomplete callbacks: %s"), *GetName()); | |
UConsole::RegisterConsoleAutoCompleteEntries.AddUObject(this, &UMyCheatManager::RegisterAutoCompleteEntries); | |
NumAutoCompletesRegistered += 1; | |
} | |
else | |
{ | |
UE_LOG(LogCheatManager, Verbose, TEXT("Didn't need to register cheat manager for autocomplete callbacks: %s"), *GetName()); | |
} | |
} | |
void UMyCheatManager::BeginDestroy() | |
{ | |
int32 NumRemoved = UConsole::RegisterConsoleAutoCompleteEntries.RemoveAll(this); | |
NumAutoCompletesRegistered -= NumRemoved; | |
Super::BeginDestroy(); | |
} | |
bool UMyCheatManager::ProcessConsoleExec(const TCHAR* Cmd, FOutputDevice& Ar, UObject* Executor) | |
{ | |
FString OriginalCmd = Cmd; | |
FString CommandName; | |
if (FParse::Token(Cmd, CommandName, true)) | |
{ | |
FAssetData Asset; | |
if (GetAssetData(GetClass(), Asset)) | |
{ | |
const FName CommandNameTag(TagNamespace + CommandName); | |
FAssetDataTagMapSharedView::FFindTagResult Result = Asset.TagsAndValues.FindTag(CommandNameTag); | |
if (Result.IsSet()) | |
{ | |
// Cmd will now just be the arguments since we parsed the command name out of it | |
const FString CmdString = Result.GetValue() + Cmd; | |
return CallFunctionByNameWithArguments(*CmdString, Ar, Executor, /* bForceCallWithNonExec */ true); | |
} | |
} | |
} | |
return CallFunctionByNameWithArguments(*OriginalCmd, Ar, Executor); | |
} | |
void UMyCheatManager::GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const | |
{ | |
Super::GetAssetRegistryTags(OutTags); | |
#if WITH_EDITOR | |
const UClass* ClassToSearch = UMyCheatManager::StaticClass(); | |
// Grab the overall class prefix | |
static const FName CheatPrefixMetaKey = TEXT("CheatPrefix"); | |
const FString ClassCheatPrefix = ClassToSearch->GetMetaData(CheatPrefixMetaKey); | |
// Find any functions with the Cheat meta (haven't bothered doing any duplicate detection or other validation) | |
TArray<FName> FunctionNames; | |
ClassToSearch->GenerateFunctionList(FunctionNames); | |
for (const FName& Name : FunctionNames) | |
{ | |
const UFunction* Func = ClassToSearch->FindFunctionByName(Name); | |
static const FName CheatNameMetaKey = TEXT("Cheat"); | |
if (!Func || !Func->HasMetaData(CheatNameMetaKey)) | |
{ | |
continue; | |
} | |
FString CheatName = Func->GetMetaData(CheatNameMetaKey); | |
// If they didn't provide a custom cheat name, then just default to the function name | |
if (CheatName.IsEmpty()) | |
{ | |
CheatName = Name.ToString(); | |
} | |
// Prefix our tags with a namespace to help avoid conflicts | |
FString CommandNameTag = TagNamespace + ClassCheatPrefix + CheatName; | |
FAssetRegistryTag CommandTag(FName(CommandNameTag), Name.ToString(), FAssetRegistryTag::TT_Hidden); | |
OutTags.Add(CommandTag); | |
} | |
#endif | |
} | |
void UMyCheatManager::RegisterAutoCompleteEntries(TArray<FAutoCompleteCommand>& Commands) const | |
{ | |
const UClass* ClassToSearch = GetClass(); | |
FAssetData Asset; | |
if (!GetAssetData(ClassToSearch, Asset)) | |
{ | |
return; | |
} | |
for (const auto& Pair : Asset.TagsAndValues.GetMap()) | |
{ | |
const FString TagName = Pair.Key.ToString(); | |
if (!TagName.StartsWith(TagNamespace)) | |
{ | |
continue; | |
} | |
const FName FunctionName(Pair.Value); | |
const UFunction* Func = ClassToSearch->FindFunctionByName(FunctionName); | |
if (Func == nullptr) | |
{ | |
continue; | |
} | |
// Build help text for each param (matches what the default autocomplete does in UConsole::BuildRuntimeAutoCompleteList) | |
FString Desc; | |
for (TFieldIterator<FProperty> PropIt(Func); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt) | |
{ | |
FProperty* Prop = *PropIt; | |
Desc += FString::Printf(TEXT("%s[%s] "), *Prop->GetName(), *Prop->GetCPPType()); | |
} | |
FAutoCompleteCommand Entry; | |
Entry.Command = TagName.Mid(TagNamespace.Len()); | |
Entry.Desc = Desc; | |
Commands.Add(Entry); | |
} | |
} | |
bool UMyCheatManager::GetAssetData(const UClass* ForClass, FAssetData& AssetData) const | |
{ | |
const FString PackagePath = FPackageName::ObjectPathToPackageName(ForClass->GetPathName()); | |
TArray<FAssetData> Assets; | |
FAssetRegistryModule::GetRegistry().GetAssetsByPackageName(FName(PackagePath), Assets); | |
if (Assets.Num() == 0) | |
{ | |
UE_LOG(LogCheatManager, Warning, TEXT("Could not find any asset data for package %s, perhaps this isn't a Blueprint subclass?"), *PackagePath); | |
return false; | |
} | |
AssetData = Assets[0]; | |
return true; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "CoreMinimal.h" | |
#include "ConsoleSettings.h" | |
#include "GameFramework/CheatManager.h" | |
#include "MyCheatManager.generated.h" | |
DECLARE_LOG_CATEGORY_EXTERN(LogCheatManager, Log, All); | |
/** | |
* Example of a cheat manager that lets you more easily customise the command names | |
* Note: To use these Cheat meta methods you must make a BP subclass of this, | |
* then use _that_ as the CheatManager class in your PlayerController, | |
* since only assets can have data stored about them in the asset registry | |
*/ | |
UCLASS(meta = (CheatPrefix = "mygame.")) | |
class YOURPROJECT_API UMyCheatManager : public UCheatManager | |
{ | |
GENERATED_BODY() | |
public: | |
UFUNCTION(meta = (Cheat = "workers.add")) | |
void AddWorkers(int32 NumWorkers = 1); | |
UFUNCTION(meta = (Cheat = "workers.remove")) | |
void RemoveWorkers(int32 NumWorkers = 1); | |
UFUNCTION(meta = (Cheat = "workers.reset")) | |
void ResetWorkers(); | |
public: | |
virtual void InitCheatManager() override; | |
virtual void BeginDestroy() override; | |
virtual bool ProcessConsoleExec(const TCHAR* Cmd, FOutputDevice& Ar, UObject* Executor) override; | |
virtual void GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const override; | |
private: | |
void RegisterAutoCompleteEntries(TArray<FAutoCompleteCommand>& Commands) const; | |
bool GetAssetData(const UClass* ForClass, FAssetData& AssetData) const; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey there! I found your gist really helpful when
OverrideNativeName
stopped working in Unreal Engine 5. I wanted to suggest some changes, but gists are a bit tricky for that.I bumped into a couple of things:
To address these issues, I've created a plugin that puts the meta cheat names in an .ini file instead of the Asset Registry. It's super easy to use: just download it from my GitHub page and drop it into your game's Plugins folder.
If you have any ideas or improvements in mind, I'd greatly appreciate your feedback and contributions.