int alliance_id;
AShooterPlayerState* State = static_cast<AShooterPlayerState*>(shooter_pc->PlayerStateField());
FTribeAlliance* FTA = static_cast <FTribeAlliance*>(State->MyTribeDataField()->FindTribeAlliance(alliance_id));
shooter_pc
in that case? (edited)Log::GetLog()->info("tribemembersInviter: {}", alliance.MembersTribeIDField().Num());
returns me this: -623357952
and cuz its negative it doesnt go into the next stepServerRequestRemoveAllianceMember_Implementation
TArray<FTribeAlliance> allys = aPlState->MyTribeDataField()->TribeAlliancesField();
if (allys.Num() > 0) {
m_AllianceID = allys[0].AllianceIDField();
}
that's how i get it now which is the wrong one -.-MembersTribeIDField()
(edited) AShooterPlayerState* state = static_cast<AShooterPlayerState*>(shooter_pc->PlayerStateField());
if (state)
{
FTribeData* mytribedata = state->MyTribeDataField();
int mytribe_id = state->TargetingTeamField();
Log::GetLog()->info("Checking For Tribe ID: {}", mytribe_id);
int tribeMemberCount = mytribedata->MembersPlayerDataIDField().Num();
Log::GetLog()->info("My Member Count: {}", tribeMemberCount); // will return the count of players in your tribe
if (tribeMemberCount > 0)
{
int alliancesCount = mytribedata->TribeAlliancesField().Num();
Log::GetLog()->info("My Alliance Count: {}", alliancesCount); // Will return the count of how many alliances you have
int allyMemberCount = state->MyTribeDataField()->TribeAlliancesField().GetData()->MembersTribeIDField().Num();
Log::GetLog()->info("My Alliance Member Count: {}", allyMemberCount); // Will return the count of you and the members of the alliance
(edited)struct FUniqueNetIdUInt64 : FUniqueNetId
{
unsigned __int64 UniqueNetId;
};
void outPos(AShooterPlayerController* spc, FString* cmd, bool /**/)
{
if (spc) {
FVector pos = spc->DefaultActorLocationField();
FString aaa = pos.ToString();
Log::GetLog()->info("Player position ({})", pos.ToString());
}
}
When the player type /outPos it should output to the screen, but my knowledge of c++ is limited and I cant figure out how to get the FString to the Log::GetLog()->info function. I keep getting the error “Cannot format argument. To enable the use of ostream operator<< include fmt/ostream.h. Otherwise provide an overload of format_arg.”
Any tips on how to proceed, is greatly appreciatedFVector Location = player->GetPlayerCharacter()->RootComponentField()->RelativeLocationField();
FMath::Abs((vector1 - vector2).Size())
FVector::Distance(location1, location2)
to get the distance (edited)if (primal_character)
(edited) if (primal_character != nullptr)
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.28.29333\include\xtree(1458): message : see reference to function template instantiation 'std::_Tree_find_result<std::_Tree_node<std::pair<const StringType,nlohmann::basic_json<std::map,std::vector,StringType,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>>,std::_Default_allocator_traits<_Alloc>::void_pointer> *> std::_Tree<std::_Tmap_traits<_Kty,_Ty,_Pr,_Alloc,false>>::_Find_lower_bound<_Other>(const _Keyty &) const' being compiled
1> with
1> [
1> StringType=std::string,
1> _Alloc=std::allocator<std::pair<const std::string,nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>>>,
1> _Kty=std::string,
1> _Ty=nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>,
1> _Pr=nlohmann::basic_json<std::map,std::vector,std::string,bool,int64_t,uint64_t,double,std::allocator,nlohmann::adl_serializer>::object_comparator_t,
1> _Other=SIZE_T,
1> _Keyty=SIZE_T
1> ]
When saving from a json variable to an ofstream file ?/mtape start
/mtape stop
(edited)COMMANDS.AddConsoleCommand("mtape", ChatCommand);
(edited)ArkApi::GetCommands()
void GivePlayerStat(AShooterCharacter* Char, EPrimalCharacterStatusValue::Type Stat, float AmountToGive)
{
if (Char && Char->MyCharacterStatusComponentField() && AmountToGive > 0)
{
UPrimalCharacterStatusComponent* Comp = Char->MyCharacterStatusComponentField();
float CurrentStat = Comp->BPGetCurrentStatusValue(Stat);
float MaxStat = Comp->BPGetMaxStatusValue(Stat);
float NewStat;
if (CurrentStat + AmountToGive >= MaxStat)
NewStat = MaxStat;
else
NewStat = CurrentStat + AmountToGive;
Comp->BPDirectSetCurrentStatusValue(Stat, NewStat);
Comp->ServerSyncReplicatedValues();
}
}
if (ArkApi::GetApiUtils().GetStatus() == ArkApi::ServerStatus::Ready)
Log::GetLog()
does will be in the API logs.#pragma comment(lib, "Permissions.lib")
1>Suicide.cpp
1>C:\******\ARK-Server-API-master\version\Core\Public\API\UE\Templates\Tuple.h(409,121): error C2760: syntax error: unexpected token ')', expected 'expression'
1>C:\******\ARK-Server-API-master\version\Core\Public\API\UE\Templates\Tuple.h(455): message : see reference to class template instantiation 'UE4Tuple_Private::TTupleImpl<TIntegerSequence<uint32,Indices...>,Types...>' being compiled
1>C:\******\ARK-Server-API-master\version\Core\Public\API\UE\Templates\Tuple.h(419,94): error C2760: syntax error: unexpected token ')', expected 'expression'
1>Done building project "Suicide.vcxproj" -- FAILED.
Tuple.h
//#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
// #define USE_TUPLE_AUTO_RETURN_TYPES (PLATFORM_COMPILER_HAS_AUTO_RETURN_TYPES && USING_CODE_ANALYSIS)
//#else
#define USE_TUPLE_AUTO_RETURN_TYPES 1
//#endif
c++
void Hook_AShooterGameMode_RemovePlayerFromTribe(AShooterGameMode* _this, unsigned __int64 TribeID, unsigned __int64 PlayerDataID, bool bDontUpdatePlayerState)
{
bool bCanNeverLeaveTribe = config["General"].value("LifetimeTribeMembership", false);
if (bCanNeverLeaveTribe) {
Log::GetLog()->info("CannotRemovePlayerFromTribe");
return;
}
AShooterGameMode_RemovePlayerFromTribe_original(_this, TribeID, PlayerDataID, bDontUpdatePlayerState);
}
void Hook_AShooterPlayerState_ServerRequestLeaveTribe_Implementation(AShooterPlayerState* _this)
{
AShooterPlayerState_ServerRequestLeaveTribe_Implementation_original(_this);
}
void Hook_AShooterPlayerState_ServerRequestLeaveTribe_Implementation(AShooterPlayerState* _this)
{
AShooterPlayerState_ServerRequestLeaveTribe_Implementation_original(_this);
}
void Hook_AShooterPlayerState_ServerRequestLeaveTribe_Implementation(AShooterPlayerState* _this)
{
return;
}
szitem["Word"]
to std::stringFTribeData* CreatedTribeData = static_cast<FTribeData*>(FMemory::Malloc(0x200));
RtlSecureZeroMemory(CreatedTribeData, 0x200);
ArkApi::GetApiUtils().GetShooterGameMode()->GetOrLoadTribeData(CreatedTribeID, CreatedTribeData);
FMemory::Free(CreatedTribeData);
c++
bool Hook_APrimalDinoCharacter_Die(APrimalDinoCharacter* _this, float KillingDamage, FDamageEvent* DamageEvent, AController* Killer, AActor* DamageCauser)
{
return APrimalDinoCharacter_Die_original(_this, KillingDamage, DamageEvent, Killer, DamageCauser);
}
thank you in advancec++
FString myDescriptiveName = _this->DescriptiveNameField();
(edited)
uint64 playerTribeId = player->TargetingTeamField();
const int dinoTribeId = dino->TargetingTeamField();
if (playerTribeId != dinoTribeId) <--- this line is crashing
return;
void Imprint(AShooterPlayerController* player, FString* message, EChatSendMode::Type /*unused*/)
{
if (!player || !player->PlayerStateField() || ArkApi::IApiUtils::IsPlayerDead(player))
return;
const uint64 steamid = ArkApi::IApiUtils::GetSteamIdFromController(player);
uint64 playerTribeId = player->TargetingTeamField();
AActor* Actor = player->GetPlayerCharacter()->GetAimedActor(ECC_GameTraceChannel2, nullptr, 0.0, 0.0, nullptr, nullptr, false, false, false);
if (Actor && Actor->IsA(APrimalDinoCharacter::GetPrivateStaticClass()) == false) {
ArkApi::GetApiUtils().SendChatMessage(player, GetText("Sender"), *GetText("Invalid"));
return;
}
APrimalDinoCharacter* dino = static_cast<APrimalDinoCharacter*>(Actor);
const int dinoTribeId = dino->TargetingTeamField();
if (playerTribeId != dinoTribeId) {
ArkApi::GetApiUtils().SendChatMessage(player, GetText("Sender"), *GetText("NotInTribe"));
return;
}
....
}
(edited)DECLARE_HOOK(AShooterPlayerController_OnAddItemFinished, void, AShooterCharacter*, bool, TArray<unsigned __int64>, unsigned __int64);
void Hook_AShooterPlayerController_OnAddItemFinished(AShooterCharacter* _this, bool bSuccess, TArray<unsigned __int64> SteamItemUserIds, unsigned __int64 SteamID)
{
const FLinearColor color{ 255, 255, 255, 255 };
ArkApi::GetApiUtils().SendServerMessageToAll(color, "[TEST]Player added item!");
}
(edited) ArkApi::GetHooks().SetHook("AShooterPlayerController.OnAddItemFinished", &Hook_AShooterPlayerController_OnAddItemFinished, &AShooterPlayerController_OnAddItemFinished_original);
void CC_RunString(APlayerController* player_controller, FString* cmd, bool /*unused*/)
{
ArkApi::GetApiUtils().SendServerMessageToAll(FColorList::Blue, "Testing!");
if (cmd != nullptr && cmd->Len() > 0)
luaL_dostring(CGELuaHandle::GetSingleton()->GetState(), cmd->ToString().c_str());
}
void RegisterLuaConCommands()
{
auto& commands = ArkApi::GetCommands();
commands.AddConsoleCommand("lua_run", &CC_RunString);
}
cheat lua_run
? (edited)c++
// A
typedef unordered_map<string, int> Sub; // possibly 30+ entries
typedef unordered_map<string, Sub> Main; // possibly 30 or fewer
// B
typedef vector<tuple<string, int>> Sub; // possibly 30+ entries
typedef unordered_map<string, Sub> Main; // possibly 30 or fewer
(edited)// m_pInstance is AShooterCharacter*
auto ctrl = m_pInstance->GetCharacterController();
auto shooter_ctrl = static_cast<AShooterPlayerController*>(ctrl);
shooter_ctrl->AddExperience(100, false, false);
AShooterPlayerController* aspc = ArkApi::GetApiUtils().FindControllerFromCharacter(character);
(edited)AShooterPlayerController* aspc = ArkApi::GetApiUtils().FindControllerFromCharacter(character);
(edited) AShooterPlayerController* FindControllerFromCharacter(AShooterCharacter* character) const
{
AShooterPlayerController* result = nullptr;
const auto& player_controllers = GetWorld()->PlayerControllerListField();
for (TWeakObjectPtr<APlayerController> player_controller : player_controllers)
{
auto* shooter_pc = static_cast<AShooterPlayerController*>(player_controller.Get());
if (shooter_pc->GetPlayerCharacter() == character)
{
result = shooter_pc;
break;
}
}
return result;
}
while that field should always have the player controllerFindControllerFromCharacter
to use the field if the field is non-nullFindControllerFromCharacter
has null checks but GetSteamName
does not for exampleAShooterPlayerController* FindControllerFromCharacter(AShooterCharacter* character) const
{
if (character)
{
AShooterPlayerController* result = (character->GetOwnerController() != nullptr) ?
static_cast<AShooterPlayerController*>(character->GetOwnerController())
:
static_cast<AShooterPlayerController*>(character->GetInstigatorController());
return result;
}
return nullptr;
}
GetOwnerController
GetOwnerController
compress2
error is fixed with DumpAll i'm pretty sure which is why we need to just release the API with it enabled. I have been providing it that way in my early releases on my discord.compress2
error is fixed with DumpAll i'm pretty sure which is why we need to just release the API with it enabled. I have been providing it that way in my early releases on my discord. compress2
error is fixed with DumpAll i'm pretty sure which is why we need to just release the API with it enabled. I have been providing it that way in my early releases on my discord. typedef std::tuple<const char*, int> CCEntry_t;
typedef std::unique_ptr<CCEntry_t> CCEntryptr_t;
typedef std::vector<CCEntryptr_t> CCVec_t; // contains the console commands along with their luareg index
(edited)auto Singleton = (UPrimalGlobals*)Globals::GEngine()()->GameSingletonField();
auto GameData = Singleton->PrimalGameDataOverrideField() ? Singleton->PrimalGameDataOverrideField() : Singleton->PrimalGameDataField();
auto EngramClasses = GameData->EngramBlueprintEntriesField();
(edited)auto Singleton = (UPrimalGlobals*)Globals::GEngine()()->GameSingletonField();
auto GameData = Singleton->PrimalGameDataOverrideField() ? Singleton->PrimalGameDataOverrideField() : Singleton->PrimalGameDataField();
auto EngramClasses = GameData->EngramBlueprintEntriesField();
for (auto x : EngramClasses)
{
auto sub = x->BluePrintEntryField().uClass->GetDefaultObject();
auto casted = static_cast<UPrimalItem*>(sub);
float damage = casted->GetWeaponTemplateMeleeDamageAmount();
}
struct EngramEntry
{
FString name;
TSubclassOf<UPrimalItem>* engram;
int level{};
};
extern TMap<FString, EngramEntry> g_EngramMap;
TMap<FString, FString, FDefaultSetAllocator, TDefaultMapKeyFuncs<FString, FString, 0> >
struct FMyStruct
{
// String which identifies our key
FString UniqueID;
// Some state which doesn't affect struct identity
float SomeFloat;
explicit FMyStruct(float InFloat)
:UniqueID (FGuid::NewGuid().ToString())
, SomeFloat(InFloat)
{
}
};
template <typename ValueType>
struct TMyStructMapKeyFuncs :
BaseKeyFuncs<
TPair<FMyStruct, ValueType>,
FString
>
{
private:
typedef BaseKeyFuncs<
TPair<FMyStruct, ValueType>,
FString
> Super;
public:
typedef typename Super::ElementInitType ElementInitType;
typedef typename Super::KeyInitType KeyInitType;
static KeyInitType GetSetKey(ElementInitType Element)
{
return Element.Key.UniqueID;
}
static bool Matches(KeyInitType A, KeyInitType B)
{
return A.Compare(B, ESearchCase::CaseSensitive) == 0;
}
static uint32 GetKeyHash(KeyInitType Key)
{
return FCrc::StrCrc32(*Key);
}
};
struct FStringStruct
{
// String which identifies our key
FString UniqueID;
// Some state which doesn't affect struct identity
float SomeFloat;
explicit FStringStruct(float InFloat)
:UniqueID(FGuid::NewGuid().ToString())
, SomeFloat(InFloat)
{
}
};
struct MyStruct
{
FString a;
void* whateveryouwant;
}
map<std::string, MyStruct> myMap
//#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
// #define USE_TUPLE_AUTO_RETURN_TYPES (PLATFORM_COMPILER_HAS_AUTO_RETURN_TYPES && USING_CODE_ANALYSIS)
//#else
#define USE_TUPLE_AUTO_RETURN_TYPES 1
//#endif
(edited)#define TUPLES_USE_DEFAULTED_FUNCTIONS 1
?version\Core\Public\API\UE\Containers\../Windows/MicrosoftPlatformString.h(24): error C2664: 'errno_t strcpy_s(char *,rsize_t,const char *)': cannot convert argument 1 from 'WIDECHAR *' to 'char *'
version\Core\Public\API\UE\Containers\../Windows/MicrosoftPlatformString.h(24): note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\ucrt\string.h(32): note: see declaration of 'strcpy_s'
It seems UE/BasicTypes is including headers that, at least on my system, contradicts each other. Has anyone encountered this before?item->DescriptiveNameBaseField() = "Trophy";
item->UpdatedItem(false);
Log::GetLog()->warn(item->DescriptiveNameBaseField().ToString());
AttackerShooterController->GetPlayerInventory()->AddItemObject(item);
This gives the actual item i want to, the log shows the name ive given it but the client doesnt see itactor->MulticastProperty(FName(L"varNameWithoutField", EFindName::FName_Add, false));
item->OwnerInventoryField()->RemoveItem(&item->ItemIDField(), false, false, true, true);
item->OwnerInventoryField()->IncrementItemTemplateQuantity(item->ClassField(), -1, true, false, nullptr, nullptr, false, false, false, false, true, false, true);
item->OwnerInventoryField()->RemoveItem(&item->ItemIDField(), false, false, true, true);
FCustomItemData cdata;
cdata.CustomDataName = FName("Alpha Trophy1", EFindName::FNAME_Add);
alphatrophyitem->CustomItemNameField() = L"Alpha Trophy";
alphatrophyitem->SetCustomItemData(&cdata);
alphatrophyitem->UpdatedItem(false);
static_cast<APrimalStructureItemContainer*>(Stand)->MyInventoryComponentField()->AddItemObject(alphatrophyitem);
c++
DECLARE_HOOK(UPrimalItem_BeginDestroy, void, UPrimalItem*);
void Hook_UPrimalItem_BeginDestroy(UPrimalItem* _this)
{
UPrimalItem_BeginDestroy_original(_this);
}
ArkApi::GetHooks().SetHook("UPrimalItem.BeginDestroy", &Hook_UPrimalItem_BeginDestroy, &UPrimalItem_BeginDestroy_original);
ArkApi::GetHooks().DisableHook("UPrimalItem.BeginDestroy", &Hook_UPrimalItem_BeginDestroy);
(edited)TWeakObjectPtr<UPrimalItem> item_ptr = GetWeakReference(item);
item_ptr.Get()
if (item_ptr.Get() != nullptr)
inline AActor* BPSpawnActor(UWorld* uworld, FString blueprint, FVector* location, FRotator* rotation, bool do_begin_play)
{
//auto* world = ArkApi::GetApiUtils().GetWorld();
if (!uworld) {
return nullptr;
}
UClass* object_class = UVictoryCore::BPLoadClass(&blueprint);
if (!object_class) {
return nullptr;
}
FActorSpawnParameters new_actor_spawn_params;
new_actor_spawn_params.bDeferBeginPlay = true;
auto* actor = uworld->SpawnActor(object_class, location, rotation, &new_actor_spawn_params);
if (actor) {
if (do_begin_play) {
actor->BeginPlay();
actor->ForceReplicateNow(false, false);
}
return actor;
}
return nullptr;
}
auto* world = ArkApi::GetApiUtils().GetWorld();
auto character = player_controller->GetPlayerCharacter();
auto location = character->RootComponentField()->RelativeLocationField();
FString blueprint = "Blueprint'/Game/PrimalEarth/Structures/StorageBox_Small.StorageBox_Small'";
auto* actor = BPSpawnActor(uworld, blueprint, &location,
UWorld* world = ArkApi::GetApiUtils().GetWorld();
FString tek_path = FString("Blueprint'/Game/PrimalEarth/Structures/StorageBox_TekShield.StorageBox_TekShield'");
UClass* structure_class = UVictoryCore::BPLoadClass(&tek_path);
if (!structure_class)
Log::GetLog()->critical("There was an error while processing new structures");
AActor* new_actor = world->SpawnActor(structure_class, &loc, &rot, ¶ms);
if (new_actor && new_actor->IsA(APrimalStructureItemContainer::GetPrivateStaticClass()))
spoling_time - GetWorld()->GetTimeSeconds()
Structure->ForceReplicateNow(false, false);
or
Structure->MulticastProperty(FName("bIsLocked", EFindName::FNAME_Add));
(edited)if (ArkApi::GetApiUtils().GetStatus() == ArkApi::ServerStatus::Ready)
{
// code...
}
here->bForceNoPinLocking();
here->bIsLocked() = false;
here->bIsPinLocked() = false;
primal_structure->bBPOverrideAllowStructureAccess() = true;
here->ForceReplicateNow(false, false);
here->MulticastProperty(FName("bIsLocked", EFindName::FNAME_Add));
auto actor = world->SpawnActor(structure_class, &location, rotation, ¶ms);
auto* primal_structure = static_cast<APrimalStructure*>(actor);
primal_structure->HealthField() = primal_structure->MaxHealthField();
primal_structure->StructureIDField() = 10 + 1;
primal_structure->TargetingTeamField() = database->getServerTribeID();
if (primal_structure->IsA(APrimalStructureItemContainer::GetPrivateStaticClass())) {
APrimalStructureItemContainer* here = static_cast<APrimalStructureItemContainer*>(primal_structure);
here->bForceNoPinLocking();
here->bIsLocked() = false;
here->bIsPinLocked() = false;
here->ForceReplicateNow(false, false);
here->MulticastProperty(FName("bIsLocked", EFindName::FNAME_Add));
}
if
condition bellow, and sometimes it does not: std::wstring blueprint = L"/Game/PrimalEarth/CoreBlueprints/Weapons/PrimalItem_WeaponMetalHatchet.PrimalItem_WeaponMetalHatchet_C";
UObject* object = Globals::StaticLoadObject(UObject::StaticClass(), nullptr, blueprint.c_str(), nullptr, 0, 0, true);
if (!object)
// Error
It seems like I need to wait for the garbage collector or something in the background to happen before I can call this code again. If I call it two times in a row without waiting a little the second call returns NULL.
I don't know what is happening exactly and I'm searching a way to always be able to load the blueprint object.
Any help appreciated thank you.if
condition bellow, and sometimes it does not: std::wstring blueprint = L"/Game/PrimalEarth/CoreBlueprints/Weapons/PrimalItem_WeaponMetalHatchet.PrimalItem_WeaponMetalHatchet_C";
UObject* object = Globals::StaticLoadObject(UObject::StaticClass(), nullptr, blueprint.c_str(), nullptr, 0, 0, true);
if (!object)
// Error
It seems like I need to wait for the garbage collector or something in the background to happen before I can call this code again. If I call it two times in a row without waiting a little the second call returns NULL.
I don't know what is happening exactly and I'm searching a way to always be able to load the blueprint object.
Any help appreciated thank you. static bool MySpawnDrop(unsigned long long steamId, const wchar_t* blueprint, int amount, float item_quality, bool force_blueprint, float min_random_quality, float life_span, float offsetX, float offsetY)
{
ArkApi::IApiUtils& utils = ArkApi::GetApiUtils();
AShooterPlayerController* aShooterPC = utils.FindPlayerFromSteamId(steamId);
if (!aShooterPC)
return false;
FVector pos = utils.GetPosition(aShooterPC);
pos.X += offsetX;
pos.Y += offsetY;
UObject* object = Globals::StaticLoadObject(UObject::StaticClass(), nullptr, blueprint, nullptr, 0, 0, true);
if (object == nullptr)
return false;
TSubclassOf<UPrimalItem> archetype;
archetype.uClass = reinterpret_cast<UClass*>(object);
UPrimalItem* item = UPrimalItem::AddNewItem(archetype, nullptr, false, false, item_quality, false, amount, force_blueprint, 0.0F, false, nullptr, min_random_quality);
if (!item)
return false;
FItemNetInfo* info = static_cast<FItemNetInfo*>(FMemory::Malloc(0x400));
RtlSecureZeroMemory(info, 0x400);
item->GetItemNetInfo(info, false);
TSubclassOf<ADroppedItem> archetype_dropped;
archetype_dropped.uClass = reinterpret_cast<UClass*>(object);
FVector zero_vector{ 0.0F, 0.0F, 0.0F };
FRotator rot{ 0.0F, 0.0F, 0.0F };
ADroppedItem* dropped = UPrimalInventoryComponent::StaticDropItem(aShooterPC, info, archetype_dropped, &rot, true, &pos, &rot, true, false, false, true, nullptr, &zero_vector, nullptr, life_span);
FMemory::Free(info);
if (!dropped)
return false;
return true;
}
if (object == nullptr)
return false;
sometimesstatic bool MySpawnDrop(unsigned long long steamId, FString* blueprint, int amount, float item_quality, bool force_blueprint, float min_random_quality, float life_span)
{
ArkApi::IApiUtils& utils = ArkApi::GetApiUtils();
AShooterPlayerController* aShooterPC = utils.FindPlayerFromSteamId(steamId);
if (!aShooterPC)
return false;
UClass* bpUClass = UVictoryCore::BPLoadClass(blueprint);
if (!bpUClass)
return false;
TSubclassOf<UPrimalItem> archetype = TSubclassOf<UPrimalItem>(bpUClass);
UPrimalItem* item = UPrimalItem::AddNewItem(archetype, nullptr, false, false, item_quality, false, amount, force_blueprint, 0.0F, false, nullptr, min_random_quality);
if (!item)
return false;
ADroppedItem* dropped = NULL;
FVector zero_vector{ 0.0F, 0.0F, 0.0F };
FRotator rot{ 0.0F, 0.0F, 0.0F };
FVector pos = utils.GetPosition(aShooterPC);
FItemNetInfo* info = static_cast<FItemNetInfo*>(FMemory::Malloc(0x400));
if (!info)
return MyRemoveItem(&item);
try
{
RtlSecureZeroMemory(info, 0x400);
item->GetItemNetInfo(info, false);
TSubclassOf<ADroppedItem> archetype_dropped = TSubclassOf<ADroppedItem>(bpUClass);
dropped = UPrimalInventoryComponent::StaticDropNewItemWithInfo(aShooterPC, info, archetype_dropped, &rot, true, &pos, &rot, true, false, false, true, nullptr, zero_vector, nullptr, life_span);
}
catch (...) {}
FMemory::Free(info);
if (dropped == NULL)
return MyRemoveItem(&item);
return true;
}
(edited)static FORCEINLINE bool MySpawnInInventory(unsigned long long steamId, FString* blueprint, int amount, float item_quality, bool force_blueprint, float min_random_quality)
{
UClass* bpUClass = UVictoryCore::BPLoadClass(blueprint);
if (!bpUClass)
return false;
AShooterPlayerController* aShooterPC = ArkApi::GetApiUtils().FindPlayerFromSteamId(steamId);
if (!aShooterPC)
return false;
AShooterCharacter* aShooterCharacter = static_cast<AShooterCharacter*>(aShooterPC->CharacterField());
if (!aShooterCharacter)
return false;
UPrimalInventoryComponent* inventoryComponent = aShooterCharacter->MyInventoryComponentField();
if (!inventoryComponent)
return false;
// Probably some cleaner way to check if item is stackable here:
UPrimalItem* tmp = (UPrimalItem*)(bpUClass);
UPrimalItem* tmpB = (UPrimalItem*)(bpUClass);
int quantityOverride = 2;
bool canStack = (inventoryComponent->bAllowItemStacking()() && tmp && tmpB && tmp->CanStackWithItem(tmpB, &quantityOverride));
TSubclassOf<UPrimalItem> archetype = TSubclassOf<UPrimalItem>(bpUClass);
UPrimalItem* item = UPrimalItem::AddNewItem(archetype, nullptr, false, !canStack, item_quality, false, amount, force_blueprint, 0.0F, false, nullptr, min_random_quality);
if (!item)
return false;
bool isUsableConsumable = item->IsUsableConsumable();
try
{
UPrimalItem* item2 = inventoryComponent->AddItemObjectEx(item, false, false, !canStack, true, !isUsableConsumable, false, aShooterCharacter, false, nullptr, false);
if (item2 == NULL)
return MyRemoveItem(&item);
}
catch (...)
{
return MyRemoveItem(&item);
}
return true;
}
MySpawnDrop()
function. On the first call I enter the if (dropped == NULL)
return MyRemoveItem(&item);
but on second call it worksstatic FORCEINLINE bool MyRemoveItem(UPrimalItem** item)
{
// Probably some cleaner way to make sure item has been removed from game here:
try
{
if ((*item)->bAddedToWorldItemMap()())
(*item)->RemoveFromWorldItemMap();
(*item)->ConditionalBeginDestroy();
(*item) = NULL;
}
catch (...) {}
return false;
}
UPrimalItem* tmp = (UPrimalItem*)(bpUClass);
UPrimalItem* tmpB = (UPrimalItem*)(bpUClass);
I'd use (UPrimalItem*)(bpUClass->GetDefaultObject(true));
AShooterCharacter* aShooterCharacter = static_cast<AShooterCharacter*>(aShooterPC->CharacterField());
Also this will get the dino pawn (and thus cast is null) if riding dino. You can use aShooterPc->GetPlayerCharacter() as a more reliable way to get the player characterbool RemoveItem(FItemNetID * itemID, bool bDoDrop, bool bSecondryAction, bool bForceRemoval, bool showHUDMessage)
(*item)->ConditionalBeginDestroy();
(*item) = NULL;
This tells GC I'm done with it, if I'm correct (edited)bAddedToWorldItemMap()
RemoveFromWorldItemMap()
so I added them tooUPrimalItem::RemoveFromWorldItemMap()
this call could be a bad thing or the thing saving me from segfault, I don't know what it does actually (edited)if
condition yet). (edited)AddNewItem()
function is responsible for generating these IDs auto actor = world->SpawnActor(structure_class, &location, rotation, ¶ms);
auto* primal_structure = static_cast<APrimalStructure*>(actor);
primal_structure->HealthField() = primal_structure->MaxHealthField();
primal_structure->TargetingTeamField() = database.get()->getServerTribeID();
if (primal_structure->IsA(APrimalStructureItemContainer::GetPrivateStaticClass())) {
APrimalStructureItemContainer* here = static_cast<APrimalStructureItemContainer*>(primal_structure);
here->BeginPlay();
here->bIsLocked() = false;
here->ForceReplicateNow(false, false);
here->MulticastProperty(FName("bIsLocked", EFindName::FNAME_Add));
}
FString classname = FString(TrophyBPPath);
UObject* trophyclass = UVictoryCore::BPLoadClass(&classname)->GetDefaultObject(true);
UPrimalItem* alphatrophyitem = static_cast<UPrimalItem*>(trophyclass);
if (alphatrophyitem) {
alphatrophyitem->NextSpoilingTimeField() = ArkApi::GetApiUtils().GetWorld()->TimeSecondsField() + 3600;
alphatrophyitem->UpdatedItem(false);
static_cast<APrimalStructureItemContainer*>(Stand)->MyInventoryComponentField()->AddItemObject(alphatrophyitem);
alphatrophyitem->AddToSlot(-1, true);
alphatrophyitem->AddToSlot(-1, true);
ServerAddItemToSlot
or ServerAddItemToSlot_Implementation
might do the trickstatic bool MyGiveItemNum(unsigned long long steamId, int itemNumId, int quantity, float quality, bool forceBlueprint)
{
ArkApi::IApiUtils& utils = ArkApi::GetApiUtils();
AShooterPlayerController* aShooterPC = utils.FindPlayerFromSteamId(steamId);
if (!aShooterPC)
return false;
UShooterCheatManager* cheatManager = utils.GetCheatManager(); // I tried with "GetCheatManagerByPC(aShooterPC);" it doesn't work either
if (!cheatManager)
return false;
cheatManager->GiveItemNumToPlayer(aShooterPC->GetLinkedPlayerID(), itemNumId, quantity, quality, forceBlueprint);
return true;
}
It works as expected on first call, but on subsequent calls nothing happens.
If I drop the item obtained with first call and call my function again it is now working, but item I receive is the same item that I just dropped (if I pick the dropped item it vanishes as it is already in my inventory).PC->GiveItem(&SpawnedItems, &fBlueprint, Amount, Quality, IsBlueprint, false, 0);
!aShooterPC
condition)static bool MyGiveItemNum(unsigned long long steamId, FString* blueprint, int quantity, float quality, bool forceBlueprint)
{
ArkApi::IApiUtils& utils = ArkApi::GetApiUtils();
AShooterPlayerController* aShooterPC = utils.FindPlayerFromSteamId(steamId);
if (!aShooterPC)
return false;
TArray<UPrimalItem*> spawnedItems;
if (!aShooterPC->GiveItem(&spawnedItems, blueprint, quantity, quality, forceBlueprint, false, 0))
return false;
return true;
}
First call works but subsequent call nothing happens (well actually I think I know what happens, it gives me the same item but the anti-dupe check is preventing the same item from getting added again) (edited)!aShooterPC->GiveItem(
False condition on the 2nd call (edited)static bool MyGiveItemNum(unsigned long long steamId, FString* blueprint, int quantity, float quality, bool forceBlueprint)
{
ArkApi::IApiUtils& utils = ArkApi::GetApiUtils();
AShooterPlayerController* aShooterPC = utils.FindPlayerFromSteamId(steamId);
if (!aShooterPC)
return false;
TArray<UPrimalItem*> spawnedItems;
if (!aShooterPC->GiveItem(&spawnedItems, blueprint, quantity, quality, forceBlueprint, false, 0))
return false;
return true;
}
First call works but subsequent call nothing happens (well actually I think I know what happens, it gives me the same item but the anti-dupe check is preventing the same item from getting added again) (edited)if
condition, and what is weird now if I call it a third time it works againif
condition before next call is workingbool ChatMessageCallback(AShooterPlayerController* _AShooterPlayerController, FString* Message, EChatSendMode::Type Mode, bool spam_check, bool command_executed)
{
// here
}
ArkApi::GetCommands().AddChatCommand("/bp", &GetIP);
void GetIP(AShooterPlayerController* PC, FString* message, int mode)
void SetItemStatValue(UPrimalItem* item, EPrimalItemStat::Type item_stat_type, const float new_value)
{
float newStat = 0.f;
if (item_stat_type == EPrimalItemStat::Armor || item_stat_type == EPrimalItemStat::MaxDurability)
{
item->ItemStatValuesField()()[item_stat_type] = 0;
float old_stat_modifier = item->GetItemStatModifier(item_stat_type);
item->ItemStatValuesField()()[item_stat_type] = 1;
newStat = (new_value - old_stat_modifier) / (item->GetItemStatModifier(item_stat_type) - old_stat_modifier);
}
else if (item_stat_type == EPrimalItemStat::WeaponDamagePercent)
{
item->ItemStatValuesField()()[item_stat_type] = 0;
float min_damage = item->GetItemStatModifier(item_stat_type);
item->ItemStatValuesField()()[item_stat_type] = 65535;
float max_damage = item->GetItemStatModifier(item_stat_type);
float diff = max_damage - min_damage;
float multiplier = 65535 / diff;
newStat = new_value / 100.f - min_damage;
newStat *= multiplier;
newStat += 1.f;
}
if (newStat >= 65536.f)
newStat = 65535;
item->ItemStatValuesField()()[item_stat_type] = newStat;
if (item_stat_type == EPrimalItemStat::MaxDurability)
{
if (item->bUseItemDurability()())
item->ItemDurabilityField() = item->GetItemStatModifier(item_stat_type);
}
#ifdef Ark
item->UpdatedItem(false);
#endif
#ifdef Atlas
item->UpdatedItem();
#endif
}
catch (...) { /*doNothing*/ }
like me ^^ (edited)catch (...) { /*doNothing*/ }
(but this one is the worst) (edited)static bool MyGiveItem(uint64 targetSteamId, FString* blueprintPath, int quantity, float quality, bool forceBlueprint, float minRandomQuality)
{
// Get target player
AShooterPlayerController* target = ArkApi::GetApiUtils().FindPlayerFromSteamId(targetSteamId);
if (!target)
return false;
// Get target player's inventory
UPrimalInventoryComponent* targetInventory = target->GetPlayerInventoryComponent();
if (!targetInventory)
return false;
// Get target player character
AShooterCharacter* targetCharacter = target->GetPlayerCharacter();
if (!targetCharacter)
return false;
// Get item class
UClass* bpUClass = UVictoryCore::BPLoadClass(blueprintPath);
if (!bpUClass)
return false;
// Is item stackable?
UPrimalItem* defaultItem = (UPrimalItem*)(bpUClass->GetDefaultObject(true));
int quantityOverride = 2;
bool canStack = (targetInventory->bAllowItemStacking()() && defaultItem && defaultItem->CanStackWithItem(defaultItem, &quantityOverride));
// Create the item
UPrimalItem* item = UPrimalItem::AddNewItem(TSubclassOf<UPrimalItem>(bpUClass), nullptr, false, !canStack, quality, false, quantity, forceBlueprint, 0.0F, false, nullptr, minRandomQuality);
if (!item)
return false;
// Add the item to target's inventory
if (!targetInventory->AddItemObjectEx(item, false, false, !canStack, true, !item->IsUsableConsumable(), false, targetCharacter, false, nullptr, false))
return false;
// Return success
return true;
}
(edited) // Is item stackable?
UPrimalItem* cdo = (UPrimalItem*)(bpUClass->GetDefaultObject(true)); //(UPrimalItem*)(bpUClass);
int quantityOverride = 2;
bool canStack = (targetInventory->bAllowItemStacking()() && cdo && cdo->CanStackWithItem(cdo, &quantityOverride));
bpUClass->GetDefaultObject(true)
should be returning the same pointer anyways for tmp
and tmpB
(edited)static FORCEINLINE bool MyGiveItem(uint64 targetSteamId, FString* blueprintPath, int quantity, float quality, bool forceBlueprint, float minRandomQuality) { /*...*/ }
this constraint causes inline functions to behave as if they were instantiated functions
The insertion, called inline expansion or inlining, occurs only if the compiler's cost-benefit analysis shows it's worthwhile.
Even with __forceinline, the compiler can't inline code in all circumstances. The compiler can't inline a function if: *Insert long list of things here*
on the MS link you gave me ^^ (edited) // New Messaging config allows for multi language support.
// In the example below you could add a Spanish group to Permissions and anyone within that group will see the messages in Spanish instead of Default (english)
"Messages":{
"Groups":{
"Default": {
"SenderNameInChat": "OFFLINE RAIDING ALERT",
"Warning": "Warning",
"LastWarning": "LAST WARNING",
"WarningNotification": "You are Offline Raiding, if you continue, then your Name, Tribe and Location could be revealed to all players.",
"msgMap": "Map",
"msgPlayer": "Player",
"msgIs": "is",
"msgFromTribe": "from tribe",
"msgOffliningTribe": "offlining tribe"
},
"Spanish": {
"SenderNameInChat": "ALERTA DE RAIDING SIN CONEXIÓN",
"Warning": "Advertencia",
"LastWarning": "ÚLTIMA ADVERTENCIA",
"WarningNotification": "Estás incursionando sin conexión, si continúas, entonces tu nombre, tribu y ubicación podrían revelarse a todos los jugadores.",
"msgMap": "Mapa",
"msgPlayer": "Jugador",
"msgIs": "es",
"msgFromTribe": "de la tribu",
"msgOffliningTribe": "tribu delineante"
}
}
}
(edited) "French": {
"SenderNameInChat": "ALERTE RAID HORS CONNEXION",
"Warning": "Avertissement",
"LastWarning": "DERNIER AVERTISSEMENT",
"WarningNotification": "Vous êtes en train de faire un raid hors connexion, si vous continuez, alors votre Nom, Tribu et Position pourraient être révélés à l'ensemble des joueurs.",
"msgMap": "Carte",
"msgPlayer": "Joueur",
"msgIs": "est",
"msgFromTribe": "de la tribu",
"msgOffliningTribe": "raid la tribu"
}
(edited)DECLARE_HOOK definition not found
but it compiles nicely without warnings.DECLARE_HOOK definition not found
but it compiles nicely without warnings. "French": {
"SenderNameInChat": "ALERTE RAID HORS CONNEXION",
"Warning": "Avertissement",
"LastWarning": "DERNIER AVERTISSEMENT",
"WarningNotification": "Vous êtes en train de faire un raid hors connexion, si vous continuez, alors votre Nom, Tribu et Position pourraient être révélés à l'ensemble des joueurs.",
"msgMap": "Carte",
"msgPlayer": "Joueur",
"msgIs": "est",
"msgFromTribe": "de la tribu",
"msgOffliningTribe": "raid la tribu"
}
(edited)FString GetTribeName(AShooterPlayerController* playerController)
{
auto playerState = playerController->GetShooterPlayerState();
if (playerState)
{
FString playerOrTribeName;
playerState->GetPlayerOrTribeName(&playerOrTribeName);
return playerOrTribeName;
}
return L"";
}
and FString GetTribeName(AShooterPlayerController* playerController)
{
auto playerState = playerController->GetShooterPlayerState();
if (playerState)
{
auto tribeData = playerState->MyTribeDataField();
if (tribeData)
return tribeData->TribeNameField();
}
return L"";
}
Or maybe there is an easier way to get the tribe name?AShooterPlayerState::GetPlayerOrTribeName()
returns the player name if he is not in a tribe, is it correct? (edited)AShooterPlayerState::GetPlayerOrTribeName()
returns the player name if he is not in a tribe, is it correct? (edited)FString *__fastcall AShooterPlayerState::GetPlayerOrTribeName(AShooterPlayerState *this, FString *result)
{
AShooterPlayerState *v3; // [rsp+40h] [rbp+8h]
FString *v4; // [rsp+48h] [rbp+10h]
v4 = result;
v3 = this;
if ( AShooterPlayerState::IsInTribe(this) )
TArray<wchar_t,FDefaultAllocator>::TArray<wchar_t,FDefaultAllocator>(&v4->Data, &v3->MyTribeData.TribeName.Data);
else
TArray<wchar_t,FDefaultAllocator>::TArray<wchar_t,FDefaultAllocator>(&v4->Data, &v3->PlayerName.Data);
return v4;
}
GetPlayerOrTribeName
function myself with IDA I should be able to find others then.void GivePointsHandler()
{
// Static variables declaration and initialization.
static std::map<unsigned long long, double> playersPoints = std::map<unsigned long long, double>();
static int loopCnt = 0;
// Iterate connected players
AShooterPlayerController* shooterPc;
AShooterCharacter* pc;
unsigned long long steamId;
const auto& player_controllers = ArkApi::GetApiUtils().GetWorld()->PlayerControllerListField();
if (player_controllers.Num() > 0)
for (TWeakObjectPtr<APlayerController> player_controller : player_controllers)
if (shooterPc = static_cast<AShooterPlayerController*>(player_controller.Get()))
if ((pc = shooterPc->GetPlayerCharacter()) && pc->IsConscious()) // If player is alive and conscious
if ((steamId = shooterPc->GetUniqueNetIdAsUINT64()) > 0Ui64) // If Steam ID is valid
{
if (playersPoints.find(steamId) == playersPoints.end())
playersPoints[steamId] = 1.0; // Player just earned his first point.
else
playersPoints[steamId] = (playersPoints[steamId] + 1.0); // Add 1 point to player points.
}
if (++loopCnt >= 300) // Increment loop counter and check if it has been 5 minutes
{
// Make a local copy of current players points
std::map<unsigned long long, double> playersPointsCopy = std::map<unsigned long long, double>(playersPoints);
// Reset current players points
playersPoints.clear();
// Reset loop counter
loopCnt = 0;
// TODO: Process/save players points local copy asynchronously here
}
}
(edited)void Load()
{
ArkApi::GetCommands().AddOnTimerCallback("giveplayerpoints", &GivePointsHandler); // Call GivePointsHandler every second
}
void Unload()
{
ArkApi::GetCommands().RemoveOnTimerCallback("giveplayerpoints");
}
PS: Edited code above with @WETBATMAN suggestions.
. (edited)AddOnTimerCallback()
from the API will call the callback every seconds, so I need something very efficient there.AddOnTimerCallback()
that triggers every minute instead of every second? (edited)void GivePointsHandler()
{
// Static variables declaration and initialization.
static std::map<unsigned long long, double> playersPoints = std::map<unsigned long long, double>();
static int loopCnt = 0;
// Iterate connected players
AShooterPlayerController* shooterPc;
AShooterCharacter* pc;
unsigned long long steamId;
const auto& player_controllers = ArkApi::GetApiUtils().GetWorld()->PlayerControllerListField();
if (player_controllers.Num() > 0)
for (TWeakObjectPtr<APlayerController> player_controller : player_controllers)
if (shooterPc = static_cast<AShooterPlayerController*>(player_controller.Get()))
if ((pc = shooterPc->GetPlayerCharacter()) && pc->IsConscious()) // If player is alive and conscious
if ((steamId = shooterPc->GetUniqueNetIdAsUINT64()) > 0Ui64) // If Steam ID is valid
{
if (playersPoints.find(steamId) == playersPoints.end())
playersPoints[steamId] = 1.0; // Player just earned his first point.
else
playersPoints[steamId] = (playersPoints[steamId] + 1.0); // Add 1 point to player points.
}
if (++loopCnt >= 300) // Increment loop counter and check if it has been 5 minutes
{
// Make a local copy of current players points
std::map<unsigned long long, double> playersPointsCopy = std::map<unsigned long long, double>(playersPoints);
// Reset current players points
playersPoints.clear();
// Reset loop counter
loopCnt = 0;
// TODO: Process/save players points local copy asynchronously here
}
}
(edited)API::Timer::Get().RecurringExecute(&callback, 60, -1, false);
&callback
on the game's main thread right?!playerCharacter->IsDead()
RemoveOnTimerCallback()
GetPlayerCharacter()
and GetCharacterStatusComponent()
two times if you enter the second if
^^GetPlayerCharacter()
and GetCharacterStatusComponent()
two times if you enter the second if
^^ eax
register and that we only need to use mov
to read from the pointer to keep things simple
doing
GetPlayerCharacter()
should translate to something like
enter 0,0
mov eax, ptr
leave
but a compiler that inlines this should produce
mov eax, ptr
which is the same as if you had read from the pointer directly. (edited)optimization issue
I never said it was an issue tho-O0
for some reasonPC
which could at any time become invalid-O3
-O3
by hand. The hundreds of extremely experience developers and the 30+ years of Computer Science is always going to beat whatever optimizations I can make.x86_64
intrinsics (edited)O3
is part of GCC and Clang (edited)bool RemoveSeeds(AShooterPlayerController* PC)
{
// Get player's inventory
UPrimalInventoryComponent* inventory = PC->GetPlayerInventory();
if (inventory == nullptr)
return false;
// Get berrybush-seed class
FString berryBushSeedBP = "Blueprint'/Game/PrimalEarth/CoreBlueprints/Items/Consumables/Seeds/PrimalItemConsumable_Seed_Test.PrimalItemConsumable_Seed_Test'";
UClass* bpUClass = UVictoryCore::BPLoadClass(&berryBushSeedBP);
if (bpUClass == nullptr)
return false;
TSubclassOf<UPrimalItem> bpSubClass = TSubclassOf<UPrimalItem>(bpUClass);
// Get berrybush-seeds from inventory
TArray<UPrimalItem*> berryBushSeedItems;
inventory->FindAllItemsOfType(&berryBushSeedItems, bpSubClass, true, true, false, false, false, false, false);
int nbBerryBushSeeds = (int)berryBushSeedItems.Num();
if (nbBerryBushSeeds <= 0)
{
FString Text = L"You don't have any berrybush seed!";
PC->ClientServerChatDirectMessage(&Text, FLinearColor(255, 0, 0), false);
return false;
}
// Remove berrybush-seeds from inventory
for (int i = 0; i < nbBerryBushSeeds; i++)
{
UPrimalItem* currentItem = berryBushSeedItems[i];
if (currentItem != nullptr)
{
FItemNetInfo itemNetInfo;
if (currentItem->GetItemNetInfo(&itemNetInfo, false) != nullptr)
{
FItemNetID itemNetId = itemNetInfo.ItemIDField();
inventory->RemoveItem(&itemNetId, false, false, true, false);
}
}
}
return true;
}
(edited)for
loop at the end, not sure why. for (int i = 0; i < nbBerryBushSeeds; i++)
{
UPrimalItem* currentItem = berryBushSeedItems[i];
if (currentItem != nullptr)
currentItem->OwnerInventoryField()->RemoveItem(¤tItem->ItemIDField(), false, false, true, true);
}
-EnableIdlePlayerKick
in command line arguments. The KickIdlePlayersPeriod
is set to 3600
.
The problem is that AFK players are getting auto-kicked before reaching the 3600 seconds.
Some people tried to increase the 3600 seconds value here without success: https://www.reddit.com/r/ARKone/comments/jmwr47/possible_workaround_for_30m_disconnect_change_the/
They say it's because the timer is client-side managed. They say that players need to restart their game in order to properly reset this timer.
I will assume that what they is true, so I need to handle a timer on server side.
I can setup stuff in the PostLogin function, but I have no idea of how I can get the number of seconds elapsed since last player action. (edited) -EnableIdlePlayerKick
in command line arguments. The KickIdlePlayersPeriod
is set to 3600
.
The problem is that AFK players are getting auto-kicked before reaching the 3600 seconds.
Some people tried to increase the 3600 seconds value here without success: https://www.reddit.com/r/ARKone/comments/jmwr47/possible_workaround_for_30m_disconnect_change_the/
They say it's because the timer is client-side managed. They say that players need to restart their game in order to properly reset this timer.
I will assume that what they is true, so I need to handle a timer on server side.
I can setup stuff in the PostLogin function, but I have no idea of how I can get the number of seconds elapsed since last player action. (edited)void __usercall AShooterPlayerController::TickActor(AShooterPlayerController *this@<rcx>, float DeltaTime@<xmm1>, ELevelTick TickType@<r8d>, FActorTickFunction *ThisTickFunction@<r9>, signed __int64 a5@<rax>)
v79 = UWorld::TimeSince(v78, v655->LastMultiUseInteractionTime);
if ( v79 > v113 )
{
if ( UWorld::TimeSince(v32, this->LastLargeMoveTime) > v50
|| (v33 = this->GetWorld(this), UWorld::TimeSince(v33, this->LastNotOnUnriddenDinoTime) > v50) )
{
v34 = this->GetWorld(this);
if ( UWorld::TimeSince(v34, this->LastMultiUseInteractionTime) > v50 )
LastNotOnUnriddenDinoTime
LastNotOnUnriddenDinoTime
UPrimalLocalProfile
struct UPrimalLocalProfile : UObject
{
TArray<TSubclassOf<UPrimalItem>,FDefaultAllocator> AchievementItemsCollectedList;
TArray<FString,FDefaultAllocator> UnlockedAchievementsList;
TArray<unsigned int,FDefaultAllocator> GlobalExplorerNoteUnlocks;
TArray<FString,FDefaultAllocator> UnlockedAchievements;
// ...
};
(edited)
FVector& pos = player->RootComponentField()->RelativeLocationField();
Character->SetActorLocation(&pos, false);
where is of type AShooterCharactervoid __fastcall AShooterPlayerController::SetPlayerPos(AShooterPlayerController *this, float X, float Y, float Z)
{
_BOOL8 v4; // r8
APawn *v5; // [rsp+20h] [rbp-48h]
FVector NewLocation; // [rsp+28h] [rbp-40h] BYREF
FVector v7[3]; // [rsp+40h] [rbp-28h] BYREF
NewLocation.X = X;
NewLocation.Y = Y;
NewLocation.Z = Z;
v5 = this->Pawn;
if ( v5 )
{
AActor::SetActorLocation(v5, &NewLocation, 0);
LOBYTE(v4) = 1;
v5->ResetCollisionSweepLocation(v5, &NewLocation, v4);
}
else if ( AShooterPlayerController::IsSpectator(this) )
{
qmemcpy(v7, &NewLocation, 0xCui64);
AShooterPlayerController::ClientSetSpectatorLocation(this, v7);
}
}
c++
TArray<AActor*> FoundActors;
UGameplayStatics::GetAllActorsOfClass(ArkApi::GetApiUtils().GetWorld(), APrimalStructure::GetPrivateStaticClass(), &FoundActors);
c++
for (const auto& actor : FoundActors)
{
APrimalStructure* myStructure = static_cast<APrimalStructure*>(actor);
}
FString & DroppedByNameField() { return *GetNativePointerField<FString*>(this, "ADroppedItem.DroppedByName"); }
unsigned __int64& DroppedByPlayerIDField() { return *GetNativePointerField<unsigned __int64*>(this, "ADroppedItem.DroppedByPlayerID"); }
void Hook_UPrimalInventoryComponent_DropItem(UPrimalInventoryComponent* _this, FItemNetInfo* theInfo, bool bOverrideSpawnTransform, FVector* LocationOverride, FRotator* RotationOverride, bool bPreventDropImpulse, bool bThrow, bool bSecondryAction, bool bSetItemDropLocation)
{
Log::GetLog()->warn("Hook_UPrimalInventoryComponent_DropItem");
int index;
auto itemNetID = theInfo->ItemIDField();
auto item = _this->FindItem(&itemNetID, false, false, &index);
if (item != nullptr) {
FString itemName;
item->NameField().ToString(&itemName);
Log::GetLog()->warn("found: {}", itemName.ToString());
}
else
{
Log::GetLog()->warn("not found");
}
UPrimalInventoryComponent_DropItem_original(_this, theInfo, bOverrideSpawnTransform, LocationOverride, RotationOverride, bPreventDropImpulse, bThrow, bSecondryAction, bSetItemDropLocation);
}
c++
void IDSteam(AShooterPlayerController* player_controller, FString* /*unused*/, EChatSendMode::Type /*unused*/)
{
const uint64 steam_id = ArkApi::IApiUtils::GetSteamIdFromController(player_controller);
ArkApi::GetApiUtils().SendChatMessage(player_controller, "ID: ", steam_id);
}
{}
for each argument you want to output. You can put multiple times the same argument by using the arg's index: {0}, {1}, {2}, etc...
ArkApi::GetApiUtils().SendChatMessage(player_controller, "ID: {}", steam_id);
c++
void IDSteam(AShooterPlayerController* player_controller, FString* /*unused*/, EChatSendMode::Type /*unused*/)
{
const uint64 steam_id = ArkApi::IApiUtils::GetSteamIdFromController(player_controller);
FString myMessage = FString::Format(L"ID: {}", steam_id);
ArkApi::GetApiUtils().SendChatMessage(player_controller, L"Server", ArkApi::Tools::Utf8Decode(myMessage.ToString()).c_str());
}
(edited)bool ChatListener::on_message_received(AShooterPlayerController* player_controller, FString* msg, EChatSendMode::Type t, bool x, bool y)
{
return true;
}
BOOL ChatListener::load()
{
Api::GetCommands().AddOnChatMessageCallback(chatMessageCallbackID, std::bind(&ChatListener::on_message_received, this));
return TRUE;
}
(edited)bool ChatListener::on_message_received(AShooterPlayerController* player_controller, FString* msg, EChatSendMode::Type t, bool x, bool y)
{
return true;
}
BOOL ChatListener::load()
{
Api::GetCommands().AddOnChatMessageCallback(chatMessageCallbackID, std::bind(&ChatListener::on_message_received, this));
return TRUE;
}
(edited)std::bind(ChatListener::on_message_received( _1, _2, _3, _4,_5))
(edited)std::bind(ChatListener::on_message_received( _1, _2, _3, _4,_5))
(edited)std::bind(this,ChatListener::on_message_received( _1, _2, _3, _4,_5));
std::bind(this,ChatListener::on_message_received( _1, _2, _3, _4,_5));
std::bind(&ChatListener::on_message_received, this, placeholders...);
AddOnChatMessageCallback(chatMessageCallbackID, std::bind(ChatListener::on_message_received,this, _1, _2, _3, _4, _5));
/dn off
that player will not see any Floating Damage Text
and if my player /dn on
he will see it.
The hook that seems right for the job... would be
DECLARE_HOOK(AShooterGameMode_NotifyDamage, void, AShooterGameMode*, AActor*, FDamageEvent*, AController*, AActor*);
but after some testing... this hook is triggered even with no players on the server. if i try to obtain a steam_id inside that hook my ark server crashes.
been loooking / testing on the hook part for the last 2 days... thought someone could point me in the right direction.
This is my very first plugin... so its super simple...
I'm hoping to get into a few bigger projects next.
Thanks in advance!! Cheers./dn off
that player will not see any Floating Damage Text
and if my player /dn on
he will see it.
The hook that seems right for the job... would be
DECLARE_HOOK(AShooterGameMode_NotifyDamage, void, AShooterGameMode*, AActor*, FDamageEvent*, AController*, AActor*);
but after some testing... this hook is triggered even with no players on the server. if i try to obtain a steam_id inside that hook my ark server crashes.
been loooking / testing on the hook part for the last 2 days... thought someone could point me in the right direction.
This is my very first plugin... so its super simple...
I'm hoping to get into a few bigger projects next.
Thanks in advance!! Cheers. DECLARE_HOOK(AShooterPlayerController_ClientAddFloatingDamageText, void, AShooterPlayerController*, FVector_NetQuantize, int, int);
void Hook_AShooterPlayerController_ClientAddFloatingDamageText(AShooterPlayerController* _this, FVector_NetQuantize AtLocation, int DamageAmount, int FromTeamID)
{
if (_this)
{
const uint64 steam_id = ArkApi::GetApiUtils().GetSteamIdFromController(_this);
if (players_disabled.Contains(steam_id)) { return; }
}
AShooterPlayerController_ClientAddFloatingDamageText_original(_this, AtLocation, DamageAmount, FromTeamID);
}
I did that to implement it, when players do /dmg off it adds their steamid to players_disabled
(TArray<uint64>
) (edited)// AShooterPlayerController* PC; (not an admin)
UCheatManager* cm = PC->CheatManagerField();
if (cm != nullptr)
{
FString className = L"SupplyCrate_Level60_Double_C";
cm->Summon(&className);
}
Trying to spawn some beacons for players (as a Christmas gift). (edited)auto* cheatManager = static_cast<UShooterCheatManager*>(player_controller->CheatManagerField());
cheatManager->Summon(&fclass_name);
UShooterCheatManager
c++
typedef std::map<uint64, FVector> SteamIdFVectorMap;
SteamIdFVectorMap lastPlayerLocation{};
...
// teleport from A to B
const FVector m1 = shooterplayer_controller->RootComponentField()->RelativeLocationField();
lastPlayerLocation.insert({ steam_id, m1 });
shooterplayer_controller->SetPlayerPos(newLoc.X, newLoc.Y, newLoc.Z);
...
// teleport back to A
if (lastPlayerLocation.size() > 0)
{
auto result = lastPlayerLocation.find(steam_id);
if (result != lastPlayerLocation.end())
{
auto playerLoc = result->second;
shooterplayer_controller->SetPlayerPos(playerLoc.X, playerLoc.Y, playerLoc.Z);
lastPlayerLocation.erase({ steam_id });
}
}
(edited)const FVector& m1
shooterplayer_controller->RootComponentField()->RelativeLocationField()
FVector Location;
_this->RootComponentField()->GetWorldLocation(&Location);
ccc
ones to make sure there is no difference between world or relative as well.bIsSleeping
?void Deposit(AShooterPlayerController* player_controller, FString* , EChatSendMode::Type )
{
try
{
player_controller->GetPlayerInventory();
UPrimalInventoryComponent* inventory = player_controller->GetPlayerInventory();
if (inventory != nullptr)
{
FString Stone = "Blueprint'/Game/PrimalEarth/CoreBlueprints/Resources/PrimalItemResource_Stone.PrimalItemResource_Stone'";
UClass* bpUClass = UVictoryCore::BPLoadClass(&Stone);
TSubclassOf<UPrimalItem> bpSubClass = TSubclassOf<UPrimalItem>(bpUClass);
TArray<UPrimalItem*> Stoneitems;
inventory->FindAllItemsOfType(&Stoneitems, bpSubClass, true, true, false, false, false, false, false);
int nStone = (int)Stoneitems.Num();
if (nStone <= 0)
{
ArkApi::GetApiUtils().SendChatMessage(player_controller, *GetText("Sender"), *GetText("NoStone"));
}
else
{
int number = 0;
for (size_t i = 0; i < nStone; i++)
{
number += Stoneitems[i]->GetItemQuantity();
UPrimalItem* currItem = Stoneitems[i];
if (currItem != nullptr)
{
currItem->OwnerInventoryField()->RemoveItem(&currItem->ItemIDField(), false, false, true, true);
}
}
FString text = L"You have " + FString::FromInt(number) + L"stone in your inventory";
ArkApi::GetApiUtils().SendChatMessage(player_controller, *GetText("Sender"), *text);
FVector position = player_controller->GetPlayerCharacter()->RootComponentField()->RelativeLocationField();
int foundation = 10;;
}
}
}
catch (const std::exception& error)
{
ArkApi::GetApiUtils().SendChatMessage(player_controller, *GetText("Sender"), *GetText("Error"));
}
}
(edited)UKismetSystemLibrary::SphereOverlapActors_NEW
on this discord for examplesUKismetSystemLibrary::SphereOverlapActors_NEW
on this discord for examples UPrimalInventoryComponent
of the supply crates if they have tribute
properties set.tribute
is for the boss arena teleports. I'm looking for 3 things: ArkData, Creature Data and the transfer to other servers buttons.
I think there's a way to hook into the item/creature download process itself and cancel it. somehow. Not sure how to prevent the tabs from showing thoughAShooterPlayerController* PC
?
The call PC->GetPlayerCharacter()->RootComponentField()->RelativeRotationField();
gives me the correct player location but the point is not on the ground (it is in the air, half player's height from what I can see). (edited)AShooterPlayerController* PC
?
The call PC->GetPlayerCharacter()->RootComponentField()->RelativeRotationField();
gives me the correct player location but the point is not on the ground (it is in the air, half player's height from what I can see). (edited)char __fastcall UVictoryCore::GetGroundLocation(UWorld *forWorld, FVector *theGroundLoc, FVector *StartLoc, FVector *OffsetUp, FVector *OffsetDown)
just found thisbool GetGroundLocation(AShooterCharacter* shooterCharacter, FVector* outGroundLocation)
{
FVector offsetUp = FVector(0.0F, 0.0F, 160.0F);
FVector offsetDown = FVector(0.0F, 0.0F, -5000.0F);
return shooterCharacter->GetGroundLocation(outGroundLocation, &offsetUp, &offsetDown);
}
(edited)APrimalCharacter
has it APrimalCharacter
, you can simply use the one from UVictoryCore
namespace. Instead of an APrimalCharacter
it needs a pointer to the UWorld
and an FVector
starting point (then it will search upside and downside for the ground, starting from this point). (edited)APrimalCharacter
with the dino ID) permanently? (edited)void ScaleActor(AActor* actor, float ratio)
{
FVector currentScale;
actor->GetActorScale3D(¤tScale);
currentScale.X = currentScale.X * ratio;
currentScale.Y = currentScale.Y * ratio;
currentScale.Z = currentScale.Z * ratio;
actor->SetActorScale3D(¤tScale);
}
But I don't know if it's the proper way to do it because I'm not even replicating state over network here. (edited)actor->ForceReplicateNow(false, false);
after scaling actor @Pelayori?For scaling, you also have to make sure that on your Mesh Component you set "Component Replicates"
https://answers.unrealengine.com/questions/375098/actor-scale-not-replicating.html (edited)meshComponentName
parameter to target another type of mesh. Also my function stops when first mesh is found but for more complex objects you might need to repeat the SetIsReplicated(true)
for every mesh component.SetIsReplicated(true)
) not a property bool EnableMeshCompReplicationAndScale(AActor* actor, float ratio)
{
USceneComponent* sceneComp = actor->RootComponentField();
if (sceneComp == nullptr)
return false;
sceneComp->SetIsReplicated(true);
sceneComp->SetWorldScale3D(FVector(ratio, ratio, ratio));
return true;
}
There you gobool EnableMeshCompReplicationAndScale(AActor* actor, float ratio)
{
USceneComponent* sceneComp = actor->RootComponentField();
if (sceneComp == nullptr)
return false;
sceneComp->SetIsReplicated(true);
sceneComp->SetWorldScale3D(FVector(ratio, ratio, ratio));
return true;
}
There you go for (auto group_iter = groups_map.begin(); group_iter != groups_map.end(); ++group_iter)
{
std::string strFoundValue{};
int intFoundValue{};
std::vector<int> arrIntFoundValue{};
try
{
auto foundValue = group_iter.value().value("SomeSetting", "");
strFoundValue = foundValue;
}
catch (const std::exception&)
{
try
{
auto foundValue = group_iter.value().value("SomeSetting", -1);
intFoundValue = foundValue;
}
catch (const std::exception&)
{
try
{
auto foundValue = group_iter.value().value("SomeSetting", std::vector<int>{});
arrIntFoundValue = foundValue;
}
catch (const std::exception&)
{
}
}
}
if (!(strFoundValue == "")) {
// string was found
}
else if (intFoundValue >= 0) {
// int was found
}
else if (arrIntFoundValue.size() > 0) {
// array was found
}
}
any feedback is appreciated (edited)is_string
, is_whateveryouneed
FVector::Distance()
GetDistanceTo
from one actor to another#include "Requests.h"
void VerifyCallback(bool success, std::string response) {
if (!success) {
Log::GetLog()->error("Failed verifying with server.");
}
Log::GetLog()->info(response);
}
void VerifyCommand(AShooterPlayerController* playerController, FString* command, EChatSendMode::Type chatType) {
uint64 steamID = ArkApi::GetApiUtils().GetSteamIdFromController(playerController);
std::string url = "http://localhost:6100/?steam64id=" + std::to_string(steamID) + "&arkid=Ragnarok-PF"; // +ArkApi::GetApiUtils().GetGameState()->ServerSessionNameField().ToString();
API::Requests::Get().CreateGetRequest(url, &VerifyCallback);
}
"MinApiVersion":
from the PluginInfo.json
file if I want to target current ArkAPI version (edited)"MinApiVersion": 3.53
?"MinApiVersion":
from the PluginInfo.json
file if I want to target current ArkAPI version (edited)bool Hook_AShooterGameMode_HandleNewPlayer(AShooterGameMode* _this, AShooterPlayerController* NewPlayer, UPrimalPlayerData* PlayerData, AShooterCharacter* PlayerCharacter, bool bIsFromLogin)
{
return AShooterGameMode_HandleNewPlayer_original(_this, NewPlayer, PlayerData, PlayerCharacter, bIsFromLogin);
}
UPrimalInventoryComponent::DropInventoryDeposit
UPrimalInventoryComponent::DropInventoryDeposit
`inventory->DropInventoryDeposit(ArkApi::GetApiUtils().GetWorld()->TimeSecondsField() + 3600, false, false, nullptr, nullptr, nullptr, nullptr, FString(""), FString(""), -1, 300, false, -1, false, nullptr, false);
dino->bDroppedInventoryDeposit() = true;
`void dropAll(UPrimalInventoryComponent* inv)
{
if (inv)
{
APrimalStructureItemContainer* deathCache = nullptr;
inv->DropInventoryDeposit(ArkApi::GetApiUtils().GetWorld()->TimeSecondsField() + 3600, false, false, nullptr, nullptr, &deathCache, nullptr, FString(""), FString(""), -1, 300, false, -1, false, nullptr, false);
if (deathCache)
{
// do stuff with the death cache
}
}
}
inline bool discord_enabled;
inline std::string discord_sender_name = "TribeChat";
inline FString discord_webhook_url = "URL";
template <typename T, typename... Args>
void PostToDiscord(T* msg, Args&&... args)
{
static_cast<AShooterGameState*>(ArkApi::GetApiUtils().GetWorld()->GameStateField())->HTTPPostRequest(discord_webhook_url, FString::Format(msg, std::forward<Args>(args)...));
}
nlohmann::json request;
FString MapName = GetMapName();
request["username"] = ArkApi::Tools::Utf8Encode(*MapName);
request["content"] = ArkApi::Tools::Utf8Encode(*Data);
void PostDiscordWebhook(FString URL, nlohmann::json Content)
{
if (!URL.IsEmpty())
API::Requests::Get().CreatePostRequest(URL.ToString(), [](bool Sucess, std::string Result) {}, Content.dump(), "application/json");
}
if (Data.Contains(L"@"))
Data = Data.Replace(L"@", L"");
c++
shooterplayer_controller->EnemyInVisible(true);
c++
shooterplayer_controller->LeaveMeAlone();
i havent tested yetplayer->GetPlayerCharacter()->MyInventoryComponentField()->InventoryItemsField()
seems not working, only print engrams of crafteable items in hand. Thanksplayer->GetPlayerCharacter()->MyInventoryComponentField()->InventoryItemsField()
seems not working, only print engrams of crafteable items in hand. Thanks bool GetGroundLocation(AShooterCharacter* shooterCharacter, FVector* outGroundLocation)
{
FVector offsetUp = FVector(0.0F, 0.0F, 160.0F);
FVector offsetDown = FVector(0.0F, 0.0F, -5000.0F);
return shooterCharacter->GetGroundLocation(outGroundLocation, &offsetUp, &offsetDown);
}
(edited)ShooterGameServer.exe!APrimalCharacter::GetGroundLocation() (0x00007ff7126f7a01) + 62 bytes [f:\build\lostisland\projects\shootergame\source\shootergame\private\primalcharacter.cpp:8125]
could you share a working example code snippet? thxFVector GetGroundLocation(FVector* startLoc)
{
FVector result(0);
auto* world = ArkApi::GetApiUtils().GetWorld();
if (world) {
FVector offsetUp = { 0, 0, 0 };
FVector offsetDown = { 0, 0, INT_MIN };
UVictoryCore::GetGroundLocation(world, &result, startLoc, &offsetUp, &offsetDown);
}
return result;
}
FVector GetGroundLocation(FVector* startLoc)
{
FVector result(0);
auto* world = ArkApi::GetApiUtils().GetWorld();
if (world) {
FVector offsetUp = { 0, 0, 0 };
FVector offsetDown = { 0, 0, INT_MIN };
UVictoryCore::GetGroundLocation(world, &result, startLoc, &offsetUp, &offsetDown);
}
return result;
}
player->GetPlayerCharacter()->MyInventoryComponentField()->InventoryItemsField()
seems not working, only print engrams of crafteable items in hand. Thanks plugins.load
Function Declaration for DECLARE_HOOK not found.
I followed the basic plugin guideFunction Declaration for DECLARE_HOOK not found.
I followed the basic plugin guide #include <API/ARK/Ark.h>
(edited)c++
#pragma once
#include <API/ARK/Ark.h>
#include <json.hpp>
#include <string>
#include <format>
DECLARE_HOOK(AShooterGameSession_OnNumConnectedPlayersChanged, void, AShooterGameSession*, int);
void Hook_AShooterGameSession_OnNumConnectedPlayersChanged(AShooterGameSession* _this, int NewPlayersCount)
{
AShooterGameSession_OnNumConnectedPlayersChanged_original(_this, NewPlayersCount);
UShooterCheatManager* cm = ArkApi::GetApiUtils().GetCheatManager();
if (NewPlayersCount < 1)
{
cm->SetGlobalPause(true);
printf("Pausing Server\n");
}
else
{
cm->SetGlobalPause(false);
printf("Pausing Server\n");
}
}
void InitHooks()
{
ArkApi::GetHooks().SetHook("AShooterGameSession.OnNumConnectedPlayersChanged", &Hook_AShooterGameSession_OnNumConnectedPlayersChanged, &AShooterGameSession_OnNumConnectedPlayersChanged_original);
}
void RemoveHooks()
{
ArkApi::GetHooks().DisableHook("AShooterGameSession.OnNumConnectedPlayersChanged", &Hook_AShooterGameSession_OnNumConnectedPlayersChanged);
}
c++
#pragma once
#include <API/ARK/Ark.h>
#include <json.hpp>
#include <string>
#include <format>
DECLARE_HOOK(AShooterGameSession_OnNumConnectedPlayersChanged, void, AShooterGameSession*, int);
void Hook_AShooterGameSession_OnNumConnectedPlayersChanged(AShooterGameSession* _this, int NewPlayersCount)
{
AShooterGameSession_OnNumConnectedPlayersChanged_original(_this, NewPlayersCount);
UShooterCheatManager* cm = ArkApi::GetApiUtils().GetCheatManager();
if (NewPlayersCount < 1)
{
cm->SetGlobalPause(true);
printf("Pausing Server\n");
}
else
{
cm->SetGlobalPause(false);
printf("Pausing Server\n");
}
}
void InitHooks()
{
ArkApi::GetHooks().SetHook("AShooterGameSession.OnNumConnectedPlayersChanged", &Hook_AShooterGameSession_OnNumConnectedPlayersChanged, &AShooterGameSession_OnNumConnectedPlayersChanged_original);
}
void RemoveHooks()
{
ArkApi::GetHooks().DisableHook("AShooterGameSession.OnNumConnectedPlayersChanged", &Hook_AShooterGameSession_OnNumConnectedPlayersChanged);
}
SetGlobalPause
is what crashesUShooterCheatManager* cm = ArkApi::GetApiUtils().GetCheatManager();
is NULL at player count changeRCONEnable=True
to make this work (edited)APrimalStructureDoor::GotoDoorState(this, X);
with X being 0, 1 or 2, but I don't know if 0 = closed, 1 = open and why is there even a 2 there ^^ (edited)PreventTransferForClassName="TekStrider_Character_BP_C"
in my Game.ini
but players are still able to upload Tek Striders using Oblisks/Beacons/etc..
I guess I have to do that with a plugin, or is my Game.ini
configuration wrong? (edited)CanCryo()
hooking but I don't find any interesting function for releasing from cryopod (unable to find a function like "CanUncryo()
"). (edited)TryDeploy
in blueprint, which can return a bool for success or not. But that needs process event hookingvoid Hook_UObject_ProcessEvent(UObject* _this, UFunction* Function, void* Params)
SpawnFromDinoDataEx
and it should have a valid PC on TamerController
parameterSpawnFromDinoDataEx
is not too late in the chain?return whatever;
Hook_UObject_ProcessEvent(UObject* _this, UFunction* Function, void* Params)
how do I know if it's a TryDeploy()
invocation? and eventually get parameters because a void*
is not very helpful ^^CanCryo()
hooking but I don't find any interesting function for releasing from cryopod (unable to find a function like "CanUncryo()
"). (edited)AShooterWeapon* NewWeapon->AssociatedPrimalItemField()
and checking the class of returned UPrimalItem*
? (edited)APrimalDinoCharacter* Hook_APrimalDinoCharacter_SpawnFromDinoDataEx(APrimalDinoCharacter* _this, FARKDinoData* InDinoData, UWorld* InWorld, FVector* AtLocation, FRotator* AtRotation, bool* dupedDino, int ForTeam, bool bGenerateNewDinoID, AShooterPlayerController* TamerController, bool beginPlay)
{
if (TamerController != nullptr)
{
if (!IsAllowedToCryo(TamerController)) // This function is mine but you can guess what it does.
return nullptr;
}
return APrimalDinoCharacter_SpawnFromDinoDataEx_original(_this, InDinoData, InWorld, AtLocation, AtRotation, dupedDino, ForTeam, bGenerateNewDinoID, TamerController, beginPlay);
}
like Pelayori suggested (edited)SpawnFromDinoDataEx()
function. The definition for it in Actor.h uses "APrimalDinoCharacter.SpawnFromDinoData"
instead of "APrimalDinoCharacter.SpawnFromDinoDataEx"
. I learned the hard way that calling APrimalDinoCharacter::SpawnFromDinoDataEx()
from your own code gets ugly. Easy to fix if you need it. Don't think it matters if you're just hooking it the normal way.SpawnFromDinoDataEx()
function. The definition for it in Actor.h uses "APrimalDinoCharacter.SpawnFromDinoData"
instead of "APrimalDinoCharacter.SpawnFromDinoDataEx"
. I learned the hard way that calling APrimalDinoCharacter::SpawnFromDinoDataEx()
from your own code gets ugly. Easy to fix if you need it. Don't think it matters if you're just hooking it the normal way. UProperty* bIsCloningProp = CloningChamber->FindProperty(FName("bIsCloning", EFindName::FNAME_Add));
if (bIsCloningProp)
{
bool bIsCloning = bIsCloningProp->Get<bool>(CloningChamber); // variable value
}
c++
FARKDinoData dd{};
UProperty* myFARKDinoData = obj->FindProperty(FName("CloningDinoData", EFindName::FNAME_Add));
if (myFARKDinoData)
{
dd = myFARKDinoData->Get<FARKDinoData>(obj); // variable value
Log::GetLog()->warn("Dino Name = {}", dd.DinoName.ToString());
}
(edited)Log::GetLog()->warn("Dino Name = {}", GetBlueprintFromClass(dd.DinoClass).ToString());
FString GetBlueprintFromClass(UClass* object)
{
if (object != nullptr)
{
FString path_name;
object->GetDefaultObject(true)->GetFullName(&path_name, nullptr);
//ShowDebugMessage(fmt::format("GetBlueprintFromClass: {}",path_name.ToString()));
if (int find_index = 0; path_name.FindChar(' ', find_index))
{
path_name = "Blueprint'" + path_name.Mid(find_index + 1,
path_name.Len() - (find_index + (path_name.EndsWith(
"_C", ESearchCase::
CaseSensitive)
? 3
: 1))) + "'";
return path_name.Replace(L"Default__", L"", ESearchCase::CaseSensitive);
}
}
return FString("");
}
struct FARKDinoData
{
UClass* DinoClass;
TArray<unsigned char> DinoData;
FString DinoNameInMap;
FString DinoName;
bool bNetInfoFromClient;
};
c++
// make sure dino is asleep first
if (dino->bIsSleeping()())
{
dino->SetSleeping(false, false); // this seems to wake the dino up from sleeping
// remove cryo buff from dino
FString classString = L"Blueprint'/Game/Extinction/CoreBlueprints/Buffs/Buff_SummoningSickness.Buff_SummoningSickness'";
UClass* classObj = UVictoryCore::BPLoadClass(&classString);
if (dino != nullptr && classObj != nullptr)
{
TArray<APrimalBuff*> dino_buffs;
dino->GetAllBuffs(&dino_buffs);
for (APrimalBuff* dino_buff : dino_buffs)
{
const FString dino_bp_buff = ArkApi::GetApiUtils().GetBlueprint(dino_buff);
if (dino_bp_buff.Contains("Buff_SummoningSickness"))
{
dino_buff->Destroy(true, false);
break;
}
}
}
}
(edited)ArkApi::GetApiUtils().GetShooterGameMode()->ActiveEventField().ToString()
gives me "None" for some reason (I have Easter event enabled tho), any idea why? I'm calling this just after AShooterGameMode_InitGame_original()
.GetGameState()->ActiveEventField()
instead of GetShooterGameMode()->ActiveEventField()
.findfunction
and then you can call it with UFunction->ProcessEvent
void*
UFunction->ProcessEvent(this,func,(void*) args)
1>C:\plugins\mysql8.0.29-64\include\jdbc\cppconn\driver.h(74,21): error C2059: syntax error: 'function-style cast'
1>C:\plugins\mysql8.0.29-64\include\jdbc\cppconn\driver.h(74,26): error C2143: syntax error: missing ';' before '{'
1>C:\plugins\mysql8.0.29-64\include\jdbc\cppconn\driver.h(74,26): error C2447: '{': missing function header (old-style formal list?)
1>C:\plugins\mysql8.0.29-64\include\jdbc\cppconn\driver.h(75,26): warning C4002: too many arguments for function-like macro invocation 'check'
1>C:\plugins\mysql8.0.29-64\include\jdbc\cppconn\driver.h(75,21): error C2059: syntax error: 'function-style cast'
1>C:\plugins\mysql8.0.29-64\include\jdbc\cppconn\driver.h(75,26): error C2143: syntax error: missing ';' before '{'
1>C:\plugins\mysql8.0.29-64\include\jdbc\cppconn\driver.h(75,26): error C2447: '{': missing function header (old-style formal list?)
1>C:\plugins\mysql8.0.29-64\include\jdbc\cppconn\driver.h(83,3): warning C4002: too many arguments for function-like macro invocation 'check'
FString classbp = "Buff_CreativeModeHelper_C_0";
TArray<APrimalBuff*> p_buffs;
AShooterCharacter* Cplayer = static_cast<AShooterCharacter*>(player->CharacterField());
Cplayer->GetAllBuffs(&p_buffs);
for (APrimalBuff* buffs : p_buffs)
{
const FString BP_buffs = ArkApi::GetApiUtils().GetBlueprint(buffs);
if (BP_buffs.Contains(classbp))
{
buffs->Destroy(true, false);
}
}
FString classbp = "Buff_CreativeModeHelper";
(edited)c++
FString WeaponName;
character->CurrentWeaponField()->AssociatedPrimalItemField()->GetItemName(&WeaponName, false, true, nullptr);
if (WeaponName.Contains(L"Handcuffs"))
{
// player is wearing handcuffs
}
INSTEAD you should test the blueprint path:
c++
auto weaponBP = ArkApi::GetApiUtils().GetBlueprint(player_controller->GetPlayerCharacter()->CurrentWeaponField()->AssociatedPrimalItemField());
if (WeaponName.Contains(L"Handcuffs"))
{
// player is wearing handcuffs
}
Why? Because GetItemName() will return the weapon name in local language of the user.. So yeah, this works fine for English players.. everyone else..forgettaboutit!!
I just thought I would share (edited)c++
FString WeaponName;
character->CurrentWeaponField()->AssociatedPrimalItemField()->GetItemName(&WeaponName, false, true, nullptr);
if (WeaponName.Contains(L"Handcuffs"))
{
// player is wearing handcuffs
}
INSTEAD you should test the blueprint path:
c++
auto weaponBP = ArkApi::GetApiUtils().GetBlueprint(player_controller->GetPlayerCharacter()->CurrentWeaponField()->AssociatedPrimalItemField());
if (WeaponName.Contains(L"Handcuffs"))
{
// player is wearing handcuffs
}
Why? Because GetItemName() will return the weapon name in local language of the user.. So yeah, this works fine for English players.. everyone else..forgettaboutit!!
I just thought I would share (edited)try
{
uint64 steamid;
FString uname;
uint64 pid;
AShooterPlayerState* state = static_cast<AShooterPlayerState*>(player->PlayerStateField());
FTribeData* tdata;
int tmembers;
std::time_t set_time = std::time(nullptr);
do
{
steamid = ArkApi::GetApiUtils().GetSteamIdFromController(player);
player->GetPlayerCharacterName(&uname);
pid = ArkApi::GetApiUtils().GetPlayerID(player);
tdata = state->MyTribeDataField();
tmembers = tdata->MembersPlayerDataIDField().Num();
} while (!uname.IsEmpty() && steamid != 0 && pid != 0);
if (!check_for_info(fmt::format("SELECT * FROM users WHERE clusterid = '{}' AND steamdid = '{}' AND playerid = '{}'", clusterid, steamid, pid)))
send_info(fmt::format("INSERT INTO users (clusterid, steamid, userid, username, lastlogin, lastmap) VALUES ('{}','{}','{}','{}','{}','{}')", clusterid, steamid, pid, escape(uname.ToString()), set_time, mapname));
else
send_info(fmt::format("UPDATE users SET lastlogin = '{}', lastmap = '{}', username = '{}'", set_time, mapname, escape(uname.ToString())));
if (state)
{
if (tmembers > 0)
{
uint64 tribeid = tdata->TribeIDField();
FString tribename = tdata->TribeNameField();
if (tribeid != 0 && !tribename.IsEmpty())
{
if (!check_for_info(fmt::format("SELECT * FROM tribe_info WHERE cluserid = '{}' AND mapid = '{}' AND tribeid = '{}'", clusterid, serverid, tribeid)))
send_info(fmt::format("INSERT INTO tribe_info (clusterid, mapid, tribeid, tribename, count) VALUES ('{}','{}','{}','{}','{}')", clusterid, serverid, tribeid, escape(tribename.ToString()), tmembers));
else
send_info(fmt::format("UPDATE tribe_info SET tribename = '{}', count = '{}' WHERE clusterid = '{}' AND mapid = '{}' AND tribeid = '{}'", escape(tribename.ToString()), tmembers, clusterid, serverid, tribeid));
if (!check_for_info(fmt::format("SELECT * FROM user_tribe WHERE steamid = '{}' AND clusterid = '{}' AND mapid = '{}' AND tribeid = '{}'", steamid, clusterid, serverid, tribeid)))
send_info(fmt::format("INSERT INTO user_tribe (clusterid, steamid, mapid, tribeid, tribename, lastseen, count) VALUES ('{}','{}','{}','{}','{}','{}','1')", clusterid, steamid, serverid, tribeid, escape(tribename.ToString()), set_time));
else
send_info(fmt::format("UPDATE user_tribe SET count = count + 1, lastseen = '{}', tribename = '{}' WHERE steamid = '{}' AND clusterid = '{}' AND mapid = '{}' AND tribeid = '{}'", set_time, escape(tribename.ToString()), steamid, clusterid, serverid, tribeid));
}
}
}
return true;
}
catch (const std::exception& exception)
{
Log::GetLog()->error("({} {}) Unexpected error {}", __FILE__, __FUNCTION__, exception.what());
return false;
}
(edited)void Hook_APlayerController_ServerReceivedPlayerControllerAck_Implementation(APlayerController* _this)
void Hook_AShooterCharacter_AuthPostSpawnInit(AShooterCharacter* _this)
void Hook_AShooterCharacter_PossessedBy(AShooterCharacter* _this, AController* InController)
TArray<unsigned char> item_bytes;
item->GetItemBytes(&item_bytes);
For a few itens and only in few maps, this crashes the server. Is there a way around id? Can I catch exceptions for this kind of thing ? (edited)TArray<unsigned char> item_bytes;
item->GetItemBytes(&item_bytes);
For a few itens and only in few maps, this crashes the server. Is there a way around id? Can I catch exceptions for this kind of thing ? (edited)DECLARE_HOOK(AMissionType_BPOnMissionComplete, void, AMissionType*, AShooterCharacter*, AShooterPlayerController*, bool, float, TArray<UPrimalItem*>*);
void Hook_AMissionType_BPOnMissionComplete(AMissionType* _this, AShooterCharacter* ForPlayer,
AShooterPlayerController* ForController, bool bLastPhaseSuccess, float XPRewarded, TArray<UPrimalItem*>* ItemsRewarded)
{
AMissionType_BPOnMissionComplete_original(_this, ForPlayer, ForController, bLastPhaseSuccess, XPRewarded, ItemsRewarded);
}
DECLARE_HOOK(AMissionType_BPOnMissionComplete, void, AMissionType*, AShooterCharacter*, AShooterPlayerController*, bool, float, TArray<UPrimalItem*>*);
void Hook_AMissionType_BPOnMissionComplete(AMissionType* _this, AShooterCharacter* ForPlayer,
AShooterPlayerController* ForController, bool bLastPhaseSuccess, float XPRewarded, TArray<UPrimalItem*>* ItemsRewarded)
{
AMissionType_BPOnMissionComplete_original(_this, ForPlayer, ForController, bLastPhaseSuccess, XPRewarded, ItemsRewarded);
}
c++
#include <API/ARK/Ark.h>
?#pragma comment(lib, "ArkApi.lib")
AddChatCommand("/command", &func);
*
operator, like FuncWithArgs(*fstr);
(which isn't the case for AddChatCommand, but the errors mention it) (edited)*message
Permissions.dll
and recompile#include <Windows.h>
#include <API/ARK/Ark.h>
#include <Logger/Logger.h>
#pragma comment(lib, "ArkApi.lib")
void Load()
{
Log::Get().Init("YourPluginName");
<-- add commands here
}
void Unload()
{
}
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_PROCESS_DETACH:
Unload();
break;
}
return TRUE;
}
And the pluginInfo.json file in the folder "YourPluginName" should look like:
{
"FullName":"YourPluginName",
"Description":"Description of your plugin here",
"Version":1.0,
"MinApiVersion":3.2
}
(edited)c++
TArray<AActor*> FoundActors;
UGameplayStatics::GetAllActorsOfClass(reinterpret_cast<UObject*>(ArkApi::GetApiUtils().GetWorld()), APrimalStructure::GetPrivateStaticClass(), &FoundActors);
for (const auto& actor : FoundActors)
{
auto structure = static_cast<APrimalStructure*>(actor);
if (structure && structure->IsA(APrimalStructureItemContainer::GetPrivateStaticClass()))
{
// do something with structure
// structure->Destroy();
}
}
(edited)Destroy()
or Suicide()
DECLARE_HOOK(APrimalStructure_IsAllowedToBuild, int, APrimalStructure*, APlayerController*, FVector, FRotator, FPlacementData*, bool, FRotator, bool);
DECLARE_HOOK(APrimalStructure_CheckNotEncroaching, bool, APrimalStructure*, FVector, FRotator, AActor*, APrimalStructure*, APrimalStructure*, bool);
(edited)void ShowM(int delay)
{
if (delay > 0)
{
DEBUG_INFO("Timer {} ", delay);
delay--
API::Timer::Get().DelayExecute(&ShowM, 1, delay);
return;
}
}
I am trying to DelayExecute inside DelayExecute like the code above, This is crashing on the seconds execution. Is there a reason i cant call DelayExecute inside DelayExecute? I am trying to show a count down for the user something like 5, 4, 3, 2, 1.c++
std::string url = "http://*****:3000/api/client"; std::vector<std::string> headers; headers.push_back("Content-type:application/json"); headers.push_back("Authorization : Bearer " + std::string("aaaa"));
nlohmann::json request;
request["id"] = "2";
request["product"] = "Carno";
request["female"] = "1";
API::Requests::Get().CreatePostRequest(url, &VerifyCallback, request.dump(), headers);
{"status_code":400,"status_msg":"INVALID_REQUEST","status_overview":"failed"} (edited)c++
std::string url = "http://*****:3000/api/client"; std::vector<std::string> headers; headers.push_back("Content-type:application/json"); headers.push_back("Authorization : Bearer " + std::string("aaaa"));
nlohmann::json request;
request["id"] = "2";
request["product"] = "Carno";
request["female"] = "1";
API::Requests::Get().CreatePostRequest(url, &VerifyCallback, request.dump(), headers);
{"status_code":400,"status_msg":"INVALID_REQUEST","status_overview":"failed"} (edited)request.dump()
c++
std::string url = "http://*****:3000/api/client"; std::vector<std::string> headers; headers.push_back("Content-type:application/json"); headers.push_back("Authorization : Bearer " + std::string("aaaa"));
nlohmann::json request;
request["id"] = "2";
request["product"] = "Carno";
request["female"] = "1";
API::Requests::Get().CreatePostRequest(url, &VerifyCallback, request.dump(), headers);
{"status_code":400,"status_msg":"INVALID_REQUEST","status_overview":"failed"} (edited)void PostDiscordWebhook(FString URL, nlohmann::json Content)
{
if (!URL.IsEmpty())
API::Requests::Get().CreatePostRequest(URL.ToString(), [](bool Sucess, std::string Result) {}, Content.dump(), "application/json");
}
c++
void PostDiscordWebhook(FString URL, nlohmann::json Content)
{
if (!URL.IsEmpty())
API::Requests::Get().CreatePostRequest(URL.ToString(), [](bool Sucess, std::string Result) {Log::GetLog()->error(Result.c_str()); }, Content.dump(), "application/json");
}
nlohmann::json request1;
request1["id","1"];
request1["product_name","Carno"];
request1["points",100];
PostDiscordWebhook(url.c_str(), request1.dump());
API::Requests::Get().CreatePostRequest(webhookUrl, [](bool, std::string) {}, webhookContent.dump(), "application/json");
request1
rather than the dumped content in the function callvoid PostDiscordWebhook(const FString& URL, const nlohmann::json& Content)
{
if(URL.IsEmpty())
return;
API::Requests::Get().CreatePostRequest(URL.ToString(),
[](bool Sucess, std::string Result)
{
Log::GetLog()->error(Result.c_str());
}, Content.dump(), "application/json");
}
nlohmann::json request1;
request1["id","1"];
request1["product_name","Carno"];
request1["points",100];
PostDiscordWebhook(url.c_str(), request1);
json["key"] = val;
json["key"] = val;
void PostDiscordWebhook(const FString& URL, const nlohmann::json& Content)
{
if(URL.IsEmpty())
return;
API::Requests::Get().CreatePostRequest(URL.ToString(),
[](bool Sucess, std::string Result)
{
Log::GetLog()->error(Result.c_str());
}, Content.dump(), "application/json");
}
nlohmann::json request1;
request1["id"] = 1;
request1["product_name"] = "Carno";
request1["points"] = 100;
PostDiscordWebhook(url.c_str(), request1);
void PostDiscordWebhook(const FString& URL, const nlohmann::json& Content)
{
if(URL.IsEmpty())
return;
API::Requests::Get().CreatePostRequest(URL.ToString(),
[](bool Sucess, std::string Result)
{
Log::GetLog()->error(Result.c_str());
}, Content.dump(), "application/json");
}
nlohmann::json request1;
request1["id"] = 1;
request1["product_name"] = "Carno";
request1["points"] = 100;
PostDiscordWebhook(url.c_str(), request1);
c++
void PostDiscordWebhook(const FString& URL, const nlohmann::json& Content)
{
if(URL.IsEmpty())
return;
API::Requests::Get().CreatePostRequest(URL.ToString(),
[](bool Sucess, std::string Result)
{
Log::GetLog()->error(Result.c_str());
}, Content.dump(), "application/json");
}
nlohmann::json request1;
request1["id"] = 1;
request1["product_name"] = "Carno";
request1["points"] = 100;
PostDiscordWebhook(url.c_str(), request1);
AShooterPlayerController::ClientServerNotificationSingle
.
The last parameter is a message id, if you make it the same for your every second notifications, the text will be updated rather than resent (might as well add 1.5 seconds of display time so it doesn't blink either way)
About the latter I'm not sure.player->ClientServerNotificationSingle(
&msg,
disp_color,
disp_scale,
disp_time,
nullptr,
nullptr,
6349
);
DECLARE_HOOK(ReportCrash, int, void*);
Not sure if its just stuff I added to the plugin or if it just changed or something. (edited)line 7090
- static APrimalDinoCharacter* SpawnFromDinoDataEx(FARKDinoData* InDinoData, UWorld* InWorld, FVector* AtLocation, FRotator* AtRotation, bool* dupedDino, int ForTeam, bool bGenerateNewDinoID, AShooterPlayerController* TamerController, bool beginPlay) { return NativeCall<APrimalDinoCharacter*, FARKDinoData*, UWorld*, FVector*, FRotator*, bool*, int, bool, AShooterPlayerController*, bool>(nullptr, "APrimalDinoCharacter.SpawnFromDinoData", InDinoData, InWorld, AtLocation, AtRotation, dupedDino, ForTeam, bGenerateNewDinoID, TamerController, beginPlay); }
+ static APrimalDinoCharacter* SpawnFromDinoDataEx(FARKDinoData* InDinoData, UWorld* InWorld, FVector* AtLocation, FRotator* AtRotation, bool* dupedDino, int ForTeam, bool bGenerateNewDinoID, AShooterPlayerController* TamerController, bool beginPlay) { return NativeCall<APrimalDinoCharacter*, FARKDinoData*, UWorld*, FVector*, FRotator*, bool*, int, bool, AShooterPlayerController*, bool>(nullptr, "APrimalDinoCharacter.SpawnFromDinoDataEx", InDinoData, InWorld, AtLocation, AtRotation, dupedDino, ForTeam, bGenerateNewDinoID, TamerController, beginPlay); }
in version/Core/Public/API/ARK/Actor.h (edited)line 7090
- static APrimalDinoCharacter* SpawnFromDinoDataEx(FARKDinoData* InDinoData, UWorld* InWorld, FVector* AtLocation, FRotator* AtRotation, bool* dupedDino, int ForTeam, bool bGenerateNewDinoID, AShooterPlayerController* TamerController, bool beginPlay) { return NativeCall<APrimalDinoCharacter*, FARKDinoData*, UWorld*, FVector*, FRotator*, bool*, int, bool, AShooterPlayerController*, bool>(nullptr, "APrimalDinoCharacter.SpawnFromDinoData", InDinoData, InWorld, AtLocation, AtRotation, dupedDino, ForTeam, bGenerateNewDinoID, TamerController, beginPlay); }
+ static APrimalDinoCharacter* SpawnFromDinoDataEx(FARKDinoData* InDinoData, UWorld* InWorld, FVector* AtLocation, FRotator* AtRotation, bool* dupedDino, int ForTeam, bool bGenerateNewDinoID, AShooterPlayerController* TamerController, bool beginPlay) { return NativeCall<APrimalDinoCharacter*, FARKDinoData*, UWorld*, FVector*, FRotator*, bool*, int, bool, AShooterPlayerController*, bool>(nullptr, "APrimalDinoCharacter.SpawnFromDinoDataEx", InDinoData, InWorld, AtLocation, AtRotation, dupedDino, ForTeam, bGenerateNewDinoID, TamerController, beginPlay); }
in version/Core/Public/API/ARK/Actor.h (edited)SpawnFromDinoData
(no Ex) that could be added.SpawnFromDinoData
(no Ex) that could be added. SpawnFromDinoData
(no Ex) that could be added. APrimalStructureItemContainer::SetContainerActive
function
For removing the need of elements you could do something like add 1 element when the structure is placed, force turn on and set the fuel burn rate of the structure to very high values (practically making it infinite lasting)
For the range, there is a formula. I decoded it from the devkit and it can be found in the safe zones plugin source code, on the bubble spawn functionAPrimalStructureItemContainer::BPGetFuelConsumptionMultiplier
bUseBPGetFuelConsumptionMultiplier
to true on the forcefieldsDECLARE_HOOK(AMissionType_BPOnMissionComplete, void, AMissionType*, AShooterCharacter*, AShooterPlayerController*, bool, float, TArray<UPrimalItem*>*);
void Hook_AMissionType_BPOnMissionComplete(AMissionType* _this, AShooterCharacter* ForPlayer,
AShooterPlayerController* ForController, bool bLastPhaseSuccess, float XPRewarded, TArray<UPrimalItem*>* ItemsRewarded)
{
AMissionType_BPOnMissionComplete_original(_this, ForPlayer, ForController, bLastPhaseSuccess, XPRewarded, ItemsRewarded);
}
(Top with other Hooks)
DECLARE_HOOK(AMissionType_BPOnMissionComplete, void, AMissionType*, AShooterCharacter*, AShooterPlayerController*, bool, float, TArray<UPrimalItem*>*);
void Hook_AMissionType_BPOnMissionComplete(AMissionType* _this, AShooterCharacter* ForPlayer, AShooterPlayerController* ForController, bool bLastPhaseSuccess, float XPRewarded, TArray<UPrimalItem*>* ItemsRewarded)
{
Log::GetLog()->info("Mission Complete");
if (bLastPhaseSuccess)
{
FString Mission_Complete = _this->MissionDisplayNameField();
Log::GetLog()->info(Mission_Complete.ToString());
}
AMissionType_BPOnMissionComplete_original(_this, ForPlayer, ForController, bLastPhaseSuccess, XPRewarded, ItemsRewarded);
}
(Load with other hooks)
ArkApi::GetHooks().SetHook("AMissionType_BPOnMissionComplete", &Hook_AMissionType_BPOnMissionComplete, &AMissionType_BPOnMissionComplete_original);
(Unload with other hooks)
ArkApi::GetHooks().DisableHook("AMissionType_BPOnMissionComplete", &Hook_AMissionType_BPOnMissionComplete);
Just seeing If I am missing something, or on the right track? I have not tried it on Gen1 as of yet. (edited)(Top with other Hooks)
DECLARE_HOOK(AMissionType_BPOnMissionComplete, void, AMissionType*, AShooterCharacter*, AShooterPlayerController*, bool, float, TArray<UPrimalItem*>*);
void Hook_AMissionType_BPOnMissionComplete(AMissionType* _this, AShooterCharacter* ForPlayer, AShooterPlayerController* ForController, bool bLastPhaseSuccess, float XPRewarded, TArray<UPrimalItem*>* ItemsRewarded)
{
Log::GetLog()->info("Mission Complete");
if (bLastPhaseSuccess)
{
FString Mission_Complete = _this->MissionDisplayNameField();
Log::GetLog()->info(Mission_Complete.ToString());
}
AMissionType_BPOnMissionComplete_original(_this, ForPlayer, ForController, bLastPhaseSuccess, XPRewarded, ItemsRewarded);
}
(Load with other hooks)
ArkApi::GetHooks().SetHook("AMissionType_BPOnMissionComplete", &Hook_AMissionType_BPOnMissionComplete, &AMissionType_BPOnMissionComplete_original);
(Unload with other hooks)
ArkApi::GetHooks().DisableHook("AMissionType_BPOnMissionComplete", &Hook_AMissionType_BPOnMissionComplete);
Just seeing If I am missing something, or on the right track? I have not tried it on Gen1 as of yet. (edited)AMissionType.BPOnMissionComplete
, replace _
with .
pc->GetPlayerInventoryComponent()->GetCurrentNumInventoryItems() - 299
__int64 __fastcall UPrimalInventoryComponent::GetCurrentNumInventoryItems(UPrimalInventoryComponent *this)
{
return (unsigned int)(this->InventoryItems.ArrayNum - this->DefaultInventoryItems.ArrayNum);
}
(edited)int GetInvCount(AShooterPlayerController* pc)
{
AShooterPlayerState* sps = pc->GetShooterPlayerState();
return pc->GetPlayerInventoryComponent()->GetCurrentNumInventoryItems() + sps->EngramItemBlueprintsField().Num();
}
UPrimalInventoryComponent::AbsoluteMaxInventoryItems
(only on players, though)EngramItemBlueprintsField
is the learned engrams, but after looking some ida code seems reasonable that is the fieldMaxInventoryItemsField
, however there are a few more extra slots that are defined in AbsoluteMaxInventoryItems
. These extra slots that the player can't use are for things like spoling meat, etc. So the inventory can store them but players can't add more items until the count is below the normal max inv itemsUPrimalInventoryComponent::AbsoluteMaxInventoryItems
(only on players, though) Permissions::
from my plugin, the namespace is not found. (edited)More information about the plugin: https://wiki.arkserverapi.com/index.php?title=Permissions
Link is dead :/ (edited)Permissions::GetGroupMembers("Admins");
freeze my server?
I call it from AFC_LoadPermissionsArrays()
function that is called every 30 seconds like seen in Max-Imprint plugin: API::Timer::Get().RecurringExecute(&AFC_LoadPermissionsArrays, 30, -1, true); // Continuously updates the admins and allowed players arrays.
Permissions::IsPlayerInGroup(
instead of Permissions::GetGroupMembers(
. Thanks again ^^#include "mysql+++.h"
#pragma comment(lib, "mysqlclient.lib")
(mysql+++.h is a newer version, but shouldn't matter.)#include "mysql+++.h"
#pragma comment(lib, "mysqlclient.lib")
(mysql+++.h is a newer version, but shouldn't matter.) if (ArkApi::Tools::IsPluginLoaded("ArkShop")) {}
using T = bool(*) (int, uint64);
HMODULE hmodule = GetModuleHandle(TEXT("ArkShop.dll"));
if (!hmodule)
return false;
T addr = (T)GetProcAddress(hmodule, "SpendPoints");
if (!addr)
Log::GetLog()->error("SpendPoints not found");
MyPlugin::UseArkShop = true;
if (!ArkApi::Tools::IsPluginLoaded("ArkShop"))
{
MyPlugin::UseArkShop = false;
}
And it works fine for meif ((iPrice > 0) && MyPlugin::UseArkShop)
{
if (ArkShop::Points::GetPoints(steam_id) >= (iPrice))
{
....
etc...
extern "C"
in their def/decl.
I'd work out why it isn't working. Is your plugin name loading before arkshop is, or do you have any other dependency??SpendPoints@Points@ArkShop@@YA_NH_K@Z
MyPlugin::UseArkShop = true;
if (!ArkApi::Tools::IsPluginLoaded("ArkShop"))
{
MyPlugin::UseArkShop = false;
}
And it works fine for me extern "C"
in their def/decl.
I'd work out why it isn't working. Is your plugin name loading before arkshop is, or do you have any other dependency? ?SpendPoints@Points@ArkShop@@YA_NH_K@Z
SpendPoints
SpendPoints
extern "C"
in their def/decl.
I'd work out why it isn't working. Is your plugin name loading before arkshop is, or do you have any other dependency? ArkApi::Tools::IsPluginLoaded("ArkShop")
+ delay loading is all you needBOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Load();
break;
case DLL_PROCESS_DETACH:
Unload();
break;
}
return TRUE;
}
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_PROCESS_DETACH:
Unload();
break;
}
return TRUE;
}
void Load()
{
Log::Get().Init("uStuck");
try
{
ReadConfig();
}
catch (const std::exception& error)
{
Log::GetLog()->error(error.what());
throw;
}
try
{
ArkApi::GetHooks().SetHook("AGameMode.InitGame", &Hook_AGameMode_InitGame, &AGameMode_InitGame_original);
ArkApi::GetHooks().SetHook("APrimalCharacter.TakeDamage", &Hook_APrimalCharacter_TakeDamage, &APrimalCharacter_TakeDamage_original);
auto& commands = ArkApi::GetCommands();
commands.AddChatCommand(uStuck::GetCmd("teleport"), &uStuckCmd);
commands.AddChatCommand(uStuck::GetCmd("location"), &PrintLocation);
}
catch (const std::exception& error)
{
Log::GetLog()->error(error.what());
throw;
}
}
void Load()
{
Log::Get().Init("uStuck");
try
{
ReadConfig();
}
catch (const std::exception& error)
{
Log::GetLog()->error(error.what());
throw;
}
try
{
ArkApi::GetHooks().SetHook("AGameMode.InitGame", &Hook_AGameMode_InitGame, &AGameMode_InitGame_original);
ArkApi::GetHooks().SetHook("APrimalCharacter.TakeDamage", &Hook_APrimalCharacter_TakeDamage, &APrimalCharacter_TakeDamage_original);
auto& commands = ArkApi::GetCommands();
commands.AddChatCommand(uStuck::GetCmd("teleport"), &uStuckCmd);
commands.AddChatCommand(uStuck::GetCmd("location"), &PrintLocation);
}
catch (const std::exception& error)
{
Log::GetLog()->error(error.what());
throw;
}
}
void ReadConfig()
{
const std::string config_path = ArkApi::Tools::GetCurrentDir() + "/ArkApi/Plugins/uStuck/config.json";
std::ifstream file{ config_path };
if (!file.is_open())
{
throw std::runtime_error("Can't open config.json");
}
file >> uStuck::config;
file.close();
}
Plugin_Init()
, right? I haven't actually tried it but I was looking at the plugin manager the other day and noticed that LoadPlugin()
tries to call that function if found after loading the library. (edited)Plugin_Unload()
Plugin_Init()
, right? I haven't actually tried it but I was looking at the plugin manager the other day and noticed that LoadPlugin()
tries to call that function if found after loading the library. (edited)#pragma once
#include "init.h"
struct FPlacementData
{
FVector AdjustedLocation;
FRotator AdjustedRotation;
bool bSnapped;
bool bDisableEncroachmentCheck;
int MySnapToIndex;
int TheirSnapToIndex;
AActor* FloorHitActor;
APrimalStructure* ParentStructure;
APrimalStructure* ForcePlacedOnFloorParentStructure;
APrimalStructure* ReplacesStructure;
APawn* AttachToPawn;
FName AttachToBone;
APrimalDinoCharacter* DinoCharacter;
};
int Hook_APrimalStructure_IsAllowedToBuild(APrimalStructure* _this, APlayerController* PC, FVector AtLocation,
FRotator AtRotation, FPlacementData* OutPlacementData,
bool bDontAdjustForMaxRange, FRotator PlayerViewRotation,
bool bFinalPlacement)
{
if (bFinalPlacement && PC != nullptr && PC->IsA(AShooterPlayerController::GetPrivateStaticClass()))
{
if (!OutPlacementData->DinoCharacter)
{
Log::GetLog()->info("1 is null");
}
if (!OutPlacementData->ParentStructure)
{
Log::GetLog()->info("2 is null");
}
if (!OutPlacementData->ForcePlacedOnFloorParentStructure)
{
Log::GetLog()->info("3 is null");
}
}
return APrimalStructure_IsAllowedToBuild_original(_this, PC, AtLocation, AtRotation, OutPlacementData, bDontAdjustForMaxRange, PlayerViewRotation, bFinalPlacement);
}
(edited)inventoryComponent->GetOwner()->TargetingTeamField()
then try loop characters in the world and get the player id that way maybeconst auto SGame_Mode = ArkApi::GetApiUtils().GetShooterGameMode();
const auto SGame_State = ArkApi::GetApiUtils().GetGameState();
AShooterGameState* Sh_Game_State = static_cast<AShooterGameState*>(ArkApi::GetApiUtils().GetShooterGameMode()->GameStateField());
SGame_Mode->EggHatchSpeedMultiplierField() = fval;
SGame_State->EggHatchSpeedMultiplierField() = fval;
Sh_Game_State->EggHatchSpeedMultiplierField() = fval;
c++
AShooterGameMode* gameMode = ArkApi::GetApiUtils().GetShooterGameMode();
static int tribeDataStructSize = GetStructSize<FTribeData>();
FTribeData* tribeData = static_cast<FTribeData*>(FMemory::Malloc(tribeDataStructSize));
RtlSecureZeroMemory(tribeData, tribeDataStructSize);
gameMode->GetOrLoadTribeData(player_controller->TargetingTeamField(), tribeData);
// do something with tribe data
const auto iDinoCount = tribeData->NumTribeDinosField();
FMemory::Free(tribeData);
FTribeData* CreatedTribeData = static_cast<FTribeData*>(FMemory::Malloc(0x128 + 0x28));
RtlSecureZeroMemory(CreatedTribeData, 0x128 + 0x28);
if (GameMode->GetOrLoadTribeData(tribe_id, CreatedTribeData))
{
PC->GetShooterPlayerState()->AddToTribe(CreatedTribeData, false, true, false, nullptr);
}
else
{
Log::GetLog()->info("no tribe data");
}
FMemory::Free(CreatedTribeData);
c++
TArray<AActor*> FoundActors;
UGameplayStatics::GetAllActorsOfClass(ArkApi::GetApiUtils().GetWorld(), APrimalDinoCharacter::GetPrivateStaticClass(), &FoundActors);
for (const auto& actor : FoundActors)
{
auto dino = static_cast<APrimalDinoCharacter*>(actor);
if (dino)
{
// do the thing
}
}
(edited)reinterpret_cast<UObject*>(ArkApi::GetApiUtils().GetWorld())
Just do
ArkApi::GetApiUtils().GetWorld()
reinterpret_cast<UObject*>(ArkApi::GetApiUtils().GetWorld())
Just do
ArkApi::GetApiUtils().GetWorld()
DECLARE_HOOK(AShooterGameMode_ForceCreateTribe, int, AShooterGameMode*, FString*, int);
Found in the Hook Creator
DECLARE_HOOK(AShooterPlayerState_ServerRequestCreateNewPlayer_Implementation, void, AShooterPlayerState*, FPrimalPlayerCharacterConfigStructReplicated);
void Hook_AShooterPlayerState_ServerRequestCreateNewPlayer_Implementation(AShooterPlayerState* _this, FPrimalPlayerCharacterConfigStructReplicated PlayerCharacterConfig)
{
AShooterPlayerState_ServerRequestCreateNewPlayer_Implementation_original(_this, PlayerCharacterConfig);
}
ArkApi::GetHooks().SetHook("AShooterPlayerState.ServerRequestCreateNewPlayer_Implementation", &Hook_AShooterPlayerState_ServerRequestCreateNewPlayer_Implementation, &AShooterPlayerState_ServerRequestCreateNewPlayer_Implementation_original);
ArkApi::GetHooks().DisableHook("AShooterPlayerState.ServerRequestCreateNewPlayer_Implementation", &Hook_AShooterPlayerState_ServerRequestCreateNewPlayer_Implementation);
(edited)APrimalStructure* _this;
_this->ConsumesPrimalItemField();
Perhaps?struct FHordeCrateNPCGroup {
TArray<UClass> NPCClasses; // devkit says Array of Primal Dino Character Class?
TArray<UClass> NPCAssets; // devkit says Array of Primal Dino Character Class?
TArray<float> NPCWeights;
TArray<int> MinLevels;
TArray<int> MaxLevels;
};
struct FHordeCrateWave {
int MinNumOfNPCs;
int MaxNumOfNPCs;
float TimeToPrepareForWave;
FHordeCrateNPCGroup NPCsToSpawn;
};
struct FHordeCrateDifficultyLevel {
bool bEnabled;
int MaxNumOfEventsForDifficulty;
int DifficultyLevel;
int MinNPCsToStart;
int MaxNPCsToStart;
FHordeCrateNPCGroup StartingNPCs;
TArray<FHordeCrateWave> NPCWavesToComplete;
float MinTimeBeforeSelfDestruct;
float MaxTimeBeforeSelfDestruct;
AActor ActorTemplate; //Dev Kit says "Actor Class"?
float MinQualityMultiplier;
float MaxQualityMultiplier;
int MinAmtRewardItems;
int MaxAmtRewardItems;
FLinearColor DifficultyColor;
float MainNodeElementPct;
};
UProperty* hordeParams = actor->FindProperty(FName("HordeParams", EFindName::FNAME_Add));
if (hordeParams != nullptr) {
try {
FHordeCrateDifficultyLevel hordeCrateDifficultyLevel = hordeParams->Get<FHordeCrateDifficultyLevel>(actor);
sendChatMessage(player_controller, "it worked for: " + path.ToString());
}
catch (std::invalid_argument ex) {
sendChatMessage(player_controller, "size error for: " + path.ToString());
}
}
Hi, I'm trying to deconstruct a Property from the Extinction Drops. For now I got most of the information from the Devkit. The Property gets found but I always run into the size error. First of all can I even get and set such a property? If yes how would you go about to figure out where the Problem is? I also don't really know what is meant with "Actor Class" and "Primal Dino Class" in this context.AActor* ActorTemplate; //Dev Kit says "Actor Class"?
AActor* ActorTemplate; //Dev Kit says "Actor Class"?
Hook_UPrimalInventoryComponent_ServerCloseRemoteInventory
is why I want to finish my autohooking prototypeasps->MyTribeDataField()
is not null and is same tribe id as you created, and then you could call asps->MulticastProperty(FName("MyTribeData", EFindName::FNAME_Add));
asps->MulticastProperty(FName("MyTribeData", EFindName::FNAME_Add));
I tried , although I don't know what meansasps->ForceReplicateNow(false, false)
or asps->ForceNetUpdate(false, false, false)
asgm->GetOrLoadTribeData(tid, tribeData)
first is false, this func is right.Ark
build config, or the Release
one?Ark
build config, or the Release
one? asps->ForceNetUpdate(false, false, false);
,I find Player in tribe,but re login its not in tribe.Call player_state->ServerRequestCreateTribe_Implementation()
Hook gameMode::GenerateTribeId and return the tribe id you want (must handle post return var states, etc...)
if you want to add any other player to tribe, wait till they are online and use add to tribe after get or load data
ADroppedItem.bAssignedToTribePickupOnly
Any hook can mark this?DECLARE_HOOK(ADroppedItem_IsAllowedToPickupItem, bool, ADroppedItem*, APlayerController*);
bool Hook_ADroppedItem_IsAllowedToPickupItem(ADroppedItem* _this, APlayerController* PC)
{
return ADroppedItem_IsAllowedToPickupItem_original(_this, PC);
}
ArkApi::GetHooks().SetHook("ADroppedItem.IsAllowedToPickupItem", &Hook_ADroppedItem_IsAllowedToPickupItem, &ADroppedItem_IsAllowedToPickupItem_original);
ArkApi::GetHooks().DisableHook("ADroppedItem.IsAllowedToPickupItem", &Hook_ADroppedItem_IsAllowedToPickupItem);
actor
will never be a controlleractor->GetOwnerController()
, if it's a APlayerController
it's a player, else is AIADroppedItemEgg
dropped from wild Wyvern or Player?APrimalDinoCharacter.UnclaimDino
APrimalDinoCharacter.UnclaimDino
DECLARE_HOOK(APrimalDinoCharacter_BPAllowClaiming, bool, APrimalDinoCharacter*, AShooterPlayerController*);
bool Hook_APrimalDinoCharacter_BPAllowClaiming(APrimalDinoCharacter* _this, AShooterPlayerController* forPlayer)
{
return APrimalDinoCharacter_BPAllowClaiming_original(_this, forPlayer);
}
ArkApi::GetHooks().SetHook("APrimalDinoCharacter.BPAllowClaiming", &Hook_APrimalDinoCharacter_BPAllowClaiming, &APrimalDinoCharacter_BPAllowClaiming_original);
ArkApi::GetHooks().DisableHook("APrimalDinoCharacter.BPAllowClaiming", &Hook_APrimalDinoCharacter_BPAllowClaiming);
DECLARE_HOOK(APrimalDinoCharacter_BPAllowClaiming, bool, APrimalDinoCharacter*, AShooterPlayerController*);
bool Hook_APrimalDinoCharacter_BPAllowClaiming(APrimalDinoCharacter* _this, AShooterPlayerController* forPlayer)
{
return APrimalDinoCharacter_BPAllowClaiming_original(_this, forPlayer);
}
ArkApi::GetHooks().SetHook("APrimalDinoCharacter.BPAllowClaiming", &Hook_APrimalDinoCharacter_BPAllowClaiming, &APrimalDinoCharacter_BPAllowClaiming_original);
ArkApi::GetHooks().DisableHook("APrimalDinoCharacter.BPAllowClaiming", &Hook_APrimalDinoCharacter_BPAllowClaiming);
ArkApi::GetHooks().SetHook("APrimalDinoCharacter.BPAllowClaiming", &Hook_APrimalDinoCharacter_BPAllowClaiming, &APrimalDinoCharacter_BPAllowClaiming_original);
DECLARE_HOOK(APrimalDinoCharacter_BPAllowClaiming, bool, APrimalDinoCharacter*, AShooterPlayerController*);
bool Hook_APrimalDinoCharacter_BPAllowClaiming(APrimalDinoCharacter* _this, AShooterPlayerController* forPlayer)
{
Log::GetLog()->info("BPAllowClaiming");
return APrimalDinoCharacter_BPAllowClaiming_original(_this, forPlayer);
}
DECLARE_HOOK(APrimalDinoCharacter_BPNotifyClaimed, void, APrimalDinoCharacter*);
void Hook_APrimalDinoCharacter_BPNotifyClaimed(APrimalDinoCharacter* _this)
{
Log::GetLog()->info("BPNotifyClaimed");
APrimalDinoCharacter_BPNotifyClaimed_original(_this);
}
DECLARE_HOOK(APrimalDinoCharacter_GetForceClaimTime, long double, APrimalDinoCharacter*);
long double Hook_APrimalDinoCharacter_GetForceClaimTime(APrimalDinoCharacter* _this)
{
Log::GetLog()->info("GetForceClaimTime");
return APrimalDinoCharacter_GetForceClaimTime_original(_this);
}
I tried this three,only BPAllowClaiming
no printingHook_AShooterPlayerController_ServerMultiUse_Implementation
BPAllowClaiming
?useIndex
(param from ServerMultiUse_Implementation)?ArkApi::GetApiUtils().SendChatMessage(player_controller, GetText("Sender"), *GetText("ShopFindUsage"));
Check so your sender and and "ShopFindUsage" isn't containing any thing wierdc++
ArkApi::GetCommands().AddChatCommand(command->command,
[&command](AShooterPlayerController* player_controller, FString* message,
EChatSendMode::Type)
{
TArray<FString> parsed;
message->ParseIntoArray(parsed, L" ", true);
if (!message->RemoveFromStart(parsed[0]))
return;
// Bypass spam check
player_controller->LastChatMessageTimeField() -= 3.0;
FString cmd = command->exec + *message;
player_controller->ServerSendChatMessage_Implementation(
&cmd, EChatSendMode::GlobalChat);
});
so this for add chat command right ?&FunctionName
&FunctionName
c++
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
ArkApi::GetCommands().AddOnChatMessageCallback("DieselFirstPlugin", &FirstCommandEver);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
ArkApi::GetCommands().RemoveOnChatMessageCallback("DieselFirstPlugin");
break;
}
return TRUE;
}
c++
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
ArkApi::GetCommands().AddOnChatMessageCallback("DieselFirstPlugin", &FirstCommandEver);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
ArkApi::GetCommands().RemoveOnChatMessageCallback("DieselFirstPlugin");
break;
}
return TRUE;
}
Plugin_Init()
or Plugin_Unload()
, ArkServerApi calls them for you after loading your DLL or on unloading it.
c++
extern "C" __declspec(dllexport) void Plugin_Init()
{
...
}
extern "C" __declspec(dllexport) void Plugin_Unload()
{
...
}
(edited)Plugin_Init()
or Plugin_Unload()
, ArkServerApi calls them for you after loading your DLL or on unloading it.
c++
extern "C" __declspec(dllexport) void Plugin_Init()
{
...
}
extern "C" __declspec(dllexport) void Plugin_Unload()
{
...
}
(edited)Plugin_Init()
or Plugin_Unload()
, ArkServerApi calls them for you after loading your DLL or on unloading it.
c++
extern "C" __declspec(dllexport) void Plugin_Init()
{
...
}
extern "C" __declspec(dllexport) void Plugin_Unload()
{
...
}
(edited)kernel32.dll
by the time your dll is loaded.Plugin_Init()
or Plugin_Unload()
, ArkServerApi calls them for you after loading your DLL or on unloading it.
c++
extern "C" __declspec(dllexport) void Plugin_Init()
{
...
}
extern "C" __declspec(dllexport) void Plugin_Unload()
{
...
}
(edited)ArkServerAPI-Template.vcxproj
(edited)c++
std::string strText = "test";
FString message2{};
auto m1 = fmt::format(" {}", strText);
message2 = message->Replace(m1, L" "); // wont work
c++
FString message2{};
message2 = message->Replace(L" test", L" "); // this works
Thanks for your help! (edited)c++
std::string strText = "test";
FString message2{};
auto m1 = fmt::format(" {}", strText);
message2 = message->Replace(m1, L" "); // wont work
c++
FString message2{};
message2 = message->Replace(L" test", L" "); // this works
Thanks for your help! (edited)c++
std::string strText = "test";
FString message2{};
auto m1 = fmt::format(" {}", strText);
message2 = message->Replace(m1, L" "); // wont work
c++
FString message2{};
message2 = message->Replace(L" test", L" "); // this works
Thanks for your help! (edited)FString m1 = FString(fmt::format(" {}", strText));
m1.c_str()
I believeFString m1 = FString(fmt::format(" {}", strText));
message2 = message.Replace(*m1, L" ");
c++
std::string strText = " random";
FString m1 = FString(fmt::format(" {}", strText));
FString m2 = FString(fmt::format(" "));
bool isRandom = message->Contains(m1);
if (isRandom)
{
message2 = message->Replace(m1, L" ");
}
Fjordhawk_Character_BP_C
blueprint in the dev kit. Note the package name when you hover over the blueprint in the Content Browser (/Game/Fjordur/Dinos/Fjordhawk/Fjordhawk_Character_BP
), we'll use that in our plugin.Check for Player Death
and On Player Died
are both interesting, and they're both called by BPOn Clear Mounted Dino
. When the Fjordhawk is removed from your shoulder, it checks if you're dead and retrieves your inventory. Check for Player Death
runs on the server and kicks off a chain of events that makes the Fjordhawk retrieve your inventory and bring it back to you. On Player Died
runs on the client and just removes the player's equipped items, which I don't think matters to us. So if we prevent Check for Player Death
from running on the server, the Fjordhawk won't return our inventory. Functions defined by graphs in the dev kit can't be directly hooked natively, but we have other options.Check for Player Death
UFunction.
UVictoryCore::BPLoadClass()
is one option for looking up Blueprint classes. This function takes an FString that includes the blueprint's package plus the blueprint's name:
c++
FString path = "Blueprint'/Game/Fjordur/Dinos/Fjordhawk/Fjordhawk_Character_BP.Fjordhawk_Character_BP'";
UClass* hawkClass = UVictoryCore::BPLoadClass(&path);
Note: BPLoadClass()
shouldn't be called until after the server has initialized. Classes aren't initialized yet when ArkServerApi first loads plugins.
To find the UFunction, we want the function's actual name in the game engine, which doesn't always completely match the names we see on the graphs. One option is to get the real name from the dev kit by hovering your cursor over the function name in the "My Blueprint" tab (left hand side of the previous image).
Or you can use something like this to dump a class's UFunction names from a plugin:
c++
void ListFunctions(UClass* Class)
{
if (!Class)
return;
for (TPair<FName, UFunction*> pair : Class->FuncMapField())
Log::GetLog()->info(pair.Key.ToString().ToString().c_str());
}
It turns out that Check for Player Death
in the dev kit is called CheckForPlayerDeath
in the game.AShooterGameMode::BeginPlay()
. This function is called once after the game has been initialized, this is where ArkServerApi sets ServerStatus::Ready
. At this point we can look up the UClass and UFunction we need and save the UFunction's unique InternalIndex
to compare against later.
2. Hook UObject::ProcessInternal()
. This function is used by Unreal Engine to execute UFunctions that are defined by graphs in the dev kit. One of the arguments passed into ProcessInternal()
is an FFrame*, the current UFunction's call stack frame. We can use the FFrame to get a pointer to the UFunction that is being executed, and then block CheckForPlayerDeath
from executing. ArkServerApi doesn't provide FFrame, we need to define it ourselves (I've posted a more complete definition of it before in this Discord).c++
#include "API/ARK/Ark.h"
#pragma comment(lib, "ArkApi.lib")
struct FFrame : FOutputDevice
{
UFunction*& NodeField() { return *GetNativePointerField<UFunction**>(this, "FFrame.Node"); }
};
int FunctionIndex = -1;
void ServerReadyInit()
{
FString path = "Blueprint'/Game/Fjordur/Dinos/Fjordhawk/Fjordhawk_Character_BP.Fjordhawk_Character_BP'";
UClass* hawkClass = UVictoryCore::BPLoadClass(&path);
if (!hawkClass)
return;
UFunction* function = hawkClass->FindFunctionByName(FName("CheckForPlayerDeath", EFindName::FNAME_Find), EIncludeSuperFlag::ExcludeSuper);
if (!function)
return;
FunctionIndex = function->InternalIndexField();
}
DECLARE_HOOK(AShooterGameMode_BeginPlay, void, AShooterGameMode*);
void Hook_AShooterGameMode_BeginPlay(AShooterGameMode* _this)
{
AShooterGameMode_BeginPlay_original(_this);
ServerReadyInit();
}
DECLARE_HOOK(UObject_ProcessInternal, void, UObject*, FFrame*, void* const);
void Hook_UObject_ProcessInternal(UObject* _this, FFrame* Stack, void* const Result)
{
// Don't execute the UFunction we found in ServerReadyInit()
if (Stack->NodeField()->InternalIndexField() == FunctionIndex)
return;
UObject_ProcessInternal_original(_this, Stack, Result);
}
extern "C" __declspec(dllexport) void Plugin_Init()
{
Log::Get().Init("FjordhawkExample");
ArkApi::GetHooks().SetHook("AShooterGameMode.BeginPlay", &Hook_AShooterGameMode_BeginPlay, &AShooterGameMode_BeginPlay_original);
ArkApi::GetHooks().SetHook("UObject.ProcessInternal", &Hook_UObject_ProcessInternal, &UObject_ProcessInternal_original);
if (ArkApi::GetApiUtils().GetStatus() != ArkApi::ServerStatus::Ready)
return;
ServerReadyInit();
}
extern "C" __declspec(dllexport) void Plugin_Unload()
{
ArkApi::GetHooks().DisableHook("AShooterGameMode.BeginPlay", &Hook_AShooterGameMode_BeginPlay);
ArkApi::GetHooks().DisableHook("UObject.ProcessInternal", &Hook_UObject_ProcessInternal);
}
c++
/**
* Get pointer to the string
*
* @Return Pointer to Array of TCHAR if Num, otherwise the empty string
*/
FORCEINLINE const TCHAR* operator*() const
{
return Data.Num() ? Data.GetData() : TEXT("");
}
The * operator gives you a pointer to the beginning of the TCHAR array. (edited)public:
s that would be required for classes really add up lol, especially if additional game classes were to be added to the APIc++
TArray<AActor*> FoundTurrets;
UGameplayStatics::GetAllActorsOfClass(ArkApi::GetApiUtils().GetWorld(), APrimalStructureTurret::GetPrivateStaticClass(), &FoundActors);
for (const auto& Turret : FoundTurrets)
{
auto TheTurret= static_cast<APrimalStructureTurret*>(Turret);
if (TheTurret)
{
// do the thing
}
}
Fjordhawk_Character_BP_C
blueprint in the dev kit. Note the package name when you hover over the blueprint in the Content Browser (/Game/Fjordur/Dinos/Fjordhawk/Fjordhawk_Character_BP
), we'll use that in our plugin. lethalorp.raidprotection <add/remove> <tribeid> <hours>
/getmyorb
lethalorp.raidprotection <add/remove> <tribeid> <hours>
c++
// Plugin1.cpp
#include "API/ARK/Ark.h"
#pragma comment(lib, "ArkApi.lib")
void FooCmd(AShooterPlayerController* spc, FString* input, EChatSendMode::Type)
{
ArkApi::GetApiUtils().SendServerMessage(spc, FColorList::Green, "FOO");
FString result;
FString cmd = "bar";
spc->ConsoleCommand(&result, &cmd, false);
}
extern "C" __declspec(dllexport) void Plugin_Init()
{
Log::Get().Init("Plugin1");
ArkApi::GetCommands().AddChatCommand("/foo", FooCmd);
}
extern "C" __declspec(dllexport) void Plugin_Unload()
{
ArkApi::GetCommands().RemoveChatCommand("/foo");
}
c++
// Plugin2.cpp
#include "API/ARK/Ark.h"
#pragma comment(lib, "ArkApi.lib")
void BarCmd(APlayerController* pc, FString*, bool)
{
ArkApi::GetApiUtils().SendServerMessage((AShooterPlayerController*)pc, FColorList::Green, "BAR");
}
extern "C" __declspec(dllexport) void Plugin_Init()
{
Log::Get().Init("Plugin2");
ArkApi::GetCommands().AddConsoleCommand("bar", BarCmd);
}
extern "C" __declspec(dllexport) void Plugin_Unload()
{
ArkApi::GetCommands().RemoveConsoleCommand("bar");
}
After typing "/foo" into chat:c++
// Plugin1.cpp
#include "API/ARK/Ark.h"
#pragma comment(lib, "ArkApi.lib")
void FooCmd(AShooterPlayerController* spc, FString* input, EChatSendMode::Type)
{
ArkApi::GetApiUtils().SendServerMessage(spc, FColorList::Green, "FOO");
FString result;
FString cmd = "bar";
spc->ConsoleCommand(&result, &cmd, false);
}
extern "C" __declspec(dllexport) void Plugin_Init()
{
Log::Get().Init("Plugin1");
ArkApi::GetCommands().AddChatCommand("/foo", FooCmd);
}
extern "C" __declspec(dllexport) void Plugin_Unload()
{
ArkApi::GetCommands().RemoveChatCommand("/foo");
}
c++
// Plugin2.cpp
#include "API/ARK/Ark.h"
#pragma comment(lib, "ArkApi.lib")
void BarCmd(APlayerController* pc, FString*, bool)
{
ArkApi::GetApiUtils().SendServerMessage((AShooterPlayerController*)pc, FColorList::Green, "BAR");
}
extern "C" __declspec(dllexport) void Plugin_Init()
{
Log::Get().Init("Plugin2");
ArkApi::GetCommands().AddConsoleCommand("bar", BarCmd);
}
extern "C" __declspec(dllexport) void Plugin_Unload()
{
ArkApi::GetCommands().RemoveConsoleCommand("bar");
}
After typing "/foo" into chat: ConsoleCommand
Can act as RCON command ?ConsoleCommand
Can act as RCON command ? API::Requests::Get().CreateGetRequest
this function#include <Requests.h>
API::Requests::Get().CreateGetRequest()
Turret.stasis()
make the turret inactive or in off state ? (edited)BPTurretPreventsTargeting
?Stasis()
function is called on actors that go into stasis when there are no players within render distance (some distance at least, idk the details) of the turret, which probably isn't what you want. (edited)BPTurretPreventsTargeting
? BPTurretPreventsTargeting
TheTurret->BPTurretPreventsTargeting()
APrimalCharacter
which means i have to pass all dinos and all players or is there is any way to pass all struct members at onceE
to set the turret to on and offc++
TheTurret->IsValidToFire(true) // to make it on
TheTurret->IsValidToFire(false) // to make it off
(edited)c++
DECLARE_HOOK(APrimalStructureTurret_SetTarget, void, APrimalStructureTurret*, AActor*);
void Hook_APrimalStructureTurret_SetTarget(APrimalStructureTurret* _this, AActor* aTarget)
{
if(){// check if the target is within specific class eg dino
return APrimalStructureTurret_SetTarget_original(_this, aTarget);
}
else{
return 0;
}
}
ArkApi::GetHooks().SetHook("APrimalStructureTurret.SetTarget", &Hook_APrimalStructureTurret_SetTarget, &APrimalStructureTurret_SetTarget_original);
bPreventTargetingByTurrets
you set on each turret.bPreventTargetingByTurrets
you set on each turret. Turret
variableC++
c++
TheTurret->IsValidToFire(true) // to make it on
TheTurret->IsValidToFire(false) // to make it off
Firstc++
//this is my .h file
TArray<ProtectedBase> AllProtectedBases = TArray<ProtectedBase>();
c++
AllProtectedBases.Emplace(MyBase);
c++
&AllProtectedBases.Emplace(MyBase);
c++
extern "C" __declspec(dllexport) void Plugin_Init()
{
...
}
extern "C" __declspec(dllexport) void Plugin_Unload()
{
...
}
c++
float Hook_AActor_TakeDamage(AActor* _this, float DamageAmount, FDamageEvent* DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
//how to get the location of the _this ?
}
AActor
?c++
float Hook_AActor_TakeDamage(AActor* _this, float DamageAmount, FDamageEvent* DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
Log::GetLog()->info(" {} Triggered ", "Hook");
if (_this) {
AShooterPlayerController* DamagedPlayer;
FVector PPosition;
if (_this->IsA(AShooterPlayerController::StaticClass())) {
Log::GetLog()->info(" damage reciever is Player ");
DamagedPlayer = (AShooterPlayerController*)_this;
PPosition = ArkApi::GetApiUtils().GetPosition(DamagedPlayer);
}
else {
PPosition = _this->DefaultActorLocationField();
Log::GetLog()->info(" damage reciever is Not Player ");
}
}
}
(edited)APlayerController
_this
was dino or any animal will this c++
auto player = static_cast<AShooterCharacter*>(player_controller->CharacterField());
x,y,z
separated with a comma each, so then you can split with ,
separator and parse the floats with std::stof
, for examplex,y,z
separated with a comma each, so then you can split with ,
separator and parse the floats with std::stof
, for example X=xxx Y=yyy Z=zzz
or so, you can look it upFString::Printf("X=%3.3f Y=%3.3f Z=%3.3f", X, Y, Z);
that's what it does (edited)c++
FString pos = position.ToString();
pos.Replace(L"x", L"");
pos.Replace(L"y", L"");
pos.Replace(L"z", L"");
pos.Replace(L"=", L"");
pos.Replace(L" ", L",");
c++
db_.query(fmt::format("SELECT steamid, position, radius, startdate, enddate FROM {}", table_protections_)).each([](int tribeid, const FString* position, FString radius, time_t startdate, time_t enddate) { // there might be problem bec of FString of position and radius here
TArray<FString> parsed;
position->ParseIntoArray(parsed, L",", true);
FVector BPosition = FVector(FCString::Atof(*parsed[0]), FCString::Atof(*parsed[1]), FCString::Atof(*parsed[2]));
ProtectedBase MyBase(BPosition, FCString::Atof(*radius), tribeid, startdate, enddate);
AllProtectedBases.Emplace(MyBase);
return true;
});
x,y,z
separated with a comma each, so then you can split with ,
separator and parse the floats with std::stof
, for example c++
bool Hook_AShooterPlayerState_AddToTribe(AShooterPlayerState* _this, FTribeData* MyNewTribe, bool bMergeTribe, bool bForce, bool bIsFromInvite, APlayerController* InviterPC)
{
AShooterPlayerController* playercontroller = static_cast<AShooterPlayerController*>(_this->GetOwnerController());
uint64 steamid = ArkApi::IApiUtils::GetSteamIdFromController(playercontroller);
return AShooterPlayerState_AddToTribe_original(_this, MyNewTribe, bMergeTribe, bForce, bIsFromInvite, InviterPC);
}
(edited)TArray<FItemMaxItemQuantityOverride>& AShooterGameState::OverrideItemMaxQuantityField()
, which I would guess is where the Game.ini overrides are held. FItemMaxItemQuantityOverride
isn't defined though, so you've got a little extra work to do. (edited)TArray<FItemMaxItemQuantityOverride>& AShooterGameState::OverrideItemMaxQuantityField()
, which I would guess is where the Game.ini overrides are held. FItemMaxItemQuantityOverride
isn't defined though, so you've got a little extra work to do. (edited)TArray<FConfigMaxItemQuantityOverride>& AShooterGameMode::ConfigOverrideItemMaxQuantityField()
UPrimalItem::GetMaxItemQuantity(UWorld* world)
does that tooUPrimalItem::GetMaxItemQuantity(UWorld* world)
does that too c++
AShooterCharacter* playerChar = player->LastControlledPlayerCharacterField();
AShooterWeapon* weapon = playerChar->CurrentWeaponField();
(edited)structure->ConsumesPrimalItemField().uClass
will give you the class of the itemHook_AActor_TakeDamage
is there is any huge diff between the event instigator and damage causer ?Lethal Quests
you can look at on my discord which may be what you are looking for. void TestRconDestroy(RCONClientConnection* rcon_connection, RCONPacket* rcon_packet, UWorld* world)
{
int tribeTeamId = 1231188679;
UShooterCheatManager* scm = ArkApi::GetApiUtils().GetCheatManager();
//scm->DestroyStructures();// work
scm->DestroyTribeIdStructures(tribeTeamId); // not work
}
DestroyTribeIdStructures
can't be used here?APlayerController* PC = ArkApi::GetApiUtils().GetWorld()->GetFirstPlayerController();
if (!PC)
return;
UShooterCheatManager* scm = ArkApi::IApiUtils::GetCheatManagerByPC((AShooterPlayerController*)PC);
scm->DestroyTribeIdStructures(tribeTeamId);
structure->StructureIDField();
I get id from this,
APrimalStructure* ps = APrimalStructure::GetFromID(ArkApi::GetApiUtils().GetWorld(), sid);
'ps' is nullptrFString result;
player->ConsoleCommand(&result, &fcommand, true);
For command I execute using ConsoleCommand the result is always empty. Is there any other way to execute commands and get the output?node-rcon
(edited)void GMBuff() { NativeCall<void>(this, "UShooterCheatManager.GMBuff"); }
my understand from other languages is that I can extend the UShooterCheatManager and override the GMBuff method. Does this works when using de Ark Api?void GMBuff() { NativeCall<void>(this, "UShooterCheatManager.GMBuff"); }
my understand from other languages is that I can extend the UShooterCheatManager and override the GMBuff method. Does this works when using de Ark Api? void GMBuff() { NativeCall<void>(this, "UShooterCheatManager.GMBuff"); }
my understand from other languages is that I can extend the UShooterCheatManager and override the GMBuff method. Does this works when using de Ark Api? HTTPPostRequest
to send something to an api, this api when it receives the data returns a status to inform the user if the process was successful, then I want to somehow evaluate that status in the request response so I can return to the player a message that the process was successful or otherwise an error occurred.void test(UPrimalInventoryComponent * inv){}
if i do ArkApi::GetCommands().AddChatCommand("/t", &test);
i gettest
function does not match what is expected.test
function does not match what is expected. UPrimalInventoryComponent * inv
when player do a commandUPrimalInventoryComponent * inv
APrimalStructureItemContainer
doesn't have a property named "CurrentResourceCount". You want to get the UPrimalInventoryComponent
associated with the bag and get the item count from that. Something like int count = bag->MyInventoryComponentField()->GetCurrentNumInventoryItems();
where "bag" is a APrimalStructureItemContainer
.
Also, there's no reason to use UObject::FindProperty()
or UProperty::Get()/Set()
unless you're working with Blueprint generated classes. APrimalStructureItemContainer
and UPrimalInventoryComponent
are native classes. Just use the definitions in the API headers to access properties, those accessor functions are faster.AShooterCharacter
, dino characters are APrimalDinoCharacter
. Both inherit from APrimalCharacter
, which has a MyInventoryComponent
field.AShooterCharacter
, dino characters are APrimalDinoCharacter
. Both inherit from APrimalCharacter
, which has a MyInventoryComponent
field. APrimalCharacter::IsAlive()
. I recommend looking over the API header files to see what fields and functions commonly used classes have.APlayerPawnTest_Male_C
or APlayerPawnTest_Female_C
. The full inheritance is APlayerPawnTest_Male_C > APlayerPawnTest_Child_C > APlayerPawnTest_C > AShooterCharacter > APrimalCharacter > ACharacter > APawn > AActor > UObject
. Classes that end in a "_C" are blueprint generated. ArkServerAPI headers just have definitions for native classes like AShooterCharacter
.APlayerPawnTest_Male_C
or APlayerPawnTest_Female_C
. The full inheritance is APlayerPawnTest_Male_C > APlayerPawnTest_Child_C > APlayerPawnTest_C > AShooterCharacter > APrimalCharacter > ACharacter > APawn > AActor > UObject
. Classes that end in a "_C" are blueprint generated. ArkServerAPI headers just have definitions for native classes like AShooterCharacter
. AShooterCharacter::LastControllerField()
to get the AShooterPlayerControllerAShooterCharacter::LastControllerField()
to get the AShooterPlayerController auto Test = Bag->LastControllerField();
Log::GetLog()->warn(Test);
FName& NameField()
. You can use NameField().ToString().ToString() to get an std::stringFName& NameField()
. You can use NameField().ToString().ToString() to get an std::string TheTurret->LastControllerField().Get()->NameField()
if works, but if i add .get() arg it will give owner of corpose too?LastControllerField()
gives you a TWeakObjectPtr<AController>. Which you need to use either Get() or -> on. You should read the code in the ArkServerApi headers to figure some of this stuff out and probably become a little more familiar with C++ (edited)APrimalStructureItemContainer
doesn't have a property named "CurrentResourceCount". You want to get the UPrimalInventoryComponent
associated with the bag and get the item count from that. Something like int count = bag->MyInventoryComponentField()->GetCurrentNumInventoryItems();
where "bag" is a APrimalStructureItemContainer
.
Also, there's no reason to use UObject::FindProperty()
or UProperty::Get()/Set()
unless you're working with Blueprint generated classes. APrimalStructureItemContainer
and UPrimalInventoryComponent
are native classes. Just use the definitions in the API headers to access properties, those accessor functions are faster. UPrimalInventoryComponent::InventoryItemsField()
instead, which gives you a TArray<UPrimalItem*>
. You could iterate over that list and ignore things you don't want to count. (edited)auto test = TheTurret->LastControllerField().Get()->NameField().ToString().ToString();
Log::GetLog()->warn("LastControllerField: {}", test);
server crashcase DLL_PROCESS_ATTACH:
if (ArkApi::GetApiUtils().GetStatus() == ArkApi::ServerStatus::Ready) {
Log::GetLog()->info("Called Load_Something");
Load_Something();
}
load();
break;
it should run load_something() after the server is booted up, but it never called it, am i missing something? i also tried to != and return it within the load();case DLL_PROCESS_ATTACH:
if (ArkApi::GetApiUtils().GetStatus() == ArkApi::ServerStatus::Ready) {
Log::GetLog()->info("Called Load_Something");
Load_Something();
}
load();
break;
it should run load_something() after the server is booted up, but it never called it, am i missing something? i also tried to != and return it within the load(); API::Requests::Get().CreateGetRequest("https://examplepage.com/api/version", &response);
but the "response" function runs after server starts completely and plugins load anyways. (edited)API::Requests::Get().CreateGetRequest("https://examplepage.com/api/version", &response);
but the "response" function runs after server starts completely and plugins load anyways. (edited)E0135 class "UProperty" has no member "NameField"
void Hook_UObject_ProcessInternal(UObject* _this, FFrame* Stack, void* const Result)
{
if (Stack->NodeField()->InternalIndexField() != FunctionIndex) {
// Process the function normally and return if this is not the function we're looking for
UObject_ProcessInternal_original(_this, Stack, Result);
return;
}
for (UProperty* prop = Stack->NodeField()->PropertyLinkField(); prop; prop = prop->PropertyLinkNextField())
{
Log::GetLog()->error("field: {}", prop->NameField().ToString());
}
(edited)DECLARE_HOOK(APrimalStructure_TakeDamage, float, APrimalStructure*, float, FDamageEvent*, AController*, AActor*);
float Hook_APrimalStructure_TakeDamage(APrimalStructure* _this, float Damage, FDamageEvent* DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
return APrimalStructure_TakeDamage_original(_this, Damage, DamageEvent, EventInstigator, DamageCauser);
}
ArkApi::GetHooks().SetHook("APrimalStructure.TakeDamage", &Hook_APrimalStructure_TakeDamage, &APrimalStructure_TakeDamage_original);
ArkApi::GetHooks().DisableHook("APrimalStructure.TakeDamage", &Hook_APrimalStructure_TakeDamage);
DECLARE_HOOK(APrimalStructure_TakeDamage, float, APrimalStructure*, float, FDamageEvent*, AController*, AActor*);
float Hook_APrimalStructure_TakeDamage(APrimalStructure* _this, float Damage, FDamageEvent* DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
return APrimalStructure_TakeDamage_original(_this, Damage, DamageEvent, EventInstigator, DamageCauser);
}
ArkApi::GetHooks().SetHook("APrimalStructure.TakeDamage", &Hook_APrimalStructure_TakeDamage, &APrimalStructure_TakeDamage_original);
ArkApi::GetHooks().DisableHook("APrimalStructure.TakeDamage", &Hook_APrimalStructure_TakeDamage);
return APrimalStructure_TakeDamage_original(_this, Damage, DamageEvent, EventInstigator, DamageCauser);
(edited)UVictoryCore::BPLoadClass(FString* PathName)
to get the UClass the function is defined in, then find the function itself with UClass::FindFunctionByName(FName InName, EIncludeSuperFlag::Type IncludeSuper)
.
Here's an example that uses the Blueprint function Buff_Companion_HLNA_C::Get Glitch Mission Complete Count()
. This function returns the number of glitches that a player has collected in a given Gen1 biome. It has FName MissionTag
and AShooterCharacter* Player
input arguments, an int32 Count
output argument, and an int32 ret count
local variable./test
chat command that calls Buff_Companion_HLNA_C::Get Glitch Mission Complete Count()
to get the number of glitches the player has collected in the Bog biome. To call a Blueprint function, we call [UObject,AActor]::ProcessEvent(UFunction* Function, void* Parameters)
. Function
points to the function we want to call. Parameters
points to memory that has the input/output arguments all packed together (defined by struct GetMissionCompleteCountParms
in the example).
The example also hooks the Buff_Companion_HLNA_C::Get Glitch Mission Complete Count()
Blueprint function by hooking UObject::ProcessInternal(UObject* _this, FFrame* Stack, void* const Result)
. Unreal Engine uses ProcessInternal()
to execute the bytecode of a Blueprint function. When ProcessInternal()
is hooked, we can hook specific Blueprint functions by checking which UFunction ProcessInternal()
is being called on.
Each Blueprint function that is executed has its own FFrame stack frame object. We can use ProcessInternal()'s
Stack
argument to access input/output arguments and local variables inside our Blueprint function hook. When the Buff_Companion_HLNA_C::Get Glitch Mission Complete Count()
hook in the example is executed, it prints the name of the AShooterCharacter* Player
input character and the value of the int32 ret count
local variable.
We can also examine previous function's frames on the stack. For example, we can see which Blueprint function called the hooked function and access the caller's variables. The example code demonstrates this by hooking the native function AMissionType::FindMissionsMatchingTag()
, and the Blueprint function AMissionType_GlitchCounter_Base_C::GetCompletedGlitchCount()
, both of which are called by Buff_Companion_HLNA_C::Get Glitch Mission Complete Count()
. These hooks print input and local variable values from the calling Buff_Companion_HLNA_C::Get Glitch Mission Complete Count()
function.::GetScriptCallstack
::PeekCode()
uint8* code
is the current instructions /** Returns the current script op code */
const uint8 PeekCode() const { return *Code; }
/** Skips over the number of op codes specified by NumOps */
void SkipCode(const int32 NumOps) { Code += NumOps; }
EX_ComputedJump = 0x4E, // Goto a local address in code, specified by an integer value.
EX_PushExecutionFlow = 0x4C, // push an address on to the execution flow stack for future execution when a EX_PopExecutionFlow is executed. Execution continues on normally and doesn't change to the pushed address.
EX_PopExecutionFlow = 0x4D, // continue execution at the last address previously pushed onto the execution flow stack.
EExprToken
enum values. Each value is an index into the global Native GNatives[EX_Max]
array. This array has pointers to native functions that execute the virtual machine instruction.
c++
void FFrame::Step(UObject *Context, RESULT_DECL)
{
int32 B = *Code++;
(Context->*GNatives[B])(*this,Result);
}
EX_ComputedJump
sanitized for exampleEX_VirtualFunction
or EX_FinalFunction
instruction that calls UObject::CallFunction(FFrame& Stack, RESULT_DECL, UFunction* Function )
with a UFunction address that was evaluated by name or read from the bytecode. Then for native UFunctions, CallFunction()
calls the UFunctions Func
field, which points to a native void execFoo(UObject* Object, FFrame* Stack, void* const Result)
, which processes any native function arguments out of the calling functions bytecode and then calls the C++ Foo()
.UVictoryCore::BPLoadClass(FString* PathName)
to get the UClass the function is defined in, then find the function itself with UClass::FindFunctionByName(FName InName, EIncludeSuperFlag::Type IncludeSuper)
.
Here's an example that uses the Blueprint function Buff_Companion_HLNA_C::Get Glitch Mission Complete Count()
. This function returns the number of glitches that a player has collected in a given Gen1 biome. It has FName MissionTag
and AShooterCharacter* Player
input arguments, an int32 Count
output argument, and an int32 ret count
local variable. ...
is NOT part of a function signature in C++?std::function
can not store variadic functions__stdcall void(void)
is finevoid(...)
__stdcall
wouldn't workcdecl
only worked because the macro was defining it to nothing, thanks Microsoft (edited)...
is disallowedfunction_ptr
holds function_signature
)_this->NumBulletsPerShotField() = 0;
Is there a way to do something similar with the consumption of tek armor (with UPrimalItem)? (edited)(Blueprint'/Game/PrimalEarth/CoreBlueprints/Items/Armor/Base/PrimalItemArmor_Base_Tek.PrimalItemArmor_Base_Tek'')
i found condition, it check if armor has "Infinite Ammo" ¿ How i can set infinite ammo ?Blueprint'/Game/PrimalEarth/CoreBlueprints/Items/Armor/Base/PrimalItemArmor_Base_Tek.PrimalItemArmor_Base_Tek''
UObject* obj = static_cast<UObject*>(item);
UProperty* bInfiniteAmmo = obj->FindProperty(FName("bInfiniteAmmo", EFindName::FNAME_Add));
but crash, crash too if i do UProperty* bInfiniteAmmo = item->FindProperty(FName("bInfiniteAmmo", EFindName::FNAME_Add));
auto item = _this->FindItem(&itemNetID, false, false, &index);
UProperty* bInfiniteAmmo = item->FindProperty(FName("bInfiniteAmmo", EFindName::FNAME_Add));
too crashc++
UProperty* bInfiniteAmmo = item->FindProperty(FName("bInfiniteAmmo", EFindName::FNAME_Add));
if(bInfiniteAmmo)
bInfiniteAmmo->Set<bool>(item, true);
c++
UProperty* bInfiniteAmmo = item->FindProperty(FName("bInfiniteAmmo", EFindName::FNAME_Add));
if(bInfiniteAmmo)
bInfiniteAmmo->Set<bool>(item, true);
UPrimalItem* Hook_UPrimalInventoryComponent_AddItem(UPrimalInventoryComponent* _this, FItemNetInfo* theItemInfo, bool bEquipItem, bool AddToSlot, bool bDontStack, FItemNetID* InventoryInsertAfterItemID, bool ShowHUDNotification, bool bDontRecalcSpoilingTime, bool bForceIncompleteStacking, AShooterCharacter* OwnerPlayer, bool bIgnoreAbsoluteMaxInventory)
{
if (bEquipItem) {
int index;
auto itemNetID = theItemInfo->ItemIDField();
auto item = _this->FindItem(&itemNetID, false, false, &index);
UProperty* bInfiniteAmmo = item->FindProperty(FName("bInfiniteAmmo", EFindName::FNAME_Add));
if (bInfiniteAmmo) {
bInfiniteAmmo->Set<bool>(item, true);
}
}
return UPrimalInventoryComponent_AddItem_original(_this, theItemInfo, bEquipItem, AddToSlot, bDontStack, InventoryInsertAfterItemID, ShowHUDNotification, bDontRecalcSpoilingTime, bForceIncompleteStacking, OwnerPlayer, bIgnoreAbsoluteMaxInventory);
}
}
if (item) {}
and crash tooUProperty* theProperty = buffCryoCooldownObj->FindProperty(FName("TimeLeft", EFindName::FNAME_Find));
if (!theProperty) {
Dodo::Logg::writeToLogFile(pluginName, "prop TimeLeft not found");
}
There is another Property "DeactivateAfterTime" which I did successfully set in all the methods above and it did deactivate the buff early, although the timer for the player is still 300 seconds it just ends after whatever I set.
Now the question is what is going on with this one property, what am I missing^^ (edited)UProperty* theProperty = buffCryoCooldownObj->FindProperty(FName("TimeLeft", EFindName::FNAME_Find));
if (!theProperty) {
Dodo::Logg::writeToLogFile(pluginName, "prop TimeLeft not found");
}
There is another Property "DeactivateAfterTime" which I did successfully set in all the methods above and it did deactivate the buff early, although the timer for the player is still 300 seconds it just ends after whatever I set.
Now the question is what is going on with this one property, what am I missing^^ (edited)ABuff_CryoCooldown_C
. The released version of ArkServerApi has a bug in UObject::FindProperty()
where the first property is not included in the search. It's fixed in GitHub, so you can rebuild ArkServerApi from source yourself and it will work. But your plugin wouldn't work for other users using 3.54/3.55. One option is to just compile the fixed FindProperty()
in your plugin and use that.
c++
UProperty* FindProperty(FName name)
{
for (UProperty* Property = ClassField()->PropertyLinkField(); Property != nullptr; Property = Property->PropertyLinkNextField())
if (Property->NameField().Compare(&name) == 0)
return Property;
return nullptr;
}
(edited)ABuff_CryoCooldown_C
. The released version of ArkServerApi has a bug in UObject::FindProperty()
where the first property is not included in the search. It's fixed in GitHub, so you can rebuild ArkServerApi from source yourself and it will work. But your plugin wouldn't work for other users using 3.54/3.55. One option is to just compile the fixed FindProperty()
in your plugin and use that.
c++
UProperty* FindProperty(FName name)
{
for (UProperty* Property = ClassField()->PropertyLinkField(); Property != nullptr; Property = Property->PropertyLinkNextField())
if (Property->NameField().Compare(&name) == 0)
return Property;
return nullptr;
}
(edited)UObject::FindProperty()
c++
FString buffPath = "Blueprint'/Game/Extinction/CoreBlueprints/Buffs/Buff_CryoCooldown.Buff_CryoCooldown'";
UClass* buffClass = UVictoryCore::BPLoadClass(&buffPath);
if (!buffClass) {
Log::GetLog()->error("UClass not found - ABuff_CryoCooldown_C");
return;
}
FName timeLeftName("TimeLeft", EFindName::FNAME_Find);
UProperty* timeLeftProp = nullptr;
for (UProperty* prop = buffClass->PropertyLinkField(); prop != nullptr; prop = prop->PropertyLinkNextField()) {
if (prop->NameField().Compare(&timeLeftName) == 0) {
timeLeftProp = prop;
break;
}
}
if (!timeLeftProp) {
Log::GetLog()->error("UProperty not found - TimeLeft");
return;
}
APrimalBuff* cdo = reinterpret_cast<APrimalBuff*>(buffClass->ClassDefaultObjectField());
float timeLeft = 0;
try {
timeLeft = timeLeftProp->Get<float>(cdo);
} catch (std::invalid_argument e) {
Log::GetLog()->error("UProperty::Get() - {}", e.what());
return;
}
Log::GetLog()->info("TimeLeft = {}", timeLeft);
[info] TimeLeft = 300
c++
FString buffPath = "Blueprint'/Game/Extinction/CoreBlueprints/Buffs/Buff_CryoCooldown.Buff_CryoCooldown'";
UClass* buffClass = UVictoryCore::BPLoadClass(&buffPath);
if (!buffClass) {
Log::GetLog()->error("UClass not found - ABuff_CryoCooldown_C");
return;
}
FName timeLeftName("TimeLeft", EFindName::FNAME_Find);
UProperty* timeLeftProp = nullptr;
for (UProperty* prop = buffClass->PropertyLinkField(); prop != nullptr; prop = prop->PropertyLinkNextField()) {
if (prop->NameField().Compare(&timeLeftName) == 0) {
timeLeftProp = prop;
break;
}
}
if (!timeLeftProp) {
Log::GetLog()->error("UProperty not found - TimeLeft");
return;
}
APrimalBuff* cdo = reinterpret_cast<APrimalBuff*>(buffClass->ClassDefaultObjectField());
float timeLeft = 0;
try {
timeLeft = timeLeftProp->Get<float>(cdo);
} catch (std::invalid_argument e) {
Log::GetLog()->error("UProperty::Get() - {}", e.what());
return;
}
Log::GetLog()->info("TimeLeft = {}", timeLeft);
[info] TimeLeft = 300
buff->ForceReplicateNow(false, false);
on the hooks APrimalBuff_BeginPlay and APrimalBuff_AddBuff with check for the correct classes. Both didn't work but than again I probably don't get the full picture^^buff->ForceReplicateNow(false, false);
on the hooks APrimalBuff_BeginPlay and APrimalBuff_AddBuff with check for the correct classes. Both didn't work but than again I probably don't get the full picture^^ DeactivatesAfterTime
which is a non replicated var.DeactivatesAfterTime
is what controls the buff's lifetime from the parent APrimalBuff
c++
UClass* BuffClass;
UProperty* TimeLeftProp;
DECLARE_HOOK(APrimalBuff_AddBuff, APrimalBuff*, APrimalBuff*, APrimalCharacter*, AActor*);
APrimalBuff* Hook_APrimalBuff_AddBuff(APrimalBuff* _this, APrimalCharacter* ForCharacter, AActor* DamageCauser)
{
APrimalBuff* ret = APrimalBuff_AddBuff_original(_this, ForCharacter, DamageCauser);
if (!BuffClass || !TimeLeftProp || !_this->IsA(BuffClass))
return ret;
try {
TimeLeftProp->Set<float>(ret, 20);
} catch (std::invalid_argument e) {
Log::GetLog()->error("UProperty::Set() - {}", e.what());
return ret;
}
ret->ForceReplicateNow(false, false);
return ret;
}
// Called when the server is ready
void OnServerReady()
{
FString buffPath = "Blueprint'/Game/Extinction/CoreBlueprints/Buffs/Buff_CryoCooldown.Buff_CryoCooldown'";
BuffClass = UVictoryCore::BPLoadClass(&buffPath);
if (!BuffClass) {
Log::GetLog()->error("UClass not found - ABuff_CryoCooldown_C");
return;
}
FName timeLeftName("TimeLeft", EFindName::FNAME_Find);
for (UProperty* prop = BuffClass->PropertyLinkField(); prop != nullptr; prop = prop->PropertyLinkNextField()) {
if (prop->NameField().Compare(&timeLeftName) == 0) {
TimeLeftProp = prop;
break;
}
}
if (!TimeLeftProp) {
Log::GetLog()->error("UProperty not found - TimeLeft");
return;
}
}
Seems to work.void OnServerReady()
{
FString buffPath = "Blueprint'/Game/Extinction/CoreBlueprints/Buffs/Buff_CryoCooldown.Buff_CryoCooldown'";
BuffClass = UVictoryCore::BPLoadClass(&buffPath);
if (!BuffClass) {
Log::GetLog()->error("UClass not found - ABuff_CryoCooldown_C");
return;
}
FName timeLeftName("TimeLeft", EFindName::FNAME_Find);
for (UProperty* prop = BuffClass->PropertyLinkField(); prop != nullptr; prop = prop->PropertyLinkNextField()) {
if (prop->NameField().Compare(&timeLeftName) == 0) {
TimeLeftProp = prop;
break;
}
}
if (!TimeLeftProp) {
Log::GetLog()->error("UProperty not found - TimeLeft");
return;
}
}
c++
UClass* BuffClass;
UProperty* TimeLeftProp;
DECLARE_HOOK(APrimalBuff_AddBuff, APrimalBuff*, APrimalBuff*, APrimalCharacter*, AActor*);
APrimalBuff* Hook_APrimalBuff_AddBuff(APrimalBuff* _this, APrimalCharacter* ForCharacter, AActor* DamageCauser)
{
APrimalBuff* ret = APrimalBuff_AddBuff_original(_this, ForCharacter, DamageCauser);
if (!BuffClass || !TimeLeftProp || !_this->IsA(BuffClass))
return ret;
try {
TimeLeftProp->Set<float>(ret, 20);
} catch (std::invalid_argument e) {
Log::GetLog()->error("UProperty::Set() - {}", e.what());
return ret;
}
ret->ForceReplicateNow(false, false);
return ret;
}
// Called when the server is ready
void OnServerReady()
{
FString buffPath = "Blueprint'/Game/Extinction/CoreBlueprints/Buffs/Buff_CryoCooldown.Buff_CryoCooldown'";
BuffClass = UVictoryCore::BPLoadClass(&buffPath);
if (!BuffClass) {
Log::GetLog()->error("UClass not found - ABuff_CryoCooldown_C");
return;
}
FName timeLeftName("TimeLeft", EFindName::FNAME_Find);
for (UProperty* prop = BuffClass->PropertyLinkField(); prop != nullptr; prop = prop->PropertyLinkNextField()) {
if (prop->NameField().Compare(&timeLeftName) == 0) {
TimeLeftProp = prop;
break;
}
}
if (!TimeLeftProp) {
Log::GetLog()->error("UProperty not found - TimeLeft");
return;
}
}
Seems to work. DECLARE_HOOK(USceneComponent_MoveComponentImpl, bool, USceneComponent*, FVector*, FQuat*, bool, FHitResult*, EMoveComponentFlags, bool);
but i cant stop the player movement.void Hook_UCharacterMovementComponent_ServerMove_Implementation(UCharacterMovementComponent* _this, float TimeStamp, FVector_NetQuantize100 InAccel, FVector_NetQuantize100 ClientLoc, char MoveFlags, char ClientRoll, unsigned int View, UPrimitiveComponent* ClientMovementBase, FName ClientBaseBoneName, char ClientMovementMode)
{
return;
}
i can move, but cant interact away from my starting position, and if i unload the plugin i got teleported to the original position.AShooterCharacter
(edited)RelativeLocationField()
/ GetPosition()
/ GetWorldLocation
are updating when i move.AShooterCharacter
(edited)FVector player_pos = _this->MoveStartLocationField();
if (//Check if player is in certain zone) {
PC->SimpleTeleportTo(&player_pos, &rotation);
return;
}
player get stucked and cant leave, because because it goes too inside xD (im using ClientLoc for calculate player location)RelativeLocationField()
instead of ClientLoc happens too (edited)FExplorerNoteEntry
{
int ExplorerNoteIndex;
int ExplorerNoteType;
FString ExplorerNoteDescription;
FName DossierTameableDinoNameTag;
}
I tried both of those defintions
struct FExplorerNoteEntry {
int& ExplorerNoteIndexField() { return *GetNativePointerField<int*>(this, "FExplorerNoteEntry.ExplorerNoteIndex"); }
int& ExplorerNoteTypeField() { return *GetNativePointerField<int*>(this, "FExplorerNoteEntry.ExplorerNoteType"); }
FString& ExplorerNoteDescriptionField() { return *GetNativePointerField<FString*>(this, "FExplorerNoteEntry.ExplorerNoteDescription"); }
FName& DossierTameableDinoNameTagField() { return *GetNativePointerField<FName*>(this, "FExplorerNoteEntry.DossierTameableDinoNameTag"); }
};
//only either one is uncommented at a time
struct FExplorerNoteEntry {
int ExplorerNoteIndex;
int ExplorerNoteType;
FString ExplorerNoteDescription;
FName DossierTameableDinoNameTag;
};
with the code for either of the variants
UPrimalGameData* gameData = ArkApi::GetApiUtils().GetGameData();
TArray<FExplorerNoteEntry> notes = gameData->ExplorerNoteEntriesField();
Dodo::Logg::writeToLogFile(pluginName, fmt::format("entrys NUM :{}", notes.Num()));
for (auto& note : notes) {
// does work for one note
Dodo::Logg::writeToLogFile(pluginName, fmt::format("index: {} ExplorerNoteDescription: {}", note.ExplorerNoteIndexField(), note.ExplorerNoteDescriptionField().ToString()));
// does not work at all
//Dodo::Logg::writeToLogFile(pluginName, fmt::format("index: {} ExplorerNoteDescription: {}", note.ExplorerNoteIndex, note.ExplorerNoteDescription.ToString()));
}
The weird thing is that the first variant does work for one note and logs the description "dilo dossier". But crashes on the next one. The second variant crashes without logging. (edited)FExplorerNoteEntry
{
int ExplorerNoteIndex;
int ExplorerNoteType;
FString ExplorerNoteDescription;
FName DossierTameableDinoNameTag;
}
I tried both of those defintions
struct FExplorerNoteEntry {
int& ExplorerNoteIndexField() { return *GetNativePointerField<int*>(this, "FExplorerNoteEntry.ExplorerNoteIndex"); }
int& ExplorerNoteTypeField() { return *GetNativePointerField<int*>(this, "FExplorerNoteEntry.ExplorerNoteType"); }
FString& ExplorerNoteDescriptionField() { return *GetNativePointerField<FString*>(this, "FExplorerNoteEntry.ExplorerNoteDescription"); }
FName& DossierTameableDinoNameTagField() { return *GetNativePointerField<FName*>(this, "FExplorerNoteEntry.DossierTameableDinoNameTag"); }
};
//only either one is uncommented at a time
struct FExplorerNoteEntry {
int ExplorerNoteIndex;
int ExplorerNoteType;
FString ExplorerNoteDescription;
FName DossierTameableDinoNameTag;
};
with the code for either of the variants
UPrimalGameData* gameData = ArkApi::GetApiUtils().GetGameData();
TArray<FExplorerNoteEntry> notes = gameData->ExplorerNoteEntriesField();
Dodo::Logg::writeToLogFile(pluginName, fmt::format("entrys NUM :{}", notes.Num()));
for (auto& note : notes) {
// does work for one note
Dodo::Logg::writeToLogFile(pluginName, fmt::format("index: {} ExplorerNoteDescription: {}", note.ExplorerNoteIndexField(), note.ExplorerNoteDescriptionField().ToString()));
// does not work at all
//Dodo::Logg::writeToLogFile(pluginName, fmt::format("index: {} ExplorerNoteDescription: {}", note.ExplorerNoteIndex, note.ExplorerNoteDescription.ToString()));
}
The weird thing is that the first variant does work for one note and logs the description "dilo dossier". But crashes on the next one. The second variant crashes without logging. (edited).ToString()
FName&
is not FName*
FName& DossierTameableDinoNameTagField() { return *GetNativePointerField<FName*>
it doesn't make sense to return FName*
as FName&
return *GetNativePointerField<T*>
returns T
If the field is actually FName
then
FName& DossierTameableDinoNameTagField() { return *GetNativePointerField<FName*>
should be fine
if it is actually FName*
then
FName& DossierTameableDinoNameTagField() { return *GetNativePointerField<FName*>
would need to be
FName* DossierTameableDinoNameTagField() { return *GetNativePointerField<FName**>
return **GetNativePointerField<FName**>
(edited)return **GetNativePointerField<FName**>
(edited)GetNativePointerField<FName**>
would return FName**
, which is turned into FName*
with first dereferenceDossierTameableDinoNameTagField
to FName*
FName&
FName& DossierTameableDinoNameTagField() { return *GetNativePointerField<FName*>
it doesn't make sense to return FName*
as FName&
FVector& LastDeathLocationField() { return *GetNativePointerField<FVector*>(this, "AShooterPlayerController.LastDeathLocation"); }
Is there a way to tell what it actually is if IDA just says
FName DossierTameableDinoNameTag;
for example.FVector& LastDeathLocationField() { return *GetNativePointerField<FVector*>(this, "AShooterPlayerController.LastDeathLocation"); }
Is there a way to tell what it actually is if IDA just says
FName DossierTameableDinoNameTag;
for example. FExplorerNoteEntry
{
int ExplorerNoteIndex;
int ExplorerNoteType;
FString ExplorerNoteDescription;
FName DossierTameableDinoNameTag;
}
I tried both of those defintions
struct FExplorerNoteEntry {
int& ExplorerNoteIndexField() { return *GetNativePointerField<int*>(this, "FExplorerNoteEntry.ExplorerNoteIndex"); }
int& ExplorerNoteTypeField() { return *GetNativePointerField<int*>(this, "FExplorerNoteEntry.ExplorerNoteType"); }
FString& ExplorerNoteDescriptionField() { return *GetNativePointerField<FString*>(this, "FExplorerNoteEntry.ExplorerNoteDescription"); }
FName& DossierTameableDinoNameTagField() { return *GetNativePointerField<FName*>(this, "FExplorerNoteEntry.DossierTameableDinoNameTag"); }
};
//only either one is uncommented at a time
struct FExplorerNoteEntry {
int ExplorerNoteIndex;
int ExplorerNoteType;
FString ExplorerNoteDescription;
FName DossierTameableDinoNameTag;
};
with the code for either of the variants
UPrimalGameData* gameData = ArkApi::GetApiUtils().GetGameData();
TArray<FExplorerNoteEntry> notes = gameData->ExplorerNoteEntriesField();
Dodo::Logg::writeToLogFile(pluginName, fmt::format("entrys NUM :{}", notes.Num()));
for (auto& note : notes) {
// does work for one note
Dodo::Logg::writeToLogFile(pluginName, fmt::format("index: {} ExplorerNoteDescription: {}", note.ExplorerNoteIndexField(), note.ExplorerNoteDescriptionField().ToString()));
// does not work at all
//Dodo::Logg::writeToLogFile(pluginName, fmt::format("index: {} ExplorerNoteDescription: {}", note.ExplorerNoteIndex, note.ExplorerNoteDescription.ToString()));
}
The weird thing is that the first variant does work for one note and logs the description "dilo dossier". But crashes on the next one. The second variant crashes without logging. (edited)UPrimalGameData::ExplorerNoteEntriesField()
gives you a TArray<FExplorerNoteEntry>
. TArray only works with types that are sized correctly at compile time (TArrays of pointers always work because the compiler knows the size of a pointer). We can see in IDA that the actual size of a FExplorerNoteEntry is 0xA8 bytes.c++
struct FExplorerNoteEntry
{
uint8 _padding[0xA8];
int& ExplorerNoteIndexField() { return *GetNativePointerField<int*>(this, "FExplorerNoteEntry.ExplorerNoteIndex"); }
int& ExplorerNoteTypeField() { return *GetNativePointerField<int*>(this, "FExplorerNoteEntry.ExplorerNoteType"); }
FString& ExplorerNoteDescriptionField() { return *GetNativePointerField<FString*>(this, "FExplorerNoteEntry.ExplorerNoteDescription"); }
FName& DossierTameableDinoNameTagField() { return *GetNativePointerField<FName*>(this, "FExplorerNoteEntry.DossierTameableDinoNameTag"); }
};
UPrimalGameData::ExplorerNoteEntriesField()
gives you a TArray<FExplorerNoteEntry>
. TArray only works with types that are sized correctly at compile time (TArrays of pointers always work because the compiler knows the size of a pointer). We can see in IDA that the actual size of a FExplorerNoteEntry is 0xA8 bytes. UPrimalGameData::ExplorerNoteEntriesField()
gives you a TArray<FExplorerNoteEntry>
. TArray only works with types that are sized correctly at compile time (TArrays of pointers always work because the compiler knows the size of a pointer). We can see in IDA that the actual size of a FExplorerNoteEntry is 0xA8 bytes. UArrayProperty
has a UProperty* Inner;
that you can use to get the size of the element type.c++
struct FExplorerNoteEntry
{
uint8 _padding[0xA8];
int& ExplorerNoteIndexField() { return *GetNativePointerField<int*>(this, "FExplorerNoteEntry.ExplorerNoteIndex"); }
int& ExplorerNoteTypeField() { return *GetNativePointerField<int*>(this, "FExplorerNoteEntry.ExplorerNoteType"); }
FString& ExplorerNoteDescriptionField() { return *GetNativePointerField<FString*>(this, "FExplorerNoteEntry.ExplorerNoteDescription"); }
FName& DossierTameableDinoNameTagField() { return *GetNativePointerField<FName*>(this, "FExplorerNoteEntry.DossierTameableDinoNameTag"); }
};
UArrayProperty
has a UProperty* Inner;
that you can use to get the size of the element type. T
at construction timeT
T
0
until a TArray
is constructedif (TArray<T>::Size == 0)
TArray<T>::Size = CalculateSizeOf<T>;
(edited)Plugin_Init
and Plugin_Unload
void Hook_NotifyItemAdded(AShooterCharacter* _this, UPrimalItem* item, bool bEquipItem) {
NotifyItemAdded_original(_this, item, bEquipItem);
if (!item) {
return;
}
try {
FString name = item->NameField().ToString();
if (!name.Contains("PrimalItem_AwesomeSpyGlass_C")) {
LOG->info("No Match" + name.ToString());
return;
}
auto controller = ArkApi::GetApiUtils().FindControllerFromCharacter(_this);
if (!controller) {
LOG->info("No Controller" + name.ToString());
return;
}
//if (!_this->HasBuff())
controller->ServerRequestInventoryUseItem(_this->MyInventoryComponentField(), item->ItemIDField());
LOG->info("Used Spyglass: " + name.ToString());
} catch (const std::runtime_error &e){
LOG->error("RuntimeError: " + std::string(e.what()));
} catch (const std::exception &e) {
LOG->error("Exception: " + std::string(e.what()));
}
}
so is there a different way to get controller than this? hooks firing correctly, just cant get controller w/o crash, and is there something I can do to catch the error properly and not crash the whole server?
SET_HOOK("AShooterCharacter.NotifyItemAdded", NotifyItemAdded);
For the Hookvoid Hook_NotifyItemAdded(AShooterCharacter* _this, UPrimalItem* item, bool bEquipItem) {
NotifyItemAdded_original(_this, item, bEquipItem);
if (!item) {
return;
}
try {
FString name = item->NameField().ToString();
if (!name.Contains("PrimalItem_AwesomeSpyGlass_C")) {
LOG->info("No Match" + name.ToString());
return;
}
auto controller = ArkApi::GetApiUtils().FindControllerFromCharacter(_this);
if (!controller) {
LOG->info("No Controller" + name.ToString());
return;
}
//if (!_this->HasBuff())
controller->ServerRequestInventoryUseItem(_this->MyInventoryComponentField(), item->ItemIDField());
LOG->info("Used Spyglass: " + name.ToString());
} catch (const std::runtime_error &e){
LOG->error("RuntimeError: " + std::string(e.what()));
} catch (const std::exception &e) {
LOG->error("Exception: " + std::string(e.what()));
}
}
so is there a different way to get controller than this? hooks firing correctly, just cant get controller w/o crash, and is there something I can do to catch the error properly and not crash the whole server?
SET_HOOK("AShooterCharacter.NotifyItemAdded", NotifyItemAdded);
For the Hook NotifyItemAdded_original(_this, item, bEquipItem);
to the bottom of your hook incase those pointers are destroyed or invalid after the original code runs.[[nodiscard]] constexpr auto operator*() const { return *immutable_ptr; }
in my container over raw pointersT
(we know T
as T
, not T*
) with *immutable_ptr
and it points to a function T
, then you will fail to compile*T
on T*
when T*
is a function produces T*
and not T
MyInventoryComponentField
isn't null?PdbInfo.json
file too. Our issue ended up being APawn
not being exported hence the function address being garbage
It is no longer necessary since we forced all symbols to be exported since 3.51 version (edited)Hook_APrimalStructure_IsAllowedToBuild(APrimalStructure* _this, APlayerController* PC, FVector AtLocation, FRotator AtRotation,FPlacementData* OutPlacementData, bool bDontAdjustForMaxRange, FRotator PlayerViewRotation, bool bFinalPlacement)
I even found a blueprint using hooks. But I couldn't find the item name. Which method should I use?UFunction *pFunction = _this->FindFunctionChecked(FName("ToggleUILock", EFindName::FNAME_Find));
if (pFunction) {
LOG->info("ProcessEvent");
_this->ProcessEvent(pFunction, {});
return TRUE;
}
But that doesnt find the function nor does item->FindFunctionChecked. function approach feels like could be more reliable, anyone got tips on what i could try to fix for function method?UPrimalInventoryComponent* comp = _this->MyInventoryComponentField();
if (comp) {
auto itemId = item->ItemIDField();
controller->ServerRequestInventoryUseItem(comp, itemId);
feels like it should workAShooterCharacter::BeginPlay
if ( UActorComponent::GetOwner(this) )
{
UPrimalInventoryComponent::InventoryRefresh(this);
Item = UPrimalInventoryComponent::FindItem(this, itemID, 0, 0, 0i64);
if ( Item )
{
Owner = UActorComponent::GetOwner(this);
v3 = Owner->GetOwnerController(Owner) != ByPC;
if ( Item->AllowUseInInventory(Item, v3, ByPC, 0) )
{
if ( AShooterPlayerController::GetPlayerCharacter(ByPC) )
{
PlayerCharacter = AShooterPlayerController::GetPlayerCharacter(ByPC);
if ( PlayerCharacter->IsConscious(PlayerCharacter) )
{
v8 = UActorComponent::GetOwner(this);
v4 = v8->GetOwnerController(v8) != ByPC;
Item->Use(Item, v4);
}
}
}
}
}
Item->Use(Item, v4);
is probably what you wantAShooterPlayerController::ServerRequestInventoryUseItem
(edited)UPrimalInventoryComponent::ServerUseInventoryItem
UPrimalInventoryComponent::ServerUseInventoryItem
Item->Use(Item, v4);
item->use
worked for you?AShooterPlayerController* playerController = playerInventory->GetOwnerController();
_this->GetOwnerController()
GetOwnerController
is throwingBOOL trySpyglass(AShooterCharacter *_this, UPrimalItem *item) {
FString name = item->NameField().ToString();
if (!name.Contains("PrimalItem_AwesomeSpyGlass_C")) {
return FALSE;
}
try {
auto controller = static_cast<AShooterPlayerController*>(_this->ControllerField());
if (!controller) {
return FALSE;
}
TArray<APrimalBuff*> buffs;
_this->GetAllBuffs(&buffs);
LOG->info("Used Spyglass: " + name.ToString());
item->Use(false);
return TRUE;
} catch (const std::runtime_error &e){
LOG->error("RuntimeError: " + std::string(e.what()));
} catch (const std::exception &e) {
LOG->error("Exception: " + std::string(e.what()));
}
return FALSE;
}
void Hook_NotifyItemAdded(AShooterCharacter* _this, UPrimalItem* item, bool bEquipItem) {
if (_this && item && item->ClassField() && !item->bIsEngram()()) {
trySpyglass(_this, item);
}
NotifyItemAdded_original(_this, item, bEquipItem);
}
for(const auto& element : container)
APrimalBuff
BuffsField
is TArray<APrimalBuff>
_this->GetAllBuffs(&buffs);
for(const auto& buff : buffs) {
auto buffName = buff->NameField().ToString();
if (buffName.Contains("Spyglass")) {
LOG->info("Buff: " + buffName.ToString());
return TRUE;
}
}
?_this->GetAllBuffs(&buffs);
for(const auto& buff : buffs) {
auto buffName = buff->NameField().ToString();
if (buffName.Contains("Spyglass")) {
LOG->info("Buff: " + buffName.ToString());
return TRUE;
}
}
? TArray
uses pointer arithmetic for getting itemssizeof(APrimalBuff)
is 0 in the APIAPrimalBuff
until I write code to TArray to avoid this issuesizeo(T*)
is always sizeof(void*)
(edited)_this->GetAllBuffs(&buffs);
for(const auto& buff : buffs) {
auto buffName = buff->NameField().ToString();
if (buffName.Contains("Spyglass")) {
LOG->info("Buff: " + buffName.ToString());
return TRUE;
}
}
? AwesomeSpyGlass_Buff
__usercall
in IDA__fastcall
then just use cdecl in x64__fastcall
__fastcall
is babe (edited)TArray<APrimalBuff*> buffs;
_this->GetAllBuffs(&buffs);
for(const auto& buff : buffs) {
auto buffName = buff->NameField().ToString();
LOG->info("Buff: " + buffName.ToString());
if (buffName.Contains("Awesome_SpyGlass")) {
return TRUE;
}
}
didnt print anything so i dont think its finding the buffs correctly, but didnt crash at least.GetAllBuffs
->Buffs()
(edited)NotifyItemAdded
is probably for sending a notification to the playerUPrimalCharacterStatusComponent::CharacterUpdatedInventory
UPrimalItem::AddToInventory
static void onNotifyItemAdded(UPrimalInventoryComponent* _this, UPrimalItem* anItem, bool bEquipItem)
{
NotifyItemAdded.original(_this, anItem, bEquipItem);
if (!_this || !anItem)
return;
AActor* owner = _this->GetOwner();
if (!owner || !owner->IsA(AShooterCharacter::StaticClass()))
return;
AShooterCharacter* playerCharacter = static_cast<AShooterCharacter*>(owner);
Log::GetLog()->error("has buff count {}\n", playerCharacter->BuffsField().Num());
anItem->Use(true);
}
UPrimalInventoryComponent::TickComponent
UPrimalInventoryComponent.NotifyItemAdded
static void onNotifyItemAdded(UPrimalInventoryComponent* _this, UPrimalItem* anItem, bool bEquipItem)
{
NotifyItemAdded.original(_this, anItem, bEquipItem);
if (!_this || !anItem)
return;
AActor* owner = _this->GetOwner();
if (!owner || !owner->IsA(AShooterCharacter::StaticClass()))
return;
AShooterCharacter* playerCharacter = static_cast<AShooterCharacter*>(owner);
Log::GetLog()->error("has buff count {}\n", playerCharacter->BuffsField().Num());
anItem->Use(true);
}
void Hook_NotifyItemAdded(AShooterCharacter* _this, UPrimalItem* item, bool bEquipItem) {
if (_this && item && item->ClassField() && !item->bIsEngram()()) {
FString name = item->NameField().ToString();
if (name.Contains("PrimalItem_AwesomeSpyGlass_C")) {
NotifyItemAdded_original(_this, item, true);
return;
}
//trySpyglass(_this, item);
}
NotifyItemAdded_original(_this, item, bEquipItem);
}
gonna try thisFString spyGlassBuffBlueprint = "Blueprint'/Game/Mods/AwesomeSpyglass/AwesomeSpyGlass_Buff.AwesomeSpyGlass_Buff'";
void trySpyglass(AShooterCharacter *_this, UPrimalItem *item) {
static auto BuffName = "AwesomeSpyGlass_Buff";
static auto ItemName = "PrimalItem_AwesomeSpyGlass_C";
FString name = item->NameField().ToString();
if (!name.StartsWith(ItemName)) {
return;
}
TArray<APrimalBuff*> buffs;
_this->GetBuffs(&buffs);
for (int i = 0; i < buffs.Num(); i++) {
APrimalBuff* buff = buffs[i];
auto buffName = buff->NameField().ToString();
if (buffName.StartsWith(BuffName)) {
return;
}
}
item->Use(false);
}
void Hook_NotifyItemAdded(UPrimalInventoryComponent* _this, UPrimalItem* anItem, bool bEquipItem) {
NotifyItemAdded_original(_this, anItem, bEquipItem);
if (!_this || !anItem || anItem->bIsEngram()())
return;
AActor* owner = _this->GetOwner();
if (!owner || !owner->IsA(AShooterCharacter::StaticClass()))
return;
auto playerCharacter = static_cast<AShooterCharacter*>(owner);
trySpyglass(playerCharacter, anItem);
}
static void onNotifyItemAdded(UPrimalInventoryComponent* _this, UPrimalItem* anItem, bool bEquipItem)
{
static FString spyGlassBuffBlueprint = "Blueprint'/Game/Mods/AwesomeSpyglass/AwesomeSpyGlass_Buff.AwesomeSpyGlass_Buff'";
static FString spyGlassBlueprint = "Blueprint'/Game/Mods/AwesomeSpyglass/PrimalItem_AwesomeSpyGlass.PrimalItem_AwesomeSpyGlass'";
static UClass* spyGlassBuffClass = UVictoryCore::BPLoadClass(&spyGlassBuffBlueprint);
static UClass* spyGlassClass = UVictoryCore::BPLoadClass(&spyGlassBlueprint);
NotifyItemAdded.original(_this, anItem, bEquipItem);
if (!_this || !anItem || !anItem->IsA(spyGlassClass))
return;
AActor* owner = _this->GetOwner();
if (!owner || !owner->IsA(AShooterCharacter::StaticClass()))
return;
AShooterCharacter* playerCharacter = static_cast<AShooterCharacter*>(owner);
if (playerCharacter->HasBuff(spyGlassBuffClass, true))
return;
anItem->Use(true);
}
HasBuff
uses IsA
for exampleonStartNewShooterPlayer(AShooterGameMode* _this, APlayerController* newPlayer, bool forceCreateNewPlayerData, bool isFromLogin, const FPrimalPlayerCharacterConfigStruct& characterConfig, UPrimalPlayerData* playerData)
isFromLogin
= trueUPrimalPlayerData
has the transferred datastatic auto onStartNewShooterPlayer(AShooterGameMode* _this, APlayerController* newPlayer, bool forceCreateNewPlayerData, bool isFromLogin, const FPrimalPlayerCharacterConfigStruct& characterConfig, UPrimalPlayerData* playerData)
{
AShooterPlayerController* playerController = static_cast<AShooterPlayerController*>(newPlayer);
StartNewShooterPlayer.original(_this, newPlayer, forceCreateNewPlayerData, isFromLogin, characterConfig, playerData);
if (!playerController)
return;
AShooterCharacter* playerPawn = playerController->LastControlledPlayerCharacterField().Get();
if (!playerPawn)
return;
static FString spyGlassBlueprint = "Blueprint'/Game/Mods/AwesomeSpyglass/PrimalItem_AwesomeSpyGlass.PrimalItem_AwesomeSpyGlass'";
static UClass* spyGlassClass = UVictoryCore::BPLoadClass(&spyGlassBlueprint);
for (auto&& item : playerPawn->MyInventoryComponentField()->InventoryItemsField())
{
if (!item->IsA(spyGlassClass))
continue;
item->Use(true);
return;
}
}
StartNewShooterPlayer.original
is my own container over functions that can be hooked, if you end up using this you'll have to change it to the function the API macro generates insteadstatic inline moar::function_ptr<void(AShooterGameMode*, APlayerController*, bool, bool, const FPrimalPlayerCharacterConfigStruct&, UPrimalPlayerData*)> StartNewShooterPlayer;
onStartNewShooterPlayer
for (auto&& item : playerPawn->MyInventoryComponentField()->InventoryItemsField())
{
Log::GetLog()->error("item {}\n", item->NameField().ToString().ToString());
if (!item->IsA(spyGlassClass))
continue;
API::Timer::Get().DelayExecute([item]()
{
item->Use(true);
}, 1);
return;
}
#include <Timer.h>
item->bIsEngram()() || item->bIsBlueprint()()
()
(edited) API::Timer::Get().DelayExecute([playerPawn]()
{
static FString spyGlassBuffBlueprint = "Blueprint'/Game/Mods/AwesomeSpyglass/AwesomeSpyGlass_Buff.AwesomeSpyGlass_Buff'";
static FString spyGlassBlueprint = "Blueprint'/Game/Mods/AwesomeSpyglass/PrimalItem_AwesomeSpyGlass.PrimalItem_AwesomeSpyGlass'";
static UClass* spyGlassBuffClass = UVictoryCore::BPLoadClass(&spyGlassBuffBlueprint);
static UClass* spyGlassClass = UVictoryCore::BPLoadClass(&spyGlassBlueprint);
for (auto&& item : playerPawn->MyInventoryComponentField()->InventoryItemsField())
{
Log::GetLog()->error("item {}\n", item->NameField().ToString().ToString());
if (item->bIsEngram()() || item->bIsBlueprint()() || !item->IsA(spyGlassClass))
continue;
item->Use(true);
return;
}
}, 1);
static auto enableSpyGlassOnLogin(AShooterCharacter* playerPawn)
{
static FString spyGlassBuffBlueprint = "Blueprint'/Game/Mods/AwesomeSpyglass/AwesomeSpyGlass_Buff.AwesomeSpyGlass_Buff'";
static FString spyGlassBlueprint = "Blueprint'/Game/Mods/AwesomeSpyglass/PrimalItem_AwesomeSpyGlass.PrimalItem_AwesomeSpyGlass'";
static UClass* spyGlassBuffClass = UVictoryCore::BPLoadClass(&spyGlassBuffBlueprint);
static UClass* spyGlassClass = UVictoryCore::BPLoadClass(&spyGlassBlueprint);
for (auto&& item : playerPawn->MyInventoryComponentField()->InventoryItemsField())
{
if (item->bIsEngram()() || item->bIsBlueprint()() || !item->IsA(spyGlassClass))
continue;
item->Use(true);
return;
}
}
static auto onStartNewShooterPlayer(AShooterGameMode* _this, APlayerController* newPlayer, bool forceCreateNewPlayerData, bool isFromLogin, const FPrimalPlayerCharacterConfigStruct& characterConfig, UPrimalPlayerData* playerData)
{
AShooterPlayerController* playerController = static_cast<AShooterPlayerController*>(newPlayer);
StartNewShooterPlayer.original(_this, newPlayer, forceCreateNewPlayerData, isFromLogin, characterConfig, playerData);
if (!playerController)
return;
AShooterCharacter* playerPawn = playerController->LastControlledPlayerCharacterField().Get();
if (!playerPawn)
return;
API::Timer::Get().DelayExecute(std::bind(enableSpyGlassOnLogin, playerPawn), 0);
}
(edited)std::bind
let's us pass playerPawn
to enableSpyGlassOnLogin
DelayExecute
expects a function with no args API::Timer::Get().DelayExecute(std::bind(enableSpyGlassOnLogin, playerPawn), 0);
is the same as like
API::Timer::Get().DelayExecute([playerPawn]()
{
enableSpyGlassOnLogin(playerPawn);
}, 0);
char NewBabyCuddleType
to EBabyCuddleType::PET
, it works, but the text in the dino no changes.
c++
void Hook_APrimalDinoCharacter_UpdateBabyCuddling_Implementation(APrimalDinoCharacter* _this, long double NewBabyNextCuddleTime, char NewBabyCuddleType, TSubclassOf<UPrimalItem> NewBabyCuddleFood)
{
NewBabyCuddleType = EBabyCuddleType::PET;
APrimalDinoCharacter_UpdateBabyCuddling_Implementation_original(_this, NewBabyNextCuddleTime, NewBabyCuddleType, NewBabyCuddleFood);
}
If i click "E" for pet the dino, it works, but the text still saying "wants to be hand-fed comfort food: food type"
any know how i can fix it?char NewBabyCuddleType
to EBabyCuddleType::PET
, it works, but the text in the dino no changes.
c++
void Hook_APrimalDinoCharacter_UpdateBabyCuddling_Implementation(APrimalDinoCharacter* _this, long double NewBabyNextCuddleTime, char NewBabyCuddleType, TSubclassOf<UPrimalItem> NewBabyCuddleFood)
{
NewBabyCuddleType = EBabyCuddleType::PET;
APrimalDinoCharacter_UpdateBabyCuddling_Implementation_original(_this, NewBabyNextCuddleTime, NewBabyCuddleType, NewBabyCuddleFood);
}
If i click "E" for pet the dino, it works, but the text still saying "wants to be hand-fed comfort food: food type"
any know how i can fix it? wants to be hand-fed comfort food: food type
(or the text when dino need to walk with you, it's still random), I need it would always say pet message. (edited)CurrentPetTypeField
yourself with MulticastProperty
functionbool isCreateEgg = false;
FItemNetInfo* Hook_UPrimalItem_GetItemNetInfo(UPrimalItem* _this, FItemNetInfo* result, bool bIsForSendingToClient) {
return UPrimalItem_GetItemNetInfo_original(_this, result, bIsForSendingToClient);
}
ADroppedItem* Hook_APrimalDinoCharacter_CreateCloneFertilizedEgg(APrimalDinoCharacter* dino, FVector AtLoc, FRotator AtRot, TSubclassOf<ADroppedItem> DroppedItemTemplateOverride) {
isCreateEgg = true;
auto result = APrimalDinoCharacter_CreateCloneFertilizedEgg_original(dino, AtLoc, AtRot, DroppedItemTemplateOverride);
isCreateEgg = false;
return result;
}
DoMate_Implementation
function, the create clone fert egg just copies the dino values into the egg, without actually incrementing any mutation counter iVar3 = rand();
bVar2 = 0.5 < (float)iVar3 / 32767.0;
local_34c = (uint)bVar2;
if ((((bVar2) && (local_1c8.EggRandomMutationsFemale < *(int *)(this + 0x2018))) ||
((!bVar2 && (local_1c8.EggRandomMutationsMale < *(int *)(this + 0x2018))))) &&
((float)(uint)local_1c8.EggNumberOfLevelUpPointsApplied[local_35c] +
*(float *)(this + 0x2024) <= 255.0)) {
local_1c8.EggNumberOfLevelUpPointsApplied[local_35c] =
(uchar)(longlong)
((float)(uint)local_1c8.EggNumberOfLevelUpPointsApplied[local_35c] +
*(float *)(this + 0x2024));
if (bVar2) {
local_1c8.EggRandomMutationsFemale = local_1c8.EggRandomMutationsFemale + 1;
}
else {
local_1c8.EggRandomMutationsMale = local_1c8.EggRandomMutationsMale + 1;
}
theres def mutation logic going on hereAPrimalDinoCharacter::DoMate
is an engine implementation, and APrimalDinoCharacter::DoMate_Implementation
has the actual logicc++
DECLARE_HOOK(APrimalDinoCharacter_APrimalDinoCharacter*, APrimalDinoCharacter*, APrimalDinoCharacter*, UWorld*, TSubclassOf<APrimalDinoCharacter>, FVector*, float, char*, char*, float, int, TArray<FDinoAncestorsEntry>*, TArray<FDinoAncestorsEntry>*, int, int);
APrimalDinoCharacter* Hook_APrimalDinoCharacter_APrimalDinoCharacter*(APrimalDinoCharacter* _this, UWorld* theWorld, TSubclassOf<APrimalDinoCharacter> EggDinoClassToSpawn, FVector* theGroundLoc, float actorRotationYaw, char* EggColorSetIndices, char* EggNumberOfLevelUpPointsApplied, float EggTamedIneffectivenessModifier, int NotifyTeamOverride, TArray<FDinoAncestorsEntry>* EggDinoAncestors, TArray<FDinoAncestorsEntry>* EggDinoAncestorsMale, int EggRandomMutationsFemale, int EggRandomMutationsMale)
{
return APrimalDinoCharacter_APrimalDinoCharacter*_original(_this, theWorld, EggDinoClassToSpawn, theGroundLoc, actorRotationYaw, EggColorSetIndices, EggNumberOfLevelUpPointsApplied, EggTamedIneffectivenessModifier, NotifyTeamOverride, EggDinoAncestors, EggDinoAncestorsMale, EggRandomMutationsFemale, EggRandomMutationsMale);
}
ArkApi::GetHooks().SetHook("APrimalDinoCharacter.APrimalDinoCharacter*", &Hook_APrimalDinoCharacter_APrimalDinoCharacter*, &APrimalDinoCharacter_APrimalDinoCharacter*_original);
ArkApi::GetHooks().DisableHook("APrimalDinoCharacter.APrimalDinoCharacter*", &Hook_APrimalDinoCharacter_APrimalDinoCharacter*);
but dont compiles.c++
DECLARE_HOOK(APrimalDinoCharacter_StaticCreateBabyDino, APrimalDinoCharacter*, APrimalDinoCharacter*, UWorld*, TSubclassOf<APrimalDinoCharacter>, FVector*, float, char*, char*, float, int, TArray<FDinoAncestorsEntry>*, TArray<FDinoAncestorsEntry>*, int, int);
APrimalDinoCharacter* Hook_APrimalDinoCharacter_StaticCreateBabyDino(APrimalDinoCharacter* _this, UWorld* theWorld, TSubclassOf<APrimalDinoCharacter> EggDinoClassToSpawn, FVector* theGroundLoc, float actorRotationYaw, char* EggColorSetIndices, char* EggNumberOfLevelUpPointsApplied, float EggTamedIneffectivenessModifier, int NotifyTeamOverride, TArray<FDinoAncestorsEntry>* EggDinoAncestors, TArray<FDinoAncestorsEntry>* EggDinoAncestorsMale, int EggRandomMutationsFemale, int EggRandomMutationsMale)
{
Log::GetLog()->info("CreateBabyDino");
return APrimalDinoCharacter_StaticCreateBabyDino_original(_this, theWorld, EggDinoClassToSpawn, theGroundLoc, actorRotationYaw, EggColorSetIndices, EggNumberOfLevelUpPointsApplied, EggTamedIneffectivenessModifier, NotifyTeamOverride, EggDinoAncestors, EggDinoAncestorsMale, EggRandomMutationsFemale, EggRandomMutationsMale);
}
ArkApi::GetHooks().SetHook("APrimalDinoCharacter.StaticCreateBabyDino", &Hook_APrimalDinoCharacter_StaticCreateBabyDino, &APrimalDinoCharacter_StaticCreateBabyDino_original);
ArkApi::GetHooks().DisableHook("APrimalDinoCharacter.StaticCreateBabyDino", &Hook_APrimalDinoCharacter_StaticCreateBabyDino);
And "CreateBabyDino" shows sucess, but i cant find the way to get the dino after it creates.c++
APrimalDinoCharacter* Dino = APrimalDinoCharacter_StaticCreateBabyDino_original(_this, theWorld, EggDinoClassToSpawn, theGroundLoc, actorRotationYaw, EggColorSetIndices, EggNumberOfLevelUpPointsApplied, EggTamedIneffectivenessModifier, NotifyTeamOverride, EggDinoAncestors, EggDinoAncestorsMale, EggRandomMutationsFemale, EggRandomMutationsMale);
server crash.
And if i try to modify the "_this" variable, it crash in the return line.
c++
_this->TamedNameField() = FString("Test");
return APrimalDinoCharacter_StaticCreateBabyDino_original(_this, theWorld, EggDinoClassToSpawn, theGroundLoc, actorRotationYaw, EggColorSetIndices, EggNumberOfLevelUpPointsApplied, EggTamedIneffectivenessModifier, NotifyTeamOverride, EggDinoAncestors, EggDinoAncestorsMale, EggRandomMutationsFemale, EggRandomMutationsMale);
With this code, it crash in the return line.c++
struct AwardData
{
float Amount{};
bool bUseTokens{};
std::string sTokenName{};
int min_level{};
int max_level{};
int KillsNeeded{};
};
typedef std::unordered_map<FString, std::vector<AwardData>> FStringAwardDataMap;
typedef std::unordered_map<uint64, FStringAwardDataMap> SteamIdBPAwardDataMap;
SteamIdBPAwardDataMap PlayerDinoRewardsMap3{};
const FString bp = "Some random dino blueprint";
const uint64 steam_id = 13213682736873613;
if (PlayerDinoRewardsMap3.size() > 0)
{
auto& result = PlayerDinoRewardsMap3.find(steam_id);
if (result != PlayerDinoRewardsMap3.end())
{
FStringAwardDataMap& awardMap = result->second;
auto& result2 = awardMap.find(bp); // this line won't compile, when i try to search for FString in FStringAwardDataMap
}
}
build
?)) (edited)#pragma once
/* READ ME
*
* This file contains some basic and useful includes.
* You can change this how you like, but I suggest
* only removing something If you know what you are doing.
*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <API/ARK/Ark.h>
#include <Logger/Logger.h>
#include <Timer.h>
#pragma comment(lib, "ArkApi.lib")
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <API/ARK/Ark.h>
#include <Logger/Logger.h>
#include <Timer.h>
#pragma comment(lib, "ArkApi.lib")
DECLARE_HOOK(AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation, void, AShooterPlayerController*, FString*, FString*);
void Hook_AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation(AShooterPlayerController* _this, FString* PlayerName, FString* AttackerName)
{
AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation_original(_this, PlayerName, AttackerName);
ArkApi::GetApiUtils().SendNotification(_this, FLinearColor(1, 0, 0),
1, 10, nullptr, "*GetText");
}
void Init()
{
ArkApi::GetHooks().SetHook("AShooterPlayerController.ClientNotifyRemotePlayerDeath_Implementation", &Hook_AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation, &AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation_original);
}
void Unload()
{
ArkApi::GetHooks().DisableHook("AShooterPlayerController.ClientNotifyRemotePlayerDeath_Implementation", &Hook_AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation);
}
// pch.cpp: Quelldatei, die dem vorkompilierten Header entspricht
/* READ ME
*
* This file contains some basic and useful includes.
* You can change this how you like, but I suggest
* only removing something If you know what you are doing.
*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <API/ARK/Ark.h>
#include <Logger/Logger.h>
#include <Timer.h>
#pragma comment(lib, "ArkApi.lib")
DECLARE_HOOK(AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation, void, AShooterPlayerController*, FString*, FString*);
void Hook_AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation(AShooterPlayerController* _this, FString* PlayerName, FString* AttackerName)
{
AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation_original(_this, PlayerName, AttackerName);
ArkApi::GetApiUtils().SendNotification(_this, FLinearColor(1, 0, 0),
1, 10, nullptr, "*GetText");
}
void Init()
{
ArkApi::GetHooks().SetHook("AShooterPlayerController.ClientNotifyRemotePlayerDeath_Implementation", &Hook_AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation, &AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation_original);
}
void Unload()
{
ArkApi::GetHooks().DisableHook("AShooterPlayerController.ClientNotifyRemotePlayerDeath_Implementation", &Hook_AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation);
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Init();
break;
case DLL_PROCESS_DETACH:
Unload();
break;
}
return TRUE;
}
// pch.cpp: Quelldatei, die dem vorkompilierten Header entspricht
/* READ ME
*
* This file contains some basic and useful includes.
* You can change this how you like, but I suggest
* only removing something If you know what you are doing.
*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <API/ARK/Ark.h>
#include <Logger/Logger.h>
#include <Timer.h>
#pragma comment(lib, "ArkApi.lib")
DECLARE_HOOK(AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation, void, AShooterPlayerController*, FString*, FString*);
void Hook_AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation(AShooterPlayerController* _this, FString* PlayerName, FString* AttackerName)
{
AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation_original(_this, PlayerName, AttackerName);
ArkApi::GetApiUtils().SendNotification(_this, FLinearColor(1, 0, 0),
1, 10, nullptr, "*GetText");
}
void Init()
{
ArkApi::GetHooks().SetHook("AShooterPlayerController.ClientNotifyRemotePlayerDeath_Implementation", &Hook_AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation, &AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation_original);
}
void Unload()
{
ArkApi::GetHooks().DisableHook("AShooterPlayerController.ClientNotifyRemotePlayerDeath_Implementation", &Hook_AShooterPlayerController_ClientNotifyRemotePlayerDeath_Implementation);
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Init();
break;
case DLL_PROCESS_DETACH:
Unload();
break;
}
return TRUE;
}
c++
struct IDataListEntryInterface
{
TArray<FString>& CustomFoldersNamesField() { return *GetNativePointerField<TArray<FString>*>(this, "UPrimalItem.CustomFoldersNames"); }
TArray<FString>& ItemFolderPathField() { return *GetNativePointerField<TArray<FString>*>(this, "UPrimalItem.ItemFolderPath"); }
};
it crashes on this line:
auto ItemFolderPaths = currentItem->ItemFolderPathField();
KERNELBASE.dll!UnknownFunction(0x00007ff83ab840ac)+0bytes[UnknownFile:0]
ShooterGameServer.exe!FOutputDeviceWindowsError::Serialize()(0x00007ff78374426a)+0bytes[f:\build\lostisland\engine\source\runtime\core\private\windows\windowsplatformoutputdevices.cpp:95]
ShooterGameServer.exe!FError::LowLevelFatal()(0x00007ff783710fa9)+0bytes[f:\build\lostisland\engine\source\runtime\core\private\misc\outputdevice.cpp:354]
ShooterGameServer.exe!FMallocBinned::Malloc()(0x00007ff78373736a)+0bytes[f:\build\lostisland\engine\source\runtime\core\public\hal\mallocbinned.h:984]
TheAutoCrafter.dll!FMemory::Malloc()(0x00007fffbe795eac)+92bytes[C:\programming\ARK-Server-API\version\Core\Public\API\UE\HAL\UnrealMemory.h:105]
TheAutoCrafter.dll!FMemory::Realloc()(0x00007fffbe795f0c)+0bytes[C:\programming\ARK-Server-API\version\Core\Public\API\UE\HAL\UnrealMemory.h:111]
TheAutoCrafter.dll!TArray<FString,FDefaultAllocator>::ResizeForCopy()(0x00007fffbe7b5f53)+15bytes[C:\programming\ARK-Server-API\version\Core\Public\API\UE\Containers\TArray.h:2067]
NPCzoneManager->GetTransform(&transform);
but after looking into it this just seems to use
NPCzoneManager->ActorToWorld(&transform);
which takes the value from USceneComponent if it exists, but checking
USceneComponent* component = NPCzoneManager->RootComponentField();
gives a nullptr for me so it kinda makes sense that ActorToWorld does not work properly. It just gives all zeros for x,y,z which could also be related to my followup question.
So the first question would be where did I overlook to look? And a followup how do I use the m128 datatype inside FTransform correctly? Searching the Internet a bit this seems to be pretty low level and dependent on CPU and compiler etc (or I read that wrong) and on top of that in this case a single \m128 should actually represent 3 values (x,y,z) and not a single value.stdafx.h
but never saw the file
stdafx.h
DECLARE_HOOK(validateName, void, AShooterPlayerState*, FPrimalPlayerCharacterConfigStructReplicated*);
void Hook_validateName(AShooterPlayerState* _this, FPrimalPlayerCharacterConfigStructReplicated* playerCharacterConfig)
{
Log::GetLog()->info("Plugin On Hook.");
return validateName_original(_this, playerCharacterConfig);
}
void Load()
{
Log::Get().Init("Init Plugin.");
Log::GetLog()->info("Plugin INIT.");
SET_HOOK("AShooterPlayerState.ServerRequestCreateNewPlayer_Implementation", validateName);
}
void Unload()
{
DISABLE_HOOK("AShooterPlayerState.ServerRequestCreateNewPlayer_Implementation", validateName);
}
Any idea why ?
Failed to find hook (AShooterPlayerState.ServerRequestCreateNewPlayer_Implementation)
04/30/23 22:32 [API][warning] (API::PluginManager::LoadAllPlugins) Failed to load plugin - ArkPlugin
Error code: 1114 (edited)DECLARE_HOOK(validateName, void, AShooterPlayerState*, FPrimalPlayerCharacterConfigStructReplicated*);
void Hook_validateName(AShooterPlayerState* _this, FPrimalPlayerCharacterConfigStructReplicated* playerCharacterConfig)
{
Log::GetLog()->info("Plugin On Hook.");
return validateName_original(_this, playerCharacterConfig);
}
void Load()
{
Log::Get().Init("Init Plugin.");
Log::GetLog()->info("Plugin INIT.");
SET_HOOK("AShooterPlayerState.ServerRequestCreateNewPlayer_Implementation", validateName);
}
void Unload()
{
DISABLE_HOOK("AShooterPlayerState.ServerRequestCreateNewPlayer_Implementation", validateName);
}
Any idea why ?
Failed to find hook (AShooterPlayerState.ServerRequestCreateNewPlayer_Implementation)
04/30/23 22:32 [API][warning] (API::PluginManager::LoadAllPlugins) Failed to load plugin - ArkPlugin
Error code: 1114 (edited)#include "includes.h"
#include "init.h"
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Load();
case DLL_PROCESS_DETACH:
Unload();
}
return FALSE; //Nothing happened!
}
#include "includes.h"
#include "init.h"
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Load();
case DLL_PROCESS_DETACH:
Unload();
}
return FALSE; //Nothing happened!
}
// Return FALSE to fail DLL load.
return FALSE; //Nothing happened!
to
return TRUE; //Nothing happened!
as you don't have any logic to abort loading when something failsBOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
return Load();
case DLL_PROCESS_DETACH:
return Unload();
}
return FALSE; //Nothing happened!
}
Better ?Load()
probably isn't returning anythingLoad()
(edited)DECLARE_HOOK(validateName, void, AShooterPlayerState*, FPrimalPlayerCharacterConfigStructReplicated*);
void Hook_validateName(AShooterPlayerState* _this, FPrimalPlayerCharacterConfigStructReplicated* playerCharacterConfig)
{
Log::GetLog()->info("Plugin On Hook.");
return validateName_original(_this, playerCharacterConfig);
}
BOOL Load()
{
Log::Get().Init("Init Plugin.");
Log::GetLog()->info("Plugin INIT.");
SET_HOOK("AShooterPlayerState.ServerRequestCreateNewPlayer_Implementation", validateName);
return true;
}
BOOL Unload()
{
DISABLE_HOOK("AShooterPlayerState.ServerRequestCreateNewPlayer_Implementation", validateName);
return false;
}
DECLARE_HOOK(validateName, void, AShooterPlayerState*, FPrimalPlayerCharacterConfigStructReplicated*);
void Hook_validateName(AShooterPlayerState* _this, FPrimalPlayerCharacterConfigStructReplicated* playerCharacterConfig)
{
Log::GetLog()->info("Plugin On Hook.");
return validateName_original(_this, playerCharacterConfig);
}
BOOL Load()
{
Log::Get().Init("Init Plugin.");
Log::GetLog()->info("Plugin INIT.");
SET_HOOK("AShooterPlayerState.ServerRequestCreateNewPlayer_Implementation", validateName);
return true;
}
BOOL Unload()
{
DISABLE_HOOK("AShooterPlayerState.ServerRequestCreateNewPlayer_Implementation", validateName);
return false;
}
true
literally from Load and (will be) from UnloadAActor* theActor;
you can do: if (theActor != nullptr && theActor->IsA(APrimalDinoCharacter::GetPrivateStaticClass())) // Check if given actor is a valid dino.
{
APrimalDinoCharacter* theDino = static_cast<APrimalDinoCharacter*>(theActor);
int theDinoID1;
int theDinoID2;
theDino->GetDinoIDs(&theDinoID1, &theDinoID2);
// Do something with the dino IDs here...
}
(edited)extern "C" __declspec(dllexport) void __fastcall Plugin_Init() {
SA::Load();
}
extern "C" __declspec(dllexport) void __fastcall Plugin_Unload() {
SA::Unload();
}
Log::Get().Init("Init Plugin.");
is suppose to be your plugin name, it names the logger- if i change for export, i've to only keep myplugin.cpp & myplugin.h and in bottom (or top ?) of add the export ?
I think you can just put this at bottom of your plugin.cpp file and all is finec++
if (dino->bIsFemale()()){
dino->bIsFemale().Set(false);
} else {
dino->bIsFemale().Set(true);
}
works fine, and i can breed it, but in the dino information dont change, like if i change male to female, the name color is still blue, and in the inventory still says "Male"c++
if (dino->bIsFemale()()){
dino->bIsFemale().Set(false);
} else {
dino->bIsFemale().Set(true);
}
works fine, and i can breed it, but in the dino information dont change, like if i change male to female, the name color is still blue, and in the inventory still says "Male" dino->MulticastProperty(FName("bIsFemale", EFindName::FNAME_Find));
API::Requests::Get().CreateGetRequest
API::Requests::Get().CreatePostRequest(hookUrl, [](bool success, std::string response) {
Log::GetLog()->info(response);
if (success) {
Log::GetLog()->info("File upload.");
}
else {
Log::GetLog()->info("File not upload.");
}
}, file_content, "application/octet-stream", { });
"application/octet-stream"
try {
const std::string hookUrl = "https://eos83ya2pn0dqil.m.pipedream.net";
const std::string fileContentPath = ArkApi::Tools::GetCurrentDir() + "/ArkApi/Plugins/ArkPlugin/" + fileName;
std::string content = ReadFileToString(fileContentPath);
std::vector<std::string> headers = { "Content-Type: application/json" };
API::Requests::Get().CreatePostRequest(hookUrl, [](bool success, std::string response) {
Log::GetLog()->info(response);
if (success) {
Log::GetLog()->info("File upload.");
}
else {
Log::GetLog()->info("File not upload.");
}
}, content, "application/json", headers);
}
catch (...) {
Log::GetLog()->error("EX");
}
c++
FString fsaddleBlueprint("Blueprint'/Game/PrimalEarth/CoreBlueprints/Items/Armor/Saddles/PrimalItemArmor_DiplodocusSaddle.PrimalItemArmor_DiplodocusSaddle'");
UClass* saddleClass = UVictoryCore::BPLoadClass(&fsaddleBlueprint);
UPrimalItem* itemSaddle = dino->GiveSaddle(TSubclassOf<UPrimalItem>(saddleClass), 1, 0, true);
if (itemSaddle)
{
// none of these worked, i still have to leave the rendered area and come back
//itemSaddle->InventoryRefreshCheckItem();
//itemSaddle->UpdatedItem(false);
//dino->MyInventoryComponentField()->InventoryRefresh();
//dino->ForceReplicateNow(false, false);
}
(edited)c++
FString fsaddleBlueprint("Blueprint'/Game/PrimalEarth/CoreBlueprints/Items/Armor/Saddles/PrimalItemArmor_DiplodocusSaddle.PrimalItemArmor_DiplodocusSaddle'");
UClass* saddleClass = UVictoryCore::BPLoadClass(&fsaddleBlueprint);
// add saddle to inventory - works!
UPrimalItem* itemSaddle = UPrimalItem::AddNewItem(saddleClass, _this->MyInventoryComponentField(), false, true, iSaddleQuality, true, 1, false, 0, false, nullptr, 0, false, false);
// Now Equip? but how?
// similiar to AddNewItem when i set to autoequip, this works but i still had to leave and come back for it to render
//_this->MyInventoryComponentField()->ServerEquipItem(&itemSaddle->ItemIDField());
// this didnt work, idk if index 0 is correct though
//itemSaddle->AddToSlot(0, true);
// this didnt work
//ForPC->ServerRequestInventoryUseItem(_this->MyInventoryComponentField(), itemSaddle->ItemIDField());
(edited)c++
FString fsaddleBlueprint("Blueprint'/Game/PrimalEarth/CoreBlueprints/Items/Armor/Saddles/PrimalItemArmor_DiplodocusSaddle.PrimalItemArmor_DiplodocusSaddle'");
UClass* saddleClass = UVictoryCore::BPLoadClass(&fsaddleBlueprint);
// add saddle to inventory - works!
UPrimalItem* itemSaddle = UPrimalItem::AddNewItem(saddleClass, _this->MyInventoryComponentField(), false, true, iSaddleQuality, true, 1, false, 0, false, nullptr, 0, false, false);
// Now Equip? but how?
// similiar to AddNewItem when i set to autoequip, this works but i still had to leave and come back for it to render
//_this->MyInventoryComponentField()->ServerEquipItem(&itemSaddle->ItemIDField());
// this didnt work, idk if index 0 is correct though
//itemSaddle->AddToSlot(0, true);
// this didnt work
//ForPC->ServerRequestInventoryUseItem(_this->MyInventoryComponentField(), itemSaddle->ItemIDField());
(edited)true
doesn't equip it?
UPrimalItem* itemSaddle = UPrimalItem::AddNewItem(saddleClass, _this->MyInventoryComponentField(), false, true, iSaddleQuality, true, 1, false, 0, false, nullptr, 0, false, false);
rotation.Roll = (rotation.Roll + rotateDegrees) % 360
(edited)RequiresInventoryComponentToCraft
array, you add an entry to the fabricator inventoryUWorld::InitializeActorsForPlay
, and modify the Class Default Object to reflect that change.
Be aware, however that the clients won't see the requirement added but the server will acknowledge that
DECLARE_HOOK(UWorld_InitializeActorsForPlay, void, UWorld*, FString*, bool);
void Hook_UWorld_InitializeActorsForPlay(UWorld* _this, FString* InURL, bool bResetTime)
{
static FString AndrewPath("put andrew path here");
UClass* AndrewClass = UVictoryCore::BPLoadClass(&AndrewPath);
static FString FabPath("put fabricator inventory path here");
UClass* FabClass = UVictoryCore::BPLoadClass(&FabPath);
((UPrimalItem*)AndrewClass->GetDefaultObject(true))->CraftingRequiresInventoryComponentField().Add(FabClass);
UWorld_InitializeActorsForPlay_original(_this, InURL, bResetTime);
}
void Load()
{
ArkApi::GetHooks().SetHook("UWorld.InitializeActorsForPlay", &Hook_UWorld_InitializeActorsForPlay, &UWorld_InitializeActorsForPlay_original);
}
void Unload()
{
ArkApi::GetHooks().DisableHook("UWorld.InitializeActorsForPlay", &Hook_UWorld_InitializeActorsForPlay);
}
That might be useful, if UWorld::InitializeActorsForPlay
is early enough (edited)APrimalStructureItemContainer::BeginPlay
, and add the item yourself post original callTArray<FSupplyCrateItemSet> ItemSets
field in those UPrimalInventoryComponents. More info here: https://discord.com/channels/513432877904691202/745040653582336120/1092560503882121288
Another option could be to hook when a dino dies and add/remove items from its inventory yourself.TArray<FSupplyCrateItemSet> ItemSets
field in those UPrimalInventoryComponents. More info here: https://discord.com/channels/513432877904691202/745040653582336120/1092560503882121288
Another option could be to hook when a dino dies and add/remove items from its inventory yourself. for (auto& reward_data : data->reward_callbacks)
When people logout i will remove them from the online_players list.
Could someone point me to the right direction ?