c++
FString dinoNamefieldClassName;
dino->DinoNameTagField().ToString(&dinoNamefieldClassName);
Log::GetLog()->info("NAME: {}", dinoNamefieldClassName.ToString());
Genesis 2 - Cuddle Plugin
"Maewing":{
"CuddleIntervalTime":30,
"CuddleIncreasePercent":0.5
},
"Lionfish Lion":{
"CuddleIntervalTime":30,
"CuddleIncreasePercent":0.5
},
"TekWyvern":{
"CuddleIntervalTime":30,
"CuddleIncreasePercent":0.5
},
"Astrodelphis":{
"CuddleIntervalTime":30,
"CuddleIncreasePercent":0.5
}
(edited)c++
TArray<unsigned short>actualCounts = currentItem->CraftingResourceRequirementsField();
This array has all the correct requirement values I was looking for. (edited)AStructurePreventionZoneVolume.AStructurePreventionZoneVolume
) and check it's location. For the range it covers you might be able to calculate the radius based on actor's scale (volumes inherit from AActor so they have location, rotation as any other actor)bool CanTame(AShooterPlayerController* ForPC, bool bIgnoreMaxTamedDinos);
void BPUntamedConsumeFoodItem(UPrimalItem* foodItem);
AShooterCharacter* ConsumeInventoryFoodItem(UPrimalItem* foodItem, float* AffinityIncrease, bool bDontDecrementItem, float* FoodIncrease, float FoodAmountMultiplier, bool bConsumeEntireStack);
But I don't know how to proceed and where to start exactly. Also there's a lot of functions related to food affinity and I think I will have to override all of them if I want max affinity for Elements (edited)APrimalDinoCharacter::GetFirstAffinityFoodItemClass
but I have some errors : #define _DWORD uint32
#define _QWORD uint64
DECLARE_HOOK(APrimalDinoCharacter_GetFirstAffinityFoodItemClass, TSubclassOf<UPrimalItem>*, APrimalDinoCharacter*, TSubclassOf<UPrimalItem>*);
TSubclassOf<UPrimalItem>* Hook_APrimalDinoCharacter_GetFirstAffinityFoodItemClass(APrimalDinoCharacter* _this, TSubclassOf<UPrimalItem>* result)
{
UClass* v2;
int i;
UStruct** v5;
UStruct* v6;
APrimalDinoCharacter* v7;
TSubclassOf<UPrimalItem>* v8;
v8 = result;
v7 = _this;
if (*((_QWORD*)_this + 806))
{
for (i = 0; i < *(_DWORD*)(*((_QWORD*)v7 + 806) + 48i64); ++i)
{
if (*(float*)(*(_QWORD*)(*((_QWORD*)v7 + 806) + 40i64) + 48i64 * i + 12) > 0.0)
{
v5 = (UStruct**)(*(_QWORD*)(*((_QWORD*)v7 + 806) + 40i64) + 48i64 * i + 32);
if (*v5 && (v2 = UPrimalItem::StaticClass(), UStruct::IsChildOf(*v5, (UStruct*)&v2->vfptr))) // ERROR HERE
v6 = *v5;
else
v6 = 0i64;
if (v6)
{
v8->uClass = *(UClass**)(*(_QWORD*)(*((_QWORD*)v7 + 806) + 40i64) + 48i64 * i + 32);
return v8;
}
}
}
}
v8->uClass = 0i64;
return v8;
//return APrimalDinoCharacter_GetFirstAffinityFoodItemClass_original(_this, result);
}
(edited)Blueprint'/Game/Genesis2/Dinos/TekWyvern/DinoSettings_Carnivore_Huge_TekWyvern.DinoSettings_Carnivore_Huge_TekWyvern'
void Load()
{
Log::Get().Init("Loading mod...");
// Get item class
FString tekWyvernSettingsPath = "Blueprint'/Game/Genesis2/Dinos/TekWyvern/DinoSettings_Carnivore_Huge_TekWyvern.DinoSettings_Carnivore_Huge_TekWyvern'";
UClass* tekWyvernSettingsClass = UVictoryCore::BPLoadClass(&tekWyvernSettingsPath);
if (tekWyvernSettingsClass != nullptr)
{
UObject* tekWyvernSettingsObj = tekWyvernSettingsClass->GetDefaultObject(true);
if (tekWyvernSettingsObj != nullptr)
{
UPrimalDinoSettings* tekWyvernSettings = (UPrimalDinoSettings*)tekWyvernSettingsObj;
tekWyvernSettings->FoodEffectivenessMultipliersField(); // ERROR here
}
}
Log::GetLog()->info("Loaded mod.");
}
(edited)BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Load();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
Unload();
break;
}
return TRUE;
}
struct UPrimalDinoSettings : UObject
{
TArray<FDinoFoodEffectivenessMultipliers,FDefaultAllocator> FoodEffectivenessMultipliers;
TArray<FDinoFoodEffectivenessMultipliers,FDefaultAllocator> ExtraFoodEffectivenessMultipliers;
float TamingAffinityNoFoodDecreasePercentageSpeed;
TArray<FDamageTypeAdjuster,FDefaultAllocator> BaseDamageTypeAdjusters;
TArray<FDamageTypeAdjuster,FDefaultAllocator> ExtraDamageTypeAdjusters;
UTexture2D *DinoFoodTypeImage;
FString DinoFoodTypeName;
bool bWakingTameDisplayItemName;
};
FDinoFoodEffectivenessMultipliers
is defined either, I'd take a guess and say it isn'tstruct FDinoFoodEffectivenessMultipliers
{
float FoodEffectivenessMultiplier;
float HealthEffectivenessMultiplier;
float TorpidityEffectivenessMultiplier;
float AffinityEffectivenessMultiplier;
float AffinityOverride;
float StaminaEffectivenessMultiplier;
int FoodItemCategory;
TSubclassOf<UPrimalItem> FoodItemParent;
float UntamedFoodConsumptionPriority;
};
struct FDamageTypeAdjuster
{
TSubclassOf<UDamageType> DamageTypeClass;
float DamageMultiplier;
unsigned __int32 bIgnoreMultiplierIfWildDinoAttacker : 1;
unsigned __int32 bIgnoreMultiplierIfTamedDinoAttacker : 1;
unsigned __int32 bOnlyUseMultiplierIfWildDinoAttacker : 1;
unsigned __int32 bOnlyUseMultiplierIfTamedDinoAttacker : 1;
unsigned __int32 bOnlyUseMultiplierIfTamed : 1;
};
void Load()
{
Log::Get().Init("Loading mod...");
// Get Voidwyrm default object
FString tekWyvernSettingsPath = "Blueprint'/Game/Genesis2/Dinos/TekWyvern/DinoSettings_Carnivore_Huge_TekWyvern.DinoSettings_Carnivore_Huge_TekWyvern'";
UClass* tekWyvernSettingsClass = UVictoryCore::BPLoadClass(&tekWyvernSettingsPath);
if (tekWyvernSettingsClass != nullptr)
{
UObject* tekWyvernSettingsObj = tekWyvernSettingsClass->GetDefaultObject(true);
if (tekWyvernSettingsObj != nullptr)
{
// Grab UPrimalItem for Element
FString elementBP = "Blueprint'/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_Element.PrimalItemResource_Element'";
UClass* elementBPUClass = UVictoryCore::BPLoadClass(&elementBP);
if (elementBPUClass != nullptr)
{
TSubclassOf<UPrimalItem> elementBPSubClass = TSubclassOf<UPrimalItem>(elementBPUClass);
// Prepare the new taming food type
FDinoFoodEffectivenessMultipliers foodType;
foodType.FoodEffectivenessMultiplier = 1.333F;
foodType.HealthEffectivenessMultiplier = 0.0F;
foodType.TorpidityEffectivenessMultiplier = 0.0F;
foodType.AffinityEffectivenessMultiplier = 1.0F;
foodType.AffinityOverride = 1000.0F;
foodType.StaminaEffectivenessMultiplier = 0.0F;
foodType.FoodItemCategory = 0;
foodType.FoodItemParent = elementBPSubClass;
foodType.UntamedFoodConsumptionPriority = 1.0F;
// Set the new taming food type
UPrimalDinoSettings* tekWyvernSettings = (UPrimalDinoSettings*)tekWyvernSettingsObj;
tekWyvernSettings->FoodEffectivenessMultipliers.Add(foodType);
}
}
}
Log::GetLog()->info("Loaded mod.");
}
API::Timer::Get().DelayExecute(&OverridePassiveTameFood, 0); // 0 Delay?
(edited)UPrimalDinoSettings
, FDinoFoodEffectivenessMultipliers
and FDamageTypeAdjuster
FString tekWyvernSettingsPath = "Blueprint'/Game/Genesis2/Dinos/TekWyvern/DinoSettings_Carnivore_Huge_TekWyvern.DinoSettings_Carnivore_Huge_TekWyvern'";
UClass* tekWyvernSettingsClass = UVictoryCore::BPLoadClass(&tekWyvernSettingsPath);
if (tekWyvernSettingsClass != nullptr)
{
UPrimalDinoSettings* tekWyvernSettings = (UPrimalDinoSettings*)tekWyvernSettingsClass->GetDefaultObject(true); // This cast seems incorrect, accessing tekWyvernSettings returns gibberish and eventually crashes server.
tekWyvernSettings->DinoFoodTypeName
I get 㘀躚ȕ
tekWyvernSettings->FoodEffectivenessMultipliers.Num()
gives me 6815769
TArray<FDinoFoodEffectivenessMultipliers>& FoodEffectivenessMultipliersField() { return *GetNativePointerField<TArray<FDinoFoodEffectivenessMultipliers>*>(this, "UPrimalDinoSettings.FoodEffectivenessMultipliers") };
(edited) tekWyvernSettings->FoodEffectivenessMultipliersField().Num()
returns 16
as expected HasBuffWithCustomTag
and if it's called with the gen2 lunar biome name return true[2021.11.24-21.51.33:840][964]2021.11.24_21.51.33: HOOK (TekWyvern_Character_BP_C_0)(Gen2SpaceBiomeBuff)
bool Hook_APrimalCharacter_HasBuffWithCustomTag(APrimalCharacter* _this, FName buffCustomTag)
{
if (_this->NameField().ToString().StartsWith("TekWyvern_Character_BP_C") && buffCustomTag.ToString().Equals("Gen2SpaceBiomeBuff"))
return true;
return APrimalCharacter_HasBuffWithCustomTag_original(_this, buffCustomTag);
}
Go to the bp file
, there's not a "Search in all BPs" option?FString lunarBuff = L"Blueprint'/Game/Genesis2/CoreBlueprints/Buffs/AreaBuffs/Gen2_AreaBuff_Space.Gen2_AreaBuff_Space_C'";
TSubclassOf<APrimalBuff> buffObj;
buffObj.uClass = UVictoryCore::BPLoadClass(&lunarBuff);
APrimalBuff* buff = APrimalBuff::StaticAddBuff(buffObj, _this, nullptr, nullptr, true);
(edited)FString lunarBuff = L"Blueprint'/Game/Genesis2/CoreBlueprints/Buffs/AreaBuffs/Gen2_AreaBuff_Space.Gen2_AreaBuff_Space_C'";
UVictoryCore::BPLoadClass(&lunarBuff); // Returns nullptr
I will try to grab the proper BP path with some logging on Gen2 now. (edited)BuffName(Gen2_AreaBuff_Space_C_452)
BuffFullName(Gen2_AreaBuff_Space_C /Game/Maps/Genesis2/Gen2.Gen2:PersistentLevel.Gen2_AreaBuff_Space_C_452)
BuffClassName(Gen2_AreaBuff_Space_C)
void DoTame(APrimalDinoCharacter* dino, AShooterCharacter* shooterCharacter)
{
int tribeId = ArkApi::GetApiUtils().GetTribeID(shooterCharacter);
dino->TamingTeamIDField() = tribeId;
dino->BPSetupTamed(true);
}
TameDino
functionint OverrideTamingTeamID
2nd argument when calling TameDino()
? @Pelayori
If I put 0
the taming team will be the one of PlayerController that I gave as 1st argument? // Backup player role
auto currentRole = playerController->RoleField();
// Elevate player role
playerController->RoleField() = TEnumAsByte<ENetRole>(ENetRole::ROLE_Authority);
// Do force tame
playerController->ForceTame(true, dino);
// Restore player role
playerController->RoleField() = currentRole;
with // Do tame
dino->TameDino(playerController, false, playerController->TargetingTeamField(), false, false, false);
(edited)Class /Game/...
)GUObjectArray()()
is not defined anymore for (auto i = 0; i < Globals::GUObjectArray()().ObjObjects.NumElements; i++)
{
auto obj = Globals::GUObjectArray()().ObjObjects.GetObjectPtr(i)->Object;
if (obj != nullptr)
{
FString fullName;
obj->GetFullName(&fullName, nullptr);
if (fullName.StartsWith("Class /Game/") && (fullName.Contains("sickness") || fullName.Contains("cryo") || fullName.Contains("buff")))
{
FString toLog = "TEST (" + fullName + ")";
shooterGameMode->PrintToServerGameLog(&toLog, true);
}
}
}
APrimalDinoCharacter_Die
when I kill another dino (not the one that has the cryosickness buff, this one is still alive and affected when the for loop gets called). (edited)ListMyBuffs
and admin gun in inspection mode:Blueprint'/Game/Extinction/CoreBlueprints/Buffs/Buff_SummoningSickness.Buff_SummoningSickness'
void ApplyCryosickness(APrimalDinoCharacter* dino)
{
FString classString = L"Blueprint'/Game/Extinction/CoreBlueprints/Buffs/Buff_SummoningSickness.Buff_SummoningSickness'";
UClass* classObj = UVictoryCore::BPLoadClass(&classString);
if (dino != nullptr && classObj != nullptr)
APrimalBuff::StaticAddBuff(TSubclassOf<APrimalBuff>(classObj), dino, nullptr, nullptr, false);
}
(edited)Woolly Rhino Horn
inside.
I would like to add another item inside the Woolly Rhino death inventory.
I've looked a bit at the APrimalDinoCharacter
(I can access it by hooking the Die() function), but I don't know how to interact with the DeathInventory itself. (edited)float chanceToUse = _this->DeathInventoryChanceToUseField();
std::string chanceToUseStr = std::to_string(chanceToUse);
float qualityPerLevel = _this->DeathInventoryQualityPerLevelMultiplierField();
std::string qualityPerLevelStr = std::to_string(qualityPerLevel);
std::string deathInventoryTemplatesStr = "Templates(";
FWeightedObjectList& deathInventoryTemplates = _this->DeathInventoryTemplatesField();
int len = deathInventoryTemplates.AssociatedObjects.Num();
int i = 0;
while (i < len)
{
UObject* currObj = deathInventoryTemplates.AssociatedObjects[i];
if (currObj != nullptr)
{
FString fullName = "";
currObj->GetFullName(&fullName, nullptr);
deathInventoryTemplatesStr += "Fullname=(" + fullName.ToString() + ")Class=(" + ArkApi::GetApiUtils().GetBlueprint(currObj).ToString() + ") ; ";
}
i++;
}
deathInventoryTemplatesStr += ")";
FString toLog = L"Rhino ChanceToUse=(" + FString(chanceToUseStr) + L") QualityPerLvl=(" + FString(qualityPerLevelStr) + L") " + FString(deathInventoryTemplatesStr);
ArkApi::GetApiUtils().GetShooterGameMode()->PrintToServerGameLog(&toLog, true);
(edited)[2021.11.30-01.53.49:760][298]2021.11.30_01.53.49: Rhino ChanceToUse=(1.000000) QualityPerLvl=(0.025000) Templates(Fullname=(Blueprint /Game/PrimalEarth/CoreBlueprints/Inventories/DinoDropInventoryComponent_Rhino.DinoDropInventoryComponent_Rhino)Class=(Blueprint'/Script/Engine.Bluepri') ; )
DinoDropInventoryComponent_Rhino
.deathInventoryTemplates.AssociatedObjects
in order to add my item, but I'm not sure and I don't know how to do it. (edited)UPrimalInventoryComponent* invComp = new UPrimalInventoryComponent();
if (invComp != nullptr)
{
_this->SetMyInventoryComponent(invComp); // "_this" is a "APrimalDinoCharacter*"
invComp->InitializeInventory();
}
auto inventory = _this->MyInventoryComponentField();
if (inventory != nullptr)
{
FString toLog = "Success";
ArkApi::GetApiUtils().GetShooterGameMode()->PrintToServerGameLog(&toLog, false);
}
(edited)ADroppedItem* DropCloneFertilizedEgg(APrimalDinoCharacter* dino, const wchar_t* fertilizedEggBlueprint)
{
ADroppedItem* retVal = nullptr;
UObject* object = Globals::StaticLoadObject(UObject::StaticClass(), nullptr, fertilizedEggBlueprint, nullptr, 0, 0, true);
if (object != nullptr)
{
TSubclassOf<ADroppedItem> eggItem;
eggItem.uClass = reinterpret_cast<UClass*>(object);
FRotator rotation{0,0,0};
FVector location;
dino->GetLandingLocation(&location);
retVal = dino->CreateCloneFertilizedEgg(location, rotation, eggItem);
}
return retVal;
}
// Example: fertilizedEggBlueprint = "Blueprint'/Game/PrimalEarth/Test/PrimalItemConsumable_Egg_Dodo_Fertilized.PrimalItemConsumable_Egg_Dodo_Fertilized'";
(edited)UPrimalInventoryComponent* invComp = new UPrimalInventoryComponent();
if (invComp != nullptr)
{
_this->SetMyInventoryComponent(invComp); // "_this" is a "APrimalDinoCharacter*"
invComp->InitializeInventory();
}
auto inventory = _this->MyInventoryComponentField();
if (inventory != nullptr)
{
FString toLog = "Success";
ArkApi::GetApiUtils().GetShooterGameMode()->PrintToServerGameLog(&toLog, false);
}
(edited)aprimaldinocharacter->MyInventoryComponentField();
SupplyCrateClassString="DinoDropInventoryComponent_Carnivore_Huge_C"
).new
with UObjects is no buenoRegisterComponen
(on the comp class I assume) and then call InventoryComponent->InitializeInventorynew Class()
load the inventory class you need
wait there's multiple types of UPrimalInventoryComponent?Globals::StaticConstructObject
is the function that can construct objectsGlobals::StaticConstructObject
is the function that can construct objects TamedInventoryComponentTemplateField()
(edited) this->MyInventoryComponent = v6;
AActor::AddOwnedComponent(this, this->MyInventoryComponent);
UActorComponent::RegisterComponent(this->MyInventoryComponent);
((void (__fastcall *)(UPrimalInventoryComponent *))this->MyInventoryComponent->APrimalCharacter::vfptr[20].__vecDelDtor)(this->MyInventoryComponent);
if ( this->MyCharacterStatusComponent )
UPrimalCharacterStatusComponent::UpdateWeightStat(this->MyCharacterStatusComponent, 0);
APrimalCharacter::vfptr[20].__vecDelDtor
? looks a bit weirdv6
being the newly created object from inventory comp classAPrimalDinoCharacter::SetInventoryComponent
rather than assigning the variablevoid __fastcall APrimalCharacter::SetMyInventoryComponent(APrimalCharacter *this, UPrimalInventoryComponent *theInventoryComponent)
{
this->MyInventoryComponent = theInventoryComponent;
}
TamedInventoryComponentTemplateField()
?
Because it will return a UObject and I never know what to look at since there's so much names fields (FieldName(), Fullname(), Fullpath(), etc.).ArkApi::GetApiUtils().GetClassBlueprint(class);
Blueprint'/Game/Genesis2/Dinos/SpaceDolphin/DinoTamedInventoryComponent_SpaceDolphin.DinoTamedInventoryComponent_SpaceDolphin'
(edited)void AddInventoryCompToAstrodelphis(APrimalDinoCharacter* dino)
{
FString astroInvCompPath = L"Blueprint'/Game/Genesis2/Dinos/SpaceDolphin/DinoTamedInventoryComponent_SpaceDolphin.DinoTamedInventoryComponent_SpaceDolphin'";
UClass* astroInvCompClass = UVictoryCore::BPLoadClass(&astroInvCompPath);
if (astroInvCompClass != nullptr)
{
UObject* astroInvCompObj = Globals::StaticConstructObject(astroInvCompClass, dino, FName(), EObjectFlags::RF_NoFlags, nullptr, false, nullptr);
if (astroInvCompObj != nullptr)
{
dino->SetMyInventoryComponent((UPrimalInventoryComponent*)astroInvCompObj);
dino->AddOwnedComponent(dino->MyInventoryComponentField());
dino->MyInventoryComponentField()->RegisterComponent();
dino->MyInventoryComponentField()->InitializeInventory();
}
}
}
(edited)FName()
to Globals::StaticConstructObject
but I will try like thisUClass* MyBPLoadClass(FString* blueprintPath)
{
static std::map<FString, UClass*> loadedClasses;
std::map<FString, UClass*>::const_iterator found = loadedClasses.find(*blueprintPath);
if (found != loadedClasses.end())
return (*found).second;
UClass* toAdd = UVictoryCore::BPLoadClass(blueprintPath);
loadedClasses[*blueprintPath] = toAdd;
return toAdd;
}
(edited)AShooterGameMode_InitGame_original()
call as you mentioned in an earlier conversation..*NewYear.*
. (edited)[2022.01.01-04.38.06:669][ 1]Server attempting to run new years event
[2022.01.01-04.38.06:669][ 1]Set New Years UTC 1 to: 1641016832.000000
[2022.01.01-04.38.06:670][ 1]Set New Years UTC 2 to: 1641049216.000000
[2022.01.01-04.38.08:362][ 51]Set New Years event location: 85868,648 - -111300,875 - 41857,234
[2022.01.01-06.00.32:017][438]Starting New Years event 1
[2022.01.01-06.00.32:336][446]Spawning New Years Presents
...
[2022.01.01-15.00.16:023][964]Starting New Years event 2
USceneComponent
scale to 1.0 before calling AShooterCharacter_LaunchMountedDino_original(_this);
. Then 5 seconds after the AShooterCharacter_LaunchMountedDino_original(_this);
call I scale again to the desired ratio. (edited)static int tribeDataStructSize = GetStructSize<FTribeData>();
FTribeData* data = static_cast<FTribeData*>(FMemory::Malloc(tribeDataStructSize));
RtlSecureZeroMemory(data, tribeDataStructSize);
ArkApi::GetApiUtils().GetShooterGameMode()->GetOrLoadTribeData(team, data);
// use the tribe data
// don't forget to free the memory!
FMemory::Free(data);
And I was wondering why not just allocate the struct on stack like this? FTribeData data = FTribeData();
ArkApi::GetApiUtils().GetShooterGameMode()->GetOrLoadTribeData(team, &data);
// use the tribe data
static int tribeDataStructSize = GetStructSize<FTribeData>();
FTribeData* data = static_cast<FTribeData*>(FMemory::Malloc(tribeDataStructSize));
RtlSecureZeroMemory(data, tribeDataStructSize);
ArkApi::GetApiUtils().GetShooterGameMode()->GetOrLoadTribeData(team, data);
// use the tribe data
// don't forget to free the memory!
FMemory::Free(data);
And I was wondering why not just allocate the struct on stack like this? FTribeData data = FTribeData();
ArkApi::GetApiUtils().GetShooterGameMode()->GetOrLoadTribeData(team, &data);
// use the tribe data
c++
auto maxHP = myStructure->MaxHealthField();
auto currentHP = myStructure->HealthField();
if ((currentHP < maxHP) && myStructure->ConsumesPrimalItemField().uClass)
{
const float myPercent = ((1.0 - (currentHP / maxHP)) / 2);
auto item2 = ((UPrimalItem*)(myStructure->ConsumesPrimalItemField().uClass->GetDefaultObject(true)));
if(item2)
{
if (item2->BaseCraftingResourceRequirementsField().Num())
{
for (int i = 0; i < item2->BaseCraftingResourceRequirementsField().Num(); ++i)
{
const int x1 = (item2->BaseCraftingResourceRequirementsField()[i].BaseResourceRequirement * myPercent) + 1;
DLog("Need = {}", x1);
DLog("ResourceItemType = {}", ArkApi::IApiUtils::GetClassBlueprint(item2->BaseCraftingResourceRequirementsField()[i].ResourceItemType.uClass).ToString());
}
}
}
}
I figured it out!! (edited)c++
struct UPrimalLocalProfile
{
void AddArkTributePlayerData(FArkTributePlayerData* ArkPlayerData) { NativeCall<void, FArkTributePlayerData*>(this, "UPrimalLocalProfile.AddArkTributePlayerData", ArkPlayerData); }
};
DECLARE_HOOK(UPrimalLocalProfile_AddArkTributePlayerData, void, FArkTributePlayerData*);
void Hook_UPrimalLocalProfile_AddArkTributePlayerData(FArkTributePlayerData* _this)
{
if (_this)
Log::GetLog()->warn("has value");
else
Log::GetLog()->warn("Null");
UPrimalLocalProfile_AddArkTributePlayerData_original(_this); // crashes
}
ArkApi::GetHooks().SetHook("UPrimalLocalProfile.AddArkTributePlayerData", &Hook_UPrimalLocalProfile_AddArkTributePlayerData, &UPrimalLocalProfile_AddArkTributePlayerData_original);
ArkApi::GetHooks().DisableHook("UPrimalLocalProfile.AddArkTributePlayerData", &Hook_UPrimalLocalProfile_AddArkTributePlayerData);
as always, thank you (edited)UPrimalLocalProfile* _this
in your casec++
struct UPrimalLocalProfile
{
void AddArkTributePlayerData(UPrimalLocalProfile* localProfile, FArkTributePlayerData* ArkPlayerData) { NativeCall<void, UPrimalLocalProfile*, FArkTributePlayerData*>(this, "UPrimalLocalProfile.AddArkTributePlayerData", localProfile, ArkPlayerData); }
};
DECLARE_HOOK(UPrimalLocalProfile_AddArkTributePlayerData, void, UPrimalLocalProfile*, FArkTributePlayerData*);
void Hook_UPrimalLocalProfile_AddArkTributePlayerData(UPrimalLocalProfile* _this, FArkTributePlayerData* PlayerData)
{
UPrimalLocalProfile_AddArkTributePlayerData_original(_this, PlayerData);
}
ArkApi::GetHooks().SetHook("UPrimalLocalProfile.AddArkTributePlayerData", &Hook_UPrimalLocalProfile_AddArkTributePlayerData, &UPrimalLocalProfile_AddArkTributePlayerData_original);
ArkApi::GetHooks().DisableHook("UPrimalLocalProfile.AddArkTributePlayerData", &Hook_UPrimalLocalProfile_AddArkTributePlayerData);
c++
struct UPrimalLocalProfile
{
void AddArkTributePlayerData(UPrimalLocalProfile* localProfile, FArkTributePlayerData* ArkPlayerData) { NativeCall<void, UPrimalLocalProfile*, FArkTributePlayerData*>(this, "UPrimalLocalProfile.AddArkTributePlayerData", localProfile, ArkPlayerData); }
};
DECLARE_HOOK(UPrimalLocalProfile_AddArkTributePlayerData, void, UPrimalLocalProfile*, FArkTributePlayerData*);
void Hook_UPrimalLocalProfile_AddArkTributePlayerData(UPrimalLocalProfile* _this, FArkTributePlayerData* PlayerData)
{
UPrimalLocalProfile_AddArkTributePlayerData_original(_this, PlayerData);
}
ArkApi::GetHooks().SetHook("UPrimalLocalProfile.AddArkTributePlayerData", &Hook_UPrimalLocalProfile_AddArkTributePlayerData, &UPrimalLocalProfile_AddArkTributePlayerData_original);
ArkApi::GetHooks().DisableHook("UPrimalLocalProfile.AddArkTributePlayerData", &Hook_UPrimalLocalProfile_AddArkTributePlayerData);
struct UPrimalLocalProfile
def looked right to me the first time. NativeCall
gives you the UPrimalLocalProfile*
arg for free. It could be that your second example happens to work out with calling conventions and an extra argument somewhere, but I wouldn't leave it that way.std::unordered_map<uint64, int[20]> tribe_counts;
. The uint64
is the tribe/team id, and the int array is a count of each structure type using the same index from the first array of structures. I can use the index of the relevant structure in the first array to store/retrieve the count for a specific tribe in the map.
Does this make sense, and can anyone suggest a more intuitive way of doing this efficiently? (edited)std::unordered_map<uint64, int[20]> tribe_counts;
. The uint64
is the tribe/team id, and the int array is a count of each structure type using the same index from the first array of structures. I can use the index of the relevant structure in the first array to store/retrieve the count for a specific tribe in the map.
Does this make sense, and can anyone suggest a more intuitive way of doing this efficiently? (edited)std::unordered_map<uint64, int[20]> tribe_counts;
. The uint64
is the tribe/team id, and the int array is a count of each structure type using the same index from the first array of structures. I can use the index of the relevant structure in the first array to store/retrieve the count for a specific tribe in the map.
Does this make sense, and can anyone suggest a more intuitive way of doing this efficiently? (edited)c++
if (dino->IsOwnedOrControlledBy(player_controller->RootComponentField()->GetOwner()))
{
}
this doesnt work.. I'm not even sure if IsOwnedOrControlledBy is the right function.
Thanks!class AShooterPlayerController* AttackMyTargetForPlayerController;
on primaldinocharacter, or maybe hook the function telling it to attack targetc++
if (dino->IsOwnedOrControlledBy(player_controller->RootComponentField()->GetOwner()))
{
}
this doesnt work.. I'm not even sure if IsOwnedOrControlledBy is the right function.
Thanks! void Hook_AShooterCharacter_ServerCallAttackTarget_Implementation(AShooterCharacter* _this, AActor* TheTarget)
void ServerDinoOrder(class APrimalDinoCharacter* aDino, EDinoTamedOrder::Type OrderType, AActor* target );
This gets called inside the function wetbatman mentioned (edited)void ServerDinoOrder(class APrimalDinoCharacter* aDino, EDinoTamedOrder::Type OrderType, AActor* target );
This gets called inside the function wetbatman mentioned (edited)UWorld* World = ArkApi::GetApiUtils().GetWorld();
for (UObject* object : World->ExtraReferencedObjectsField())
{
if (object != nullptr)
{
// never called...
}
}
UWorld* World = ArkApi::GetApiUtils().GetWorld();
for (UObject* object : World->ExtraReferencedObjectsField())
{
if (object != nullptr)
{
// never called...
}
}
structureFound->ClassField()->GetDefaultObject(false)
that give me the default object, but I get it from a placed structure... That require the structure already placed on the server.
Ark server have bad perfs, so I would have liked to save perfs by doing the action only at startup and not every time :/
I probably have to find myself the offset where are listed the original structuresBlueprintGeneratedClass StructureTurretBaseBP_Heavy.StructureTurretBaseBP_Heavy_C
rightAShooterPlayerController::ClientServerNotificationSingle()
. It's attached to the top of the screen though, not quite as flexible as what you're looking for.
C++
AShooterPlayerController::ClientServerNotificationSingle(FString* MessageText, FLinearColor MessageColor, float DisplayScale, float DisplayTime, UTexture2D* MessageIcon, USoundBase* SoundToPlay, int MessageTypeID)
You can do things like:
C++
// Display a notification
spc->ClientServerNotificationSingle(&message1, FColorList::Green, 0.6f, FLT_MAX, nullptr, nullptr, MY_MSG_ID);
// Edit the notification to display new text
spc->ClientServerNotificationSingle(&message2, FColorList::Green, 0.6f, FLT_MAX, nullptr, nullptr, MY_MSG_ID);
// Hide the notification
spc->ClientServerNotificationSingle(&message2, FColorList::Green, 0.6f, 0, nullptr, nullptr, MY_MSG_ID);
item->ItemDurabilityField() = floatNewValue;
item->UpdatedItem(false);
UKismetSystemLibrary::SphereOverlap_NEW
and set the class as AMissionDispatcher::GetPrivateStaticClass()
(edited)UKismetSystemLibrary::SphereOverlap_NEW
and set the class as AMissionDispatcher::GetPrivateStaticClass()
(edited)#define WIN32_LEAN_AND_MEAN
in preprocessor definitionsServerTransferAllFromRemoteInventory
server runs -> ServerTransferAllFromRemoteInventory_Validate
(if it exists) and ServerTransferAllFromRemoteInventory_Implementation
_Implementation
functions here. They get declared when C++ functions are specified as certain types of UFunctions. They get called by the auto-generated code used to call native UFunctions from Blueprint scripts. So if there's an _Implementation
version of the function, that's usually what you want. _Implementation
functions can sometimes be overridden in Blueprint, or be client-only.
I happen to know the native ServerTransferAll....
UFunctions are called on the server in this case, so hooking those native _Implementation
functions should do the trick.c++
auto world = ArkApi::GetApiUtils().GetWorld();
FLinearColor red = FLinearColor(1, 0, 0);
auto playerComponent = player_controller->GetPlayerCharacter()->RootComponentField();
auto ridingDino = player_controller->GetPlayerCharacter()->RidingDinoField().Get();
FVector ForwardVector;
playerComponent->GetForwardVector(&ForwardVector);
FVector StartLocation = playerComponent->RelativeLocationField();
if (ridingDino)
{
StartLocation = ridingDino->RootComponentField()->RelativeLocationField();
}
FVector EndLocation = ((ForwardVector * 1000.f) + StartLocation);
Log::GetLog()->warn("StartLocation={},{},{}", StartLocation.X, StartLocation.Y, StartLocation.Z);
Log::GetLog()->warn("EndLocation={},{},{}", EndLocation.X, EndLocation.Y, EndLocation.Z);
static int FHitResultStructSize = GetStructSize<FHitResult>();
FHitResult* hit = static_cast<FHitResult*>(FMemory::Malloc(FHitResultStructSize));
RtlSecureZeroMemory(hit, FHitResultStructSize);
player_controller->MulticastDrawDebugLine(StartLocation, EndLocation, red, 30, 10.0, true); // show trace line
auto hit2 = UVictoryCore::VTraceSingleBP(world, hit, &StartLocation, &EndLocation, ECollisionChannel::ECC_Visibility, 0, FName("None", EFindName::FNAME_Add), true, nullptr);
if (hit)
{
if (hit->bBlockingHit()()) // we hit something
{
auto actor = hit->GetActor();
if (actor)
{
auto blueprint = ArkApi::GetApiUtils().GetBlueprint(actor);
Log::GetLog()->warn("Actor={}", blueprint.ToString()); // what did we hit?
}
}
}
FMemory::Free(hit);
(edited)static UClass* StaticClass() { return NativeCall<UClass*>(nullptr, "APrimalStructureExplosive.StaticClass"); }
static UClass* GetPrivateStaticClass() { return NativeCall<UClass*>(nullptr, "APrimalStructureExplosive.GetPrivateStaticClass"); }
(edited)void AChatCmd(AShooterPlayerController* spc, FString* input, EChatSendMode::Type)
. So you can get the command from input
void AChatCmd(AShooterPlayerController* spc, FString* input, EChatSendMode::Type)
. So you can get the command from input
bool Hook_AShooterGameMode_HandleNewPlayer(AShooterGameMode* _this, AShooterPlayerController* player, UPrimalPlayerData* PlayerData, AShooterCharacter* PlayerCharacter, bool bIsFromLogin)
{
What is the best way to check if AShooterPlayerController* player is a structure? (Laying in a bed) I am trying to drop an inventory on login, it works fine unless they are laying in a bed.APrimalStructureItemContainer* drop(APrimalCharacter* player, UPrimalInventoryComponent* inv, uint64 steamid)
{
if (inv)
{
if (MysticUser::database->checkoverflow(steamid))
{
APrimalStructureItemContainer* cache = nullptr;
FVector plocal = player->RootComponentField()->RelativeLocationField();
inv->DropInventoryDeposit(ArkApi::GetApiUtils().GetWorld()->GetTimeSeconds() + 7200, false, true, nullptr, nullptr, &cache, nullptr, FString(""), FString(""), -1, 300, false, -1, false, &plocal, true);
//player->GetPlayerInventory()->DropInventoryDeposit(ArkApi::GetApiUtils().GetWorld()->GetTimeSeconds() + 7200, false, true, nullptr, nullptr, &cache, nullptr, FString(""), FString(""), -1, 300, false, -1, true, &plocal, true);
Log::GetLog()->info("Dropping inventory for player id {}", steamid);
MysticUser::database->resetoverflow(steamid);
}
}
}
bool Hook_AShooterGameMode_HandleNewPlayer(AShooterGameMode* _this, AShooterPlayerController* player, UPrimalPlayerData* PlayerData, AShooterCharacter* PlayerCharacter, bool bIsFromLogin)
{
if (player && bIsFromLogin)
{
UPrimalInventoryComponent* inv = player->GetPlayerInventory();
APrimalCharacter* pchar = static_cast <APrimalCharacter*>(PlayerCharacter);
uint64 steamid = ArkApi::GetApiUtils().GetSteamIdFromController(player);
Log::GetLog()->info("player id {}", steamid);
drop(pchar, inv, steamid);
}
return AShooterGameMode_HandleNewPlayer_original(_this, player, PlayerData, PlayerCharacter, bIsFromLogin);
}
UPrimalInventoryComponent* inv = PlayerCharacter->MyInventoryComponentField();
is better in this case than using the controller to get the inventory;UPrimalInventoryComponent* inv = PlayerCharacter->MyInventoryComponentField();
It crashes instantly when it gets to that line, that is in or out of a bed. APrimalCharacter* pchar = static_cast <APrimalCharacter*>(PlayerCharacter);
UPrimalInventoryComponent* inv = pchar->MyInventoryComponentField();
UPrimalInventoryComponent* inv = PlayerCharacter->MyInventoryComponentField();
It crashes instantly when it gets to that line, that is in or out of a bed. void drop(AShooterCharacter* player, UPrimalInventoryComponent* inv, uint64 steamid)
{
if (!(inv && player))
return;
FVector targetLoc;
player->GetTargetingLocation(&targetLoc, player);
APrimalStructureItemContainer* cache = nullptr;
inv->DropInventoryDeposit(ArkApi::GetApiUtils().GetWorld()->GetTimeSeconds() + 7200, false, true, nullptr, nullptr, &cache, nullptr, FString(""), FString(""), -1, 300, false, -1, false, &targetLoc, true);
Log::GetLog()->info("Dropping inventory for player id {}", steamid);
}
bool Hook_AShooterGameMode_HandleNewPlayer(AShooterGameMode* _this, AShooterPlayerController* player, UPrimalPlayerData* PlayerData, AShooterCharacter* PlayerCharacter, bool bIsFromLogin)
{
if (!(player && bIsFromLogin && PlayerCharacter))
return AShooterGameMode_HandleNewPlayer_original(_this, player, PlayerData, PlayerCharacter, bIsFromLogin);
UPrimalInventoryComponent* invChar = PlayerCharacter->MyInventoryComponentField();
if (!invChar)
return AShooterGameMode_HandleNewPlayer_original(_this, player, PlayerData, PlayerCharacter, bIsFromLogin);
uint64 steamid = ArkApi::GetApiUtils().GetSteamIdFromController(player);
Log::GetLog()->info("player id {}", steamid);
drop(PlayerCharacter, invChar, steamid);
return AShooterGameMode_HandleNewPlayer_original(_this, player, PlayerData, PlayerCharacter, bIsFromLogin);
}
(edited)void Hook_AShooterCharacter_PossessedBy(AShooterCharacter* _this, AController* InController)
as well , I am kind of taken back on how the following is working for youUPrimalInventoryComponent* invChar = PlayerCharacter->MyInventoryComponentField();
I kept getting complete crashes with it, but will try again, and incorporate a delay.API::Timer::Get().DelayExecute(&drop, 5, player_character);
void drop(AShooterCharacter* player, UPrimalInventoryComponent* inv, uint64 steamid)
{
if (!(inv && player))
return;
FVector targetLoc;
player->GetTargetingLocation(&targetLoc, player);
APrimalStructureItemContainer* cache = nullptr;
inv->DropInventoryDeposit(ArkApi::GetApiUtils().GetWorld()->GetTimeSeconds() + 7200, false, true, nullptr, nullptr, &cache, nullptr, FString(""), FString(""), -1, 300, false, -1, false, &targetLoc, true);
if (cache) {
targetLoc.Z -= 100; //because the locations seems to be more where the head would be
cache->SetActorLocation(&targetLoc, false);
}
Log::GetLog()->info("Dropping inventory for player id {}", steamid);
}
void drop(AShooterCharacter* player, UPrimalInventoryComponent* inv, uint64 steamid)
{
if (!(inv && player))
return;
FVector targetLoc;
player->GetTargetingLocation(&targetLoc, player);
APrimalStructureItemContainer* cache = nullptr;
inv->DropInventoryDeposit(ArkApi::GetApiUtils().GetWorld()->GetTimeSeconds() + 7200, false, true, nullptr, nullptr, &cache, nullptr, FString(""), FString(""), -1, 300, false, -1, false, &targetLoc, true);
if (cache) {
targetLoc.Z -= 100; //because the locations seems to be more where the head would be
cache->SetActorLocation(&targetLoc, false);
}
Log::GetLog()->info("Dropping inventory for player id {}", steamid);
}
void Hook_UPrimalInventoryComponent_InitializeInventory(UPrimalInventoryComponent* _this) {
UPrimalInventoryComponent_InitializeInventory_original(_this);
LOG->info("Initialize: " + _this->ClassField()->NameField().ToString().ToString());
for (auto &&item: _this->InventoryItemsField()) {
LOG->info("Item: " + item->NameField().ToString().ToString());
}
}
void Hook_UPrimalInventoryComponent_InitializeInventory(UPrimalInventoryComponent* _this) {
UPrimalInventoryComponent_InitializeInventory_original(_this);
LOG->info("Initialize: " + _this->ClassField()->NameField().ToString().ToString());
for (auto &&item: _this->InventoryItemsField()) {
LOG->info("Item: " + item->NameField().ToString().ToString());
}
}
struct FSupplyCrateItemEntry {
uint8 _padding[0x90];
TArray<TSubclassOf<UPrimalItem>>& ItemsField() { return *GetNativePointerField<TArray<TSubclassOf<UPrimalItem>>*>(this, "FSupplyCrateItemEntry.Items"); }
float& ChanceToActuallyGiveItemField() { return *GetNativePointerField<float*>(this, "FSupplyCrateItemEntry.ChanceToActuallyGiveItem"); }
};
struct FSupplyCrateItemSet {
uint8 _padding[0x40];
TArray<FSupplyCrateItemEntry>& ItemEntriesField() { return *GetNativePointerField<TArray<FSupplyCrateItemEntry>*>(this, "FSupplyCrateItemSet.ItemEntries"); }
};
void removeItems(UClass* baseInventoryClass, TArray<UClass*>& itemsToRemove) {
for (auto i = 0; i < Globals::GUObjectArray()().ObjObjects.NumElements; i++)
{
auto obj = Globals::GUObjectArray()().ObjObjects.GetObjectPtr(i)->Object;
if (obj != nullptr)
{
if (obj->IsA(baseInventoryClass)) {
UProperty* setProperty = Dodo::UObjectUtils::findProperty(obj,"AdditionalItemSets");
if (!setProperty)
continue;
TArray<FSupplyCrateItemSet> itemSets = setProperty->Get<TArray<FSupplyCrateItemSet>>(obj);
for (auto& itemSet : itemSets) {
for (auto& entry : itemSet.ItemEntriesField()) {
for (auto& item : entry.ItemsField()) {
if (itemsToRemove.Contains(item.uClass)) {
entry.ChanceToActuallyGiveItemField() = 0;
}
}
}
}
setProperty->Set<TArray<FSupplyCrateItemSet>>(obj, itemSets);
}
}
}
}
(edited)TArray<UClass*> setupItemsToRemove() {
// probably best to make a loop from a config file
TArray<UClass*> returnArr;
FString itemPath = FString("Blueprint'/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_ApexDrop_Sauro.PrimalItemResource_ApexDrop_Sauro'");
UClass* itemClass = UVictoryCore::BPLoadClass(&itemPath);
if (!itemClass)
return returnArr;
returnArr.Add(itemClass);
itemPath = FString("Blueprint'/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_ApexDrop_Rex.PrimalItemResource_ApexDrop_Rex'");
itemClass = UVictoryCore::BPLoadClass(&itemPath);
if (!itemClass)
return returnArr;
returnArr.Add(itemClass);
return returnArr;
}
void Plugin_ServerReadyInit() { // for example inside shootergamemode begin play hook
FString path = FString("Blueprint'/Game/PrimalEarth/CoreBlueprints/Inventories/DinoDropInventoryComponent_BP_Base.DinoDropInventoryComponent_BP_Base'");
UClass* baseInventoryClass = UVictoryCore::BPLoadClass(&path);
if (!baseInventoryClass)
return;
TArray<UClass*> itemsToRemove = setupItemsToRemove();
if (itemsToRemove.Num() == 0)
return;
removeItems(baseInventoryClass, itemsToRemove);
}
DECLARE_HOOK(AShooterGameMode_BeginPlay, void, AShooterGameMode*);
void removeProperty(UObject* obj, const char* property) {
UProperty* setProperty = obj->FindProperty(FName(property, EFindName::FNAME_Find));
if (!setProperty)
return;
TArray<FSupplyCrateItemSet> itemSets = setProperty->Get<TArray<FSupplyCrateItemSet>>(obj);
for (auto& itemSet : itemSets) {
for (auto& entry : itemSet.ItemEntriesField()) {
for (auto& item : entry.ItemsField()) {
auto name = item.uClass->NameField().ToString();
if (name.Contains("_ApexDrop_")
|| name.Contains("RecipeNote_")
|| name.Contains("PrimalItemCostume_")
|| name.Contains("PrimalItemSkin_")
|| name.Contains("PrimalItemArmor_Cloth")
|| name.Contains("PrimalItemArmor_Hide")
|| name.Contains("PrimalItemArmor_Fur")
|| name.Contains("PrimalItemArmor_Chitin")
|| name.Contains("PrimalItemArmor_Metal")
|| name.Contains("PrimalItemArmor_Scuba")
|| name.Contains("PrimalItemConsumable_Berry")
|| name.Contains("PrimalItem_WeaponStone")
|| name.Contains("PrimalItem_WeaponMetal")
|| name.Contains("PrimalItem_WeaponGPS")
|| name.Contains("PrimalItem_WeaponCompass")
|| name.Contains("PrimalItem_WeaponSickle")
|| name.Contains("PrimalItem_WeaponBow_")
|| name.Contains("PrimalItem_WeaponTorch_")
|| name.Contains("PrimalItem_WeaponCrossBow_")
|| name.Contains("PrimalItem_WeaponBoomerang")
|| name.Contains("PrimalItem_WeaponMachined")
|| name.Contains("PrimalItem_WeaponGun")
|| name.Contains("PrimalItem_WeaponPike")
|| name.Contains("PrimalItem_WeaponGrenade")
|| name.Contains("PrimalItem_WeaponProd_")
|| name.Contains("PrimalItem_WeaponSpear")
|| name.Contains("PrimalItem_WeaponSlingshot")
) {
LOG->info("Removed2: " + name.ToString());
entry.ChanceToActuallyGiveItemField() = 0;
} else {
LOG->info("Kept: " + name.ToString());
}
}
}
}
//setProperty->Set<TArray<FSupplyCrateItemSet>>(obj, itemSets);
}
void removeItems() {
FString path = FString("Blueprint'/Game/PrimalEarth/CoreBlueprints/Inventories/DinoDropInventoryComponent_BP_Base.DinoDropInventoryComponent_BP_Base'");
UClass* baseInventoryClass = UVictoryCore::BPLoadClass(&path);
for (auto i = 0; i < Globals::GUObjectArray()().ObjObjects.NumElements; i++)
{
auto obj = Globals::GUObjectArray()().ObjObjects.GetObjectPtr(i)->Object;
if (obj == nullptr) {
continue;
}
if (!obj->IsA(baseInventoryClass)) {
continue;
}
removeProperty(obj, "AdditionalItemSets");
removeProperty(obj, "ItemSets");
}
}
void Hook_AShooterGameMode_BeginPlay(AShooterGameMode* mode) {
AShooterGameMode_BeginPlay_original(mode);
removeItems();
}
void Load() {
SET_HOOKV2(AShooterGameMode, BeginPlay);
removeItems();
}
API::Timer::Get().DelayExecute(&removeItems, 1);
struct FSupplyCrateItemEntry {
uint8 _padding[0x18];
TArray<TSubclassOf<UPrimalItem>> Items;
uint8 _padding2[0x5C];
float ChanceToActuallyGiveItem;
uint8 _padding3[0x2];
};
is what i was expecting. long as my math wasnt off somewhere there lol struct FSupplyCrateItemEntry {
uint8 _padding[0x18];
TArray<TSubclassOf<UPrimalItem>> Items;
uint8 _padding2[0x68];
};
struct UPrimalSupplyCrateItemSet : UObject {
FSupplyCrateItemSet ItemSet;
};
struct FSupplyCrateItemSet {
FString SetName;
TArray<FSupplyCrateItemEntry,FDefaultAllocator> ItemEntries;
float MinNumItems;
float MaxNumItems;
float NumItemsPower;
float SetWeight;
bool bItemsRandomWithoutReplacement;
TSubclassOf<UPrimalSupplyCrateItemSet> ItemSetOverride;
};
bool shouldFilter(FString& name) {
return (.... truncated for paste);
}
struct RemoveUndesiredItems
{
bool operator()(const TSubclassOf<UPrimalItem>& item) const
{
if (!item.uClass) {
return false;
}
auto name = item.uClass->NameField().ToString();
if (shouldFilter(name)) {
//LOG->info("Removed: " + name.ToString());
return true;
} else {
//LOG->info("Kept: " + name.ToString());
return false;
}
}
};
void filterItems(FSupplyCrateItemEntry& entry) {
entry.Items.RemoveAll(RemoveUndesiredItems());
}
void removeProperty(UObject* obj, const char* property) {
UProperty* setProperty = obj->FindProperty(FName(property, EFindName::FNAME_Find));
if (!setProperty)
return;
TArray<FSupplyCrateItemSet> itemSets = setProperty->Get<TArray<FSupplyCrateItemSet>>(obj);
for (auto& itemSet : itemSets) {
LOG->info("ItemSet: " + itemSet.SetName.ToString());
for (auto entry : itemSet.ItemEntries) {
filterItems(entry);
}
}
}
ConfigOverrideSupplyCrateItems
ini option, but I don't know if you can use that to remove everything you're trying to remove. (edited)c++
int& ModifedButtonCountField() { return *GetNativePointerField<int*>(this, "AShooterPlayerController.ModifedButtonCount"); }
GetNativePointerField()
calls the GetAddress()
function (exported by ArkServerApi) which adds the offset of the "ModifiedButtonCount" (from pdb) to the object's base address (this
) to get an absolute address for the variable. (We're still not completely immune from WC changing their defs, but it's more robust than option 1)this
/_this
, an address from an object property, etc) and accessor functions that use "base address" + "offset found in pdb".
There are some weird one-off class definitions in ArkServerApi like:
c++
struct UFunction : UStruct
{
unsigned int FunctionFlags;
unsigned __int16 RepOffset;
char NumParms;
unsigned __int16 ParmsSize;
unsigned __int16 ReturnValueOffset;
unsigned __int16 RPCId;
unsigned __int16 RPCResponseId;
UProperty* FirstPropertyToInit;
};
This basically looks correct at a glance, but it doesn't actually work because UFunction has a UStruct base class (which has a size of 1 in ArkServerApi), so the UFunction variable offsets are incorrect.
Struct definitions (i.e., type names that start with the letter 'F') in ArkServerApi are a little more haphazard. Some of them use option 1 so they actually have the correct size (although some of them are incorrect or out of date), some of them use the option 2 accessor functions. I believe option 1 is used sometimes because it's common to have TArrays of struct types, and TArrays need to use the correct type size at compile time (TArrays of class objects, as opposed to structs, are typically TArrays of pointers to class instances, which we know are 8 bytes).c++
int& ModifedButtonCountField() { return *GetNativePointerField<int*>(this, "AShooterPlayerController.ModifedButtonCount"); }
GetNativePointerField()
calls the GetAddress()
function (exported by ArkServerApi) which adds the offset of the "ModifiedButtonCount" (from pdb) to the object's base address (this
) to get an absolute address for the variable. (We're still not completely immune from WC changing their defs, but it's more robust than option 1) void filterItems(FSupplyCrateItemSet& itemSet) {
auto items = itemSet.ItemEntriesField();
LOG->info("ItemSet: " + itemSet.SetNameField().ToString() + " - " + STR(items.Num()));
std::string after = "";
for (auto& entry : items) {
entry.Items.RemoveAll(RemoveUndesiredItems());
for (auto& i : entry.Items) {
if (i.uClass) {
after = after + (i.uClass->NameField().ToString().ToString()) + ", ";
}
}
}
LOG->info("After: " + after);
i verified the entries are being properly removed. but they still dropvoid Hook_APrimalStructure_Destroyed(APrimalStructure* _this)
{
if (no_drop)
{
if (_this->IsA(APrimalStructureItemContainer::GetPrivateStaticClass()))
{
auto invetory = static_cast <APrimalStructureItemContainer*>(_this);
invetory->bDropInventoryOnDestruction() = false;
}
}
APrimalStructure_Destroyed_original(_this);
}
are their other classes than Item Containers that I am not aware of?DECLARE_HOOK(UPrimalInventoryComponent_DropInventoryDeposit, bool, UPrimalInventoryComponent*, long double, bool, bool, TSubclassOf<APrimalStructureItemContainer>, APrimalStructureItemContainer*, APrimalStructureItemContainer**, AActor*, FString, FString, unsigned __int64, float, bool, int, bool, FVector*, bool);
but it gives me an error on the following bool Hook_UPrimalInventoryComponent_DropInventoryDeposit(UPrimalInventoryComponent* _this, long double DestroyAtTime, bool bDoPreventSendingData, bool bIgnorEquippedItems, TSubclassOf<APrimalStructureItemContainer> OverrideInventoryDepositClass, APrimalStructureItemContainer* CopyStructureValues, APrimalStructureItemContainer** DepositStructureResult, AActor* GroundIgnoreActor, FString CurrentCustomFolderFilter, FString CurrentNameFilter, unsigned __int64 DeathCacheCharacterID, float DropInventoryOnGroundTraceDistance, bool bForceDrop, int OverrideMaxItemsDropped, bool bOverrideDepositLocation, FVector* DepositLocationOverride, bool bForceLocation)
{
return UPrimalInventoryComponent_DropInventoryDeposit_original(_this, (HERE ->>>>>> double <<<<<<), bDoPreventSendingData, bIgnorEquippedItems, OverrideInventoryDepositClass, CopyStructureValues, DepositStructureResult, GroundIgnoreActor, CurrentCustomFolderFilter, CurrentNameFilter, __int64, DropInventoryOnGroundTraceDistance, bForceDrop, OverrideMaxItemsDropped, bOverrideDepositLocation, DepositLocationOverride, bForceLocation);
}
says that Type name is not allowed for "double" did they change this hook at some point? do I need to find a new that is not in the hooker creator (edited)long double
it thinks the param name is double instead of DestroyAtTime
return UPrimalInventoryComponent_DropInventoryDeposit_original(_this, DestroyAtTime, bDoPreventSendingData, bIgnorEquippedItems, OverrideInventoryDepositClass, CopyStructureValues, DepositStructureResult, GroundIgnoreActor, CurrentCustomFolderFilter, CurrentNameFilter, DeathCacheCharacterID, DropInventoryOnGroundTraceDistance, bForceDrop, OverrideMaxItemsDropped, bOverrideDepositLocation, DepositLocationOverride, bForceLocation);
(edited)FSupplyCrateItemEntry
and completed the currently empty definition of FSupplyCrateItemSet
in Inventory.h.
c++
struct UPrimalSupplyCrateItemSet
{
FSupplyCrateItemSet& ItemSetField() { return *GetNativePointerField<FSupplyCrateItemSet*>(this, "UPrimalSupplyCrateItemSet.ItemSet"); }
};
struct FSupplyCrateItemEntry
{
FString ItemEntryName;
float EntryWeight;
TArray<TSubclassOf<UPrimalItem>> Items;
TArray<FString> ItemClassStrings;
TArray<float> ItemsWeights;
TArray<int32> ItemQuantityOverrides;
float MinQuantity;
float MaxQuantity;
bool bApplyQuantityToSingleItem;
float QuantityPower;
float MinRandomQuality;
float MinQuality;
float MaxQuality;
float QualityPower;
uint32 bForceBlueprint : 1;
uint32 bForcePreventGrinding : 1;
float ChanceToBeBlueprintOverride;
float ItemStatClampsMultiplier;
float ChanceToActuallyGiveItem;
float RequiresMinQuality;
};
struct FSupplyCrateItemSet
{
FString SetName;
TArray<FSupplyCrateItemEntry> ItemEntries;
float MinNumItems;
float MaxNumItems;
float NumItemsPower;
float SetWeight;
bool bItemsRandomWithoutReplacement;
TSubclassOf<UPrimalSupplyCrateItemSet> ItemSetOverride;
};
APrimalDinoCharacter::DeathInventoryTemplates
field to remove meat from all associated item sets. Kind of a redundant approach since the same item sets can be associated with multiple dino classes, but it just runs once.APrimalStructureItemContainer_SupplyCrate
should have a BeginPlay
you can hook it just isn't defined already but it inherits from APrimalStructureItemContainer
which does have one.UDinoDropInventoryComponent_BP_Base_C
CDOs that I modified the item sets in. I'm not sure about the "cloned data" theory, but I didn't spend a ton of time investigating it all.auto obj = Globals::GUObjectArray()().ObjObjects.GetObjectPtr(i)->Object;
if (obj == nullptr) {
continue;
}
if (!obj->IsA(baseInventoryClass)) {
continue;
}
auto inv = CAST(UPrimalInventoryComponent*, obj);
auto itemSets = FORCECAST(TArray<SAOmega::Loot::FSupplyCrateItemSet>, inv->ItemSetsField());
auto additionalSets = FORCECAST(TArray<SAOmega::Loot::FSupplyCrateItemSet>, inv->AdditionalItemSetsField());
for (auto& set : itemSets) {
for (auto& entry : set.ItemEntriesField()) {
entry.Items.RemoveAll(RemoveUndesiredItems());
}
}
for (auto& set : additionalSets) {
for (auto& entry : set.ItemEntriesField()) {
entry.Items.RemoveAll(RemoveUndesiredItems());
}
}
still doesnt work, so i think the CDO on the death template seems to be an important part.c++
auto itemSets = FORCECAST(TArray<SAOmega::Loot::FSupplyCrateItemSet>, inv->ItemSetsField());
auto additionalSets = FORCECAST(TArray<SAOmega::Loot::FSupplyCrateItemSet>, inv->AdditionalItemSetsField());
I think you're copying the TArray and modifying the copy.void removeItems() {
for (auto i = 0; i < Globals::GUObjectArray()().ObjObjects.NumElements; i++)
{
auto obj = Globals::GUObjectArray()().ObjObjects.GetObjectPtr(i)->Object;
if (obj == nullptr) {
continue;
}
// Skip invalid UObjects
if (!obj->IsValidLowLevelFast(true))
continue;
// Skip over UObjects that aren't CDOs
if (obj != obj->ClassField()->ClassDefaultObjectField())
continue;
// Skip over UObjects that aren't an APrimalDinoCharacter
if (!obj->IsA(APrimalDinoCharacter::StaticClass()))
continue;
APrimalDinoCharacter* dinoCDO = (APrimalDinoCharacter*)obj;
for (UObject* listItem : dinoCDO->DeathInventoryTemplatesField().AssociatedObjects) {
// DeathInventoryTemplates.AssociatedObjects is an array of UBlueprint*
UBlueprint *bp = (UBlueprint *) listItem;
// The BP class is a sublcass of UPrimalInventoryComponent
if (!bp->GeneratedClassField().uClass->IsChildOf(UPrimalInventoryComponent::StaticClass())) {
continue;
}
// Get the CDO for this inventory component and remove meat from its item sets
UPrimalInventoryComponent *component = (UPrimalInventoryComponent *) bp->GeneratedClassField().uClass->ClassDefaultObjectField();
auto itemSets = FORCECAST(TArray<SAOmega::Loot::FSupplyCrateItemSet>, component->ItemSetsField());
auto additionalSets = FORCECAST(TArray<SAOmega::Loot::FSupplyCrateItemSet>,
component->AdditionalItemSetsField());
for (auto &set: itemSets) {
for (auto &entry: set.ItemEntriesField()) {
entry.Items.RemoveAll(RemoveUndesiredItems());
}
}
for (auto &set: additionalSets) {
for (auto &entry: set.ItemEntriesField()) {
entry.Items.RemoveAll(RemoveUndesiredItems());
}
}
}
}
}
(edited)struct FSupplyCrateItemEntry {
uint8 _padding[0x18];
TArray<TSubclassOf<UPrimalItem>> Items;
uint8 _padding2[0x68];
};
struct FSupplyCrateItemSet {
uint8 _padding[0x40];
TArray<FSupplyCrateItemEntry,FDefaultAllocator>& ItemEntriesField() {
return *GetNativePointerField<TArray<FSupplyCrateItemEntry,FDefaultAllocator>*>(this, "FSupplyCrateItemSet.ItemEntries");
}
};
bool shouldFilter(FString& name) {
return (name.Contains("_ApexDrop_")
|| name.Contains("RecipeNote_")
|| name.Contains("PrimalItemCostume_")
|| name.Contains("PrimalItemSkin_")
|| name.Contains("PrimalItemArmor_Cloth")
|| name.Contains("PrimalItemArmor_Hide")
|| name.Contains("PrimalItemArmor_Fur")
|| name.Contains("PrimalItemArmor_Chitin")
|| name.Contains("PrimalItemArmor_Metal")
|| name.Contains("PrimalItemArmor_Scuba")
|| name.Contains("PrimalItemConsumable_Berry")
|| name.Contains("PrimalItem_WeaponStone")
|| name.Contains("PrimalItem_WeaponMetal")
|| name.Contains("PrimalItem_WeaponGPS")
|| name.Contains("PrimalItem_WeaponCompass")
|| name.Contains("PrimalItem_WeaponSickle")
|| name.Contains("PrimalItem_WeaponBow_")
|| name.Contains("PrimalItem_WeaponTorch_")
|| name.Contains("PrimalItem_WeaponCrossBow_")
|| name.Contains("PrimalItem_WeaponBoomerang")
|| name.Contains("PrimalItem_WeaponMachined")
|| name.Contains("PrimalItem_WeaponGun")
|| name.Contains("PrimalItem_WeaponPike")
|| name.Contains("PrimalItem_WeaponGrenade")
|| name.Contains("PrimalItem_WeaponProd_")
|| name.Contains("PrimalItem_WeaponSpear")
|| name.Contains("PrimalItem_WeaponSlingshot")
|| name.Contains("_Wishbone_")
);
}
struct RemoveUndesiredItems
{
bool operator()(const TSubclassOf<UPrimalItem>& item) const {
if (!item.uClass) {
return false;
}
auto name = item.uClass->NameField().ToString();
return shouldFilter(name);
}
};
void removeItems() {
for (auto i = 0; i < Globals::GUObjectArray()().ObjObjects.NumElements; i++)
{
auto obj = Globals::GUObjectArray()().ObjObjects.GetObjectPtr(i)->Object;
if (obj == nullptr) {
continue;
}
// Skip invalid UObjects
if (!obj->IsValidLowLevelFast(true))
continue;
// Skip over UObjects that aren't CDOs
if (obj != obj->ClassField()->ClassDefaultObjectField())
continue;
// Skip over UObjects that aren't an APrimalDinoCharacter
if (!obj->IsA(APrimalDinoCharacter::StaticClass()))
continue;
APrimalDinoCharacter* dinoCDO = (APrimalDinoCharacter*)obj;
for (UObject* listItem : dinoCDO->DeathInventoryTemplatesField().AssociatedObjects) {
// DeathInventoryTemplates.AssociatedObjects is an array of UBlueprint*
UBlueprint *bp = (UBlueprint *) listItem;
// The BP class is a sublcass of UPrimalInventoryComponent
if (!bp->GeneratedClassField().uClass->IsChildOf(UPrimalInventoryComponent::StaticClass())) {
continue;
}
// Get the CDO for this inventory component and remove meat from its item sets
UPrimalInventoryComponent *component = (UPrimalInventoryComponent *) bp->GeneratedClassField().uClass->ClassDefaultObjectField();
auto itemSets = FORCECAST(TArray<SAOmega::Loot::FSupplyCrateItemSet>, component->ItemSetsField());
auto additionalSets = FORCECAST(TArray<SAOmega::Loot::FSupplyCrateItemSet>,
component->AdditionalItemSetsField());
for (auto &set: itemSets) {
for (auto &entry: set.ItemEntriesField()) {
entry.Items.RemoveAll(RemoveUndesiredItems());
}
}
for (auto &set: additionalSets) {
for (auto &entry: set.ItemEntriesField()) {
entry.Items.RemoveAll(RemoveUndesiredItems());
}
}
}
}
}
(edited)class BlueprintCache {
private:
FString path;
UClass* cls;
public:
BlueprintCache(char* path) {
this->path = path;
}
BlueprintCache(FString path) {
this->path = path;
}
UClass* Get() {
if (!cls) {
cls = UVictoryCore::BPLoadClass(&path);
}
return cls;
}
};
Then can do in namespace/static prop
BlueprintCache dinoDropBP("Blueprint'/Game/PrimalEarth/CoreBlueprints/Inventories/DinoDropInventoryComponent_BP_Base.DinoDropInventoryComponent_BP_Base'");
and reuse over multiple methods obj->IsA(dinoDropBP.Get())
notably for like the spyglass thing worked with Sub on before, needed same blueprints in 2 diff methods so the static cache method doesnt get shared there, this will let it share the cache.#define CAST(as, from) static_cast<as>(from)
#define FORCECAST(as, from) *CAST(as*, CAST(void*, &from))
bit rusty with C++, thats pretty much best way to handle force casting right?auto itemSets = FORCECAST(TArray<SAOmega::Loot::FSupplyCrateItemSet>, component->ItemSetsField());
if (ArkApi::GetApiUtils().GetStatus() == ArkApi::ServerStatus::Ready)
c++
void OnServerReady()
{
UClass* dinoDropInvClass = UVictoryCore::BPLoadClass("Bluprint'/Game/PrimalEarth/CoreBlueprints/Inventories/DinoDropInventoryComponent_BP_Base.DinoDropInventoryComponent_BP_Base'");
for (int i = 0; i < Globals::GUObjectArray()().ObjObjects.NumElements; i++) {
UObject* obj = Globals::GUObjectArray()().ObjObjects.GetObjectPtr(i)->Object;
if (!obj->IsValidLowLevelFast(true))
continue;
if (!obj->IsA(dinoDropInvClass))
continue;
if (obj != obj->ClassField()->ClassDefaultObjectField())
continue;
UPrimalInventoryComponent* dinoDropInv = (UPrimalInventoryComponent*)obj;
for (FSupplyCrateItemSet& set : dinoDropInv->ItemSetsField())
for (FSupplyCrateItemEntry& entry : set.ItemEntries)
if (entry.Items.RemoveAll(RemoveArms()) > 0)
Log::GetLog()->info("Removed arms from {} ItemSets", dinoDropInv->NameField().ToString().ToString());
for (FSupplyCrateItemSet& set : dinoDropInv->AdditionalItemSetsField())
for (FSupplyCrateItemEntry& entry : set.ItemEntries)
if (entry.Items.RemoveAll(RemoveArms()) > 0)
Log::GetLog()->info("Removed arms from {} AdditionalItemSets", dinoDropInv->NameField().ToString().ToString());
}
}
c++
void OnServerReady()
{
UClass* dinoDropInvClass = UVictoryCore::BPLoadClass("Bluprint'/Game/PrimalEarth/CoreBlueprints/Inventories/DinoDropInventoryComponent_BP_Base.DinoDropInventoryComponent_BP_Base'");
for (int i = 0; i < Globals::GUObjectArray()().ObjObjects.NumElements; i++) {
UObject* obj = Globals::GUObjectArray()().ObjObjects.GetObjectPtr(i)->Object;
if (!obj->IsValidLowLevelFast(true))
continue;
if (!obj->IsA(dinoDropInvClass))
continue;
if (obj != obj->ClassField()->ClassDefaultObjectField())
continue;
UPrimalInventoryComponent* dinoDropInv = (UPrimalInventoryComponent*)obj;
// Doesn't work
// itemSets->GetData() and dinoDropInv->ItemSetsField()->GetData() return different addresses
auto itemSets = dinoDropInv->ItemSetsField();
auto additionalItemSets = dinoDropInv->AdditionalItemSetsField();
for (FSupplyCrateItemSet& set : itemSets)
for (FSupplyCrateItemEntry& entry : set.ItemEntries)
if (entry.Items.RemoveAll(RemoveArms()) > 0)
Log::GetLog()->info("Removed arms from {} ItemSets", dinoDropInv->NameField().ToString().ToString());
for (FSupplyCrateItemSet& set : additionalItemSets)
for (FSupplyCrateItemEntry& entry : set.ItemEntries)
if (entry.Items.RemoveAll(RemoveArms()) > 0)
Log::GetLog()->info("Removed arms from {} AdditionalItemSets", dinoDropInv->NameField().ToString().ToString());
}
}
If I reload this second version without restarting my server, I get the log every time.
[info] Removed arms from Default__DinoDropInventoryComponent_Carnivore_Huge_Rex_C AdditionalItemSets
The reason is that this copy constructor is being used:
c++
FORCEINLINE TArray(const TArray& Other)
{
CopyToEmpty(Other.GetData(), Other.Num(), 0, 0);
}
#define CAST(as, from) static_cast<as>(from)
#define FORCECAST(as, from) *CAST(as*, CAST(void*, &from))`
But it seems its working though. stuffs not dropping. does this cast logic here avoid the copy somehow?*
operator)struct FSupplyCrateItemEntry {
uint8 _padding[0x18];
TArray<TSubclassOf<UPrimalItem>> Items;
uint8 _padding2[0x68];
};
struct FSupplyCrateItemSet {
uint8 _padding[0x40];
TArray<FSupplyCrateItemEntry,FDefaultAllocator>& ItemEntriesField() {
return *GetNativePointerField<TArray<FSupplyCrateItemEntry,FDefaultAllocator>*>(this, "FSupplyCrateItemSet.ItemEntries");
}
};
Didnt get to updating ItemEntry one yetstruct MyArkStruct
{
float& someField() { return *GetNativePointerField<float*>(this, "MyArkStruct.some"); }
}
FDefaultAllocator
is not needed as it's the default if you want to clean that up a bitauto
keyword, that will copy almost always (unless it's a pointer), so you would need auto&
for references and not copiesGetNativeField
just forces the type to be pointer, and GetNativePointerField
forces you to specify if you want a pointer or notGetNativePointerField
because it's what has always been used in the API definitions made originally. But both should be of use if you know one forces a pointer while the other does notauto st = all_wild.begin();
if (st->second.Get() != nullptr)
{
APrimalDinoCharacter* cd = st->second.Get();
if (cd->TargetingTeamField() < 50000 && cd->TamingTeamIDField() < 50000)
{
//dostuff
}
}
all_wild.erase(st->first);
(edited)Fatalerror!
VERSION:357.11
ShooterGameServer.exe!AActor::ThrottledTick()(0x00007ff773d2a4e0)+0bytes[f:\build\lostisland\projects\shootergame\intermediate\build\win64\shootergameserver\inc\engine\engine.generated.1.cpp:1778]
ShooterGameServer.exe!AShooterGameState::Tick()(0x00007ff772157898)+0bytes[f:\build\lostisland\projects\shootergame\source\shootergame\private\shootergamestate.cpp:519]
ShooterGameServer.exe!AActor::TickActor()(0x00007ff7737969ee)+0bytes[f:\build\lostisland\engine\source\runtime\engine\private\actor.cpp:789]
ShooterGameServer.exe!FActorTickFunction::ExecuteTick()(0x00007ff77379518a)+30bytes[f:\build\lostisland\engine\source\runtime\engine\private\actor.cpp:154]
ShooterGameServer.exe!TGraphTask<FTickTaskSequencer::FTickFunctionTask>::ExecuteTask()(0x00007ff773a63d4c)+28bytes[f:\build\lostisland\engine\source\runtime\core\public\async\taskgraphinterfaces.h:871]
ShooterGameServer.exe!FNamedTaskThread::ProcessTasksNamedThread()(0x00007ff7731e4325)+0bytes[f:\build\lostisland\engine\source\runtime\core\private\async\taskgraph.cpp:939]
ShooterGameServer.exe!FNamedTaskThread::ProcessTasksUntilQuit()(0x00007ff7731e3c4e)+0bytes[f:\build\lostisland\engine\source\runtime\core\private\async\taskgraph.cpp:680]
ShooterGameServer.exe!FTaskGraphImplementation::WaitUntilTasksComplete()(0x00007ff7731e5b24)+0bytes[f:\build\lostisland\engine\source\runtime\core\private\async\taskgraph.cpp:1777]
ShooterGameServer.exe!FTickTaskSequencer::ReleaseTickGroup()(0x00007ff773a360d3)+0bytes[f:\build\lostisland\engine\source\runtime\engine\private\ticktaskmanager.cpp:205]
ShooterGameServer.exe!FTickTaskManager::RunTickGroup()(0x00007ff773a38fac)+0bytes[f:\build\lostisland\engine\source\runtime\engine\private\ticktaskmanager.cpp:867]
ShooterGameServer.exe!UWorld::RunTickGroup()(0x00007ff7738fc33e)+0bytes[f:\build\lostisland\engine\source\runtime\engine\private\leveltick.cpp:697]
ShooterGameServer.exe!UWorld::Tick()(0x00007ff7738fd93b)+13bytes[f:\build\lostisland\engine\source\runtime\engine\private\leveltick.cpp:1210]
ShooterGameServer.exe!UGameEngine::Tick()(0x00007ff7738682c1)+0bytes[f:\build\lostisland\engine\source\runtime\engine\private\gameengine.cpp:1195]
ShooterGameServer.exe!FEngineLoop::Tick()(0x00007ff771617e12)+0bytes[f:\build\lostisland\engine\source\runtime\launch\private\launchengineloop.cpp:2647]
ShooterGameServer.exe!GuardedMain()(0x00007ff771613bcc)+0bytes[f:\build\lostisland\engine\source\runtime\launch\private\launch.cpp:140]
ShooterGameServer.exe!GuardedMainWrapper()(0x00007ff771618d9a)+5bytes[f:\build\lostisland\engine\source\runtime\launch\private\windows\launchwindows.cpp:125]
ShooterGameServer.exe!WinMain()(0x00007ff771618ee9)+8bytes[f:\build\lostisland\engine\source\runtime\launch\private\windows\launchwindows.cpp:213]
ShooterGameServer.exe!__tmainCRTStartup()(0x00007ff774903619)+21bytes[f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c:618]
KERNEL32.DLL!UnknownFunction(0x00007ff93fab7974)+0bytes[UnknownFile:0]
ntdll.dll!UnknownFunction(0x00007ff954e5a351)+0bytes[UnknownFile:0]
ntdll.dll!UnknownFunction(0x00007ff954e5a351)+0bytes[UnknownFile:0]
LastRemoteFunctionSpike_Lower_Visual
static FString f("Blueprint'/Game/Mods/Mystic/Test_CCA.Test_CCA'");
UClass* load = UVictoryCore::BPLoadClass(&f);
if (!load)
return;
UObject* CDO = load->GetDefaultObject(true);
if (!CDO)
return;
UFunction* cca = CDO->FindFunctionChecked(FName("SetInfo", EFindName::FNAME_Add));
if (!cca)
return;
CDO->ProcessEvent(cca, &DName);