diff --git a/.gitmodules b/.gitmodules index 5da2c449b..dc4ad5b4c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "Shared/Network"] path = Shared/Network url = https://github.com/openmultiplayer/open.mp-network +[submodule "CAPI"] + path = CAPI + url = https://github.com/openmultiplayer/open.mp-capi diff --git a/CAPI b/CAPI new file mode 160000 index 000000000..55afceb0f --- /dev/null +++ b/CAPI @@ -0,0 +1 @@ +Subproject commit 55afceb0fc2c31bf74ab752d76b572ba8c6302ca diff --git a/CMakeLists.txt b/CMakeLists.txt index f4746b251..e803a083d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,6 +144,7 @@ add_subdirectory(lib) if(BUILD_SERVER) message("Configuring server") + add_subdirectory(CAPI) add_subdirectory(SDK) add_subdirectory(Shared) add_subdirectory(Server) diff --git a/Server/Components/CAPI/CMakeLists.txt b/Server/Components/CAPI/CMakeLists.txt new file mode 100644 index 000000000..0aeecea02 --- /dev/null +++ b/Server/Components/CAPI/CMakeLists.txt @@ -0,0 +1,8 @@ +get_filename_component(ProjectId ${CMAKE_CURRENT_SOURCE_DIR} NAME) +add_server_component(${ProjectId}) + +target_compile_definitions(${ProjectId} PRIVATE -DCAPI_COMPONENT_BUILD) +target_compile_options(${ProjectId} PRIVATE -Wno-unused-variable) +target_link_libraries(${ProjectId} PRIVATE OMP-CAPI) + +set_target_properties(${ProjectId} PROPERTIES PREFIX "$") diff --git a/Server/Components/CAPI/Impl/Actors/APIs.cpp b/Server/Components/CAPI/Impl/Actors/APIs.cpp new file mode 100644 index 000000000..12af2b6cb --- /dev/null +++ b/Server/Components/CAPI/Impl/Actors/APIs.cpp @@ -0,0 +1,189 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" + +OMP_CAPI(Actor_Create, objectPtr(int model, float x, float y, float z, float rot, int* id)) +{ + IActorsComponent* component = ComponentManager::Get()->actors; + if (component) + { + IActor* actor = component->create(model, { x, y, z }, rot); + if (actor) + { + *id = actor->getID(); + return actor; + } + } + return nullptr; +} + +OMP_CAPI(Actor_Destroy, bool(objectPtr actor)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, false); + actors->release(actor_->getID()); + return true; +} + +OMP_CAPI(Actor_FromID, objectPtr(int actorid)) +{ + IActorsComponent* component = ComponentManager::Get()->actors; + if (component) + { + return component->get(actorid); + } + return nullptr; +} + +OMP_CAPI(Actor_GetID, int(objectPtr actor)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, INVALID_ACTOR_ID); + return actor_->getID(); +} + +OMP_CAPI(Actor_IsStreamedInFor, bool(objectPtr actor, objectPtr player)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, false); + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + return actor_->isStreamedInForPlayer(*player_); +} + +OMP_CAPI(Actor_SetVirtualWorld, bool(objectPtr actor, int vw)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, false); + actor_->setVirtualWorld(vw); + return true; +} + +OMP_CAPI(Actor_GetVirtualWorld, int(objectPtr actor)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, 0); + return actor_->getVirtualWorld(); +} + +OMP_CAPI(Actor_ApplyAnimation, bool(objectPtr actor, StringCharPtr name, StringCharPtr library, float delta, bool loop, bool lockX, bool lockY, bool freeze, int time)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, false); + const AnimationData animationData(delta, loop, lockX, lockY, freeze, uint32_t(time), library, name); + actor_->applyAnimation(animationData); + return true; +} + +OMP_CAPI(Actor_ClearAnimations, bool(objectPtr actor)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, false); + actor_->clearAnimations(); + return true; +} + +OMP_CAPI(Actor_SetPos, bool(objectPtr actor, float x, float y, float z)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, false); + actor_->setPosition({ x, y, z }); + return true; +} + +OMP_CAPI(Actor_GetPos, bool(objectPtr actor, float* x, float* y, float* z)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, false); + const Vector3& pos = actor_->getPosition(); + + *x = pos.x; + *y = pos.y; + *z = pos.z; + return true; +} + +OMP_CAPI(Actor_SetFacingAngle, bool(objectPtr actor, float angle)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, false); + actor_->setRotation(Vector3(0.0f, 0.0f, angle)); + return true; +} + +OMP_CAPI(Actor_GetFacingAngle, float(objectPtr actor)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, 0.0f); + return actor_->getRotation().ToEuler().z; +} + +OMP_CAPI(Actor_SetHealth, bool(objectPtr actor, float hp)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, false); + actor_->setHealth(hp); + return true; +} + +OMP_CAPI(Actor_GetHealth, float(objectPtr actor)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, 0.0f); + return actor_->getHealth(); +} + +OMP_CAPI(Actor_SetInvulnerable, bool(objectPtr actor, bool toggle)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, false); + actor_->setInvulnerable(toggle); + return true; +} + +OMP_CAPI(Actor_IsInvulnerable, bool(objectPtr actor)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, false); + return actor_->isInvulnerable(); +} + +OMP_CAPI(Actor_IsValid, bool(objectPtr actor)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, false); + if (!actors->get(actor_->getID())) + return false; + return true; +} + +OMP_CAPI(Actor_SetSkin, bool(objectPtr actor, int skin)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, false); + actor_->setSkin(skin); + return true; +} + +OMP_CAPI(Actor_GetSkin, int(objectPtr actor)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, 0); + return actor_->getSkin(); +} + +OMP_CAPI(Actor_GetAnimation, bool(objectPtr actor, OutputStringViewPtr library, OutputStringViewPtr name, float* delta, bool* loop, bool* lockX, bool* lockY, bool* freeze, int* time)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, false); + const AnimationData& anim = actor_->getAnimation(); + + SET_CAPI_STRING_VIEW(library, anim.lib); + SET_CAPI_STRING_VIEW(name, anim.name); + *delta = anim.delta; + *loop = anim.loop; + *lockX = anim.lockX; + *lockY = anim.lockY; + *freeze = anim.freeze; + *time = int(anim.time); + return true; +} + +OMP_CAPI(Actor_GetSpawnInfo, bool(objectPtr actor, float* x, float* y, float* z, float* angle, int* skin)) +{ + POOL_ENTITY_RET(actors, IActor, actor, actor_, false); + const ActorSpawnData& spawnData = actor_->getSpawnData(); + + *x = spawnData.position.x; + *y = spawnData.position.y; + *z = spawnData.position.z; + *angle = spawnData.facingAngle; + *skin = spawnData.skin; + return true; +} diff --git a/Server/Components/CAPI/Impl/Actors/Events.hpp b/Server/Components/CAPI/Impl/Actors/Events.hpp new file mode 100644 index 000000000..af628551d --- /dev/null +++ b/Server/Components/CAPI/Impl/Actors/Events.hpp @@ -0,0 +1,30 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#pragma once +#include "../ComponentManager.hpp" +#include "sdk.hpp" + +template +struct ActorEvents : public ActorEventHandler, public Singleton> +{ + void onPlayerGiveDamageActor(IPlayer& player, IActor& actor, float amount, unsigned weapon, BodyPart part) override + { + ComponentManager::Get()->CallEvent("onPlayerGiveDamageActor", EventReturnHandler::None, &player, &actor, amount, int(weapon), int(part)); + } + + void onActorStreamIn(IActor& actor, IPlayer& forPlayer) override + { + ComponentManager::Get()->CallEvent("onActorStreamIn", EventReturnHandler::None, &actor, &forPlayer); + } + + void onActorStreamOut(IActor& actor, IPlayer& forPlayer) override + { + ComponentManager::Get()->CallEvent("onActorStreamOut", EventReturnHandler::None, &actor, &forPlayer); + } +}; diff --git a/Server/Components/CAPI/Impl/Checkpoints/APIs.cpp b/Server/Components/CAPI/Impl/Checkpoints/APIs.cpp new file mode 100644 index 000000000..03ccafd8d --- /dev/null +++ b/Server/Components/CAPI/Impl/Checkpoints/APIs.cpp @@ -0,0 +1,161 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" + +OMP_CAPI(Checkpoint_Set, bool(objectPtr player, float x, float y, float z, float radius)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerCheckpointData* playerCheckpointData = queryExtension(player_); + if (playerCheckpointData) + { + ICheckpointData& cp = playerCheckpointData->getCheckpoint(); + cp.setPosition({ x, y, z }); + cp.setRadius(radius); + cp.enable(); + return true; + } + return false; +} + +OMP_CAPI(Checkpoint_Disable, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerCheckpointData* playerCheckpointData = queryExtension(player_); + if (playerCheckpointData) + { + ICheckpointData& cp = playerCheckpointData->getCheckpoint(); + cp.disable(); + return true; + } + return false; +} + +OMP_CAPI(Checkpoint_IsPlayerIn, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerCheckpointData* playerCheckpointData = queryExtension(player_); + if (playerCheckpointData) + { + ICheckpointData& cp = playerCheckpointData->getCheckpoint(); + if (cp.isEnabled()) + { + bool isIn = cp.isPlayerInside(); + return isIn; + } + } + return false; +} + +OMP_CAPI(Checkpoint_IsActive, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerCheckpointData* playerData = queryExtension(player_); + if (playerData) + { + bool active = playerData->getCheckpoint().isEnabled(); + return active; + } + return false; +} + +OMP_CAPI(Checkpoint_Get, bool(objectPtr player, float* x, float* y, float* z, float* radius)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerCheckpointData* playerData = queryExtension(player_); + if (playerData) + { + const ICheckpointData& data = playerData->getCheckpoint(); + *x = data.getPosition().x; + *y = data.getPosition().y; + *z = data.getPosition().z; + *radius = data.getRadius(); + return true; + } + return false; +} + +OMP_CAPI(RaceCheckpoint_Set, bool(objectPtr player, int type, float x, float y, float z, float nextX, float nextY, float nextZ, float radius)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerCheckpointData* playerCheckpointData = queryExtension(player_); + if (playerCheckpointData) + { + IRaceCheckpointData& cp = playerCheckpointData->getRaceCheckpoint(); + if (type >= 0 && type <= 8) + { + cp.setType(RaceCheckpointType(type)); + cp.setPosition({ x, y, z }); + cp.setNextPosition({ nextX, nextY, nextZ }); + cp.setRadius(radius); + cp.enable(); + return true; + } + } + return false; +} + +OMP_CAPI(RaceCheckpoint_Disable, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerCheckpointData* playerCheckpointData = queryExtension(player_); + if (playerCheckpointData) + { + IRaceCheckpointData& cp = playerCheckpointData->getRaceCheckpoint(); + cp.disable(); + return true; + } + return false; +} + +OMP_CAPI(RaceCheckpoint_IsPlayerIn, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerCheckpointData* playerCheckpointData = queryExtension(player_); + if (playerCheckpointData) + { + IRaceCheckpointData& cp = playerCheckpointData->getRaceCheckpoint(); + if (cp.getType() != RaceCheckpointType::RACE_NONE && cp.isEnabled()) + { + bool isIn = cp.isPlayerInside(); + return isIn; + } + } + return false; +} + +OMP_CAPI(RaceCheckpoint_IsActive, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerCheckpointData* playerData = queryExtension(player_); + if (playerData) + { + bool active = playerData->getCheckpoint().isEnabled(); + return active; + } + return false; +} + +OMP_CAPI(RaceCheckpoint_Get, bool(objectPtr player, float* x, float* y, float* z, float* nextX, float* nextY, float* nextZ, float* radius)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerCheckpointData* playerData = queryExtension(player_); + if (playerData) + { + const IRaceCheckpointData& data = playerData->getRaceCheckpoint(); + *x = data.getPosition().x; + *y = data.getPosition().y; + *z = data.getPosition().z; + *nextX = data.getNextPosition().x; + *nextY = data.getNextPosition().y; + *nextZ = data.getNextPosition().z; + *radius = data.getRadius(); + return true; + } + return false; +} diff --git a/Server/Components/CAPI/Impl/Checkpoints/Events.hpp b/Server/Components/CAPI/Impl/Checkpoints/Events.hpp new file mode 100644 index 000000000..ae85ef201 --- /dev/null +++ b/Server/Components/CAPI/Impl/Checkpoints/Events.hpp @@ -0,0 +1,36 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#pragma once +#include "../ComponentManager.hpp" +#include "sdk.hpp" + +template +struct CheckpointEvents : public PlayerCheckpointEventHandler, public Singleton> +{ + void onPlayerEnterCheckpoint(IPlayer& player) override + { + ComponentManager::Get()->CallEvent("onPlayerEnterCheckpoint", EventReturnHandler::None, &player); + } + + void onPlayerLeaveCheckpoint(IPlayer& player) override + { + ComponentManager::Get()->CallEvent("onPlayerLeaveCheckpoint", EventReturnHandler::None, &player); + } + + void onPlayerEnterRaceCheckpoint(IPlayer& player) override + { + ComponentManager::Get()->CallEvent("onPlayerEnterRaceCheckpoint", EventReturnHandler::None, &player); + } + + void onPlayerLeaveRaceCheckpoint(IPlayer& player) override + { + + ComponentManager::Get()->CallEvent("onPlayerLeaveRaceCheckpoint", EventReturnHandler::None, &player); + } +}; diff --git a/Server/Components/CAPI/Impl/Classes/APIs.cpp b/Server/Components/CAPI/Impl/Classes/APIs.cpp new file mode 100644 index 000000000..7d150eec4 --- /dev/null +++ b/Server/Components/CAPI/Impl/Classes/APIs.cpp @@ -0,0 +1,136 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" + +OMP_CAPI(Class_Add, objectPtr(uint8_t team, int skin, float x, float y, float z, float angle, uint8_t weapon1, uint32_t ammo1, uint8_t weapon2, uint32_t ammo2, uint8_t weapon3, uint32_t ammo3, int* id)) +{ + IClassesComponent* component = ComponentManager::Get()->classes; + if (component) + { + + WeaponSlots slots = { + WeaponSlotData { weapon1, ammo1 }, + WeaponSlotData { weapon2, ammo2 }, + WeaponSlotData { weapon3, ammo3 } + }; + + IClass* class_ = component->create(skin, team, { x, y, z }, angle, slots); + + if (class_) + { + *id = class_->getID(); + return class_; + } + } + + return nullptr; +} + +OMP_CAPI(Class_FromID, objectPtr(int classid)) +{ + IClassesComponent* component = ComponentManager::Get()->classes; + if (component) + { + return component->get(classid); + } + return nullptr; +} + +OMP_CAPI(Class_GetID, int(objectPtr cls)) +{ + POOL_ENTITY_RET(classes, IClass, cls, class_, 0xFFFF); + return class_->getID(); +} + +OMP_CAPI(Player_SetSpawnInfo, bool(objectPtr player, uint8_t team, int skin, float x, float y, float z, float angle, uint8_t weapon1, uint32_t ammo1, uint8_t weapon2, uint32_t ammo2, uint8_t weapon3, uint32_t ammo3)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerClassData* classData = queryExtension(player_); + if (classData) + { + WeaponSlots slots = { + WeaponSlotData { weapon1, ammo1 }, + WeaponSlotData { weapon2, ammo2 }, + WeaponSlotData { weapon3, ammo3 } + }; + + classData->setSpawnInfo(PlayerClass(skin, team, { x, y, z }, angle, slots)); + return true; + } + return false; +} + +OMP_CAPI(Player_GetSpawnInfo, bool(objectPtr player, uint8_t* team, int* skin, float* x, float* y, float* z, float* angle, uint8_t* weapon1, uint32_t* ammo1, uint8_t* weapon2, uint32_t* ammo2, uint8_t* weapon3, uint32_t* ammo3)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerClassData* playerData = queryExtension(player_); + if (playerData) + { + const PlayerClass& data = playerData->getClass(); + *team = uint8_t(data.team); + *skin = data.skin; + *x = data.spawn.x; + *y = data.spawn.y; + *z = data.spawn.z; + *angle = data.angle; + *weapon1 = data.weapons[0].id; + *ammo1 = data.weapons[0].ammo; + *weapon2 = data.weapons[1].id; + *ammo2 = data.weapons[1].ammo; + *weapon3 = data.weapons[2].id; + *ammo3 = data.weapons[2].ammo; + return true; + } + return false; +} + +OMP_CAPI(Class_Count, int()) +{ + IClassesComponent* component = ComponentManager::Get()->classes; + if (component) + { + int count = component->count(); + return count; + } + return 0; +} + +OMP_CAPI(Class_GetData, bool(objectPtr classptr, uint8_t* teamid, int* skin, float* x, float* y, float* z, float* angle, uint8_t* weapon1, uint32_t* weapon1_ammo, uint8_t* weapon2, uint32_t* weapon2_ammo, uint8_t* weapon3, uint32_t* weapon3_ammo)) +{ + POOL_ENTITY_RET(classes, IClass, classptr, class_, false); + const PlayerClass& data = class_->getClass(); + *teamid = uint8_t(data.team); + *skin = data.skin; + *x = data.spawn.x; + *y = data.spawn.y; + *z = data.spawn.z; + *angle = data.angle; + *weapon1 = data.weapons[0].id; + *weapon1_ammo = data.weapons[0].ammo; + *weapon2 = data.weapons[1].id; + *weapon2_ammo = data.weapons[1].ammo; + *weapon3 = data.weapons[2].id; + *weapon3_ammo = data.weapons[2].ammo; + return true; +} + +OMP_CAPI(Class_Edit, bool(objectPtr classptr, uint8_t teamid, int skin, float x, float y, float z, float angle, uint8_t weapon1, uint32_t ammo1, uint8_t weapon2, uint32_t ammo2, uint8_t weapon3, uint32_t ammo3)) +{ + POOL_ENTITY_RET(classes, IClass, classptr, class_, false); + WeaponSlots weapons; + weapons[0].id = weapon1; + weapons[0].ammo = ammo1; + weapons[1].id = weapon2; + weapons[1].ammo = ammo2; + weapons[2].id = weapon3; + weapons[2].ammo = ammo3; + PlayerClass data = PlayerClass(skin, teamid, { x, y, z }, angle, weapons); + class_->setClass(data); + return true; +} diff --git a/Server/Components/CAPI/Impl/Classes/Events.hpp b/Server/Components/CAPI/Impl/Classes/Events.hpp new file mode 100644 index 000000000..003a46285 --- /dev/null +++ b/Server/Components/CAPI/Impl/Classes/Events.hpp @@ -0,0 +1,20 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#pragma once +#include "../ComponentManager.hpp" +#include "sdk.hpp" + +template +struct ClassEvents : public ClassEventHandler, public Singleton> +{ + bool onPlayerRequestClass(IPlayer& player, unsigned int classId) override + { + return ComponentManager::Get()->CallEvent("onPlayerRequestClass", EventReturnHandler::StopAtFalse, &player, int(classId)); + } +}; diff --git a/Server/Components/CAPI/Impl/Component/APIs.cpp b/Server/Components/CAPI/Impl/Component/APIs.cpp new file mode 100644 index 000000000..f84c9b67c --- /dev/null +++ b/Server/Components/CAPI/Impl/Component/APIs.cpp @@ -0,0 +1,104 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" + +class Component final : public IComponent +{ +private: + Impl::String name_; + uint64_t uid_; + ComponentVersion version_; + + ComponentOnReadyCallback onReadyCB_; + ComponentOnResetCallback onResetCB_; + ComponentOnFreeCallback onFreeCB_; + +public: + UID getUID() override + { + return uid_; + } + + Component(UID uid, const Impl::String& name, const ComponentVersion& version) + : name_(name) + , uid_(uid) + , version_(version) + { + onReadyCB_ = nullptr; + onResetCB_ = nullptr; + onFreeCB_ = nullptr; + } + + ~Component() + { + ComponentManager::Get()->FreeEvents(); + } + + StringView componentName() const override + { + return name_.c_str(); + } + + SemanticVersion componentVersion() const override + { + return SemanticVersion(version_.major, version_.minor, version_.patch, version_.prerel); + } + + void onLoad(ICore* c) override + { + c->logLn(LogLevel::Message, "%s component has been loaded", name_.c_str()); + } + + void onInit(IComponentList* components) override + { + } + + void onReady() override + { + if (onReadyCB_) + { + onReadyCB_(); + } + } + + void onFree(IComponent* component) override + { + } + + void free() override + { + if (onFreeCB_) + { + onFreeCB_(); + } + delete this; + } + + void reset() override + { + if (onResetCB_) + { + onResetCB_(); + } + } + + void setCallbacks(ComponentOnReadyCallback onReadyCB, ComponentOnResetCallback onResetCB, ComponentOnFreeCallback onFreeCB) + { + onReadyCB_ = onReadyCB; + onResetCB_ = onResetCB; + onFreeCB_ = onFreeCB; + } +}; + +OMP_CAPI(Component_Create, voidPtr(uint64_t uid, StringCharPtr name, ComponentVersion version, voidPtr onReadyCB, voidPtr onResetCB, voidPtr onFreeCB)) +{ + auto component = new Component(uid, name, version); + component->setCallbacks(ComponentOnReadyCallback(onReadyCB), ComponentOnResetCallback(onResetCB), ComponentOnFreeCallback(onFreeCB)); + return component; +} diff --git a/Server/Components/CAPI/Impl/ComponentManager.cpp b/Server/Components/CAPI/Impl/ComponentManager.cpp new file mode 100644 index 000000000..8421d8269 --- /dev/null +++ b/Server/Components/CAPI/Impl/ComponentManager.cpp @@ -0,0 +1,218 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "Actors/Events.hpp" +#include "Checkpoints/Events.hpp" +#include "Classes/Events.hpp" +#include "Core/Events.hpp" +#include "Console/Events.hpp" +#include "Dialogs/Events.hpp" +#include "Menus/Events.hpp" +#include "Objects/Events.hpp" +#include "Pickups/Events.hpp" +#include "Players/Events.hpp" +#include "TextDraws/Events.hpp" +#include "Vehicles/Events.hpp" +#include "GangZones/Events.hpp" +#include "CustomModels/Events.hpp" +#include "NPCs/Events.hpp" +#include "ComponentManager.hpp" + +#define ADD_EVENT_HANDLER(component, event_instance) \ + if (component) \ + { \ + component->getEventDispatcher().addEventHandler(event_instance::Get(), EventPriority_Highest); \ + component->getEventDispatcher().addEventHandler(event_instance::Get(), EventPriority_FairlyHigh); \ + component->getEventDispatcher().addEventHandler(event_instance::Get(), EventPriority_Default); \ + component->getEventDispatcher().addEventHandler(event_instance::Get(), EventPriority_FairlyLow); \ + component->getEventDispatcher().addEventHandler(event_instance::Get(), EventPriority_Lowest); \ + } + +#define ADD_PLAYER_EVENT_HANDLER(component, event_name, event_instance) \ + if (component) \ + { \ + component->getPlayer##event_name##Dispatcher().addEventHandler(event_instance::Get(), EventPriority_Highest); \ + component->getPlayer##event_name##Dispatcher().addEventHandler(event_instance::Get(), EventPriority_FairlyHigh); \ + component->getPlayer##event_name##Dispatcher().addEventHandler(event_instance::Get(), EventPriority_Default); \ + component->getPlayer##event_name##Dispatcher().addEventHandler(event_instance::Get(), EventPriority_FairlyLow); \ + component->getPlayer##event_name##Dispatcher().addEventHandler(event_instance::Get(), EventPriority_Lowest); \ + } + +#define REMOVE_EVENT_HANDLER(component, event_instance) \ + if (component) \ + { \ + component->getEventDispatcher().removeEventHandler(event_instance::Get()); \ + component->getEventDispatcher().removeEventHandler(event_instance::Get()); \ + component->getEventDispatcher().removeEventHandler(event_instance::Get()); \ + component->getEventDispatcher().removeEventHandler(event_instance::Get()); \ + component->getEventDispatcher().removeEventHandler(event_instance::Get()); \ + } + +#define REMOVE_PLAYER_EVENT_HANDLER(component, event_name, event_instance) \ + if (component) \ + { \ + component->getPlayer##event_name##Dispatcher().removeEventHandler(event_instance::Get()); \ + component->getPlayer##event_name##Dispatcher().removeEventHandler(event_instance::Get()); \ + component->getPlayer##event_name##Dispatcher().removeEventHandler(event_instance::Get()); \ + component->getPlayer##event_name##Dispatcher().removeEventHandler(event_instance::Get()); \ + component->getPlayer##event_name##Dispatcher().removeEventHandler(event_instance::Get()); \ + } + +#define RETRIEVE_RELEVANT_EVENT_MAP(container, priority) \ + FlatHashMap>* container = nullptr; \ + switch (priority) \ + { \ + case EventPriorityType_Highest: \ + container = &highestPriorityEvents; \ + break; \ + case EventPriorityType_FairlyHigh: \ + container = &fairlyHighPriorityEvents; \ + break; \ + case EventPriorityType_Default: \ + container = &defaultPriorityEvents; \ + break; \ + case EventPriorityType_FairlyLow: \ + container = &fairlyLowPriorityEvents; \ + break; \ + case EventPriorityType_Lowest: \ + container = &lowestPriorityEvents; \ + break; \ + default: \ + container = &defaultPriorityEvents; \ + break; \ + } + +void ComponentManager::Init(ICore* c, IComponentList* clist) +{ + core = c; + componentList = clist; + players = &core->getPlayers(); + + actors = GetComponent(); + objects = GetComponent(); + pickups = GetComponent(); + textlabels = GetComponent(); + vehicles = GetComponent(); + classes = GetComponent(); + console = GetComponent(); + models = GetComponent(); + menus = GetComponent(); + textdraws = GetComponent(); + gangzones = GetComponent(); + checkpoints = GetComponent(); + dialogs = GetComponent(); + npcs = GetComponent(); +} + +void ComponentManager::InitializeEvents() +{ + ADD_EVENT_HANDLER(core, CoreEvents); + ADD_EVENT_HANDLER(actors, ActorEvents); + ADD_EVENT_HANDLER(checkpoints, CheckpointEvents); + ADD_EVENT_HANDLER(classes, ClassEvents); + ADD_EVENT_HANDLER(dialogs, DialogEvents); + ADD_EVENT_HANDLER(menus, MenuEvents); + ADD_EVENT_HANDLER(textdraws, TextDrawEvents); + ADD_EVENT_HANDLER(pickups, PickupEvents); + ADD_EVENT_HANDLER(vehicles, VehicleEvents); + ADD_EVENT_HANDLER(objects, ObjectEvents); + ADD_EVENT_HANDLER(console, ConsoleEvents); + ADD_EVENT_HANDLER(gangzones, GangZoneEvents); + ADD_EVENT_HANDLER(models, CustomModelsEvents); + ADD_EVENT_HANDLER(npcs, NPCEvents); + + ADD_PLAYER_EVENT_HANDLER(players, Spawn, PlayerEvents); + ADD_PLAYER_EVENT_HANDLER(players, Connect, PlayerEvents); + ADD_PLAYER_EVENT_HANDLER(players, Stream, PlayerEvents); + ADD_PLAYER_EVENT_HANDLER(players, Text, PlayerEvents); + ADD_PLAYER_EVENT_HANDLER(players, Shot, PlayerEvents); + ADD_PLAYER_EVENT_HANDLER(players, Change, PlayerEvents); + ADD_PLAYER_EVENT_HANDLER(players, Damage, PlayerEvents); + ADD_PLAYER_EVENT_HANDLER(players, Click, PlayerEvents); + ADD_PLAYER_EVENT_HANDLER(players, Check, PlayerEvents); + ADD_PLAYER_EVENT_HANDLER(players, Update, PlayerEvents); +} + +void ComponentManager::FreeEvents() +{ + REMOVE_EVENT_HANDLER(core, CoreEvents); + REMOVE_EVENT_HANDLER(actors, ActorEvents); + REMOVE_EVENT_HANDLER(checkpoints, CheckpointEvents); + REMOVE_EVENT_HANDLER(classes, ClassEvents); + REMOVE_EVENT_HANDLER(dialogs, DialogEvents); + REMOVE_EVENT_HANDLER(menus, MenuEvents); + REMOVE_EVENT_HANDLER(textdraws, TextDrawEvents); + REMOVE_EVENT_HANDLER(pickups, PickupEvents); + REMOVE_EVENT_HANDLER(vehicles, VehicleEvents); + REMOVE_EVENT_HANDLER(objects, ObjectEvents); + REMOVE_EVENT_HANDLER(console, ConsoleEvents); + REMOVE_EVENT_HANDLER(gangzones, GangZoneEvents); + REMOVE_EVENT_HANDLER(models, CustomModelsEvents); + REMOVE_EVENT_HANDLER(npcs, NPCEvents); + + REMOVE_PLAYER_EVENT_HANDLER(players, Spawn, PlayerEvents); + REMOVE_PLAYER_EVENT_HANDLER(players, Connect, PlayerEvents); + REMOVE_PLAYER_EVENT_HANDLER(players, Stream, PlayerEvents); + REMOVE_PLAYER_EVENT_HANDLER(players, Text, PlayerEvents); + REMOVE_PLAYER_EVENT_HANDLER(players, Shot, PlayerEvents); + REMOVE_PLAYER_EVENT_HANDLER(players, Change, PlayerEvents); + REMOVE_PLAYER_EVENT_HANDLER(players, Damage, PlayerEvents); + REMOVE_PLAYER_EVENT_HANDLER(players, Click, PlayerEvents); + REMOVE_PLAYER_EVENT_HANDLER(players, Check, PlayerEvents); + REMOVE_PLAYER_EVENT_HANDLER(players, Update, PlayerEvents); +} + +bool ComponentManager::AddEventHandler(const Impl::String& name, EventPriorityType priority, EventCallback_Common callback) +{ + if (name.length()) + { + RETRIEVE_RELEVANT_EVENT_MAP(container, priority); + if (container) + { + auto it = container->find(name); + if (it == container->end()) + { + it = container->insert({ name, FlatHashSet() }).first; + } + + it->second.insert(callback); + return true; + } + } + return false; +} + +bool ComponentManager::RemoveEventHandler(const Impl::String& name, EventPriorityType priority, EventCallback_Common callback) +{ + if (name.length()) + { + RETRIEVE_RELEVANT_EVENT_MAP(container, priority); + if (container) + { + auto it = container->find(name); + if (it != container->end()) + { + it->second.erase(callback); + return true; + } + } + } + return false; +} + +void ComponentManager::RemoveAllHandlers(const Impl::String& name, EventPriorityType priority) +{ + if (name.length()) + { + RETRIEVE_RELEVANT_EVENT_MAP(container, priority); + if (container) + { + container->erase(name); + } + } +} diff --git a/Server/Components/CAPI/Impl/ComponentManager.hpp b/Server/Components/CAPI/Impl/ComponentManager.hpp new file mode 100644 index 000000000..93460b807 --- /dev/null +++ b/Server/Components/CAPI/Impl/ComponentManager.hpp @@ -0,0 +1,272 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#pragma once +#include + +#include +#include "../Utils/MacroMagic.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum class EventReturnHandler +{ + None, + StopAtFalse, + StopAtTrue +}; + +class ComponentManager : public Singleton +{ +public: + ICore* core = nullptr; + IPlayerPool* players = nullptr; + IPickupsComponent* pickups = nullptr; + IObjectsComponent* objects = nullptr; + ITextLabelsComponent* textlabels = nullptr; + IVehiclesComponent* vehicles = nullptr; + IActorsComponent* actors = nullptr; + IClassesComponent* classes = nullptr; + IConsoleComponent* console = nullptr; + ICustomModelsComponent* models = nullptr; + IMenusComponent* menus = nullptr; + ITextDrawsComponent* textdraws = nullptr; + IGangZonesComponent* gangzones = nullptr; + ICheckpointsComponent* checkpoints = nullptr; + IDialogsComponent* dialogs = nullptr; + INPCComponent* npcs = nullptr; + + /// Store open.mp components + void Init(ICore* c, IComponentList* clist); + + // Add event handlers to components' event dispatchers + void InitializeEvents(); + + // Remove event handlers from components' event dispatchers + void FreeEvents(); + + // Add an event callback of an event to omp capi handler maps + bool AddEventHandler(const Impl::String& name, EventPriorityType priority, EventCallback_Common callback); + + // Remove an event callback of an event from omp capi handler maps + bool RemoveEventHandler(const Impl::String& name, EventPriorityType priority, EventCallback_Common callback); + + // Remove all event callbacks of an event + void RemoveAllHandlers(const Impl::String& name, EventPriorityType priority); + + /// Get open.mp core instance + ICore* GetCore() + { + return core; + } + + /// Get an open.mp component + template + ComponentType* GetComponent() + { + return componentList->queryComponent(); + } + + // Call event + template + bool CallEvent(const Impl::String& name, EventReturnHandler returnHandler, Args... args) + { + FlatHashMap>* container = nullptr; + if constexpr (PRIORITY == EventPriorityType_Highest) + { + container = &highestPriorityEvents; + } + else if constexpr (PRIORITY == EventPriorityType_FairlyHigh) + { + container = &fairlyHighPriorityEvents; + } + else if constexpr (PRIORITY == EventPriorityType_Default) + { + container = &defaultPriorityEvents; + } + else if constexpr (PRIORITY == EventPriorityType_FairlyHigh) + { + container = &fairlyLowPriorityEvents; + } + else if constexpr (PRIORITY == EventPriorityType_Lowest) + { + container = &lowestPriorityEvents; + } + + bool result = returnHandler == EventReturnHandler::StopAtTrue ? false : true; + + if (container) + { + auto callbacks = container->find(name); + if (callbacks != container->end()) + { + auto result = CallEventOfPriority(callbacks, returnHandler, args...); + } + } + + return result; + } + +private: + IComponentList* componentList = nullptr; + + FlatHashMap> highestPriorityEvents; + FlatHashMap> fairlyHighPriorityEvents; + FlatHashMap> defaultPriorityEvents; + FlatHashMap> fairlyLowPriorityEvents; + FlatHashMap> lowestPriorityEvents; + + template + bool CallEventOfPriority(Con container, EventReturnHandler returnHandler, Args... args) + { + EventArgs_Common eventArgs; + constexpr std::size_t size = sizeof...(Args); + Impl::DynamicArray argsList; + + if constexpr (size > 0) + { + argsList.reserve(size); + (argsList.push_back(&args), ...); + } + + eventArgs.size = size; + eventArgs.list = size > 0 ? argsList.data() : nullptr; + + if (container->second.empty()) + { + return returnHandler == EventReturnHandler::StopAtTrue ? false : true; + } + + bool result = true; + for (auto cb : container->second) + { + if (cb) + { + auto ret = cb(&eventArgs); + switch (returnHandler) + { + case EventReturnHandler::StopAtFalse: + if (!ret) + { + return false; + } + break; + case EventReturnHandler::StopAtTrue: + if (ret) + { + return true; + } + break; + case EventReturnHandler::None: + default: + break; + } + result = ret; + } + } + + return result; + } +}; + +/// Get player from players pool +template +inline PlayerDataType* GetPlayerData(IPlayer* player) +{ + if (player == nullptr) + { + return nullptr; + } + return queryExtension(*player); +} + +/// Component check, return fail_ret if not available +#define COMPONENT_CHECK_RET(pool, failret) \ + if (!ComponentManager::Get()->pool) \ + return failret; \ + auto pool = ComponentManager::Get()->pool; + +/// Cast object pointer to entity type, return fail_ret if not available +#define ENTITY_CAST_RET(entity_type, entity, output, failret) \ + if (!entity) \ + return failret; \ + entity_type* output = reinterpret_cast(entity) + +#define ENTITY_CAST(entity_type, entity, output) \ + if (!entity) \ + return; \ + entity_type* output = reinterpret_cast(entity) + +#define POOL_ENTITY_RET(pool, entity_type, entity, output, failret) \ + if (!ComponentManager::Get()->pool) \ + return failret; \ + auto pool = ComponentManager::Get()->pool; \ + ENTITY_CAST_RET(entity_type, entity, output, failret) + +#define POOL_ENTITY(pool, entity_type, entity, output) \ + if (!ComponentManager::Get()->pool) \ + return; \ + auto pool = ComponentManager::Get()->pool; \ + ENTITY_CAST(entity_type, entity, output) + +#define PLAYER_POOL_ENTITY_RET(player, pool_type, entity_type, entity, entity_output, failret) \ + auto playerData = GetPlayerData(player); \ + if (playerData == nullptr) \ + return failret; \ + entity_type* entity_output = reinterpret_cast(entity); \ + if (entity_output == nullptr) \ + return failret; + +#define PLAYER_DATA_RET(player, entity_type, entity_output, failret) \ + auto entity_output = GetPlayerData(player); \ + if (entity_output == nullptr) \ + return failret + +#define COPY_STRING_TO_CAPI_STRING_BUFFER(output, src, len_) \ + if (output) \ + { \ + if (output->data && output->capacity >= len_) \ + { \ + if (len_ && src) \ + { \ + output->len = len_; \ + memcpy(output->data, src, uint32_t(len_)); \ + } \ + if (output->capacity > len_) \ + { \ + output->data[len_] = '\0'; \ + } \ + } \ + } + +#define SET_CAPI_STRING_VIEW(output, str_view) \ + if (output) \ + { \ + output->len = uint32_t(str_view.length()); \ + output->data = str_view.data(); \ + } + +#define CREATE_CAPI_STRING_VIEW(str) \ + CAPIStringView { static_cast(str.length()), str.data() } diff --git a/Server/Components/CAPI/Impl/Config/APIs.cpp b/Server/Components/CAPI/Impl/Config/APIs.cpp new file mode 100644 index 000000000..a46a4c8f9 --- /dev/null +++ b/Server/Components/CAPI/Impl/Config/APIs.cpp @@ -0,0 +1,36 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" +#include + +OMP_CAPI(Config_GetAsBool, bool(StringCharPtr cvar)) +{ + bool value = getConfigOptionAsBool(ComponentManager::Get()->core, cvar); + return value; +} + +OMP_CAPI(Config_GetAsInt, int(StringCharPtr cvar)) +{ + int value = getConfigOptionAsInt(ComponentManager::Get()->core, cvar); + return value; +} + +OMP_CAPI(Config_GetAsFloat, float(StringCharPtr cvar)) +{ + float value = getConfigOptionAsFloat(ComponentManager::Get()->core, cvar); + return value; +} + +OMP_CAPI(Config_GetAsString, int(StringCharPtr cvar, OutputStringBufferPtr output)) +{ + Impl::String value = Impl::String(); + int len = getConfigOptionAsString(ComponentManager::Get()->core, cvar, value); + COPY_STRING_TO_CAPI_STRING_BUFFER(output, value.data(), len); + return len; +} diff --git a/Server/Components/CAPI/Impl/Console/APIs.cpp b/Server/Components/CAPI/Impl/Console/APIs.cpp new file mode 100644 index 000000000..b00dec980 --- /dev/null +++ b/Server/Components/CAPI/Impl/Console/APIs.cpp @@ -0,0 +1,9 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" diff --git a/Server/Components/CAPI/Impl/Console/Events.hpp b/Server/Components/CAPI/Impl/Console/Events.hpp new file mode 100644 index 000000000..0d42a2ed1 --- /dev/null +++ b/Server/Components/CAPI/Impl/Console/Events.hpp @@ -0,0 +1,30 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#pragma once +#include "../ComponentManager.hpp" +#include "sdk.hpp" + +template +struct ConsoleEvents : public ConsoleEventHandler, public Singleton> +{ + bool onConsoleText(StringView command, StringView parameters, const ConsoleCommandSenderData& sender) override + { + return ComponentManager::Get()->CallEvent("onConsoleText", EventReturnHandler::StopAtTrue, CREATE_CAPI_STRING_VIEW(command), CREATE_CAPI_STRING_VIEW(parameters)); + } + + void onRconLoginAttempt(IPlayer& player, StringView password, bool success) override + { + PeerNetworkData data = player.getNetworkData(); + PeerAddress::AddressString addressString; + PeerAddress::ToString(data.networkID.address, addressString); + StringView addressStringView = StringView(addressString.data(), addressString.length()); + + ComponentManager::Get()->CallEvent("onRconLoginAttempt", EventReturnHandler::StopAtTrue, CREATE_CAPI_STRING_VIEW(addressStringView), CREATE_CAPI_STRING_VIEW(password), success); + } +}; diff --git a/Server/Components/CAPI/Impl/Core/APIs.cpp b/Server/Components/CAPI/Impl/Core/APIs.cpp new file mode 100644 index 000000000..d91ec2810 --- /dev/null +++ b/Server/Components/CAPI/Impl/Core/APIs.cpp @@ -0,0 +1,534 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" +#include +#include + +OMP_CAPI(Core_TickCount, uint32_t()) +{ + auto tick = ComponentManager::Get()->core->getTickCount(); + return tick; +} + +OMP_CAPI(Core_MaxPlayers, int()) +{ + int max = *ComponentManager::Get()->core->getConfig().getInt("max_players"); + return max; +} + +OMP_CAPI(Core_Log, bool(StringCharPtr text)) +{ + ComponentManager::Get()->core->printLn("%s", text); + return true; +} + +OMP_CAPI(Core_IsAdminTeleportAllowed, bool()) +{ + bool allowed = *ComponentManager::Get()->core->getConfig().getBool("rcon.allow_teleport"); + return allowed; +} + +OMP_CAPI(Core_AllowAdminTeleport, bool(bool allow)) +{ + *ComponentManager::Get()->core->getConfig().getBool("rcon.allow_teleport") = allow; + return true; +} + +OMP_CAPI(Core_AreAllAnimationsEnabled, bool()) +{ + bool allowed = *ComponentManager::Get()->core->getConfig().getBool("game.use_all_animations"); + return allowed; +} + +OMP_CAPI(Core_EnableAllAnimations, bool(bool allow)) +{ + *ComponentManager::Get()->core->getConfig().getBool("game.use_all_animations") = allow; + return true; +} + +OMP_CAPI(Core_IsAnimationLibraryValid, bool(StringCharPtr name)) +{ + auto valid = animationLibraryValid(name, true); + return valid; +} + +OMP_CAPI(Core_AreInteriorWeaponsAllowed, bool()) +{ + bool allowed = *ComponentManager::Get()->core->getConfig().getBool("game.allow_interior_weapons"); + return allowed; +} + +OMP_CAPI(Core_AllowInteriorWeapons, bool(bool allow)) +{ + if (allow) + { + *ComponentManager::Get()->core->getConfig().getBool("game.allow_interior_weapons") = true; + } + else + { + IPlayerPool* players = ComponentManager::Get()->players; + for (IPlayer* player : players->entries()) + { + if (player->getInterior() && player->areWeaponsAllowed()) + { + // Because they are allowed weapons currently this will send a full client reset. + player->resetWeapons(); + } + } + // By the time the player reports having no weapons, this is set and so we remember the old + // ones still. + *ComponentManager::Get()->core->getConfig().getBool("game.allow_interior_weapons") = false; + } + return true; +} + +OMP_CAPI(Core_BlockIpAddress, bool(StringCharPtr ipAddress, int timeMS)) +{ + if (strlen(ipAddress) < 1) + { + return false; + } + BanEntry entry(ipAddress); + for (INetwork* network : ComponentManager::Get()->core->getNetworks()) + { + network->ban(entry, Milliseconds(timeMS)); + } + return true; +} + +OMP_CAPI(Core_UnBlockIpAddress, bool(StringCharPtr ipAddress)) +{ + BanEntry entry(ipAddress); + for (INetwork* network : ComponentManager::Get()->core->getNetworks()) + { + network->unban(entry); + } + return true; +} + +OMP_CAPI(NPC_Connect, bool(StringCharPtr name, StringCharPtr script)) +{ + ComponentManager::Get()->core->connectBot(name, script); + return true; +} + +OMP_CAPI(Core_DisableEntryExitMarkers, bool()) +{ + *ComponentManager::Get()->core->getConfig().getBool("game.use_entry_exit_markers") = false; + return true; +} + +OMP_CAPI(Core_DisableNameTagsLOS, bool()) +{ + *ComponentManager::Get()->core->getConfig().getBool("game.use_nametag_los") = false; + return true; +} + +OMP_CAPI(Core_EnableZoneNames, bool(bool enable)) +{ + *ComponentManager::Get()->core->getConfig().getBool("game.use_zone_names") = enable; + return true; +} + +OMP_CAPI(Core_ShowGameTextForAll, bool(StringCharPtr msg, int time, int style)) +{ + if (strlen(msg) < 1) + { + return false; + } + ComponentManager::Get()->players->sendGameTextToAll(msg, Milliseconds(time), style); + return true; +} + +OMP_CAPI(Core_HideGameTextForAll, bool(int style)) +{ + ComponentManager::Get()->players->hideGameTextForAll(style); + return true; +} + +OMP_CAPI(Core_NetworkStats, int(OutputStringBufferPtr output)) +{ + std::stringstream stream; + NetworkStats stats; + + for (INetwork* network : ComponentManager::Get()->core->getNetworks()) + { + if (network->getNetworkType() == ENetworkType::ENetworkType_RakNetLegacy) + { + stats = network->getStatistics(); + } + } + + stream + << "Server Ticks: " << ComponentManager::Get()->core->tickRate() << std::endl + << "Messages in Send buffer: " << stats.messageSendBuffer << std::endl + << "Messages sent: " << stats.messagesSent << std::endl + << "Bytes sent: " << stats.totalBytesSent << std::endl + << "Acks sent: " << stats.acknowlegementsSent << std::endl + << "Acks in send buffer: " << stats.acknowlegementsPending << std::endl + << "Messages waiting for ack: " << stats.messagesOnResendQueue << std::endl + << "Messages resent: " << stats.messageResends << std::endl + << "Bytes resent: " << stats.messagesTotalBytesResent << std::endl + << "Packetloss: " << std::setprecision(1) << std::fixed << stats.packetloss << "%" << std::endl + << "Messages received: " << stats.messagesReceived << std::endl + << "Bytes received: " << stats.bytesReceived << std::endl + << "Acks received: " << stats.acknowlegementsReceived << std::endl + << "Duplicate acks received: " << stats.duplicateAcknowlegementsReceived << std::endl + << "Inst. KBits per second: " << std::setprecision(1) << (stats.bitsPerSecond / 1000.0) << std::endl + << "KBits per second sent: " << std::setprecision(1) << (stats.bpsSent / 1000.0) << std::endl + << "KBits per second received: " << std::setprecision(1) << (stats.bpsReceived / 1000.0) << std::endl; + + int len = stream.str().size(); + COPY_STRING_TO_CAPI_STRING_BUFFER(output, stream.str().c_str(), len); + return len; +} + +OMP_CAPI(Player_GetNetworkStats, int(objectPtr player, OutputStringBufferPtr output)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + std::stringstream stream; + NetworkStats stats = player_->getNetworkData().network->getStatistics(player_); + + stream + << "Network Active: " << int(stats.isActive) << std::endl + << "Network State: " << stats.connectMode << std::endl + << "Messages in Send buffer: " << stats.messageSendBuffer << std::endl + << "Messages sent: " << stats.messagesSent << std::endl + << "Bytes sent: " << stats.totalBytesSent << std::endl + << "Acks sent: " << stats.acknowlegementsSent << std::endl + << "Acks in send buffer: " << stats.acknowlegementsPending << std::endl + << "Messages waiting for ack: " << stats.messagesOnResendQueue << std::endl + << "Messages resent: " << stats.messageResends << std::endl + << "Bytes resent: " << stats.messagesTotalBytesResent << std::endl + << "Packetloss: " << std::setprecision(1) << std::fixed << stats.packetloss << "%" << std::endl + << "Messages received: " << stats.messagesReceived << std::endl + << "Bytes received: " << stats.bytesReceived << std::endl + << "Acks received: " << stats.acknowlegementsReceived << std::endl + << "Duplicate acks received: " << stats.duplicateAcknowlegementsReceived << std::endl + << "Inst. KBits per second: " << std::setprecision(1) << (stats.bitsPerSecond / 1000.0) << std::endl + << "KBits per second sent: " << std::setprecision(1) << (stats.bpsSent / 1000.0) << std::endl + << "KBits per second received: " << std::setprecision(1) << (stats.bpsReceived / 1000.0) << std::endl; + + int len = stream.str().size(); + COPY_STRING_TO_CAPI_STRING_BUFFER(output, stream.str().c_str(), len); + return len; +} + +OMP_CAPI(Core_ServerTickRate, int()) +{ + int tick = ComponentManager::Get()->core->tickRate(); + return tick; +} + +OMP_CAPI(Core_GetWeaponName, bool(int weaponid, OutputStringViewPtr output)) +{ + SET_CAPI_STRING_VIEW(output, ComponentManager::Get()->core->getWeaponName(PlayerWeapon(weaponid))); + return true; +} + +OMP_CAPI(Core_SetChatRadius, bool(float globalChatRadius)) +{ + *ComponentManager::Get()->core->getConfig().getBool("game.use_chat_radius") = true; + *ComponentManager::Get()->core->getConfig().getFloat("game.chat_radius") = globalChatRadius; + return true; +} + +OMP_CAPI(Core_SetMarkerRadius, bool(float playerMarkerRadius)) +{ + *ComponentManager::Get()->core->getConfig().getBool("game.use_player_marker_draw_radius") = true; + *ComponentManager::Get()->core->getConfig().getFloat("game.player_marker_draw_radius") = playerMarkerRadius; + return true; +} + +OMP_CAPI(Player_NetStatsBytesReceived, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + NetworkStats stats = player_->getNetworkData().network->getStatistics(player_); + int bytes = stats.bytesReceived; + return bytes; +} + +OMP_CAPI(Player_NetStatsBytesSent, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + NetworkStats stats = player_->getNetworkData().network->getStatistics(player_); + int bytes = stats.totalBytesSent; + return bytes; +} + +OMP_CAPI(Player_NetStatsConnectionStatus, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + NetworkStats stats = player_->getNetworkData().network->getStatistics(player_); + int status = stats.connectMode; + return status; +} + +OMP_CAPI(Player_NetStatsGetConnectedTime, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + NetworkStats stats = player_->getNetworkData().network->getStatistics(player_); + int ms = stats.connectionElapsedTime; + return ms; +} + +OMP_CAPI(Player_NetStatsGetIpPort, bool(objectPtr player, OutputStringBufferPtr output)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PeerNetworkData data = player_->getNetworkData(); + PeerAddress::AddressString addressString; + if (PeerAddress::ToString(data.networkID.address, addressString)) + { + Impl::String ip_port((StringView(addressString))); + ip_port += ":"; + ip_port += std::to_string(data.networkID.port); + int len = ip_port.length(); + COPY_STRING_TO_CAPI_STRING_BUFFER(output, ip_port.c_str(), len); + return len; + } + return 0; +} + +OMP_CAPI(Player_NetStatsMessagesReceived, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + NetworkStats stats = player_->getNetworkData().network->getStatistics(player_); + int received = stats.messagesReceived; + return received; +} + +OMP_CAPI(Player_NetStatsMessagesRecvPerSecond, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + NetworkStats stats = player_->getNetworkData().network->getStatistics(player_); + int received = stats.messagesReceivedPerSecond; + return received; +} + +OMP_CAPI(Player_NetStatsMessagesSent, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + NetworkStats stats = player_->getNetworkData().network->getStatistics(player_); + int sent = stats.messagesSent; + return sent; +} + +OMP_CAPI(Player_NetStatsPacketLossPercent, float(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0.0f); + NetworkStats stats = player_->getNetworkData().network->getStatistics(player_); + auto packetLoss = stats.packetloss; + return packetLoss; +} + +OMP_CAPI(Core_SendRconCommand, bool(StringCharPtr command)) +{ + IConsoleComponent* console = ComponentManager::Get()->console; + if (console) + { + console->send(command); + return true; + } + return false; +} + +OMP_CAPI(Core_SetDeathDropAmount, bool(int amount)) +{ + *ComponentManager::Get()->core->getConfig().getInt("game.death_drop_amount") = amount; + return true; +} + +OMP_CAPI(Core_GameMode_SetText, bool(StringCharPtr string)) +{ + ComponentManager::Get()->core->setData(SettableCoreDataType::ModeText, string); + return true; +} + +OMP_CAPI(Core_SetGravity, bool(float gravity)) +{ + ComponentManager::Get()->core->setGravity(gravity); + return true; +} + +OMP_CAPI(Core_GetGravity, float()) +{ + float gravity = ComponentManager::Get()->core->getGravity(); + return gravity; +} + +OMP_CAPI(Core_SetNameTagsDrawDistance, bool(float distance)) +{ + *ComponentManager::Get()->core->getConfig().getFloat("game.nametag_draw_radius") = distance; + return true; +} + +OMP_CAPI(Core_SetWeather, bool(int weatherid)) +{ + ComponentManager::Get()->core->setWeather(weatherid); + return true; +} + +OMP_CAPI(Core_SetWorldTime, bool(int hour)) +{ + ComponentManager::Get()->core->setWorldTime(Hours(hour)); + return true; +} + +OMP_CAPI(Core_ShowNameTags, bool(bool show)) +{ + *ComponentManager::Get()->core->getConfig().getBool("game.use_nametags") = show; + return true; +} + +OMP_CAPI(Core_ShowPlayerMarkers, bool(int mode)) +{ + *ComponentManager::Get()->core->getConfig().getInt("game.player_marker_mode") = mode; + return true; +} + +OMP_CAPI(Core_UsePedAnims, bool()) +{ + *ComponentManager::Get()->core->getConfig().getBool("game.use_player_ped_anims") = true; + return true; +} + +OMP_CAPI(Core_GetWeather, int()) +{ + int weather = *ComponentManager::Get()->core->getConfig().getInt("game.weather"); + return weather; +} + +OMP_CAPI(Core_GetWorldTime, int()) +{ + int hour = *ComponentManager::Get()->core->getConfig().getInt("game.time"); + return hour; +} + +OMP_CAPI(Core_ToggleChatTextReplacement, bool(bool enable)) +{ + *ComponentManager::Get()->core->getConfig().getBool("chat_input_filter") = enable; + return true; +} + +OMP_CAPI(Core_IsChatTextReplacementToggled, bool()) +{ + bool toggled = *ComponentManager::Get()->core->getConfig().getBool("chat_input_filter"); + return toggled; +} + +OMP_CAPI(Core_IsNickNameValid, bool(StringCharPtr name)) +{ + auto valid = ComponentManager::Get()->players->isNameValid(name); + return valid; +} + +OMP_CAPI(Core_AllowNickNameCharacter, bool(int character, bool allow)) +{ + ComponentManager::Get()->players->allowNickNameCharacter(character, allow); + return true; +} + +OMP_CAPI(Core_IsNickNameCharacterAllowed, bool(int character)) +{ + bool allowed = ComponentManager::Get()->players->isNickNameCharacterAllowed(character); + return allowed; +} + +OMP_CAPI(Core_ClearBanList, bool()) +{ + ICore* core = ComponentManager::Get()->core; + if (!core) + { + return false; + } + + core->getConfig().clearBans(); + return true; +} + +OMP_CAPI(Core_IsIpAddressBanned, bool(StringCharPtr ip)) +{ + ICore* core = ComponentManager::Get()->core; + if (!core) + { + return false; + } + BanEntry entry(ip); + bool isBanned = core->getConfig().isBanned(entry); + return isBanned; +} + +OMP_CAPI(Core_GetWeaponSlot, int(uint8_t weapon)) +{ + int slot = WeaponSlotData { weapon }.slot(); + return slot; +} + +OMP_CAPI(Core_AddRule, bool(StringCharPtr name, StringCharPtr value)) +{ + ICore* core = ComponentManager::Get()->core; + if (!core) + { + return false; + } + + for (INetwork* network : core->getNetworks()) + { + INetworkQueryExtension* query = queryExtension(network); + + if (query) + { + query->addRule(name, value); + return true; + } + } + return false; +} + +OMP_CAPI(Core_IsValidRule, bool(StringCharPtr name)) +{ + ICore* core = ComponentManager::Get()->core; + if (!core) + { + return false; + } + + for (INetwork* network : core->getNetworks()) + { + INetworkQueryExtension* query = queryExtension(network); + + if (query) + { + bool valid = query->isValidRule(name); + return valid; + } + } + return false; +} + +OMP_CAPI(Core_RemoveRule, bool(StringCharPtr name)) +{ + ICore* core = ComponentManager::Get()->core; + if (!core) + { + return false; + } + + for (INetwork* network : core->getNetworks()) + { + INetworkQueryExtension* query = queryExtension(network); + + if (query) + { + query->removeRule(name); + return true; + } + } + return false; +} diff --git a/Server/Components/CAPI/Impl/Core/Events.hpp b/Server/Components/CAPI/Impl/Core/Events.hpp new file mode 100644 index 000000000..3547420df --- /dev/null +++ b/Server/Components/CAPI/Impl/Core/Events.hpp @@ -0,0 +1,20 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#pragma once +#include "../ComponentManager.hpp" +#include "sdk.hpp" + +template +struct CoreEvents : public CoreEventHandler, public Singleton> +{ + void onTick(Microseconds elapsed, TimePoint now) override + { + ComponentManager::Get()->CallEvent("onTick", EventReturnHandler::None, int(elapsed.count())); + } +}; diff --git a/Server/Components/CAPI/Impl/CustomModels/APIs.cpp b/Server/Components/CAPI/Impl/CustomModels/APIs.cpp new file mode 100644 index 000000000..217985eb9 --- /dev/null +++ b/Server/Components/CAPI/Impl/CustomModels/APIs.cpp @@ -0,0 +1,117 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" + +OMP_CAPI(CustomModel_AddCharModel, bool(int baseid, int newid, StringCharPtr dff, StringCharPtr textureLibrary)) +{ + auto models = ComponentManager::Get()->models; + if (!models) + { + return false; + } + + bool ret = models->addCustomModel(ModelType::Skin, newid, baseid, dff, textureLibrary); + return ret; +} + +OMP_CAPI(CustomModel_AddSimpleModel, bool(int virtualWorld, int baseid, int newid, StringCharPtr dff, StringCharPtr textureLibrary)) +{ + auto models = ComponentManager::Get()->models; + if (!models) + { + return false; + } + + bool ret = models->addCustomModel(ModelType::Object, newid, baseid, dff, textureLibrary, virtualWorld); + return ret; +} + +OMP_CAPI(CustomModel_AddSimpleModelTimed, bool(int virtualWorld, int baseid, int newid, StringCharPtr dff, StringCharPtr textureLibrary, int timeOn, int timeOff)) +{ + auto models = ComponentManager::Get()->models; + if (!models) + { + return false; + } + + bool ret = models->addCustomModel(ModelType::Object, newid, baseid, dff, textureLibrary, virtualWorld, uint8_t(timeOn), uint8_t(timeOff)); + return ret; +} + +OMP_CAPI(Player_GetCustomSkin, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + IPlayerCustomModelsData* data = queryExtension(player_); + if (!data) + { + return 0; + } + auto skin = data->getCustomSkin(); + return skin; +} + +OMP_CAPI(CustomModel_RedirectDownload, bool(objectPtr player, StringCharPtr url)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerCustomModelsData* data = queryExtension(player_); + if (!data) + { + return false; + } + if (!data->sendDownloadUrl(url)) + { + ComponentManager::Get()->core->logLn(LogLevel::Warning, "This native can be used only within OnPlayerRequestDownload callback."); + return false; + } + return true; +} + +OMP_CAPI(CustomModel_FindModelFileNameFromCRC, int(int crc, OutputStringViewPtr output)) +{ + auto models = ComponentManager::Get()->models; + if (!models) + { + return false; + } + + auto result = models->getModelNameFromChecksum(crc); + SET_CAPI_STRING_VIEW(output, result); + return true; +} + +OMP_CAPI(CustomModel_IsValid, bool(int modelId)) +{ + auto models = ComponentManager::Get()->models; + if (!models) + { + return false; + } + + auto valid = models->isValidCustomModel(modelId); + return valid; +} + +OMP_CAPI(CustomModel_GetPath, bool(int modelId, OutputStringViewPtr dffPath, OutputStringViewPtr txdPath)) +{ + auto models = ComponentManager::Get()->models; + if (!models) + { + return false; + } + + StringView dffPathSV {}; + StringView txdPathSV {}; + + auto status = models->getCustomModelPath(modelId, dffPathSV, txdPathSV); + + SET_CAPI_STRING_VIEW(dffPath, dffPathSV); + SET_CAPI_STRING_VIEW(txdPath, txdPathSV); + + return status; +} diff --git a/Server/Components/CAPI/Impl/CustomModels/Events.hpp b/Server/Components/CAPI/Impl/CustomModels/Events.hpp new file mode 100644 index 000000000..028e1b11d --- /dev/null +++ b/Server/Components/CAPI/Impl/CustomModels/Events.hpp @@ -0,0 +1,25 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#pragma once +#include "../ComponentManager.hpp" +#include "sdk.hpp" + +template +struct CustomModelsEvents : public PlayerModelsEventHandler, public Singleton> +{ + virtual void onPlayerFinishedDownloading(IPlayer& player) override + { + ComponentManager::Get()->CallEvent("onPlayerFinishedDownloading", EventReturnHandler::None, &player, player.getVirtualWorld()); + } + + virtual bool onPlayerRequestDownload(IPlayer& player, ModelDownloadType type, uint32_t checksum) override + { + return ComponentManager::Get()->CallEvent("onPlayerRequestDownload", EventReturnHandler::StopAtFalse, &player, int(type), int(checksum)); + } +}; diff --git a/Server/Components/CAPI/Impl/Dialogs/APIs.cpp b/Server/Components/CAPI/Impl/Dialogs/APIs.cpp new file mode 100644 index 000000000..1bea1ef18 --- /dev/null +++ b/Server/Components/CAPI/Impl/Dialogs/APIs.cpp @@ -0,0 +1,73 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" + +OMP_CAPI(Dialog_Show, bool(objectPtr player, int dialog, int style, StringCharPtr title, StringCharPtr body, StringCharPtr button1, StringCharPtr button2)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerDialogData* data = queryExtension(player_); + if (!data) + { + return false; + } + + if (dialog == INVALID_DIALOG_ID) + { + static bool warned = false; + if (!warned) + { + ComponentManager::Get()->core->logLn(LogLevel::Warning, "Invalid dialog ID %d used. Use `Dialog_Hide()`.", dialog); + warned = true; + } + + data->hide(*player_); + return true; + } + + data->show(*player_, dialog & 0xFFFF, DialogStyle(style), title, body, button1, button2); + return true; +} + +OMP_CAPI(Player_GetDialog, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, INVALID_DIALOG_ID); + IPlayerDialogData* data = queryExtension(player_); + auto dialog = data->getActiveID(); + return dialog; +} + +OMP_CAPI(Player_GetDialogData, bool(objectPtr player, int* dialogid, int* style, OutputStringViewPtr title, OutputStringViewPtr body, OutputStringViewPtr button1, OutputStringViewPtr button2)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerDialogData* data = queryExtension(player_); + DialogStyle styleVar {}; + StringView titleVar {}; + StringView bodyVar {}; + StringView button1Var {}; + StringView button2Var {}; + data->get(*dialogid, styleVar, titleVar, bodyVar, button1Var, button2Var); + *style = int(styleVar); + SET_CAPI_STRING_VIEW(title, titleVar); + SET_CAPI_STRING_VIEW(body, bodyVar); + SET_CAPI_STRING_VIEW(button1, button1Var); + SET_CAPI_STRING_VIEW(button2, button2Var); + return true; +} + +OMP_CAPI(Dialog_Hide, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerDialogData* dialog = queryExtension(player_); + if (dialog && dialog->getActiveID() != INVALID_DIALOG_ID) + { + dialog->hide(*player_); + return true; + } + return true; +} diff --git a/Server/Components/CAPI/Impl/Dialogs/Events.hpp b/Server/Components/CAPI/Impl/Dialogs/Events.hpp new file mode 100644 index 000000000..72c80a4c8 --- /dev/null +++ b/Server/Components/CAPI/Impl/Dialogs/Events.hpp @@ -0,0 +1,20 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#pragma once +#include "../ComponentManager.hpp" +#include "sdk.hpp" + +template +struct DialogEvents : public PlayerDialogEventHandler, public Singleton> +{ + void onDialogResponse(IPlayer& player, int dialogId, DialogResponse response, int listItem, StringView inputText) override + { + ComponentManager::Get()->CallEvent("onDialogResponse", EventReturnHandler::None, &player, dialogId, int(response), listItem, CREATE_CAPI_STRING_VIEW(inputText)); + } +}; diff --git a/Server/Components/CAPI/Impl/Events/APIs.cpp b/Server/Components/CAPI/Impl/Events/APIs.cpp new file mode 100644 index 000000000..30a22804a --- /dev/null +++ b/Server/Components/CAPI/Impl/Events/APIs.cpp @@ -0,0 +1,25 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" + +OMP_CAPI(Event_AddHandler, bool(StringCharPtr name, int priority, void* callback)) +{ + return ComponentManager::Get()->AddEventHandler(name, EventPriorityType(priority), EventCallback_Common(callback)); +} + +OMP_CAPI(Event_RemoveHandler, bool(StringCharPtr name, int priority, void* callback)) +{ + return ComponentManager::Get()->RemoveEventHandler(name, EventPriorityType(priority), EventCallback_Common(callback)); +} + +OMP_CAPI(Event_RemoveAllHandlers, bool(StringCharPtr name, int priority)) +{ + ComponentManager::Get()->RemoveAllHandlers(name, EventPriorityType(priority)); + return true; +} diff --git a/Server/Components/CAPI/Impl/GangZones/APIs.cpp b/Server/Components/CAPI/Impl/GangZones/APIs.cpp new file mode 100644 index 000000000..9a30ebb56 --- /dev/null +++ b/Server/Components/CAPI/Impl/GangZones/APIs.cpp @@ -0,0 +1,219 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" + +OMP_CAPI(GangZone_Create, objectPtr(float minx, float miny, float maxx, float maxy, int* id)) +{ + IGangZonesComponent* component = ComponentManager::Get()->gangzones; + if (component) + { + Vector2 min = { minx, miny }; + Vector2 max = { maxx, maxy }; + + int id_ = component->reserveLegacyID(); + if (id_ == INVALID_GANG_ZONE_ID) + { + return nullptr; + } + + GangZonePos pos; + pos.min.x = truncf(min.x); + pos.min.y = truncf(min.y); + pos.max.x = truncf(max.x); + pos.max.y = truncf(max.y); + + IGangZone* gz = component->create(pos); + if (gz) + { + component->setLegacyID(id_, gz->getID()); + *id = id_; + return gz; + } + else + { + component->releaseLegacyID(id_); + } + } + return nullptr; +} + +OMP_CAPI(GangZone_Destroy, bool(objectPtr gangzone)) +{ + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gz, false); + gangzones->release(gz->getID()); + gangzones->releaseLegacyID(gangzones->toLegacyID(gz->getID())); + return true; +} + +OMP_CAPI(GangZone_FromID, objectPtr(int gangzoneid)) +{ + IGangZonesComponent* component = ComponentManager::Get()->gangzones; + if (component) + { + return component->get(gangzoneid); + } + return nullptr; +} + +OMP_CAPI(GangZone_GetID, int(objectPtr gangzone)) +{ + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gangzone_, INVALID_GANG_ZONE_ID); + return gangzone_->getID(); +} + +OMP_CAPI(GangZone_ShowForPlayer, bool(objectPtr player, objectPtr gangzone, uint32_t color)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gz, false); + gz->showForPlayer(*player_, Colour::FromRGBA(color)); + return true; +} + +OMP_CAPI(GangZone_ShowForAll, bool(objectPtr gangzone, uint32_t color)) +{ + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gz, false); + IPlayerPool* pool = ComponentManager::Get()->players; + for (IPlayer* player : pool->entries()) + { + gz->showForPlayer(*player, Colour::FromRGBA(color)); + } + return true; +} + +OMP_CAPI(GangZone_HideForPlayer, bool(objectPtr player, objectPtr gangzone)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gz, false); + gz->hideForPlayer(*player_); + return true; +} + +OMP_CAPI(GangZone_HideForAll, bool(objectPtr gangzone)) +{ + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gz, false); + IPlayerPool* pool = ComponentManager::Get()->players; + for (IPlayer* player : pool->entries()) + { + gz->hideForPlayer(*player); + } + return true; +} + +OMP_CAPI(GangZone_FlashForPlayer, bool(objectPtr player, objectPtr gangzone, uint32_t color)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gz, false); + gz->flashForPlayer(*player_, Colour::FromRGBA(color)); + return true; +} + +OMP_CAPI(GangZone_FlashForAll, bool(objectPtr gangzone, uint32_t color)) +{ + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gz, false); + IPlayerPool* pool = ComponentManager::Get()->players; + for (IPlayer* player : pool->entries()) + { + gz->flashForPlayer(*player, Colour::FromRGBA(color)); + } + return true; +} + +OMP_CAPI(GangZone_StopFlashForPlayer, bool(objectPtr player, objectPtr gangzone)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gz, false); + gz->stopFlashForPlayer(*player_); + return true; +} + +OMP_CAPI(GangZone_StopFlashForAll, bool(objectPtr gangzone)) +{ + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gz, false); + IPlayerPool* pool = ComponentManager::Get()->players; + for (IPlayer* player : pool->entries()) + { + gz->stopFlashForPlayer(*player); + } + return true; +} + +OMP_CAPI(GangZone_IsValid, bool(objectPtr gangzone)) +{ + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gz, false); + if (!gangzones->get(gz->getID())) + { + return false; + } + return true; +} + +OMP_CAPI(GangZone_IsPlayerIn, bool(objectPtr player, objectPtr gangzone)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gz, false); + return gz->isPlayerInside(*player_); +} + +OMP_CAPI(GangZone_IsVisibleForPlayer, bool(objectPtr player, objectPtr gangzone)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gz, false); + return gz->isShownForPlayer(*player_); +} + +OMP_CAPI(GangZone_GetColorForPlayer, int(objectPtr player, objectPtr gangzone)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gz, 0); + if (gz->isShownForPlayer(*player_)) + { + return gz->getColourForPlayer(*player_).RGBA(); + } + return 0; +} + +OMP_CAPI(GangZone_GetFlashColorForPlayer, int(objectPtr player, objectPtr gangzone)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gz, 0); + if (gz->isShownForPlayer(*player_)) + { + return gz->getFlashingColourForPlayer(*player_).RGBA(); + } + return 0; +} + +OMP_CAPI(GangZone_IsFlashingForPlayer, bool(objectPtr player, objectPtr gangzone)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gz, false); + return gz->isFlashingForPlayer(*player_); +} + +OMP_CAPI(GangZone_GetPos, bool(objectPtr gangzone, float* minx, float* miny, float* maxx, float* maxy)) +{ + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gz, false); + const GangZonePos& pos = gz->getPosition(); + *minx = pos.min.x; + *miny = pos.min.y; + *maxx = pos.max.x; + *maxy = pos.max.y; + return true; +} + +OMP_CAPI(GangZone_UseCheck, bool(objectPtr gangzone, bool enable)) +{ + POOL_ENTITY_RET(gangzones, IGangZone, gangzone, gz, false); + gangzones->useGangZoneCheck(*gz, enable); + return true; +} + +/* + Per-Player GangZones +*/ diff --git a/Server/Components/CAPI/Impl/GangZones/Events.hpp b/Server/Components/CAPI/Impl/GangZones/Events.hpp new file mode 100644 index 000000000..92538ef5f --- /dev/null +++ b/Server/Components/CAPI/Impl/GangZones/Events.hpp @@ -0,0 +1,48 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#pragma once +#include "../ComponentManager.hpp" +#include "sdk.hpp" + +template +struct GangZoneEvents : public GangZoneEventHandler, public Singleton> +{ + void onPlayerEnterGangZone(IPlayer& player, IGangZone& zone) override + { + if (zone.getLegacyPlayer() == nullptr) + { + ComponentManager::Get()->CallEvent("onPlayerEnterGangZone", EventReturnHandler::None, &player, &zone); + } + else if (auto data = queryExtension(player)) + { + } + } + + void onPlayerLeaveGangZone(IPlayer& player, IGangZone& zone) override + { + if (zone.getLegacyPlayer() == nullptr) + { + ComponentManager::Get()->CallEvent("onPlayerLeaveGangZone", EventReturnHandler::None, &player, &zone); + } + else if (auto data = queryExtension(player)) + { + } + } + + void onPlayerClickGangZone(IPlayer& player, IGangZone& zone) override + { + if (zone.getLegacyPlayer() == nullptr) + { + ComponentManager::Get()->CallEvent("onPlayerClickGangZone", EventReturnHandler::None, &player, &zone); + } + else if (auto data = queryExtension(player)) + { + } + } +}; diff --git a/Server/Components/CAPI/Impl/Menus/APIs.cpp b/Server/Components/CAPI/Impl/Menus/APIs.cpp new file mode 100644 index 000000000..291538943 --- /dev/null +++ b/Server/Components/CAPI/Impl/Menus/APIs.cpp @@ -0,0 +1,176 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" + +OMP_CAPI(Menu_Create, objectPtr(StringCharPtr title, uint32_t columns, float x, float y, float column1Width, float column2Width, int* id)) +{ + IMenusComponent* component = ComponentManager::Get()->menus; + if (component) + { + IMenu* menu = component->create(title, { x, y }, uint8_t(columns), column1Width, column2Width); + if (menu) + { + *id = menu->getID(); + return menu; + } + } + return nullptr; +} + +OMP_CAPI(Menu_Destroy, bool(objectPtr menu)) +{ + POOL_ENTITY_RET(menus, IMenu, menu, menu_, false); + ComponentManager::Get()->menus->release(menu_->getID()); + return true; +} + +OMP_CAPI(Menu_FromID, objectPtr(int menuid)) +{ + IMenusComponent* component = ComponentManager::Get()->menus; + if (component) + { + return component->get(menuid); + } + return nullptr; +} + +OMP_CAPI(Menu_GetID, int(objectPtr menu)) +{ + POOL_ENTITY_RET(menus, IMenu, menu, menu_, INVALID_MENU_ID); + return menu_->getID(); +} + +OMP_CAPI(Menu_AddItem, int(objectPtr menu, uint8_t column, StringCharPtr text)) +{ + POOL_ENTITY_RET(menus, IMenu, menu, menu_, INVALID_MENU_ID); + auto index = menu_->addCell(text, column); + return index; +} + +OMP_CAPI(Menu_SetColumnHeader, bool(objectPtr menu, uint8_t column, StringCharPtr headerTitle)) +{ + POOL_ENTITY_RET(menus, IMenu, menu, menu_, false); + menu_->setColumnHeader(headerTitle, column); + return true; +} + +OMP_CAPI(Menu_ShowForPlayer, bool(objectPtr menu, objectPtr player)) +{ + POOL_ENTITY_RET(menus, IMenu, menu, menu_, false); + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + menu_->showForPlayer(*player_); + return true; +} + +OMP_CAPI(Menu_HideForPlayer, bool(objectPtr menu, objectPtr player)) +{ + POOL_ENTITY_RET(menus, IMenu, menu, menu_, false); + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + menu_->hideForPlayer(*player_); + return true; +} + +OMP_CAPI(Menu_Disable, bool(objectPtr menu)) +{ + POOL_ENTITY_RET(menus, IMenu, menu, menu_, false); + menu_->disable(); + return true; +} + +OMP_CAPI(Menu_DisableRow, bool(objectPtr menu, uint8_t row)) +{ + POOL_ENTITY_RET(menus, IMenu, menu, menu_, false); + menu_->disableRow(row); + return true; +} + +OMP_CAPI(Player_GetMenu, objectPtr(objectPtr player)) +{ + IMenusComponent* component = ComponentManager::Get()->menus; + if (component) + { + POOL_ENTITY_RET(players, IPlayer, player, player_, nullptr); + IPlayerMenuData* menuData = queryExtension(player_); + if (menuData) + { + return component->get(menuData->getMenuID()); + } + } + return nullptr; +} + +OMP_CAPI(Menu_IsValid, bool(objectPtr menu)) +{ + POOL_ENTITY_RET(menus, IMenu, menu, menu_, false); + if (!menus->get(menu_->getID())) + return false; + return true; +} + +OMP_CAPI(Menu_IsDisabled, bool(objectPtr menu)) +{ + POOL_ENTITY_RET(menus, IMenu, menu, menu_, false); + auto disabled = !menu_->isEnabled(); + return disabled; +} + +OMP_CAPI(Menu_IsRowDisabled, bool(objectPtr menu, int row)) +{ + POOL_ENTITY_RET(menus, IMenu, menu, menu_, false); + auto disabled = !menu_->isRowEnabled(MenuRow(row)); + return disabled; +} + +OMP_CAPI(Menu_GetColumns, int(objectPtr menu)) +{ + POOL_ENTITY_RET(menus, IMenu, menu, menu_, 0); + auto columns = menu_->getColumnCount(); + return columns; +} + +OMP_CAPI(Menu_GetItems, int(objectPtr menu, int column)) +{ + POOL_ENTITY_RET(menus, IMenu, menu, menu_, 0); + auto rows = menu_->getRowCount(MenuColumn(column)); + return rows; +} + +OMP_CAPI(Menu_GetPos, bool(objectPtr menu, float* x, float* y)) +{ + POOL_ENTITY_RET(menus, IMenu, menu, menu_, false); + auto pos = menu_->getPosition(); + *x = pos.x; + *y = pos.y; + return true; +} + +OMP_CAPI(Menu_GetColumnWidth, bool(objectPtr menu, float* column1Width, float* column2Width)) +{ + POOL_ENTITY_RET(menus, IMenu, menu, menu_, false); + auto widths = menu_->getColumnWidths(); + *column1Width = widths.x; + *column2Width = widths.y; + return true; +} + +OMP_CAPI(Menu_GetColumnHeader, bool(objectPtr menu, int column, OutputStringViewPtr header)) +{ + POOL_ENTITY_RET(menus, IMenu, menu, menu_, false); + auto result = menu_->getColumnHeader(MenuColumn(column)); + SET_CAPI_STRING_VIEW(header, result); + return true; +} + +OMP_CAPI(Menu_GetItem, bool(objectPtr menu, int column, int row, OutputStringViewPtr item)) +{ + POOL_ENTITY_RET(menus, IMenu, menu, menu_, false); + auto result = menu_->getCell(MenuColumn(column), MenuRow(row)); + SET_CAPI_STRING_VIEW(item, result); + return true; +} diff --git a/Server/Components/CAPI/Impl/Menus/Events.hpp b/Server/Components/CAPI/Impl/Menus/Events.hpp new file mode 100644 index 000000000..2df8cbd40 --- /dev/null +++ b/Server/Components/CAPI/Impl/Menus/Events.hpp @@ -0,0 +1,25 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#pragma once +#include "../ComponentManager.hpp" +#include "sdk.hpp" + +template +struct MenuEvents : public MenuEventHandler, public Singleton> +{ + void onPlayerSelectedMenuRow(IPlayer& player, MenuRow row) override + { + ComponentManager::Get()->CallEvent("onPlayerSelectedMenuRow", EventReturnHandler::None, &player, int(row)); + } + + void onPlayerExitedMenu(IPlayer& player) override + { + ComponentManager::Get()->CallEvent("onPlayerExitedMenu", EventReturnHandler::None, &player); + } +}; diff --git a/Server/Components/CAPI/Impl/NPCs/APIs.cpp b/Server/Components/CAPI/Impl/NPCs/APIs.cpp new file mode 100644 index 000000000..320962932 --- /dev/null +++ b/Server/Components/CAPI/Impl/NPCs/APIs.cpp @@ -0,0 +1,1112 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" +#include + +OMP_CAPI(NPC_Create, objectPtr(const char* name)) +{ + COMPONENT_CHECK_RET(npcs, nullptr); + if (name) + { + auto npc = npcs->create(name); + if (npc) + { + return npc; + } + } + return nullptr; +} + +OMP_CAPI(NPC_Destroy, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npcs->destroy(*npc_); + return true; +} + +OMP_CAPI(NPC_FromID, objectPtr(int npcid)) +{ + COMPONENT_CHECK_RET(npcs, nullptr); + return npcs->get(npcid); +} + +OMP_CAPI(NPC_GetID, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, INVALID_PLAYER_ID); + return npc_->getID(); +} + +OMP_CAPI(NPC_IsValid, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npcs->get(npc_->getID()) != nullptr; +} + +OMP_CAPI(NPC_Spawn, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->spawn(); + return true; +} + +OMP_CAPI(NPC_Respawn, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->respawn(); + return true; +} + +OMP_CAPI(NPC_SetPos, bool(objectPtr npc, float x, float y, float z)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setPosition(Vector3(x, y, z), true); + return true; +} + +OMP_CAPI(NPC_GetPos, bool(objectPtr npc, float* x, float* y, float* z)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + Vector3 position = npc_->getPosition(); + *x = position.x; + *y = position.y; + *z = position.z; + return true; +} + +OMP_CAPI(NPC_SetRot, bool(objectPtr npc, float rx, float ry, float rz)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setRotation(Vector3(rx, ry, rz), true); + return true; +} + +OMP_CAPI(NPC_GetRot, bool(objectPtr npc, float* rx, float* ry, float* rz)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + Vector3 rotation = npc_->getRotation().ToEuler(); + *rx = rotation.x; + *ry = rotation.y; + *rz = rotation.z; + return true; +} + +OMP_CAPI(NPC_SetFacingAngle, bool(objectPtr npc, float angle)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + auto rotation = npc_->getRotation().ToEuler(); + rotation.z = angle; + npc_->setRotation(rotation, true); + return true; +} + +OMP_CAPI(NPC_GetFacingAngle, bool(objectPtr npc, float* angle)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + auto rotation = npc_->getRotation().ToEuler(); + *angle = rotation.z; + return true; +} + +OMP_CAPI(NPC_SetVirtualWorld, bool(objectPtr npc, int virtualWorld)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setVirtualWorld(virtualWorld); + return true; +} + +OMP_CAPI(NPC_GetVirtualWorld, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0); + return npc_->getVirtualWorld(); +} + +OMP_CAPI(NPC_SetInterior, bool(objectPtr npc, int interior)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setInterior(interior); + return true; +} + +OMP_CAPI(NPC_GetInterior, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0); + return npc_->getInterior(); +} + +OMP_CAPI(NPC_Move, bool(objectPtr npc, float x, float y, float z, int moveType, float moveSpeed, float stopRange)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->move(Vector3(x, y, z), NPCMoveType(moveType), moveSpeed, stopRange); +} + +OMP_CAPI(NPC_MoveToPlayer, bool(objectPtr npc, objectPtr player, int moveType, float moveSpeed, float stopRange, int posCheckUpdateDelay, bool autoRestart)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->moveToPlayer(*player_, NPCMoveType(moveType), moveSpeed, stopRange, Milliseconds(posCheckUpdateDelay), autoRestart); +} + +OMP_CAPI(NPC_StopMove, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->stopMove(); + return true; +} + +OMP_CAPI(NPC_IsMoving, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->isMoving(); +} + +OMP_CAPI(NPC_SetSkin, bool(objectPtr npc, int model)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setSkin(model); + return true; +} + +OMP_CAPI(NPC_IsStreamedIn, bool(objectPtr npc, objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->isStreamedInForPlayer(*player_); +} + +OMP_CAPI(NPC_IsAnyStreamedIn, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + auto streamedIn = npc_->streamedForPlayers(); + return streamedIn.size() > 1; +} + +OMP_CAPI(NPC_GetAll, int(int* npcsArr, int maxNPCs)) +{ + COMPONENT_CHECK_RET(npcs, 0); + if (!npcsArr) + { + return 0; + } + + int index = 0; + for (INPC* npc : *npcs) + { + if (index >= maxNPCs) + { + break; + } + npcsArr[index] = npc->getID(); + index++; + } + return index; +} + +OMP_CAPI(NPC_SetHealth, bool(objectPtr npc, float health)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setHealth(health); + return true; +} + +OMP_CAPI(NPC_GetHealth, float(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0.0f); + return npc_->getHealth(); +} + +OMP_CAPI(NPC_SetArmour, bool(objectPtr npc, float armour)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setArmour(armour); + return true; +} + +OMP_CAPI(NPC_GetArmour, float(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0.0f); + return npc_->getArmour(); +} + +OMP_CAPI(NPC_IsDead, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->isDead(); +} + +OMP_CAPI(NPC_SetInvulnerable, bool(objectPtr npc, bool toggle)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setInvulnerable(toggle); + return true; +} + +OMP_CAPI(NPC_IsInvulnerable, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->isInvulnerable(); +} + +OMP_CAPI(NPC_SetWeapon, bool(objectPtr npc, uint8_t weapon)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setWeapon(weapon); + return true; +} + +OMP_CAPI(NPC_GetWeapon, uint8_t(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0); + return npc_->getWeapon(); +} + +OMP_CAPI(NPC_SetAmmo, bool(objectPtr npc, int ammo)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setAmmo(ammo); + return true; +} + +OMP_CAPI(NPC_GetAmmo, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0); + return npc_->getAmmo(); +} + +OMP_CAPI(NPC_SetAmmoInClip, bool(objectPtr npc, int ammo)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setAmmoInClip(ammo); + return true; +} + +OMP_CAPI(NPC_GetAmmoInClip, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0); + return npc_->getAmmoInClip(); +} + +OMP_CAPI(NPC_EnableReloading, bool(objectPtr npc, bool enable)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->enableReloading(enable); + return true; +} + +OMP_CAPI(NPC_IsReloadEnabled, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->isReloadEnabled(); +} + +OMP_CAPI(NPC_IsReloading, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->isReloading(); +} + +OMP_CAPI(NPC_EnableInfiniteAmmo, bool(objectPtr npc, bool enable)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->enableInfiniteAmmo(enable); + return true; +} + +OMP_CAPI(NPC_IsInfiniteAmmoEnabled, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->isInfiniteAmmoEnabled(); +} + +OMP_CAPI(NPC_GetWeaponState, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0); + return int(npc_->getWeaponState()); +} + +OMP_CAPI(NPC_SetKeys, bool(objectPtr npc, uint16_t upAndDown, uint16_t leftAndRight, uint16_t keys)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setKeys(upAndDown, leftAndRight, keys); + return true; +} + +OMP_CAPI(NPC_GetKeys, bool(objectPtr npc, uint16_t* upAndDown, uint16_t* leftAndRight, uint16_t* keys)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + uint16_t ud, lr, k; + npc_->getKeys(ud, lr, k); + if (upAndDown) + { + *upAndDown = ud; + } + if (leftAndRight) + { + *leftAndRight = lr; + } + if (keys) + { + *keys = k; + } + return true; +} + +OMP_CAPI(NPC_SetWeaponSkillLevel, bool(objectPtr npc, uint8_t skill, int level)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setWeaponSkillLevel(PlayerWeaponSkill(skill), level); + return true; +} + +OMP_CAPI(NPC_GetWeaponSkillLevel, int(objectPtr npc, int skill)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0); + return npc_->getWeaponSkillLevel(PlayerWeaponSkill(skill)); +} + +OMP_CAPI(NPC_MeleeAttack, bool(objectPtr npc, int time, bool secondaryAttack)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->meleeAttack(time, secondaryAttack); + return true; +} + +OMP_CAPI(NPC_StopMeleeAttack, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->stopMeleeAttack(); + return true; +} + +OMP_CAPI(NPC_IsMeleeAttacking, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->isMeleeAttacking(); +} + +OMP_CAPI(NPC_SetFightingStyle, bool(objectPtr npc, int style)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setFightingStyle(PlayerFightingStyle(style)); + return true; +} + +OMP_CAPI(NPC_GetFightingStyle, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0); + return int(npc_->getFightingStyle()); +} + +OMP_CAPI(NPC_Shoot, bool(objectPtr npc, uint8_t weapon, int hitId, int hitType, float endX, float endY, float endZ, float offsetX, float offsetY, float offsetZ, bool isHit, uint8_t checkInBetweenFlags)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->shoot(hitId, PlayerBulletHitType(hitType), weapon, Vector3(endX, endY, endZ), Vector3(offsetX, offsetY, offsetZ), isHit, EntityCheckType(checkInBetweenFlags)); + return true; +} + +OMP_CAPI(NPC_IsShooting, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->isShooting(); +} + +OMP_CAPI(NPC_AimAt, bool(objectPtr npc, float x, float y, float z, bool shoot, int shootDelay, bool updateAngle, float offsetFromX, float offsetFromY, float offsetFromZ, uint8_t checkInBetweenFlags)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->aimAt(Vector3(x, y, z), shoot, shootDelay, updateAngle, Vector3(offsetFromX, offsetFromY, offsetFromZ), EntityCheckType(checkInBetweenFlags)); + return true; +} + +OMP_CAPI(NPC_AimAtPlayer, bool(objectPtr npc, objectPtr atPlayer, bool shoot, int shootDelay, bool updateAngle, float offsetX, float offsetY, float offsetZ, float offsetFromX, float offsetFromY, float offsetFromZ, uint8_t checkInBetweenFlags)) +{ + POOL_ENTITY_RET(players, IPlayer, atPlayer, atPlayer_, false); + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->aimAtPlayer(*atPlayer_, shoot, shootDelay, updateAngle, Vector3(offsetX, offsetY, offsetZ), Vector3(offsetFromX, offsetFromY, offsetFromZ), EntityCheckType(checkInBetweenFlags)); + return true; +} + +OMP_CAPI(NPC_StopAim, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->stopAim(); + return true; +} + +OMP_CAPI(NPC_IsAiming, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->isAiming(); +} + +OMP_CAPI(NPC_IsAimingAtPlayer, bool(objectPtr npc, objectPtr atPlayer)) +{ + POOL_ENTITY_RET(players, IPlayer, atPlayer, atPlayer_, false); + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->isAimingAtPlayer(*atPlayer_); +} + +OMP_CAPI(NPC_SetWeaponAccuracy, bool(objectPtr npc, int weapon, float accuracy)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setWeaponAccuracy(weapon, accuracy); + return true; +} + +OMP_CAPI(NPC_GetWeaponAccuracy, float(objectPtr npc, int weapon)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0.0f); + return npc_->getWeaponAccuracy(weapon); +} + +OMP_CAPI(NPC_SetWeaponReloadTime, bool(objectPtr npc, int weapon, int time)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setWeaponReloadTime(weapon, time); + return true; +} + +OMP_CAPI(NPC_GetWeaponReloadTime, int(objectPtr npc, int weapon)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0); + return npc_->getWeaponReloadTime(weapon); +} + +OMP_CAPI(NPC_GetWeaponActualReloadTime, int(objectPtr npc, int weapon)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0); + return npc_->getWeaponActualReloadTime(weapon); +} + +OMP_CAPI(NPC_SetWeaponShootTime, bool(objectPtr npc, int weapon, int time)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setWeaponShootTime(weapon, time); + return true; +} + +OMP_CAPI(NPC_GetWeaponShootTime, int(objectPtr npc, int weapon)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0); + return npc_->getWeaponShootTime(weapon); +} + +OMP_CAPI(NPC_SetWeaponClipSize, bool(objectPtr npc, int weapon, int size)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setWeaponClipSize(weapon, size); + return true; +} + +OMP_CAPI(NPC_GetWeaponClipSize, int(objectPtr npc, int weapon)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0); + return npc_->getWeaponClipSize(weapon); +} + +OMP_CAPI(NPC_GetWeaponActualClipSize, int(objectPtr npc, int weapon)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0); + return npc_->getWeaponActualClipSize(weapon); +} + +OMP_CAPI(NPC_EnterVehicle, bool(objectPtr npc, objectPtr vehicle, int seatId, int moveType)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->enterVehicle(*vehicle_, seatId, NPCMoveType(moveType)); + return true; +} + +OMP_CAPI(NPC_ExitVehicle, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->exitVehicle(); + return true; +} + +OMP_CAPI(NPC_PutInVehicle, bool(objectPtr npc, objectPtr vehicle, int seatId)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->putInVehicle(*vehicle_, seatId); + return true; +} + +OMP_CAPI(NPC_RemoveFromVehicle, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->removeFromVehicle(); + return true; +} + +OMP_CAPI(NPC_GetVehicle, objectPtr(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, nullptr); + return npc_->getVehicle(); +} + +OMP_CAPI(NPC_GetVehicleID, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, INVALID_VEHICLE_ID); + auto vehicle = npc_->getVehicle(); + if (vehicle) + { + return vehicle->getID(); + } + return INVALID_VEHICLE_ID; +} + +OMP_CAPI(NPC_GetEnteringVehicle, objectPtr(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, nullptr); + return npc_->getEnteringVehicle(); +} + +OMP_CAPI(NPC_GetEnteringVehicleID, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, INVALID_VEHICLE_ID); + auto vehicle = npc_->getEnteringVehicle(); + if (vehicle) + { + return vehicle->getID(); + } + return INVALID_VEHICLE_ID; +} + +OMP_CAPI(NPC_GetVehicleSeat, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, SEAT_NONE); + return npc_->getVehicleSeat(); +} + +OMP_CAPI(NPC_GetEnteringVehicleSeat, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, SEAT_NONE); + return npc_->getEnteringVehicleSeat(); +} + +OMP_CAPI(NPC_IsEnteringVehicle, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->getEnteringVehicle() && npc_->getVehicleSeat() != SEAT_NONE; +} + +OMP_CAPI(NPC_UseVehicleSiren, bool(objectPtr npc, bool use)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->useVehicleSiren(use); + return true; +} + +OMP_CAPI(NPC_IsVehicleSirenUsed, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->isVehicleSirenUsed(); +} + +OMP_CAPI(NPC_SetVehicleHealth, bool(objectPtr npc, float health)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setVehicleHealth(health); + return true; +} + +OMP_CAPI(NPC_GetVehicleHealth, float(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0.0f); + return npc_->getVehicleHealth(); +} + +OMP_CAPI(NPC_SetVehicleHydraThrusters, bool(objectPtr npc, int direction)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setVehicleHydraThrusters(direction); + return true; +} + +OMP_CAPI(NPC_GetVehicleHydraThrusters, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0); + return npc_->getVehicleHydraThrusters(); +} + +OMP_CAPI(NPC_SetVehicleGearState, bool(objectPtr npc, int gearState)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setVehicleGearState(gearState); + return true; +} + +OMP_CAPI(NPC_GetVehicleGearState, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0); + return npc_->getVehicleGearState(); +} + +OMP_CAPI(NPC_SetVehicleTrainSpeed, bool(objectPtr npc, float speed)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setVehicleTrainSpeed(speed); + return true; +} + +OMP_CAPI(NPC_GetVehicleTrainSpeed, float(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0.0f); + return npc_->getVehicleTrainSpeed(); +} + +OMP_CAPI(NPC_CreatePath, int()) +{ + COMPONENT_CHECK_RET(npcs, -1); + return npcs->createPath(); +} + +OMP_CAPI(NPC_DestroyPath, bool(int pathId)) +{ + COMPONENT_CHECK_RET(npcs, false); + return npcs->destroyPath(pathId); +} + +OMP_CAPI(NPC_DestroyAllPath, bool()) +{ + COMPONENT_CHECK_RET(npcs, false); + npcs->destroyAllPaths(); + return true; +} + +OMP_CAPI(NPC_GetPathCount, int()) +{ + COMPONENT_CHECK_RET(npcs, -1); + return npcs->getPathCount(); +} + +OMP_CAPI(NPC_AddPointToPath, bool(int pathId, float x, float y, float z, float stopRange)) +{ + COMPONENT_CHECK_RET(npcs, false); + return npcs->addPointToPath(pathId, Vector3(x, y, z), stopRange); +} + +OMP_CAPI(NPC_RemovePointFromPath, bool(int pathId, int pointIndex)) +{ + COMPONENT_CHECK_RET(npcs, false); + return npcs->removePointFromPath(pathId, pointIndex); +} + +OMP_CAPI(NPC_ClearPath, bool(int pathId)) +{ + COMPONENT_CHECK_RET(npcs, false); + return npcs->clearPath(pathId); +} + +OMP_CAPI(NPC_GetPathPointCount, int(int pathId)) +{ + COMPONENT_CHECK_RET(npcs, -1); + return npcs->getPathPointCount(pathId); +} + +OMP_CAPI(NPC_GetPathPoint, bool(int pathId, int pointIndex, float* x, float* y, float* z, float* stopRange)) +{ + COMPONENT_CHECK_RET(npcs, false); + Vector3 position; + float sr; + if (npcs->getPathPoint(pathId, pointIndex, position, sr)) + { + *x = position.x; + *y = position.y; + *z = position.z; + *stopRange = sr; + return true; + } + return false; +} + +OMP_CAPI(NPC_GetCurrentPathPointIndex, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, -1); + return npc_->getCurrentPathPointIndex(); +} + +OMP_CAPI(NPC_IsValidPath, bool(int pathId)) +{ + COMPONENT_CHECK_RET(npcs, false); + return npcs->isValidPath(pathId); +} + +OMP_CAPI(NPC_HasPathPointInRange, bool(int pathId, float x, float y, float z, float radius)) +{ + COMPONENT_CHECK_RET(npcs, false); + return npcs->hasPathPointInRange(pathId, Vector3(x, y, z), radius); +} + +OMP_CAPI(NPC_MoveByPath, bool(objectPtr npc, int pathId, int moveType, float moveSpeed, bool reverse)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->moveByPath(pathId, NPCMoveType(moveType), moveSpeed, reverse); +} + +OMP_CAPI(NPC_ResetAnimation, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->resetAnimation(); + return true; +} + +OMP_CAPI(NPC_SetAnimation, bool(objectPtr npc, int animationId, float delta, bool loop, bool lockX, bool lockY, bool freeze, int time)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setAnimation(animationId, delta, loop, lockX, lockY, freeze, time); + return true; +} + +OMP_CAPI(NPC_GetAnimation, bool(objectPtr npc, int* animationId, float* delta, bool* loop, bool* lockX, bool* lockY, bool* freeze, int* time)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + int aId; + float d; + bool l, lx, ly, f; + int t; + npc_->getAnimation(aId, d, l, lx, ly, f, t); + *animationId = aId; + *delta = d; + *loop = l; + *lockX = lx; + *lockY = ly; + *freeze = f; + *time = t; + return true; +} + +OMP_CAPI(NPC_ApplyAnimation, bool(objectPtr npc, const char* animlib, const char* animname, float delta, bool loop, bool lockX, bool lockY, bool freeze, int time)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + if (!animlib || !animname) + { + return false; + } + const AnimationData animationData(delta, loop, lockX, lockY, freeze, time, animlib, animname); + npc_->applyAnimation(animationData); + return true; +} + +OMP_CAPI(NPC_ClearAnimations, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->clearAnimations(); + return true; +} + +OMP_CAPI(NPC_SetSpecialAction, bool(objectPtr npc, int action)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->setSpecialAction(PlayerSpecialAction(action)); + return true; +} + +OMP_CAPI(NPC_GetSpecialAction, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, 0); + return npc_->getSpecialAction(); +} + +OMP_CAPI(NPC_StartPlayback, bool(objectPtr npc, const char* recordName, bool autoUnload, float startPosX, float startPosY, float startPosZ, float startRotX, float startRotY, float startRotZ)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + if (!recordName) + { + return false; + } + return npc_->startPlayback(recordName, autoUnload, Vector3(startPosX, startPosY, startPosZ), Vector3(startRotX, startRotY, startRotZ)); +} + +OMP_CAPI(NPC_StartPlaybackEx, bool(objectPtr npc, int recordId, bool autoUnload, float startPosX, float startPosY, float startPosZ, float startRotX, float startRotY, float startRotZ)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->startPlayback(recordId, autoUnload, Vector3(startPosX, startPosY, startPosZ), Vector3(startRotX, startRotY, startRotZ)); +} + +OMP_CAPI(NPC_StopPlayback, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->stopPlayback(); + return true; +} + +OMP_CAPI(NPC_PausePlayback, bool(objectPtr npc, bool paused)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->pausePlayback(paused); + return true; +} + +OMP_CAPI(NPC_IsPlayingPlayback, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->isPlayingPlayback(); +} + +OMP_CAPI(NPC_IsPlaybackPaused, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->isPlaybackPaused(); +} + +OMP_CAPI(NPC_LoadRecord, int(const char* filePath)) +{ + COMPONENT_CHECK_RET(npcs, -1); + if (filePath) + { + return npcs->loadRecord(filePath); + } + return -1; +} + +OMP_CAPI(NPC_UnloadRecord, bool(int recordId)) +{ + COMPONENT_CHECK_RET(npcs, false); + return npcs->unloadRecord(recordId); +} + +OMP_CAPI(NPC_IsValidRecord, bool(int recordId)) +{ + COMPONENT_CHECK_RET(npcs, false); + return npcs->isValidRecord(recordId); +} + +OMP_CAPI(NPC_GetRecordCount, int()) +{ + COMPONENT_CHECK_RET(npcs, 0); + return static_cast(npcs->getRecordCount()); +} + +OMP_CAPI(NPC_UnloadAllRecords, bool()) +{ + COMPONENT_CHECK_RET(npcs, false); + npcs->unloadAllRecords(); + return true; +} + +OMP_CAPI(NPC_OpenNode, bool(int nodeId)) +{ + COMPONENT_CHECK_RET(npcs, false); + return npcs->openNode(nodeId); +} + +OMP_CAPI(NPC_CloseNode, bool(int nodeId)) +{ + COMPONENT_CHECK_RET(npcs, false); + npcs->closeNode(nodeId); + return true; +} + +OMP_CAPI(NPC_IsNodeOpen, bool(int nodeId)) +{ + COMPONENT_CHECK_RET(npcs, false); + return npcs->isNodeOpen(nodeId); +} + +OMP_CAPI(NPC_GetNodeType, int(int nodeId)) +{ + COMPONENT_CHECK_RET(npcs, 0); + return npcs->getNodeType(nodeId); +} + +OMP_CAPI(NPC_SetNodePoint, bool(int nodeId, int pointId)) +{ + COMPONENT_CHECK_RET(npcs, false); + return npcs->setNodePoint(nodeId, static_cast(pointId)); +} + +OMP_CAPI(NPC_GetNodePointPosition, bool(int nodeId, float* x, float* y, float* z)) +{ + COMPONENT_CHECK_RET(npcs, false); + Vector3 position; + if (npcs->getNodePointPosition(nodeId, position)) + { + *x = position.x; + *y = position.y; + *z = position.z; + return true; + } + return false; +} + +OMP_CAPI(NPC_GetNodePointCount, int(int nodeId)) +{ + COMPONENT_CHECK_RET(npcs, 0); + return npcs->getNodePointCount(nodeId); +} + +OMP_CAPI(NPC_GetNodeInfo, bool(int nodeId, uint32_t* vehicleNodes, uint32_t* pedNodes, uint32_t* naviNodes)) +{ + COMPONENT_CHECK_RET(npcs, false); + uint32_t vehNodes, pedNodes32, naviNodes32; + if (npcs->getNodeInfo(nodeId, vehNodes, pedNodes32, naviNodes32)) + { + *vehicleNodes = vehNodes; + *pedNodes = pedNodes32; + *naviNodes = naviNodes32; + return true; + } + return false; +} + +OMP_CAPI(NPC_PlayNode, bool(objectPtr npc, int nodeId, int moveType, float moveSpeed, float radius, bool setAngle)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->playNode(nodeId, static_cast(moveType), moveSpeed, radius, setAngle); +} + +OMP_CAPI(NPC_StopPlayingNode, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->stopPlayingNode(); + return true; +} + +OMP_CAPI(NPC_PausePlayingNode, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->pausePlayingNode(); + return true; +} + +OMP_CAPI(NPC_ResumePlayingNode, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->resumePlayingNode(); + return true; +} + +OMP_CAPI(NPC_IsPlayingNodePaused, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->isPlayingNodePaused(); +} + +OMP_CAPI(NPC_IsPlayingNode, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->isPlayingNode(); +} + +OMP_CAPI(NPC_ChangeNode, int(objectPtr npc, int nodeId, int linkId)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, -1); + return static_cast(npc_->changeNode(nodeId, static_cast(linkId))); +} + +OMP_CAPI(NPC_UpdateNodePoint, bool(objectPtr npc, int pointId)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + return npc_->updateNodePoint(static_cast(pointId)); +} + +OMP_CAPI(NPC_SetSurfingOffset, bool(objectPtr npc, float x, float y, float z)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + auto data = npc_->getSurfingData(); + data.offset = Vector3(x, y, z); + npc_->setSurfingData(data); + return true; +} + +OMP_CAPI(NPC_GetSurfingOffset, bool(objectPtr npc, float* x, float* y, float* z)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + auto data = npc_->getSurfingData(); + *x = data.offset.x; + *y = data.offset.y; + *z = data.offset.z; + return true; +} + +OMP_CAPI(NPC_SetSurfingVehicle, bool(objectPtr npc, objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + auto data = npc_->getSurfingData(); + data.ID = vehicle_->getID(); + data.type = PlayerSurfingData::Type::Vehicle; + npc_->setSurfingData(data); + return true; +} + +OMP_CAPI(NPC_GetSurfingVehicle, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, INVALID_VEHICLE_ID); + auto data = npc_->getSurfingData(); + auto vehicles = ComponentManager::Get()->vehicles; + if (data.type != PlayerSurfingData::Type::Vehicle || vehicles == nullptr) + { + return INVALID_VEHICLE_ID; + } + return data.ID; +} + +OMP_CAPI(NPC_SetSurfingObject, bool(objectPtr npc, objectPtr object)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + auto data = npc_->getSurfingData(); + data.ID = object_->getID(); + data.type = PlayerSurfingData::Type::Object; + npc_->setSurfingData(data); + return true; +} + +OMP_CAPI(NPC_GetSurfingObject, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, INVALID_OBJECT_ID); + auto data = npc_->getSurfingData(); + auto objects = ComponentManager::Get()->objects; + if (data.type != PlayerSurfingData::Type::Object || objects == nullptr) + { + return INVALID_OBJECT_ID; + } + return data.ID; +} + +OMP_CAPI(NPC_SetSurfingPlayerObject, bool(objectPtr npc, objectPtr player, int objectId)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + auto playerObjects = queryExtension(*player_); + if (playerObjects) + { + auto object = playerObjects->get(objectId); + if (object) + { + auto data = npc_->getSurfingData(); + data.ID = object->getID(); + data.type = PlayerSurfingData::Type::PlayerObject; + npc_->setSurfingData(data); + return true; + } + } + return false; +} + +OMP_CAPI(NPC_GetSurfingPlayerObject, int(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, INVALID_OBJECT_ID); + auto data = npc_->getSurfingData(); + auto objects = ComponentManager::Get()->objects; + if (data.type != PlayerSurfingData::Type::PlayerObject || objects == nullptr) + { + return INVALID_OBJECT_ID; + } + return data.ID; +} + +OMP_CAPI(NPC_ResetSurfingData, bool(objectPtr npc)) +{ + POOL_ENTITY_RET(npcs, INPC, npc, npc_, false); + npc_->resetSurfingData(); + return true; +} diff --git a/Server/Components/CAPI/Impl/NPCs/Events.hpp b/Server/Components/CAPI/Impl/NPCs/Events.hpp new file mode 100644 index 000000000..c7a227450 --- /dev/null +++ b/Server/Components/CAPI/Impl/NPCs/Events.hpp @@ -0,0 +1,131 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#pragma once +#include "../ComponentManager.hpp" +#include "sdk.hpp" + +template +struct NPCEvents : public NPCEventHandler, public Singleton> +{ + void onNPCFinishMove(INPC& npc) override + { + ComponentManager::Get()->CallEvent("onNPCFinishMove", EventReturnHandler::None, &npc); + } + + void onNPCCreate(INPC& npc) override + { + ComponentManager::Get()->CallEvent("onNPCCreate", EventReturnHandler::None, &npc); + } + + void onNPCDestroy(INPC& npc) override + { + ComponentManager::Get()->CallEvent("onNPCDestroy", EventReturnHandler::None, &npc); + } + + void onNPCWeaponStateChange(INPC& npc, PlayerWeaponState newState, PlayerWeaponState oldState) override + { + ComponentManager::Get()->CallEvent("onNPCWeaponStateChange", EventReturnHandler::None, &npc, int(newState), int(oldState)); + } + + bool onNPCTakeDamage(INPC& npc, IPlayer& damager, float damage, uint8_t weapon, BodyPart bodyPart) override + { + return ComponentManager::Get()->CallEvent("onNPCTakeDamage", EventReturnHandler::StopAtFalse, &npc, &damager, damage, int(weapon), int(bodyPart)); + } + + bool onNPCGiveDamage(INPC& npc, IPlayer& damaged, float damage, uint8_t weapon, BodyPart bodyPart) override + { + return ComponentManager::Get()->CallEvent("onNPCGiveDamage", EventReturnHandler::StopAtFalse, &npc, &damaged, damage, int(weapon), int(bodyPart)); + } + + void onNPCDeath(INPC& npc, IPlayer* killer, int reason) override + { + ComponentManager::Get()->CallEvent("onNPCDeath", EventReturnHandler::None, &npc, killer, reason); + } + + void onNPCSpawn(INPC& npc) override + { + ComponentManager::Get()->CallEvent("onNPCSpawn", EventReturnHandler::None, &npc); + } + + void onNPCRespawn(INPC& npc) override + { + ComponentManager::Get()->CallEvent("onNPCRespawn", EventReturnHandler::None, &npc); + } + + void onNPCPlaybackStart(INPC& npc, int recordId) override + { + ComponentManager::Get()->CallEvent("onNPCPlaybackStart", EventReturnHandler::None, &npc, recordId); + } + + void onNPCPlaybackEnd(INPC& npc, int recordId) override + { + ComponentManager::Get()->CallEvent("onNPCPlaybackEnd", EventReturnHandler::None, &npc, recordId); + } + + bool onNPCShotMissed(INPC& npc, const PlayerBulletData& bulletData) override + { + return ComponentManager::Get()->CallEvent("onNPCShotMissed", EventReturnHandler::StopAtFalse, &npc, + int(bulletData.weapon), bulletData.offset.x, bulletData.offset.y, bulletData.offset.z); + } + + bool onNPCShotPlayer(INPC& npc, IPlayer& target, const PlayerBulletData& bulletData) override + { + return ComponentManager::Get()->CallEvent("onNPCShotPlayer", EventReturnHandler::StopAtFalse, &npc, &target, + int(bulletData.weapon), bulletData.offset.x, bulletData.offset.y, bulletData.offset.z); + } + + bool onNPCShotNPC(INPC& npc, INPC& target, const PlayerBulletData& bulletData) override + { + return ComponentManager::Get()->CallEvent("onNPCShotNPC", EventReturnHandler::StopAtFalse, &npc, &target, + int(bulletData.weapon), bulletData.offset.x, bulletData.offset.y, bulletData.offset.z); + } + + bool onNPCShotVehicle(INPC& npc, IVehicle& target, const PlayerBulletData& bulletData) override + { + return ComponentManager::Get()->CallEvent("onNPCShotVehicle", EventReturnHandler::StopAtFalse, &npc, &target, + int(bulletData.weapon), bulletData.offset.x, bulletData.offset.y, bulletData.offset.z); + } + + bool onNPCShotObject(INPC& npc, IObject& target, const PlayerBulletData& bulletData) override + { + return ComponentManager::Get()->CallEvent("onNPCShotObject", EventReturnHandler::StopAtFalse, &npc, &target, + int(bulletData.weapon), bulletData.offset.x, bulletData.offset.y, bulletData.offset.z); + } + + bool onNPCShotPlayerObject(INPC& npc, IPlayerObject& target, const PlayerBulletData& bulletData) override + { + return ComponentManager::Get()->CallEvent("onNPCShotPlayerObject", EventReturnHandler::StopAtFalse, &npc, &target, + int(bulletData.weapon), bulletData.offset.x, bulletData.offset.y, bulletData.offset.z); + } + + void onNPCFinishNodePoint(INPC& npc, int nodeId, uint16_t pointId) override + { + ComponentManager::Get()->CallEvent("onNPCFinishNodePoint", EventReturnHandler::None, &npc, nodeId, int(pointId)); + } + + void onNPCFinishNode(INPC& npc, int nodeId) override + { + ComponentManager::Get()->CallEvent("onNPCFinishNode", EventReturnHandler::None, &npc, nodeId); + } + + bool onNPCChangeNode(INPC& npc, int newNodeId, int oldNodeId) override + { + return ComponentManager::Get()->CallEvent("onNPCChangeNode", EventReturnHandler::StopAtFalse, &npc, newNodeId, oldNodeId); + } + + void onNPCFinishMovePath(INPC& npc, int pathId) override + { + ComponentManager::Get()->CallEvent("onNPCFinishMovePath", EventReturnHandler::None, &npc, pathId); + } + + void onNPCFinishMovePathPoint(INPC& npc, int pathId, int pointId) override + { + ComponentManager::Get()->CallEvent("onNPCFinishMovePathPoint", EventReturnHandler::None, &npc, pathId, pointId); + } +}; diff --git a/Server/Components/CAPI/Impl/Objects/APIs.cpp b/Server/Components/CAPI/Impl/Objects/APIs.cpp new file mode 100644 index 000000000..c869f7c29 --- /dev/null +++ b/Server/Components/CAPI/Impl/Objects/APIs.cpp @@ -0,0 +1,830 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" + +OMP_CAPI(Object_Create, objectPtr(int modelid, float x, float y, float z, float rotationX, float rotationY, float rotationZ, float drawDistance, int* id)) +{ + IObjectsComponent* component = ComponentManager::Get()->objects; + if (component) + { + IObject* object = component->create(modelid, { x, y, z }, { rotationX, rotationY, rotationZ }, drawDistance); + if (object) + { + *id = object->getID(); + return object; + } + } + return nullptr; +} + +OMP_CAPI(Object_Destroy, bool(objectPtr object)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + ComponentManager::Get()->objects->release(object_->getID()); + return true; +} + +OMP_CAPI(Object_FromID, objectPtr(int objectid)) +{ + IObjectsComponent* component = ComponentManager::Get()->objects; + if (component) + { + return component->get(objectid); + } + return nullptr; +} + +OMP_CAPI(Object_GetID, int(objectPtr object)) +{ + POOL_ENTITY_RET(menus, IObject, object, object_, INVALID_OBJECT_ID); + return object_->getID(); +} + +OMP_CAPI(Object_AttachToVehicle, bool(objectPtr object, objectPtr vehicle, float offsetX, float offsetY, float offsetZ, float rotationX, float rotationY, float rotationZ)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + if (vehicle) + { + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + object_->attachToVehicle(*vehicle_, { offsetX, offsetY, offsetZ }, { rotationX, rotationY, rotationZ }); + } + else + { + object_->resetAttachment(); + } + return true; +} + +OMP_CAPI(Object_AttachToObject, bool(objectPtr object, objectPtr objAttachedTo, float offsetX, float offsetY, float offsetZ, float rotationX, float rotationY, float rotationZ, bool syncRotation)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + if (objAttachedTo) + { + POOL_ENTITY_RET(objects, IObject, objAttachedTo, objAttachedTo_, false); + object_->attachToObject(*objAttachedTo_, { offsetX, offsetY, offsetZ }, { rotationX, rotationY, rotationZ }, syncRotation); + } + else + { + object_->resetAttachment(); + } + return true; +} + +OMP_CAPI(Object_AttachToPlayer, bool(objectPtr object, objectPtr player, float offsetX, float offsetY, float offsetZ, float rotationX, float rotationY, float rotationZ)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + if (player) + { + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + object_->attachToPlayer(*player_, { offsetX, offsetY, offsetZ }, { rotationX, rotationY, rotationZ }); + } + else + { + object_->resetAttachment(); + } + return true; +} + +OMP_CAPI(Object_SetPos, bool(objectPtr object, float x, float y, float z)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + object_->setPosition({ x, y, z }); + return true; +} + +OMP_CAPI(Object_GetPos, bool(objectPtr object, float* x, float* y, float* z)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + const Vector3& position = object_->getPosition(); + *x = position.x; + *y = position.y; + *z = position.z; + return true; +} + +OMP_CAPI(Object_SetRot, bool(objectPtr object, float rotationX, float rotationY, float rotationZ)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + object_->setRotation({ rotationX, rotationY, rotationZ }); + return true; +} + +OMP_CAPI(Object_GetRot, bool(objectPtr object, float* rotationX, float* rotationY, float* rotationZ)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + const Vector3& rotation = object_->getRotation().ToEuler(); + *rotationX = rotation.x; + *rotationY = rotation.y; + *rotationZ = rotation.z; + return true; +} + +OMP_CAPI(Object_GetModel, int(objectPtr object)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, 0); + int model = object_->getModel(); + return model; +} + +OMP_CAPI(Object_SetNoCameraCollision, bool(objectPtr object)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + object_->setCameraCollision(false); + return true; +} + +OMP_CAPI(Object_IsValid, bool(objectPtr object)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + if (!objects->get(object_->getID())) + return false; + return true; +} + +OMP_CAPI(Object_Move, int(objectPtr object, float x, float y, float z, float speed, float rotationX, float rotationY, float rotationZ)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, 0); + ObjectMoveData data; + data.targetPos = { x, y, z }; + data.targetRot = { rotationX, rotationY, rotationZ }; + data.speed = speed; + + object_->move(data); + + float estimatedTime = (glm::length(data.targetPos - object_->getPosition()) / speed) * 1000.0f; + int time = static_cast(estimatedTime); + return time; +} + +OMP_CAPI(Object_Stop, bool(objectPtr object)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + object_->stop(); + return true; +} + +OMP_CAPI(Object_IsMoving, bool(objectPtr object)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + bool moving = object_->isMoving(); + return moving; +} + +OMP_CAPI(Object_BeginEditing, bool(objectPtr player, objectPtr object)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerObjectData* data = queryExtension(player_); + if (!data) + { + return false; + } + + POOL_ENTITY_RET(objects, IObject, object, object_, false); + data->beginEditing(*object_); + return true; +} + +OMP_CAPI(Object_BeginSelecting, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerObjectData* data = queryExtension(player_); + if (!data) + { + return false; + } + + data->beginSelecting(); + return true; +} + +OMP_CAPI(Object_EndEditing, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerObjectData* data = queryExtension(player_); + if (!data) + { + return false; + } + + data->endEditing(); + return true; +} + +OMP_CAPI(Object_SetMaterial, bool(objectPtr object, int materialIndex, int modelId, StringCharPtr textureLibrary, StringCharPtr textureName, uint32_t materialColor)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + object_->setMaterial(materialIndex, modelId, textureLibrary, textureName, Colour::FromARGB(materialColor)); + return true; +} + +OMP_CAPI(Object_SetMaterialText, bool(objectPtr object, StringCharPtr text, int materialIndex, int materialSize, StringCharPtr fontface, int fontsize, bool bold, uint32_t fontColor, uint32_t backgroundColor, int textalignment)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + object_->setMaterialText(materialIndex, text, ObjectMaterialSize(materialSize), fontface, fontsize, bold, Colour::FromARGB(fontColor), Colour::FromARGB(backgroundColor), ObjectMaterialTextAlign(textalignment)); + return true; +} + +OMP_CAPI(Object_SetDefaultCameraCollision, bool(bool disable)) +{ + IObjectsComponent* objects = ComponentManager::Get()->objects; + if (objects) + { + objects->setDefaultCameraCollision(!disable); + return true; + } + return false; +} + +OMP_CAPI(Object_GetDrawDistance, float(objectPtr object)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, 0.0f); + float distance = object_->getDrawDistance(); + return distance; +} + +OMP_CAPI(Object_GetMoveSpeed, float(objectPtr object)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, 0.0f); + float speed = object_->getMovingData().speed; + return speed; +} + +OMP_CAPI(Object_GetMovingTargetPos, bool(objectPtr object, float* targetX, float* targetY, float* targetZ)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + const ObjectMoveData& data = object_->getMovingData(); + *targetX = data.targetPos.x; + *targetY = data.targetPos.y; + *targetZ = data.targetPos.z; + return true; +} + +OMP_CAPI(Object_GetMovingTargetRot, bool(objectPtr object, float* rotationX, float* rotationY, float* rotationZ)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + const ObjectMoveData& data = object_->getMovingData(); + *rotationX = data.targetRot.x; + *rotationY = data.targetRot.y; + *rotationZ = data.targetRot.z; + return true; +} + +OMP_CAPI(Object_GetAttachedData, bool(objectPtr object, int* parentVehicle, int* parentObject, int* parentPlayer)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + const ObjectAttachmentData data = object_->getAttachmentData(); + *parentVehicle = INVALID_VEHICLE_ID; + *parentObject = INVALID_OBJECT_ID; + *parentPlayer = INVALID_PLAYER_ID; + + if (data.type == ObjectAttachmentData::Type::Object) + { + *parentObject = data.ID; + } + else if (data.type == ObjectAttachmentData::Type::Player) + { + *parentPlayer = data.ID; + } + else if (data.type == ObjectAttachmentData::Type::Vehicle) + { + *parentVehicle = data.ID; + } + return true; +} + +OMP_CAPI(Object_GetAttachedOffset, bool(objectPtr object, float* offsetX, float* offsetY, float* offsetZ, float* rotationX, float* rotationY, float* rotationZ)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + const ObjectAttachmentData data = object_->getAttachmentData(); + *offsetX = data.offset.x; + *offsetY = data.offset.y; + *offsetZ = data.offset.z; + *rotationX = data.rotation.x; + *rotationY = data.rotation.y; + *rotationZ = data.rotation.z; + return true; +} + +OMP_CAPI(Object_GetSyncRotation, bool(objectPtr object)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, 0.0f); + bool rotation = object_->getAttachmentData().syncRotation; + return rotation; +} + +OMP_CAPI(Object_IsMaterialSlotUsed, bool(objectPtr object, int materialIndex)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + const ObjectMaterialData* data = nullptr; + bool result = object_->getMaterialData(materialIndex, data); + if (result) + { + bool used = data->used; + return used; + } + return false; +} + +OMP_CAPI(Object_GetMaterial, bool(objectPtr object, int materialIndex, int* modelid, OutputStringViewPtr textureLibrary, OutputStringViewPtr textureName, int* materialColor)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + const ObjectMaterialData* data = nullptr; + bool result = object_->getMaterialData(materialIndex, data); + if (result) + { + *modelid = data->model; + SET_CAPI_STRING_VIEW(textureLibrary, data->textOrTXD); + SET_CAPI_STRING_VIEW(textureName, data->fontOrTexture); + *materialColor = data->materialColour.ARGB(); + return true; + } + return false; +} + +OMP_CAPI(Object_GetMaterialText, bool(objectPtr object, int materialIndex, OutputStringViewPtr text, int* materialSize, OutputStringViewPtr fontFace, int* fontSize, bool* bold, int* fontColor, int* backgroundColor, int* textAlignment)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + const ObjectMaterialData* data = nullptr; + bool result = object_->getMaterialData(materialIndex, data); + if (result) + { + SET_CAPI_STRING_VIEW(text, data->textOrTXD); + SET_CAPI_STRING_VIEW(fontFace, data->fontOrTexture); + *materialSize = data->materialSize; + *fontSize = data->fontSize; + *bold = data->bold; + *fontColor = data->fontColour.ARGB(); + *backgroundColor = data->backgroundColour.ARGB(); + *textAlignment = data->alignment; + return true; + } + return false; +} + +OMP_CAPI(Object_IsObjectNoCameraCollision, bool(objectPtr object)) +{ + POOL_ENTITY_RET(objects, IObject, object, object_, false); + bool collision = !object_->getCameraCollision(); + return collision; +} + +OMP_CAPI(Object_GetType, uint8_t(objectPtr player, int objectid)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + IPlayerObjectData* data = queryExtension(player_); + if (!data) + { + return 0; + } + + if (data->get(objectid) != nullptr) + { + uint8_t type = 2; // Player object type + return type; + } + + IObjectsComponent* component = ComponentManager::Get()->objects; + if (component) + { + if (component->get(objectid) != nullptr) + { + uint8_t type = 1; // Global object type + return type; + } + } + + return 0; +} + +/* + Per-Player Objects +*/ + +OMP_CAPI(PlayerObject_Create, objectPtr(objectPtr player, int modelid, float x, float y, float z, float rotationX, float rotationY, float rotationZ, float drawDistance, int* id)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, nullptr); + IPlayerObjectData* data = queryExtension(player_); + if (!data) + { + return nullptr; + } + + IPlayerObject* object = data->create(modelid, { x, y, z }, { rotationX, rotationY, rotationZ }, drawDistance); + if (object) + { + *id = object->getID(); + return object; + } + return nullptr; +} + +OMP_CAPI(PlayerObject_Destroy, bool(objectPtr player, objectPtr object)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + auto data = queryExtension(player_); + if (!data) + { + return false; + } + + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + data->release(object_->getID()); + return true; +} + +OMP_CAPI(PlayerObject_FromID, objectPtr(objectPtr player, int objectid)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, nullptr); + IPlayerObjectData* playerObjects = GetPlayerData(player_); + if (playerObjects) + { + return playerObjects->get(objectid); + } + return nullptr; +} + +OMP_CAPI(PlayerObject_GetID, int(objectPtr player, objectPtr object)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, INVALID_OBJECT_ID); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, INVALID_OBJECT_ID); + return object_->getID(); +} + +OMP_CAPI(PlayerObject_AttachToVehicle, bool(objectPtr player, objectPtr object, objectPtr vehicle, float offsetX, float offsetY, float offsetZ, float rotationX, float rotationY, float rotationZ)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + if (vehicle) + { + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + object_->attachToVehicle(*vehicle_, { offsetX, offsetY, offsetZ }, { rotationX, rotationY, rotationZ }); + } + else + { + object_->resetAttachment(); + } + return true; +} + +OMP_CAPI(PlayerObject_AttachToPlayer, bool(objectPtr player, objectPtr object, objectPtr playerAttachedTo, float offsetX, float offsetY, float offsetZ, float rotationX, float rotationY, float rotationZ)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + if (playerAttachedTo) + { + POOL_ENTITY_RET(players, IPlayer, playerAttachedTo, playerAttachedTo_, false); + object_->attachToPlayer(*playerAttachedTo_, { offsetX, offsetY, offsetZ }, { rotationX, rotationY, rotationZ }); + } + else + { + object_->resetAttachment(); + } + return true; +} + +OMP_CAPI(PlayerObject_AttachToObject, bool(objectPtr player, objectPtr object, objectPtr attachedTo, float offsetX, float offsetY, float offsetZ, float rotationX, float rotationY, float rotationZ)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + if (attachedTo) + { + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, attachedTo, attachedTo_, false); + object_->attachToObject(*attachedTo_, { offsetX, offsetY, offsetZ }, { rotationX, rotationY, rotationZ }); + } + else + { + object_->resetAttachment(); + } + return true; +} + +OMP_CAPI(PlayerObject_SetPos, bool(objectPtr player, objectPtr object, float x, float y, float z)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + object_->setPosition({ x, y, z }); + return true; +} + +OMP_CAPI(PlayerObject_GetPos, bool(objectPtr player, objectPtr object, float* x, float* y, float* z)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + const Vector3& position = object_->getPosition(); + *x = position.x; + *y = position.y; + *z = position.z; + return true; +} + +OMP_CAPI(PlayerObject_SetRot, bool(objectPtr player, objectPtr object, float rotationX, float rotationY, float rotationZ)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + object_->setRotation({ rotationX, rotationY, rotationZ }); + return true; +} + +OMP_CAPI(PlayerObject_GetRot, bool(objectPtr player, objectPtr object, float* rotationX, float* rotationY, float* rotationZ)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + const Vector3& rotation = object_->getRotation().ToEuler(); + *rotationX = rotation.x; + *rotationY = rotation.y; + *rotationZ = rotation.z; + return true; +} + +OMP_CAPI(PlayerObject_GetModel, int(objectPtr player, objectPtr object)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, 0); + + if (!object) + { + return 0; + } + int model = object_->getModel(); + return model; +} + +OMP_CAPI(PlayerObject_SetNoCameraCollision, bool(objectPtr player, objectPtr object)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + object_->setCameraCollision(false); + return true; +} + +OMP_CAPI(PlayerObject_IsValid, bool(objectPtr player, objectPtr object)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + if (!playerData->get(object_->getID())) + return false; + return true; +} + +OMP_CAPI(PlayerObject_Move, int(objectPtr player, objectPtr object, float x, float y, float z, float speed, float rotationX, float rotationY, float rotationZ)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, 0); + + ObjectMoveData data; + data.targetPos = { x, y, z }; + data.targetRot = { rotationX, rotationY, rotationZ }; + data.speed = speed; + + object_->move(data); + + float estimatedTime = (glm::length(data.targetPos - object_->getPosition()) / speed) * 1000.0f; + int time = static_cast(estimatedTime); + return time; +} + +OMP_CAPI(PlayerObject_Stop, bool(objectPtr player, objectPtr object)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + object_->stop(); + return true; +} + +OMP_CAPI(PlayerObject_IsMoving, bool(objectPtr player, objectPtr object)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + bool moving = object_->isMoving(); + return moving; +} + +OMP_CAPI(PlayerObject_BeginEditing, bool(objectPtr player, objectPtr object)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerObjectData* data = queryExtension(player_); + if (!data) + { + return false; + } + + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + data->beginEditing(*object_); + return true; +} + +OMP_CAPI(PlayerObject_SetMaterial, bool(objectPtr player, objectPtr object, int materialIndex, int modelId, StringCharPtr textureLibrary, StringCharPtr textureName, uint32_t materialColor)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + object_->setMaterial(materialIndex, modelId, textureLibrary, textureName, Colour::FromARGB(materialColor)); + return true; +} + +OMP_CAPI(PlayerObject_SetMaterialText, bool(objectPtr player, objectPtr object, StringCharPtr text, int materialIndex, int materialSize, StringCharPtr fontface, int fontsize, bool bold, uint32_t fontColor, uint32_t backgroundColor, int textalignment)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + object_->setMaterialText(materialIndex, text, ObjectMaterialSize(materialSize), fontface, fontsize, bold, Colour::FromARGB(fontColor), Colour::FromARGB(backgroundColor), ObjectMaterialTextAlign(textalignment)); + return true; +} + +OMP_CAPI(PlayerObject_GetDrawDistance, float(objectPtr player, objectPtr object)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0.0f); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, 0.0f); + + float distance = object_->getDrawDistance(); + return distance; +} + +OMP_CAPI(PlayerObject_GetMoveSpeed, float(objectPtr player, objectPtr object)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0.0f); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, 0.0f); + + float speed = object_->getMovingData().speed; + return speed; +} + +OMP_CAPI(PlayerObject_GetMovingTargetPos, bool(objectPtr player, objectPtr object, float* targetX, float* targetY, float* targetZ)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + const ObjectMoveData& data = object_->getMovingData(); + *targetX = data.targetPos.x; + *targetY = data.targetPos.y; + *targetZ = data.targetPos.z; + return true; +} + +OMP_CAPI(PlayerObject_GetMovingTargetRot, bool(objectPtr player, objectPtr object, float* rotationX, float* rotationY, float* rotationZ)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + const ObjectMoveData& data = object_->getMovingData(); + *rotationX = data.targetRot.x; + *rotationY = data.targetRot.y; + *rotationZ = data.targetRot.z; + return true; +} + +OMP_CAPI(PlayerObject_GetAttachedData, bool(objectPtr player, objectPtr object, int* parentVehicle, int* parentObject, int* parentPlayer)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + const ObjectAttachmentData data = object_->getAttachmentData(); + *parentVehicle = INVALID_VEHICLE_ID; + *parentObject = INVALID_OBJECT_ID; + *parentPlayer = INVALID_PLAYER_ID; + + if (data.type == ObjectAttachmentData::Type::Object) + { + *parentObject = data.ID; + } + else if (data.type == ObjectAttachmentData::Type::Player) + { + *parentPlayer = data.ID; + } + else if (data.type == ObjectAttachmentData::Type::Vehicle) + { + *parentVehicle = data.ID; + } + return true; +} + +OMP_CAPI(PlayerObject_GetAttachedOffset, bool(objectPtr player, objectPtr object, float* offsetX, float* offsetY, float* offsetZ, float* rotationX, float* rotationY, float* rotationZ)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + const ObjectAttachmentData data = object_->getAttachmentData(); + *offsetX = data.offset.x; + *offsetY = data.offset.y; + *offsetZ = data.offset.z; + *rotationX = data.rotation.x; + *rotationY = data.rotation.y; + *rotationZ = data.rotation.z; + return true; +} + +OMP_CAPI(PlayerObject_GetSyncRotation, bool(objectPtr player, objectPtr object)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0.0f); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, 0.0f); + + bool rotation = object_->getAttachmentData().syncRotation; + return rotation; +} + +OMP_CAPI(PlayerObject_IsMaterialSlotUsed, bool(objectPtr player, objectPtr object, int materialIndex)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + const ObjectMaterialData* data = nullptr; + bool result = object_->getMaterialData(materialIndex, data); + if (result) + { + bool used = data->used; + return used; + } + return false; +} + +OMP_CAPI(PlayerObject_GetMaterial, bool(objectPtr player, objectPtr object, int materialIndex, int* modelid, OutputStringViewPtr textureLibrary, OutputStringViewPtr textureName, int* materialColor)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + const ObjectMaterialData* data = nullptr; + bool result = object_->getMaterialData(materialIndex, data); + if (result) + { + SET_CAPI_STRING_VIEW(textureLibrary, data->textOrTXD); + SET_CAPI_STRING_VIEW(textureName, data->fontOrTexture); + *modelid = data->model; + *materialColor = data->materialColour.ARGB(); + return true; + } + return false; +} + +OMP_CAPI(PlayerObject_GetMaterialText, bool(objectPtr player, objectPtr object, int materialIndex, OutputStringViewPtr text, int* materialSize, OutputStringViewPtr fontFace, int* fontSize, bool* bold, int* fontColor, int* backgroundColor, int* textAlignment)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + const ObjectMaterialData* data = nullptr; + bool result = object_->getMaterialData(materialIndex, data); + if (result) + { + SET_CAPI_STRING_VIEW(text, data->textOrTXD); + SET_CAPI_STRING_VIEW(fontFace, data->fontOrTexture); + *materialSize = data->materialSize; + *fontSize = data->fontSize; + *bold = data->bold; + *fontColor = data->fontColour.ARGB(); + *backgroundColor = data->backgroundColour.ARGB(); + *textAlignment = data->alignment; + return true; + } + return false; +} + +OMP_CAPI(PlayerObject_IsNoCameraCollision, bool(objectPtr player, objectPtr object)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + + bool collision = !object_->getCameraCollision(); + return collision; +} + +OMP_CAPI(Player_GetSurfingPlayerObject, objectPtr(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, nullptr); + + const PlayerSurfingData& data = player_->getSurfingData(); + if (data.type == PlayerSurfingData::Type::PlayerObject) + { + auto playerObjects = GetPlayerData(player_); + if (playerObjects) + { + return playerObjects->get(data.ID); + } + } + return nullptr; +} + +OMP_CAPI(Player_GetCameraTargetPlayerObject, objectPtr(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, nullptr); + IObject* object = player_->getCameraTargetObject(); + if (object) + { + return object; + } + return nullptr; +} diff --git a/Server/Components/CAPI/Impl/Objects/Events.hpp b/Server/Components/CAPI/Impl/Objects/Events.hpp new file mode 100644 index 000000000..9d95ad5ff --- /dev/null +++ b/Server/Components/CAPI/Impl/Objects/Events.hpp @@ -0,0 +1,54 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#pragma once +#include "../ComponentManager.hpp" +#include "sdk.hpp" + +template +struct ObjectEvents : public ObjectEventHandler, public Singleton> +{ + void onMoved(IObject& object) override + { + ComponentManager::Get()->CallEvent("onObjectMove", EventReturnHandler::None, &object); + } + + void onPlayerObjectMoved(IPlayer& player, IPlayerObject& object) override + { + ComponentManager::Get()->CallEvent("onPlayerObjectMove", EventReturnHandler::None, &player, &object); + } + + void onObjectEdited(IPlayer& player, IObject& object, ObjectEditResponse response, Vector3 offset, Vector3 rotation) override + { + ComponentManager::Get()->CallEvent("onPlayerEditObject", EventReturnHandler::None, &player, &object, int(response), offset.x, offset.y, offset.z, rotation.x, rotation.y, rotation.z); + } + + void onPlayerObjectEdited(IPlayer& player, IPlayerObject& object, ObjectEditResponse response, Vector3 offset, Vector3 rotation) override + { + ComponentManager::Get()->CallEvent("onPlayerEditPlayerObject", EventReturnHandler::None, &player, &object, int(response), offset.x, offset.y, offset.z, rotation.x, rotation.y, rotation.z); + } + + void onPlayerAttachedObjectEdited(IPlayer& player, int index, bool saved, const ObjectAttachmentSlotData& data) override + { + ComponentManager::Get()->CallEvent("onPlayerEditAttachedObject", EventReturnHandler::None, + &player, saved, index, data.model, data.bone, + data.offset.x, data.offset.y, data.offset.z, + data.rotation.x, data.rotation.y, data.rotation.z, + data.scale.x, data.scale.y, data.scale.z); + } + + void onObjectSelected(IPlayer& player, IObject& object, int model, Vector3 position) override + { + ComponentManager::Get()->CallEvent("onPlayerSelectObject", EventReturnHandler::None, &player, &object, model, position.x, position.y, position.z); + } + + void onPlayerObjectSelected(IPlayer& player, IPlayerObject& object, int model, Vector3 position) override + { + ComponentManager::Get()->CallEvent("onPlayerSelectPlayerObject", EventReturnHandler::None, &player, &object, model, position.x, position.y, position.z); + } +}; diff --git a/Server/Components/CAPI/Impl/Pickups/APIs.cpp b/Server/Components/CAPI/Impl/Pickups/APIs.cpp new file mode 100644 index 000000000..45b1936be --- /dev/null +++ b/Server/Components/CAPI/Impl/Pickups/APIs.cpp @@ -0,0 +1,185 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" + +OMP_CAPI(Pickup_Create, objectPtr(int model, int type, float x, float y, float z, int virtualWorld, int* id)) +{ + IPickupsComponent* component = ComponentManager::Get()->pickups; + if (component) + { + int id_ = component->reserveLegacyID(); + if (id_ == INVALID_PICKUP_ID) + { + return nullptr; + } + + IPickup* pickup = component->create(model, PickupType(type), { x, y, z }, virtualWorld, false); + if (pickup) + { + component->setLegacyID(id_, pickup->getID()); + *id = id_; + return pickup; + } + else + { + component->releaseLegacyID(id_); + } + } + return nullptr; +} + +OMP_CAPI(Pickup_AddStatic, bool(int model, int type, float x, float y, float z, int virtualWorld)) +{ + IPickupsComponent* component = ComponentManager::Get()->pickups; + if (component) + { + int id_ = component->reserveLegacyID(); + if (id_ == INVALID_PICKUP_ID) + { + return false; + } + + IPickup* pickup = component->create(model, PickupType(type), { x, y, z }, virtualWorld, true); + if (pickup) + { + component->setLegacyID(id_, pickup->getID()); + return true; + } + else + { + component->releaseLegacyID(id_); + } + } + return false; +} + +OMP_CAPI(Pickup_Destroy, bool(objectPtr pickup)) +{ + POOL_ENTITY_RET(pickups, IPickup, pickup, p, false); + pickups->release(p->getID()); + pickups->releaseLegacyID(pickups->toLegacyID(p->getID())); + return true; +} + +OMP_CAPI(Pickup_FromID, objectPtr(int pickupid)) +{ + IPickupsComponent* component = ComponentManager::Get()->pickups; + if (component) + { + return component->get(pickupid); + } + return nullptr; +} + +OMP_CAPI(Pickup_GetID, int(objectPtr pickup)) +{ + POOL_ENTITY_RET(pickups, IPickup, pickup, pickup_, INVALID_PICKUP_ID); + return pickup_->getID(); +} + +OMP_CAPI(Pickup_IsValid, bool(objectPtr pickup)) +{ + POOL_ENTITY_RET(pickups, IPickup, pickup, p, false); + if (!pickups->get(p->getID())) + return false; + return true; +} + +OMP_CAPI(Pickup_IsStreamedIn, bool(objectPtr player, objectPtr pickup)) +{ + POOL_ENTITY_RET(pickups, IPickup, pickup, p, false); + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + return p->isStreamedInForPlayer(*player_); +} + +OMP_CAPI(Pickup_GetPos, bool(objectPtr pickup, float* x, float* y, float* z)) +{ + POOL_ENTITY_RET(pickups, IPickup, pickup, p, false); + auto pos = p->getPosition(); + *x = pos.x; + *y = pos.y; + *z = pos.z; + return true; +} + +OMP_CAPI(Pickup_GetModel, int(objectPtr pickup)) +{ + POOL_ENTITY_RET(pickups, IPickup, pickup, p, 0); + return p->getModel(); +} + +OMP_CAPI(Pickup_GetType, int(objectPtr pickup)) +{ + POOL_ENTITY_RET(pickups, IPickup, pickup, p, -1); + return p->getType(); +} + +OMP_CAPI(Pickup_GetVirtualWorld, int(objectPtr pickup)) +{ + POOL_ENTITY_RET(pickups, IPickup, pickup, p, 0); + return p->getVirtualWorld(); +} + +OMP_CAPI(Pickup_SetPos, bool(objectPtr pickup, float x, float y, float z, bool update)) +{ + POOL_ENTITY_RET(pickups, IPickup, pickup, p, false); + if (update) + { + p->setPosition({ x, y, z }); + } + else + { + p->setPositionNoUpdate({ x, y, z }); + } + return true; +} + +OMP_CAPI(Pickup_SetModel, bool(objectPtr pickup, int model, bool update)) +{ + POOL_ENTITY_RET(pickups, IPickup, pickup, p, false); + p->setModel(model, update); + return true; +} + +OMP_CAPI(Pickup_SetType, bool(objectPtr pickup, int type, bool update)) +{ + POOL_ENTITY_RET(pickups, IPickup, pickup, p, false); + p->setType(PickupType(type), update); + return true; +} + +OMP_CAPI(Pickup_SetVirtualWorld, bool(objectPtr pickup, int virtualworld)) +{ + POOL_ENTITY_RET(pickups, IPickup, pickup, p, false); + p->setVirtualWorld(virtualworld); + return true; +} + +OMP_CAPI(Pickup_ShowForPlayer, bool(objectPtr player, objectPtr pickup)) +{ + POOL_ENTITY_RET(pickups, IPickup, pickup, p, false); + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + p->setPickupHiddenForPlayer(*player_, false); + return true; +} + +OMP_CAPI(Pickup_HideForPlayer, bool(objectPtr player, objectPtr pickup)) +{ + POOL_ENTITY_RET(pickups, IPickup, pickup, p, false); + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + p->setPickupHiddenForPlayer(*player_, true); + return true; +} + +OMP_CAPI(Pickup_IsHiddenForPlayer, bool(objectPtr player, objectPtr pickup)) +{ + POOL_ENTITY_RET(pickups, IPickup, pickup, p, false); + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + return p->isPickupHiddenForPlayer(*player_); +} diff --git a/Server/Components/CAPI/Impl/Pickups/Events.hpp b/Server/Components/CAPI/Impl/Pickups/Events.hpp new file mode 100644 index 000000000..6cd4e25b4 --- /dev/null +++ b/Server/Components/CAPI/Impl/Pickups/Events.hpp @@ -0,0 +1,26 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#pragma once +#include "../ComponentManager.hpp" +#include "sdk.hpp" + +template +struct PickupEvents : public PickupEventHandler, public Singleton> +{ + void onPlayerPickUpPickup(IPlayer& player, IPickup& pickup) override + { + if (pickup.getLegacyPlayer() == nullptr) + { + ComponentManager::Get()->CallEvent("onPlayerPickUpPickup", EventReturnHandler::None, &player, &pickup); + } + else if (auto data = queryExtension(player)) + { + } + } +}; diff --git a/Server/Components/CAPI/Impl/Players/APIs.cpp b/Server/Components/CAPI/Impl/Players/APIs.cpp new file mode 100644 index 000000000..4a83c45bc --- /dev/null +++ b/Server/Components/CAPI/Impl/Players/APIs.cpp @@ -0,0 +1,1424 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" + +OMP_CAPI(Player_FromID, objectPtr(int playerid)) +{ + IPlayerPool* component = ComponentManager::Get()->players; + if (component) + { + return component->get(playerid); + } + return nullptr; +} + +OMP_CAPI(Player_GetID, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, INVALID_PLAYER_ID); + return player_->getID(); +} + +OMP_CAPI(Player_SendClientMessage, bool(objectPtr player, uint32_t color, StringCharPtr text)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->sendClientMessage(Colour::FromRGBA(color), text); + return true; +} + +OMP_CAPI(All_SendClientMessage, bool(uint32_t color, StringCharPtr text)) +{ + ComponentManager::Get()->players->sendClientMessageToAll(Colour::FromRGBA(color), text); + return true; +} + +OMP_CAPI(Player_SetCameraPos, bool(objectPtr player, float x, float y, float z)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setCameraPosition({ x, y, z }); + return true; +} + +OMP_CAPI(Player_SetDrunkLevel, bool(objectPtr player, int level)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setDrunkLevel(level); + return true; +} + +OMP_CAPI(Player_SetInterior, bool(objectPtr player, int interior)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setInterior(interior); + return true; +} + +OMP_CAPI(Player_SetWantedLevel, bool(objectPtr player, int level)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setWantedLevel(level); + return true; +} + +OMP_CAPI(Player_SetWeather, bool(objectPtr player, int weather)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setWeather(weather); + return true; +} + +OMP_CAPI(Player_GetWeather, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + int weather = player_->getWeather(); + return weather; +} + +OMP_CAPI(Player_SetSkin, bool(objectPtr player, int skin)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setSkin(skin); + return true; +} + +OMP_CAPI(Player_SetShopName, bool(objectPtr player, StringCharPtr name)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setShopName(name); + return true; +} + +OMP_CAPI(Player_GiveMoney, bool(objectPtr player, int amount)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->giveMoney(amount); + return true; +} + +OMP_CAPI(Player_SetCameraLookAt, bool(objectPtr player, float x, float y, float z, int cutType)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setCameraLookAt({ x, y, z }, cutType); + return true; +} + +OMP_CAPI(Player_SetCameraBehind, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setCameraBehind(); + return true; +} + +OMP_CAPI(Player_CreateExplosion, bool(objectPtr player, float x, float y, float z, int type, float radius)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->createExplosion({ x, y, z }, type, radius); + return true; +} + +OMP_CAPI(All_CreateExplosion, bool(float x, float y, float z, int type, float radius)) +{ + ComponentManager::Get()->players->createExplosionForAll({ x, y, z }, type, radius); + return true; +} + +OMP_CAPI(Player_PlayAudioStream, bool(objectPtr player, StringCharPtr url, float x, float y, float z, float distance, bool usePos)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->playAudio(url, usePos, { x, y, z }, distance); + return true; +} + +OMP_CAPI(Player_StopAudioStream, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->stopAudio(); + return true; +} + +OMP_CAPI(All_SendDeathMessage, bool(objectPtr killer, objectPtr killee, int weapon)) +{ + if (killee) + { + POOL_ENTITY_RET(players, IPlayer, killer, killer_, false); + ENTITY_CAST_RET(IPlayer, killee, killee_, false); + ComponentManager::Get()->players->sendDeathMessageToAll(killer_, *killee_, weapon); + } + else + { + ComponentManager::Get()->players->sendEmptyDeathMessageToAll(); + } + return true; +} + +OMP_CAPI(Player_ToggleWidescreen, bool(objectPtr player, bool enable)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->useWidescreen(enable); + return true; +} + +OMP_CAPI(Player_IsWidescreenToggled, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + auto enabled = player_->hasWidescreen(); + return enabled; +} + +OMP_CAPI(Player_SetHealth, bool(objectPtr player, float health)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setHealth(health); + return true; +} + +OMP_CAPI(Player_GetHealth, float(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0.0f); + auto health = player_->getHealth(); + return health; +} + +OMP_CAPI(Player_SetArmor, bool(objectPtr player, float armor)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setArmour(armor); + return true; +} + +OMP_CAPI(Player_GetArmor, float(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0.0f); + float armor = player_->getArmour(); + return armor; +} + +OMP_CAPI(Player_SetTeam, bool(objectPtr player, int team)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setTeam(team); + return true; +} + +OMP_CAPI(Player_GetTeam, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + auto team = player_->getTeam(); + return team; +} + +OMP_CAPI(Player_SetScore, bool(objectPtr player, int score)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setScore(score); + return true; +} + +OMP_CAPI(Player_GetScore, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + auto score = player_->getScore(); + return score; +} + +OMP_CAPI(Player_GetSkin, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + auto skin = player_->getSkin(); + return skin; +} + +OMP_CAPI(Player_SetColor, bool(objectPtr player, uint32_t color)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setColour(Colour::FromRGBA(color)); + return true; +} + +OMP_CAPI(Player_GetColor, uint32_t(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + auto color = player_->getColour().RGBA(); + return color; +} + +OMP_CAPI(Player_GetDefaultColor, uint32_t(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + // The player doesn't need to be connected for this to work. + auto color = ComponentManager::Get()->players->getDefaultColour(player_->getID()).RGBA(); + return color; +} + +OMP_CAPI(Player_GetDrunkLevel, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + auto level = player_->getDrunkLevel(); + return level; +} + +OMP_CAPI(Player_GiveWeapon, bool(objectPtr player, int weapon, int ammo)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + WeaponSlotData data; + data.id = weapon; + data.ammo = ammo; + player_->giveWeapon(data); + return true; +} + +OMP_CAPI(Player_RemoveWeapon, bool(objectPtr player, int weapon)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->removeWeapon(weapon); + return true; +} + +OMP_CAPI(Player_GetMoney, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + auto money = player_->getMoney(); + return money; +} + +OMP_CAPI(Player_ResetMoney, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->resetMoney(); + return true; +} + +OMP_CAPI(Player_SetName, int(objectPtr player, StringCharPtr name)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + int status = int(player_->setName(name)); + return status; +} + +OMP_CAPI(Player_GetName, int(objectPtr player, OutputStringViewPtr name)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + auto result = player_->getName(); + int len = result.length(); + SET_CAPI_STRING_VIEW(name, result); + return len; +} + +OMP_CAPI(Player_GetState, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + int state = player_->getState(); + return state; +} + +OMP_CAPI(Player_GetPing, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + int ping = player_->getPing(); + return ping; +} + +OMP_CAPI(Player_GetWeapon, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + int weapon = player_->getArmedWeapon(); + return weapon; +} + +OMP_CAPI(Player_SetTime, bool(objectPtr player, int hour, int minute)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setTime(std::chrono::hours(hour), std::chrono::minutes(minute)); + return true; +} + +OMP_CAPI(Player_GetTime, bool(objectPtr player, int* hour, int* minute)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + auto data = player_->getTime(); + *hour = data.first.count(); + *minute = data.second.count(); + return true; +} + +OMP_CAPI(Player_ToggleClock, bool(objectPtr player, bool enable)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->useClock(enable); + return true; +} + +OMP_CAPI(Player_HasClock, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + auto enable = player_->hasClock(); + return enable; +} + +OMP_CAPI(Player_ForceClassSelection, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->forceClassSelection(); + return true; +} + +OMP_CAPI(Player_GetWantedLevel, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + auto wanted = player_->getWantedLevel(); + return wanted; +} + +OMP_CAPI(Player_SetFightingStyle, bool(objectPtr player, int style)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setFightingStyle(PlayerFightingStyle(style)); + return true; +} + +OMP_CAPI(Player_GetFightingStyle, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + int style = int(player_->getFightingStyle()); + return style; +} + +OMP_CAPI(Player_SetVelocity, bool(objectPtr player, float x, float y, float z)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setVelocity({ x, y, z }); + return true; +} + +OMP_CAPI(Player_GetVelocity, bool(objectPtr player, float* x, float* y, float* z)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + auto velocity = player_->getVelocity(); + *x = velocity.x; + *y = velocity.y; + *z = velocity.z; + return true; +} + +OMP_CAPI(Player_GetCameraPos, bool(objectPtr player, float* x, float* y, float* z)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + auto pos = player_->getAimData().camPos; + *x = pos.x; + *y = pos.y; + *z = pos.z; + return true; +} + +OMP_CAPI(Player_GetDistanceFromPoint, float(objectPtr player, float x, float y, float z)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0.0f); + Vector3 playerCoords = player_->getPosition(); + float distance = glm::distance(playerCoords, { x, y, z }); + return distance; +} + +OMP_CAPI(Player_GetInterior, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + int interior = player_->getInterior(); + return interior; +} + +OMP_CAPI(Player_SetPos, bool(objectPtr player, float x, float y, float z)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setPosition({ x, y, z }); + return true; +} + +OMP_CAPI(Player_GetPos, bool(objectPtr player, float* x, float* y, float* z)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + auto pos = player_->getPosition(); + *x = pos.x; + *y = pos.y; + *z = pos.z; + return true; +} + +OMP_CAPI(Player_GetVirtualWorld, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + int vw = player_->getVirtualWorld(); + return vw; +} + +OMP_CAPI(Player_IsNPC, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + auto bot = player_->isBot(); + return bot; +} + +OMP_CAPI(Player_IsStreamedIn, bool(objectPtr player, objectPtr other)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + ENTITY_CAST_RET(IPlayer, other, other_, false); + bool streamed = player_->isStreamedInForPlayer(*other_); + return streamed; +} + +OMP_CAPI(Player_PlayGameSound, bool(objectPtr player, int sound, float x, float y, float z)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->playSound(sound, { x, y, z }); + return true; +} + +OMP_CAPI(Player_SpectatePlayer, bool(objectPtr player, objectPtr target, int mode)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + ENTITY_CAST_RET(IPlayer, target, target_, false); + player_->spectatePlayer(*target_, PlayerSpectateMode(mode)); + return true; +} + +OMP_CAPI(Player_SpectateVehicle, bool(objectPtr player, objectPtr target, int mode)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + POOL_ENTITY_RET(vehicles, IVehicle, target, target_, false); + player_->spectateVehicle(*target_, PlayerSpectateMode(mode)); + return true; +} + +OMP_CAPI(Player_SetVirtualWorld, bool(objectPtr player, int vw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setVirtualWorld(vw); + return true; +} + +OMP_CAPI(Player_SetWorldBounds, bool(objectPtr player, float xMax, float xMin, float yMax, float yMin)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + Vector4 coords = { xMax, xMin, yMax, yMin }; + player_->setWorldBounds(coords); + return true; +} + +OMP_CAPI(Player_ClearWorldBounds, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setWorldBounds(Vector4(MAX_WORLD_BOUNDS, MIN_WORLD_BOUNDS, MAX_WORLD_BOUNDS, MIN_WORLD_BOUNDS)); + return true; +} + +OMP_CAPI(Player_GetWorldBounds, bool(objectPtr player, float* xmax, float* xmin, float* ymax, float* ymin)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + auto bounds = player_->getWorldBounds(); + *xmax = bounds.x; + *xmin = bounds.y; + *ymax = bounds.z; + *ymin = bounds.w; + return true; +} + +OMP_CAPI(Player_ClearAnimations, bool(objectPtr player, int syncType)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->clearTasks(PlayerAnimationSyncType(syncType)); + return true; +} + +OMP_CAPI(Player_GetLastShotVectors, bool(objectPtr player, float* origin_x, float* origin_y, float* origin_z, float* hit_x, float* hit_y, float* hit_z)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PlayerBulletData data = player_->getBulletData(); + auto origin = data.origin; + auto hitPos = data.hitPos; + + *origin_x = origin.x; + *origin_y = origin.y; + *origin_z = origin.z; + + *hit_x = hitPos.x; + *hit_y = hitPos.y; + *hit_z = hitPos.z; + + return true; +} + +OMP_CAPI(Player_GetCameraTargetPlayer, objectPtr(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, nullptr); + return player_->getCameraTargetPlayer(); +} + +OMP_CAPI(Player_GetCameraTargetActor, objectPtr(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, nullptr); + return player_->getCameraTargetActor(); +} + +OMP_CAPI(Player_GetCameraTargetObject, objectPtr(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, nullptr); + return player_->getCameraTargetObject(); +} + +OMP_CAPI(Player_GetCameraTargetVehicle, objectPtr(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, nullptr); + return player_->getCameraTargetVehicle(); +} + +OMP_CAPI(Player_PutInVehicle, bool(objectPtr player, objectPtr vehicle, int seat)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->putPlayer(*player_, seat); + return true; +} + +OMP_CAPI(Player_RemoveBuilding, bool(objectPtr player, int model, float x, float y, float z, float radius)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->removeDefaultObjects(model, { x, y, z }, radius); + return true; +} + +OMP_CAPI(Player_GetBuildingsRemoved, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + int count = player_->getDefaultObjectsRemoved(); + return count; +} + +OMP_CAPI(Player_RemoveFromVehicle, bool(objectPtr player, bool force)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->removeFromVehicle(force); + return true; +} + +OMP_CAPI(Player_RemoveMapIcon, bool(objectPtr player, int icon)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->unsetMapIcon(icon); + return true; +} + +OMP_CAPI(Player_SetMapIcon, bool(objectPtr player, int iconID, float x, float y, float z, int type, uint32_t color, int style)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setMapIcon(iconID, { x, y, z }, type, Colour::FromRGBA(color), MapIconStyle(style)); + return true; +} + +OMP_CAPI(Player_ResetWeapons, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->resetWeapons(); + return true; +} + +OMP_CAPI(Player_SetAmmo, bool(objectPtr player, uint8_t id, uint32_t ammo)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + WeaponSlotData data; + data.id = id; + data.ammo = ammo; + player_->setWeaponAmmo(data); + return true; +} + +OMP_CAPI(Player_SetArmedWeapon, bool(objectPtr player, uint8_t weapon)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setArmedWeapon(weapon); + return true; +} + +OMP_CAPI(Player_SetChatBubble, bool(objectPtr player, StringCharPtr text, uint32_t color, float drawdistance, int expiretime)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setChatBubble(text, Colour::FromRGBA(color), drawdistance, std::chrono::milliseconds(expiretime)); + return true; +} + +OMP_CAPI(Player_SetPosFindZ, bool(objectPtr player, float x, float y, float z)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setPositionFindZ({ x, y, z }); + return true; +} + +OMP_CAPI(Player_SetSkillLevel, bool(objectPtr player, uint8_t weapon, int level)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setSkillLevel(PlayerWeaponSkill(weapon), level); + return true; +} + +OMP_CAPI(Player_SetSpecialAction, bool(objectPtr player, uint32_t action)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setAction(PlayerSpecialAction(action)); + return true; +} + +OMP_CAPI(Player_ShowNameTagForPlayer, bool(objectPtr player, objectPtr other, bool enable)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + ENTITY_CAST_RET(IPlayer, other, other_, false); + player_->toggleOtherNameTag(*other_, enable); + return true; +} + +OMP_CAPI(Player_ToggleControllable, bool(objectPtr player, bool enable)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setControllable(enable); + return true; +} + +OMP_CAPI(Player_ToggleSpectating, bool(objectPtr player, bool enable)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setSpectating(enable); + return true; +} + +OMP_CAPI(Player_ApplyAnimation, bool(objectPtr player, StringCharPtr animlib, StringCharPtr animname, float delta, bool loop, bool lockX, bool lockY, bool freeze, uint32_t time, int sync)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + AnimationData animationData(delta, loop, lockX, lockY, freeze, time, animlib, animname); + player_->applyAnimation(animationData, PlayerAnimationSyncType(sync)); + return true; +} + +OMP_CAPI(Player_GetAnimationName, bool(int index, OutputStringViewPtr lib, OutputStringViewPtr name)) +{ + Pair anim = splitAnimationNames(index); + SET_CAPI_STRING_VIEW(lib, anim.first); + SET_CAPI_STRING_VIEW(name, anim.second); + return true; +} + +OMP_CAPI(Player_EditAttachedObject, bool(objectPtr player, int index)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_DATA_RET(player_, IPlayerObjectData, data, false); + data->editAttachedObject(index); + return true; +} + +OMP_CAPI(Player_EnableCameraTarget, bool(objectPtr player, bool enable)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->useCameraTargeting(enable); + return true; +} + +OMP_CAPI(Player_EnableStuntBonus, bool(objectPtr player, bool enable)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->useStuntBonuses(enable); + return true; +} + +OMP_CAPI(All_EnableStuntBonus, bool(bool enable)) +{ + ComponentManager::Get()->core->useStuntBonuses(enable); + return true; +} + +OMP_CAPI(Player_GetPlayerAmmo, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + int ammo = player_->getArmedWeaponAmmo(); + return ammo; +} + +OMP_CAPI(Player_GetAnimationIndex, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + int id = player_->getAnimationData().ID; + return id; +} + +OMP_CAPI(Player_GetFacingAngle, float(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0.0f); + GTAQuat quat = player_->getRotation(); + float angle = quat.ToEuler().z; + return angle; +} + +OMP_CAPI(Player_GetIp, int(objectPtr player, OutputStringBufferPtr ip)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + PeerNetworkData data = player_->getNetworkData(); + if (!data.networkID.address.ipv6) + { + PeerAddress::AddressString addressString; + if (PeerAddress::ToString(data.networkID.address, addressString)) + { + auto len = addressString.length(); + COPY_STRING_TO_CAPI_STRING_BUFFER(ip, addressString.data(), len); + return len; + } + } + return 0; +} + +OMP_CAPI(Player_GetSpecialAction, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + int action = player_->getAction(); + return action; +} + +OMP_CAPI(Player_GetVehicleID, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, INVALID_VEHICLE_ID); + PLAYER_DATA_RET(player_, IPlayerVehicleData, data, INVALID_VEHICLE_ID); + IVehicle* vehicle = data->getVehicle(); + int id = 0; + if (vehicle) + { + id = vehicle->getID(); + } + return id; +} + +OMP_CAPI(Player_GetVehicleSeat, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + PLAYER_DATA_RET(player_, IPlayerVehicleData, data, 0); + int seat = data->getSeat(); + return seat; +} + +OMP_CAPI(Player_GetWeaponData, bool(objectPtr player, int slot, int* weaponid, int* ammo)) +{ + if (slot < 0 || slot >= MAX_WEAPON_SLOTS) + { + return false; + } + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + const WeaponSlotData& weapon = player_->getWeaponSlot(slot); + *weaponid = weapon.id; + *ammo = weapon.ammo; + return true; +} + +OMP_CAPI(Player_GetWeaponState, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + int state = player_->getAimData().weaponState; + return state; +} + +OMP_CAPI(Player_InterpolateCameraPos, bool(objectPtr player, float from_x, float from_y, float from_z, float to_x, float to_y, float to_z, int time, int cut)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->interpolateCameraPosition({ from_x, from_y, from_z }, { to_x, to_y, to_z }, time, PlayerCameraCutType(cut)); + return true; +} + +OMP_CAPI(Player_InterpolateCameraLookAt, bool(objectPtr player, float from_x, float from_y, float from_z, float to_x, float to_y, float to_z, int time, int cut)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->interpolateCameraLookAt({ from_x, from_y, from_z }, { to_x, to_y, to_z }, time, PlayerCameraCutType(cut)); + return true; +} + +OMP_CAPI(Player_IsPlayerAttachedObjectSlotUsed, bool(objectPtr player, int index)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + PLAYER_DATA_RET(player_, IPlayerObjectData, data, 0); + bool ret = data->hasAttachedObject(index); + return ret; +} + +OMP_CAPI(Player_AttachCameraToObject, bool(objectPtr player, objectPtr object)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + POOL_ENTITY_RET(objects, IObject, object, object_, false); + player_->attachCameraToObject(*object_); + return true; +} + +OMP_CAPI(Player_AttachCameraToPlayerObject, bool(objectPtr player, objectPtr object)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerObjectData, IPlayerObject, object, object_, false); + player_->attachCameraToObject(*object_); + return true; +} + +OMP_CAPI(Player_GetCameraAspectRatio, float(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0.0f); + float ratio = player_->getAimData().aspectRatio; + return ratio; +} + +OMP_CAPI(Player_GetCameraFrontVector, bool(objectPtr player, float* x, float* y, float* z)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + auto vector = player_->getAimData().camFrontVector; + *x = vector.x; + *y = vector.y; + *z = vector.z; + return true; +} + +OMP_CAPI(Player_GetCameraMode, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + int cameraMode = player_->getAimData().camMode; + return cameraMode; +} + +OMP_CAPI(Player_GetKeys, bool(objectPtr player, int* keys, int* updown, int* leftright)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + const PlayerKeyData& keyData = player_->getKeyData(); + *keys = keyData.keys; + *updown = keyData.upDown; + *leftright = keyData.leftRight; + return true; +} + +OMP_CAPI(Player_GetSurfingVehicle, objectPtr(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, nullptr); + PlayerSurfingData data = player_->getSurfingData(); + if (player_->getState() == PlayerState_OnFoot && data.type == PlayerSurfingData::Type::Vehicle) + { + IVehiclesComponent* vehicles = ComponentManager::Get()->vehicles; + if (vehicles) + { + return vehicles->get(data.ID); + } + } + return nullptr; +} + +OMP_CAPI(Player_GetSurfingObject, objectPtr(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, nullptr); + PlayerSurfingData data = player_->getSurfingData(); + if (player_->getState() == PlayerState_OnFoot && data.type == PlayerSurfingData::Type::Object) + { + IObjectsComponent* objects = ComponentManager::Get()->objects; + if (objects) + { + return objects->get(data.ID); + } + } + return nullptr; +} + +OMP_CAPI(Player_GetTargetPlayer, objectPtr(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, nullptr); + IPlayer* target = player_->getTargetPlayer(); + return target; +} + +OMP_CAPI(Player_GetTargetActor, objectPtr(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, nullptr); + return player_->getTargetActor(); +} + +OMP_CAPI(Player_IsInVehicle, bool(objectPtr player, objectPtr targetVehicle)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_DATA_RET(player_, IPlayerVehicleData, data, false); + POOL_ENTITY_RET(vehicles, IVehicle, targetVehicle, targetVehicle_, false); + IVehicle* vehicle = data->getVehicle(); + bool ret = bool(vehicle == targetVehicle_); + return ret; +} + +OMP_CAPI(Player_IsInAnyVehicle, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_DATA_RET(player_, IPlayerVehicleData, data, false); + IVehicle* vehicle = data->getVehicle(); + bool ret = bool(vehicle != nullptr); + return ret; +} + +OMP_CAPI(Player_IsInRangeOfPoint, bool(objectPtr player, float range, float x, float y, float z)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + bool ret = bool(range >= glm::distance(player_->getPosition(), { x, y, z })); + return ret; +} + +OMP_CAPI(Player_PlayCrimeReport, bool(objectPtr player, objectPtr suspect, int crime)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + ENTITY_CAST_RET(IPlayer, suspect, suspect_, false); + bool ret = bool(player_->playerCrimeReport(*suspect_, crime)); + return ret; +} + +OMP_CAPI(Player_RemoveAttachedObject, bool(objectPtr player, int index)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_DATA_RET(player_, IPlayerObjectData, data, false); + data->removeAttachedObject(index); + return true; +} + +OMP_CAPI(Player_SetAttachedObject, bool(objectPtr player, int index, int modelid, int bone, float offsetX, float offsetY, float offsetZ, float rotationX, float rotationY, float rotationZ, float scaleX, float scaleY, float scaleZ, uint32_t materialcolor1, uint32_t materialcolor2)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_DATA_RET(player_, IPlayerObjectData, data, false); + ObjectAttachmentSlotData attachment; + attachment.model = modelid; + attachment.bone = bone; + attachment.offset = { offsetX, offsetY, offsetZ }; + attachment.rotation = { rotationX, rotationY, rotationZ }; + attachment.scale = { scaleX, scaleY, scaleZ }; + attachment.colour1 = Colour::FromARGB(materialcolor1); + attachment.colour2 = Colour::FromARGB(materialcolor2); + data->setAttachedObject(index, attachment); + return true; +} + +OMP_CAPI(Player_GetAttachedObject, bool(objectPtr player, int index, int* modelid, int* bone, float* offsetX, float* offsetY, float* offsetZ, float* rotationX, float* rotationY, float* rotationZ, float* scaleX, float* scaleY, float* scaleZ, int* materialcolor1, int* materialcolor2)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_DATA_RET(player_, IPlayerObjectData, data, false); + ObjectAttachmentSlotData attachment = data->getAttachedObject(index); + *modelid = attachment.model; + *bone = attachment.bone; + *offsetX = attachment.offset.x; + *offsetY = attachment.offset.y; + *offsetZ = attachment.offset.z; + *rotationX = attachment.rotation.x; + *rotationY = attachment.rotation.y; + *rotationZ = attachment.rotation.z; + *scaleX = attachment.scale.x; + *scaleY = attachment.scale.y; + *scaleZ = attachment.scale.z; + *materialcolor1 = attachment.colour1.ARGB(); + *materialcolor2 = attachment.colour2.ARGB(); + return true; +} + +OMP_CAPI(Player_SetFacingAngle, bool(objectPtr player, float angle)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + Vector3 rotation = player_->getRotation().ToEuler(); + rotation.z = angle; + player_->setRotation(rotation); + return true; +} + +OMP_CAPI(Player_SetMarkerForPlayer, bool(objectPtr player, objectPtr other, uint32_t color)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + ENTITY_CAST_RET(IPlayer, other, other_, false); + player_->setOtherColour(*other_, Colour::FromRGBA(color)); + return true; +} + +OMP_CAPI(Player_GetMarkerForPlayer, uint32_t(objectPtr player, objectPtr other)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + ENTITY_CAST_RET(IPlayer, other, other_, 0); + Colour color; + bool hasPlayerSpecificColor = player_->getOtherColour(*other_, color); + if (!hasPlayerSpecificColor) + { + color = other_->getColour(); + } + uint32_t rgba = color.RGBA(); + return rgba; +} + +OMP_CAPI(Player_AllowTeleport, bool(objectPtr player, bool allow)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->allowTeleport(allow); + return true; +} + +OMP_CAPI(Player_IsTeleportAllowed, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + bool allowed = bool(player_->isTeleportAllowed()); + return allowed; +} + +OMP_CAPI(Player_DisableRemoteVehicleCollisions, bool(objectPtr player, bool disable)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setRemoteVehicleCollisions(!disable); + return true; +} + +OMP_CAPI(Player_GetCameraZoom, float(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0.0f); + float cameraZoom = player_->getAimData().camZoom; + return cameraZoom; +} + +OMP_CAPI(Player_SelectTextDraw, bool(objectPtr player, uint32_t hoverColour)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_DATA_RET(player_, IPlayerTextDrawData, data, false); + data->beginSelection(Colour::FromRGBA(hoverColour)); + return true; +} + +OMP_CAPI(Player_CancelSelectTextDraw, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_DATA_RET(player_, IPlayerTextDrawData, data, false); + data->endSelection(); + return true; +} + +OMP_CAPI(Player_SendClientCheck, bool(objectPtr player, int actionType, int address, int offset, int count)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->sendClientCheck(actionType, address, offset, count); + return true; +} + +OMP_CAPI(Player_Spawn, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->spawn(); + return true; +} + +OMP_CAPI(Player_GPCI, bool(objectPtr player, OutputStringViewPtr gpci)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + auto result = player_->getSerial(); + SET_CAPI_STRING_VIEW(gpci, result); + return true; +} + +OMP_CAPI(Player_IsAdmin, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_DATA_RET(player_, IPlayerConsoleData, data, false); + bool access = data->hasConsoleAccess(); + return access; +} + +OMP_CAPI(Player_Kick, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->kick(); + return true; +} + +OMP_CAPI(Player_ShowGameText, bool(objectPtr player, StringCharPtr text, int time, int style)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + if (strlen(text) < 1) + { + return false; + } + player_->sendGameText(text, Milliseconds(time), style); + return true; +} + +OMP_CAPI(Player_HideGameText, bool(objectPtr player, int style)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->hideGameText(style); + return true; +} + +OMP_CAPI(Player_HasGameText, bool(objectPtr player, int style)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + bool has = player_->hasGameText(style); + return has; +} + +OMP_CAPI(Player_GetGameText, bool(objectPtr player, int style, OutputStringViewPtr message, int* time, int* remaining)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + Milliseconds mt; + Milliseconds mr; + StringView ms; + if (player_->getGameText(style, ms, mt, mr)) + { + SET_CAPI_STRING_VIEW(message, ms); + *time = int(mt.count()); + *remaining = int(mr.count()); + return true; + } + return false; +} + +OMP_CAPI(Player_Ban, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->ban(); + return true; +} + +OMP_CAPI(Player_BanEx, bool(objectPtr player, StringCharPtr reason)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->ban(reason); + return true; +} + +OMP_CAPI(Player_SendDeathMessage, bool(objectPtr player, objectPtr killer, objectPtr killee, int weapon)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + ENTITY_CAST_RET(IPlayer, killee, killee_, false); + if (killee_) + { + ENTITY_CAST_RET(IPlayer, killer, killer_, false); + player_->sendDeathMessage(*killee_, killer_, weapon); + } + else + { + player_->sendEmptyDeathMessage(); + } + return true; +} + +OMP_CAPI(Player_SendMessageToPlayer, bool(objectPtr player, objectPtr sender, StringCharPtr message)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + ENTITY_CAST_RET(IPlayer, sender, sender_, false); + player_->sendChatMessage(*sender_, message); + return true; +} + +OMP_CAPI(Player_GetVersion, int(objectPtr player, OutputStringViewPtr version)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + auto versionStr = player_->getClientVersionName(); + auto len = versionStr.length(); + SET_CAPI_STRING_VIEW(version, versionStr); + return len; +} + +OMP_CAPI(Player_GetSkillLevel, int(objectPtr player, int skill)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + auto skills = player_->getSkillLevels(); + if (skill >= 11 || skill < 0) + { + return 0; + } + int ret = skills[skill]; + return ret; +} + +OMP_CAPI(Player_GetZAim, float(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0.0f); + float z = player_->getAimData().aimZ; + return z; +} + +OMP_CAPI(Player_GetSurfingOffsets, bool(objectPtr player, float* offsetX, float* offsetY, float* offsetZ)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + const PlayerSurfingData& data = player_->getSurfingData(); + *offsetX = 0.0f; + *offsetY = 0.0f; + *offsetZ = 0.0f; + if (data.type != PlayerSurfingData::Type::None) + { + *offsetX = data.offset.x; + *offsetY = data.offset.y; + *offsetZ = data.offset.z; + } + return true; +} + +OMP_CAPI(Player_GetRotationQuat, bool(objectPtr player, float* x, float* y, float* z, float* w)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + glm::quat rotQuat = player_->getRotation().q; + + // In samp or YSF, GetPlayerRotationQuat declaration is like this: + // GetPlayerRotationQuat(playerid, &Float:w, &Float:x = 0.0, &Float:y = 0.0, &Float:z = 0.0); + // Meaning first output arg is W and not X; Vector4's first member is X and it is used in many other places, + // We can't just simply change ParamCast for Vector4 just because one function doesn't follow it. + + *x = rotQuat.w; + *y = rotQuat.x; + *z = rotQuat.y; + *w = rotQuat.z; + return true; +} + +OMP_CAPI(Player_GetPlayerSpectateID, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + int spectateId = player_->getSpectateData().spectateID; + return spectateId; +} + +OMP_CAPI(Player_GetSpectateType, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + int spectateType = int(player_->getSpectateData().type); + return spectateType; +} + +OMP_CAPI(Player_GetRawIp, uint32_t(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + uint32_t ip = player_->getNetworkData().networkID.address.v4; + return ip; +} + +OMP_CAPI(Player_SetGravity, bool(objectPtr player, float gravity)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->setGravity(gravity); + return true; +} + +OMP_CAPI(Player_GetGravity, float(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0.0f); + float gravity = player_->getGravity(); + return gravity; +} + +OMP_CAPI(Player_SetAdmin, bool(objectPtr player, bool set)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_DATA_RET(player_, IPlayerConsoleData, data, false); + data->setConsoleAccessibility(set); + return true; +} + +OMP_CAPI(Player_IsSpawned, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PlayerState state = player_->getState(); + bool spawned = false; + switch (state) + { + case PlayerState_OnFoot: + case PlayerState_Driver: + case PlayerState_Passenger: + case PlayerState_Spawned: + { + spawned = true; + break; + } + default: + spawned = false; + } + return spawned; +} + +OMP_CAPI(Player_IsControllable, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + bool controllable = bool(player_->getControllable()); + return controllable; +} + +OMP_CAPI(Player_IsCameraTargetEnabled, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + bool enabled = bool(player_->hasCameraTargeting()); + return enabled; +} + +OMP_CAPI(Player_ToggleGhostMode, bool(objectPtr player, bool toggle)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->toggleGhostMode(toggle); + return true; +} + +OMP_CAPI(Player_GetGhostMode, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + bool enabled = bool(player_->isGhostModeEnabled()); + return enabled; +} + +OMP_CAPI(Player_AllowWeapons, bool(objectPtr player, bool allow)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + player_->allowWeapons(allow); + return true; +} + +OMP_CAPI(Player_AreWeaponsAllowed, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + bool allowed = bool(player_->areWeaponsAllowed()); + return allowed; +} + +OMP_CAPI(Player_IsPlayerUsingOfficialClient, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + bool ret = bool(player_->isUsingOfficialClient()); + return ret; +} + +OMP_CAPI(Player_GetAnimationFlags, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + PlayerAnimationData data = player_->getAnimationData(); + int flags = data.flags; + return flags; +} + +OMP_CAPI(Player_IsInDriveByMode, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_DATA_RET(player_, IPlayerVehicleData, data, false); + bool driveby = bool(data->isInDriveByMode()); + return driveby; +} + +OMP_CAPI(Player_IsCuffed, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + bool cuffed = false; + if (player_->getState() == PlayerState_OnFoot) + { + cuffed = bool(player_->getAction() == SpecialAction_Cuffed); + } + else if (player_->getState() == PlayerState_Passenger) + { + IPlayerVehicleData* data = queryExtension(player_); + if (data) + { + cuffed = bool(data->isCuffed()); + } + } + return cuffed; +} + +OMP_CAPI(Player_IsUsingOmp, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + bool ret = bool(player_->isUsingOmp()); + return ret; +} diff --git a/Server/Components/CAPI/Impl/Players/Events.hpp b/Server/Components/CAPI/Impl/Players/Events.hpp new file mode 100644 index 000000000..243552456 --- /dev/null +++ b/Server/Components/CAPI/Impl/Players/Events.hpp @@ -0,0 +1,141 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#pragma once +#include "../ComponentManager.hpp" +#include "sdk.hpp" + +template +class PlayerEvents : public PlayerSpawnEventHandler, public PlayerConnectEventHandler, public PlayerStreamEventHandler, public PlayerTextEventHandler, public PlayerShotEventHandler, public PlayerChangeEventHandler, public PlayerDamageEventHandler, public PlayerClickEventHandler, public PlayerCheckEventHandler, public PlayerUpdateEventHandler, public Singleton> +{ +public: + void onPlayerConnect(IPlayer& player) override + { + ComponentManager::Get()->CallEvent("onPlayerConnect", EventReturnHandler::None, &player); + } + + void onPlayerSpawn(IPlayer& player) override + { + ComponentManager::Get()->CallEvent("onPlayerSpawn", EventReturnHandler::None, &player); + } + + bool onPlayerCommandText(IPlayer& player, StringView cmdtext) override + { + return ComponentManager::Get()->CallEvent("onPlayerCommandText", EventReturnHandler::StopAtTrue, &player, CREATE_CAPI_STRING_VIEW(cmdtext)); + } + + void onPlayerKeyStateChange(IPlayer& player, uint32_t newKeys, uint32_t oldKeys) override + { + ComponentManager::Get()->CallEvent("onPlayerKeyStateChange", EventReturnHandler::None, &player, int(newKeys), int(oldKeys)); + } + + void onIncomingConnection(IPlayer& player, StringView ipAddress, unsigned short port) override + { + ComponentManager::Get()->CallEvent("onIncomingConnection", EventReturnHandler::None, &player, CREATE_CAPI_STRING_VIEW(ipAddress), int(port)); + } + + void onPlayerDisconnect(IPlayer& player, PeerDisconnectReason reason) override + { + ComponentManager::Get()->CallEvent("onPlayerDisconnect", EventReturnHandler::None, &player, int(reason)); + } + + bool onPlayerRequestSpawn(IPlayer& player) override + { + return ComponentManager::Get()->CallEvent("onPlayerRequestSpawn", EventReturnHandler::StopAtFalse, &player); + } + + void onPlayerStreamIn(IPlayer& player, IPlayer& forPlayer) override + { + ComponentManager::Get()->CallEvent("onPlayerStreamIn", EventReturnHandler::None, &player, &forPlayer); + } + + void onPlayerStreamOut(IPlayer& player, IPlayer& forPlayer) override + { + ComponentManager::Get()->CallEvent("onPlayerStreamOut", EventReturnHandler::None, &player, &forPlayer); + } + + bool onPlayerText(IPlayer& player, StringView message) override + { + return ComponentManager::Get()->CallEvent("onPlayerText", EventReturnHandler::StopAtFalse, &player, CREATE_CAPI_STRING_VIEW(message)); + } + + bool onPlayerShotMissed(IPlayer& player, const PlayerBulletData& bulletData) override + { + return ComponentManager::Get()->CallEvent("onPlayerShotMissed", EventReturnHandler::StopAtFalse, &player, + int(bulletData.weapon), bulletData.offset.x, bulletData.offset.y, bulletData.offset.z); + } + + bool onPlayerShotPlayer(IPlayer& player, IPlayer& target, const PlayerBulletData& bulletData) override + { + return ComponentManager::Get()->CallEvent("onPlayerShotPlayer", EventReturnHandler::StopAtFalse, &player, &target, + int(bulletData.weapon), bulletData.offset.x, bulletData.offset.y, bulletData.offset.z); + } + + bool onPlayerShotVehicle(IPlayer& player, IVehicle& target, const PlayerBulletData& bulletData) override + { + return ComponentManager::Get()->CallEvent("onPlayerShotVehicle", EventReturnHandler::StopAtFalse, &player, &target, + int(bulletData.weapon), bulletData.offset.x, bulletData.offset.y, bulletData.offset.z); + } + + bool onPlayerShotObject(IPlayer& player, IObject& target, const PlayerBulletData& bulletData) override + { + return ComponentManager::Get()->CallEvent("onPlayerShotObject", EventReturnHandler::StopAtFalse, &player, &target, + int(bulletData.weapon), bulletData.offset.x, bulletData.offset.y, bulletData.offset.z); + } + + bool onPlayerShotPlayerObject(IPlayer& player, IPlayerObject& target, const PlayerBulletData& bulletData) override + { + return ComponentManager::Get()->CallEvent("onPlayerShotPlayerObject", EventReturnHandler::StopAtFalse, &player, &target, + int(bulletData.weapon), bulletData.offset.x, bulletData.offset.y, bulletData.offset.z); + } + + void onPlayerDeath(IPlayer& player, IPlayer* killer, int reason) override + { + ComponentManager::Get()->CallEvent("onPlayerDeath", EventReturnHandler::None, &player, killer, reason); + } + + void onPlayerTakeDamage(IPlayer& player, IPlayer* from, float amount, unsigned weapon, BodyPart part) override + { + ComponentManager::Get()->CallEvent("onPlayerTakeDamage", EventReturnHandler::None, &player, from, amount, int(weapon), int(part)); + } + + void onPlayerGiveDamage(IPlayer& player, IPlayer& to, float amount, unsigned weapon, BodyPart part) override + { + ComponentManager::Get()->CallEvent("onPlayerGiveDamage", EventReturnHandler::None, &player, &to, amount, int(weapon), int(part)); + } + + void onPlayerInteriorChange(IPlayer& player, unsigned newInterior, unsigned oldInterior) override + { + ComponentManager::Get()->CallEvent("onPlayerInteriorChange", EventReturnHandler::None, &player, int(newInterior), int(oldInterior)); + } + + void onPlayerStateChange(IPlayer& player, PlayerState newState, PlayerState oldState) override + { + ComponentManager::Get()->CallEvent("onPlayerStateChange", EventReturnHandler::None, &player, int(newState), int(oldState)); + } + + void onPlayerClickMap(IPlayer& player, Vector3 pos) override + { + ComponentManager::Get()->CallEvent("onPlayerClickMap", EventReturnHandler::None, &player, pos.x, pos.y, pos.z); + } + + void onPlayerClickPlayer(IPlayer& player, IPlayer& clicked, PlayerClickSource source) override + { + ComponentManager::Get()->CallEvent("onPlayerClickPlayer", EventReturnHandler::None, &player, &clicked, int(source)); + } + + void onClientCheckResponse(IPlayer& player, int actionType, int address, int results) override + { + ComponentManager::Get()->CallEvent("onClientCheckResponse", EventReturnHandler::None, &player, actionType, address, results); + } + + bool onPlayerUpdate(IPlayer& player, TimePoint now) override + { + return ComponentManager::Get()->CallEvent("onPlayerUpdate", EventReturnHandler::StopAtFalse, &player); + } +}; diff --git a/Server/Components/CAPI/Impl/Recording/APIs.cpp b/Server/Components/CAPI/Impl/Recording/APIs.cpp new file mode 100644 index 000000000..0463ffa6d --- /dev/null +++ b/Server/Components/CAPI/Impl/Recording/APIs.cpp @@ -0,0 +1,33 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" + +OMP_CAPI(Recording_Start, bool(objectPtr player, int type, StringCharPtr file)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerRecordingData* recording = queryExtension(player_); + if (recording) + { + recording->start(PlayerRecordingType(type), file); + return true; + } + return false; +} + +OMP_CAPI(Recording_Stop, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerRecordingData* recording = queryExtension(player_); + if (recording) + { + recording->stop(); + return true; + } + return false; +} diff --git a/Server/Components/CAPI/Impl/TextDraws/APIs.cpp b/Server/Components/CAPI/Impl/TextDraws/APIs.cpp new file mode 100644 index 000000000..6433af528 --- /dev/null +++ b/Server/Components/CAPI/Impl/TextDraws/APIs.cpp @@ -0,0 +1,707 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" + +OMP_CAPI(TextDraw_Create, objectPtr(float x, float y, StringCharPtr text, int* id)) +{ + ITextDrawsComponent* component = ComponentManager::Get()->textdraws; + if (component) + { + ITextDraw* textdraw = component->create({ x, y }, text); + if (textdraw) + { + *id = textdraw->getID(); + return textdraw; + } + } + return nullptr; +} + +OMP_CAPI(TextDraw_Destroy, bool(objectPtr textdraw)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + ComponentManager::Get()->textdraws->release(textdraw_->getID()); + return true; +} + +OMP_CAPI(TextDraw_FromID, objectPtr(int textdrawid)) +{ + ITextDrawsComponent* component = ComponentManager::Get()->textdraws; + if (component) + { + return component->get(textdrawid); + } + return nullptr; +} + +OMP_CAPI(TextDraw_GetID, int(objectPtr textdraw)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, INVALID_TEXTDRAW); + return textdraw_->getID(); +} + +OMP_CAPI(TextDraw_IsValid, bool(objectPtr textdraw)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + if (!textdraws->get(textdraw_->getID())) + return false; + return true; +} + +OMP_CAPI(TextDraw_IsVisibleForPlayer, bool(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + return textdraw_->isShownForPlayer(*player_); +} + +OMP_CAPI(TextDraw_SetLetterSize, bool(objectPtr textdraw, float sizeX, float sizeY)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->setLetterSize({ sizeX, sizeY }); + return true; +} + +OMP_CAPI(TextDraw_SetTextSize, bool(objectPtr textdraw, float sizeX, float sizeY)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->setTextSize({ sizeX, sizeY }); + return true; +} + +OMP_CAPI(TextDraw_SetAlignment, bool(objectPtr textdraw, int alignment)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->setAlignment(TextDrawAlignmentTypes(alignment)); + return true; +} + +OMP_CAPI(TextDraw_SetColor, bool(objectPtr textdraw, uint32_t color)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->setColour(Colour::FromRGBA(color)); + return true; +} + +OMP_CAPI(TextDraw_SetUseBox, bool(objectPtr textdraw, bool use)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->useBox(use); + return true; +} + +OMP_CAPI(TextDraw_SetBoxColor, bool(objectPtr textdraw, uint32_t color)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->setBoxColour(Colour::FromRGBA(color)); + return true; +} + +OMP_CAPI(TextDraw_SetShadow, bool(objectPtr textdraw, int size)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->setShadow(size); + return true; +} + +OMP_CAPI(TextDraw_SetOutline, bool(objectPtr textdraw, int size)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->setOutline(size); + return true; +} + +OMP_CAPI(TextDraw_SetBackgroundColor, bool(objectPtr textdraw, uint32_t color)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->setBackgroundColour(Colour::FromRGBA(color)); + return true; +} + +OMP_CAPI(TextDraw_SetFont, bool(objectPtr textdraw, int font)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->setStyle(TextDrawStyle(font)); + return true; +} + +OMP_CAPI(TextDraw_SetSetProportional, bool(objectPtr textdraw, bool set)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->setProportional(set); + return true; +} + +OMP_CAPI(TextDraw_SetSelectable, bool(objectPtr textdraw, bool set)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->setSelectable(set); + return true; +} + +OMP_CAPI(TextDraw_ShowForPlayer, bool(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->showForPlayer(*player_); + return true; +} + +OMP_CAPI(TextDraw_HideForPlayer, bool(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->hideForPlayer(*player_); + return true; +} + +OMP_CAPI(TextDraw_ShowForAll, bool(objectPtr textdraw)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + IPlayerPool* pool = ComponentManager::Get()->players; + for (IPlayer* player : pool->entries()) + { + textdraw_->showForPlayer(*player); + } + return true; +} + +OMP_CAPI(TextDraw_HideForAll, bool(objectPtr textdraw)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + IPlayerPool* pool = ComponentManager::Get()->players; + for (IPlayer* player : pool->entries()) + { + textdraw_->hideForPlayer(*player); + } + return true; +} + +OMP_CAPI(TextDraw_SetString, bool(objectPtr textdraw, StringCharPtr text)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->setText(text); + return true; +} + +OMP_CAPI(TextDraw_SetPreviewModel, bool(objectPtr textdraw, int model)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->setPreviewModel(model); + return true; +} + +OMP_CAPI(TextDraw_SetPreviewRot, bool(objectPtr textdraw, float rotationX, float rotationY, float rotationZ, float zoom)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->setPreviewRotation({ rotationX, rotationY, rotationZ }); + textdraw_->setPreviewZoom(zoom); + return true; +} + +OMP_CAPI(TextDraw_SetPreviewVehCol, bool(objectPtr textdraw, int color1, int color2)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->setPreviewVehicleColour(color1, color2); + return true; +} + +OMP_CAPI(TextDraw_SetPos, bool(objectPtr textdraw, float x, float y)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + textdraw_->setPosition({ x, y }); + return true; +} + +OMP_CAPI(TextDraw_GetString, bool(objectPtr textdraw, OutputStringViewPtr text)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, 0); + auto textStr = textdraw_->getText(); + SET_CAPI_STRING_VIEW(text, textStr); + return true; +} + +OMP_CAPI(TextDraw_GetLetterSize, bool(objectPtr textdraw, float* sizeX, float* sizeY)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + const Vector2& size = textdraw_->getLetterSize(); + *sizeX = size.x; + *sizeY = size.y; + return true; +} + +OMP_CAPI(TextDraw_GetTextSize, bool(objectPtr textdraw, float* sizeX, float* sizeY)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + const Vector2& size = textdraw_->getTextSize(); + *sizeX = size.x; + *sizeY = size.y; + return true; +} + +OMP_CAPI(TextDraw_GetPos, bool(objectPtr textdraw, float* x, float* y)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + const Vector2& pos = textdraw_->getPosition(); + *x = pos.x; + *y = pos.y; + return true; +} + +OMP_CAPI(TextDraw_GetColor, int(objectPtr textdraw)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, 0); + return textdraw_->getLetterColour().RGBA(); +} + +OMP_CAPI(TextDraw_GetBoxColor, int(objectPtr textdraw)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, 0); + return textdraw_->getBoxColour().RGBA(); +} + +OMP_CAPI(TextDraw_GetBackgroundColor, int(objectPtr textdraw)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, 0); + return textdraw_->getBackgroundColour().RGBA(); +} + +OMP_CAPI(TextDraw_GetShadow, int(objectPtr textdraw)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, 0); + return textdraw_->getShadow(); +} + +OMP_CAPI(TextDraw_GetOutline, int(objectPtr textdraw)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, 0); + return textdraw_->getOutline(); +} + +OMP_CAPI(TextDraw_GetFont, int(objectPtr textdraw)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, -1); + return static_cast(textdraw_->getStyle()); +} + +OMP_CAPI(TextDraw_IsBox, bool(objectPtr textdraw)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + return textdraw_->hasBox(); +} + +OMP_CAPI(TextDraw_IsProportional, bool(objectPtr textdraw)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + return textdraw_->isProportional(); +} + +OMP_CAPI(TextDraw_IsSelectable, bool(objectPtr textdraw)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + return textdraw_->isSelectable(); +} + +OMP_CAPI(TextDraw_GetAlignment, int(objectPtr textdraw)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, 0); + return static_cast(textdraw_->getAlignment()); +} + +OMP_CAPI(TextDraw_GetPreviewModel, int(objectPtr textdraw)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, 0); + return textdraw_->getPreviewModel(); +} + +OMP_CAPI(TextDraw_GetPreviewRot, bool(objectPtr textdraw, float* x, float* y, float* z, float* zoom)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + const Vector3& rotation = textdraw_->getPreviewRotation(); + *x = rotation.x; + *y = rotation.y; + *z = rotation.z; + *zoom = textdraw_->getPreviewZoom(); + return true; +} + +OMP_CAPI(TextDraw_GetPreviewVehColor, bool(objectPtr textdraw, int* color1, int* color2)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + Pair colors = textdraw_->getPreviewVehicleColour(); + *color1 = colors.first; + *color2 = colors.second; + return true; +} + +OMP_CAPI(TextDraw_SetStringForPlayer, bool(objectPtr textdraw, objectPtr player, StringCharPtr text)) +{ + POOL_ENTITY_RET(textdraws, ITextDraw, textdraw, textdraw_, false); + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + textdraw_->setTextForPlayer(*player_, text); + return true; +} + +/* + Per-Player TextDraws +*/ + +OMP_CAPI(PlayerTextDraw_Create, objectPtr(objectPtr player, float x, float y, StringCharPtr text, int* id)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, nullptr); + IPlayerTextDrawData* playerTextDraws = GetPlayerData(player_); + if (playerTextDraws) + { + IPlayerTextDraw* textdraw = playerTextDraws->create({ x, y }, text); + if (textdraw) + { + *id = textdraw->getID(); + return textdraw; + } + } + return nullptr; +} + +OMP_CAPI(PlayerTextDraw_Destroy, bool(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + if (playerData) + { + playerData->release(td->getID()); + return true; + } + return false; +} + +OMP_CAPI(PlayerTextDraw_FromID, objectPtr(objectPtr player, int textdrawid)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, nullptr); + IPlayerTextDrawData* playerTextDraws = GetPlayerData(player_); + if (playerTextDraws) + { + return playerTextDraws->get(textdrawid); + } + return nullptr; +} + +OMP_CAPI(PlayerTextDraw_GetID, int(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, INVALID_TEXTDRAW); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, textdraw_, INVALID_TEXTDRAW); + return textdraw_->getID(); +} + +OMP_CAPI(PlayerTextDraw_IsValid, bool(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + if (!playerData->get(td->getID())) + return false; + return true; +} + +OMP_CAPI(PlayerTextDraw_IsVisible, bool(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + return td->isShown(); +} + +OMP_CAPI(PlayerTextDraw_SetLetterSize, bool(objectPtr player, objectPtr textdraw, float x, float y)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->setLetterSize({ x, y }); + return true; +} + +OMP_CAPI(PlayerTextDraw_SetTextSize, bool(objectPtr player, objectPtr textdraw, float x, float y)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->setTextSize({ x, y }); + return true; +} + +OMP_CAPI(PlayerTextDraw_SetAlignment, bool(objectPtr player, objectPtr textdraw, int alignment)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->setAlignment(TextDrawAlignmentTypes(alignment)); + return true; +} + +OMP_CAPI(PlayerTextDraw_SetColor, bool(objectPtr player, objectPtr textdraw, uint32_t color)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->setColour(Colour::FromRGBA(color)); + return true; +} + +OMP_CAPI(PlayerTextDraw_UseBox, bool(objectPtr player, objectPtr textdraw, bool use)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->useBox(use); + return true; +} + +OMP_CAPI(PlayerTextDraw_SetBoxColor, bool(objectPtr player, objectPtr textdraw, uint32_t color)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->setBoxColour(Colour::FromRGBA(color)); + return true; +} + +OMP_CAPI(PlayerTextDraw_SetShadow, bool(objectPtr player, objectPtr textdraw, int size)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->setShadow(size); + return true; +} + +OMP_CAPI(PlayerTextDraw_SetOutline, bool(objectPtr player, objectPtr textdraw, int size)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->setOutline(size); + return true; +} + +OMP_CAPI(PlayerTextDraw_SetBackgroundColor, bool(objectPtr player, objectPtr textdraw, uint32_t color)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->setBackgroundColour(Colour::FromRGBA(color)); + return true; +} + +OMP_CAPI(PlayerTextDraw_SetFont, bool(objectPtr player, objectPtr textdraw, int font)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->setStyle(TextDrawStyle(font)); + return true; +} + +OMP_CAPI(PlayerTextDraw_SetProportional, bool(objectPtr player, objectPtr textdraw, bool set)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->setProportional(set); + return true; +} + +OMP_CAPI(PlayerTextDraw_SetSelectable, bool(objectPtr player, objectPtr textdraw, bool set)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->setSelectable(set); + return true; +} + +OMP_CAPI(PlayerTextDraw_Show, bool(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->show(); + return true; +} + +OMP_CAPI(PlayerTextDraw_Hide, bool(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->hide(); + return true; +} + +OMP_CAPI(PlayerTextDraw_SetString, bool(objectPtr player, objectPtr textdraw, StringCharPtr text)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->setText(text); + return true; +} + +OMP_CAPI(PlayerTextDraw_SetPreviewModel, bool(objectPtr player, objectPtr textdraw, int model)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->setPreviewModel(model); + return true; +} + +OMP_CAPI(PlayerTextDraw_SetPreviewRot, bool(objectPtr player, objectPtr textdraw, float rx, float ry, float rz, float zoom)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->setPreviewRotation({ rx, ry, rz }); + td->setPreviewZoom(zoom); + return true; +} + +OMP_CAPI(PlayerTextDraw_SetPreviewVehCol, bool(objectPtr player, objectPtr textdraw, int color1, int color2)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->setPreviewVehicleColour(color1, color2); + return true; +} + +OMP_CAPI(PlayerTextDraw_SetPos, bool(objectPtr player, objectPtr textdraw, float x, float y)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + td->setPosition({ x, y }); + return true; +} + +OMP_CAPI(PlayerTextDraw_GetString, bool(objectPtr player, objectPtr textdraw, OutputStringViewPtr text)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + auto result = td->getText(); + SET_CAPI_STRING_VIEW(text, result); + return true; +} + +OMP_CAPI(PlayerTextDraw_GetLetterSize, bool(objectPtr player, objectPtr textdraw, float* x, float* y)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + auto size = td->getLetterSize(); + *x = size.x; + *y = size.y; + return true; +} + +OMP_CAPI(PlayerTextDraw_GetTextSize, bool(objectPtr player, objectPtr textdraw, float* x, float* y)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + auto size = td->getTextSize(); + *x = size.x; + *y = size.y; + return true; +} + +OMP_CAPI(PlayerTextDraw_GetPos, bool(objectPtr player, objectPtr textdraw, float* x, float* y)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + auto pos = td->getPosition(); + *x = pos.x; + *y = pos.y; + return true; +} + +OMP_CAPI(PlayerTextDraw_GetColor, int(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, 0); + return td->getLetterColour().RGBA(); +} + +OMP_CAPI(PlayerTextDraw_GetBoxColor, int(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, 0); + return td->getBoxColour().RGBA(); +} + +OMP_CAPI(PlayerTextDraw_GetBackgroundColor, int(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, 0); + return td->getBackgroundColour().RGBA(); +} + +OMP_CAPI(PlayerTextDraw_GetShadow, int(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, 0); + return td->getShadow(); +} + +OMP_CAPI(PlayerTextDraw_GetOutline, int(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, 0); + return td->getOutline(); +} + +OMP_CAPI(PlayerTextDraw_GetFont, int(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, -1); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, -1); + return td->getStyle(); +} + +OMP_CAPI(PlayerTextDraw_IsBox, bool(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + return td->hasBox(); +} + +OMP_CAPI(PlayerTextDraw_IsProportional, bool(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + return td->isProportional(); +} + +OMP_CAPI(PlayerTextDraw_IsSelectable, bool(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + return td->isSelectable(); +} + +OMP_CAPI(PlayerTextDraw_GetAlignment, int(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, 0); + return static_cast(td->getAlignment()); +} + +OMP_CAPI(PlayerTextDraw_GetPreviewModel, int(objectPtr player, objectPtr textdraw)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, 0); + return td->getPreviewModel(); +} + +OMP_CAPI(PlayerTextDraw_GetPreviewRot, bool(objectPtr player, objectPtr textdraw, float* rx, float* ry, float* rz, float* zoom)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + auto rotation = td->getPreviewRotation(); + + *rx = rotation.x; + *ry = rotation.y; + *rz = rotation.z; + *zoom = td->getPreviewZoom(); + return true; +} + +OMP_CAPI(PlayerTextDraw_GetPreviewVehColor, bool(objectPtr player, objectPtr textdraw, int* color1, int* color2)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextDrawData, IPlayerTextDraw, textdraw, td, false); + Pair colours = td->getPreviewVehicleColour(); + *color1 = colours.first; + *color2 = colours.second; + return true; +} diff --git a/Server/Components/CAPI/Impl/TextDraws/Events.hpp b/Server/Components/CAPI/Impl/TextDraws/Events.hpp new file mode 100644 index 000000000..bce96a73f --- /dev/null +++ b/Server/Components/CAPI/Impl/TextDraws/Events.hpp @@ -0,0 +1,37 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#pragma once +#include "../ComponentManager.hpp" +#include "sdk.hpp" + +template +struct TextDrawEvents : public TextDrawEventHandler, public Singleton> +{ + virtual bool onPlayerCancelTextDrawSelection(IPlayer& player) override + { + ComponentManager::Get()->CallEvent("onPlayerCancelTextDrawSelection", EventReturnHandler::None, &player); + return true; + } + + virtual bool onPlayerCancelPlayerTextDrawSelection(IPlayer& player) override + { + ComponentManager::Get()->CallEvent("onPlayerCancelPlayerTextDrawSelection", EventReturnHandler::None, &player); + return true; + } + + void onPlayerClickTextDraw(IPlayer& player, ITextDraw& td) override + { + ComponentManager::Get()->CallEvent("onPlayerClickTextDraw", EventReturnHandler::None, &player, &td); + } + + void onPlayerClickPlayerTextDraw(IPlayer& player, IPlayerTextDraw& td) override + { + ComponentManager::Get()->CallEvent("onPlayerClickPlayerTextDraw", EventReturnHandler::None, &player, &td); + } +}; diff --git a/Server/Components/CAPI/Impl/TextLabels/APIs.cpp b/Server/Components/CAPI/Impl/TextLabels/APIs.cpp new file mode 100644 index 000000000..5d965e6ca --- /dev/null +++ b/Server/Components/CAPI/Impl/TextLabels/APIs.cpp @@ -0,0 +1,325 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" + +OMP_CAPI(TextLabel_Create, objectPtr(StringCharPtr text, uint32_t color, float x, float y, float z, float drawDistance, int virtualWorld, bool los, int* id)) +{ + ITextLabelsComponent* component = ComponentManager::Get()->textlabels; + if (component) + { + ITextLabel* textlabel = component->create(text, Colour::FromRGBA(color), { x, y, z }, drawDistance, virtualWorld, los); + if (textlabel) + { + *id = textlabel->getID(); + return textlabel; + } + } + return nullptr; +} + +OMP_CAPI(TextLabel_Destroy, bool(objectPtr textlabel)) +{ + POOL_ENTITY_RET(textlabels, ITextLabel, textlabel, textlabel_, false); + ComponentManager::Get()->textlabels->release(textlabel_->getID()); + return true; +} + +OMP_CAPI(TextLabel_FromID, objectPtr(int textlabelid)) +{ + ITextLabelsComponent* component = ComponentManager::Get()->textlabels; + if (component) + { + return component->get(textlabelid); + } + return nullptr; +} + +OMP_CAPI(TextLabel_GetID, int(objectPtr textlabel)) +{ + POOL_ENTITY_RET(textlabels, ITextLabel, textlabel, textlabel_, false); + return textlabel_->getID(); +} + +OMP_CAPI(TextLabel_AttachToPlayer, bool(objectPtr textlabel, objectPtr player, float offsetX, float offsetY, float offsetZ)) +{ + POOL_ENTITY_RET(textlabels, ITextLabel, textlabel, textlabel_, false); + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + textlabel_->attachToPlayer(*player_, { offsetX, offsetY, offsetZ }); + return true; +} + +OMP_CAPI(TextLabel_AttachToVehicle, bool(objectPtr textlabel, objectPtr vehicle, float offsetX, float offsetY, float offsetZ)) +{ + POOL_ENTITY_RET(textlabels, ITextLabel, textlabel, textlabel_, false); + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + textlabel_->attachToVehicle(*vehicle_, { offsetX, offsetY, offsetZ }); + return true; +} + +OMP_CAPI(TextLabel_UpdateText, bool(objectPtr textlabel, uint32_t color, StringCharPtr text)) +{ + POOL_ENTITY_RET(textlabels, ITextLabel, textlabel, textlabel_, false); + textlabel_->setColourAndText(Colour::FromRGBA(color), text); + return true; +} + +OMP_CAPI(TextLabel_IsValid, bool(objectPtr textlabel)) +{ + POOL_ENTITY_RET(textlabels, ITextLabel, textlabel, textlabel_, false); + if (!textlabels->get(textlabel_->getID())) + return false; + return true; +} + +OMP_CAPI(TextLabel_IsStreamedIn, bool(objectPtr player, objectPtr textlabel)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + POOL_ENTITY_RET(textlabels, ITextLabel, textlabel, textlabel_, false); + auto streamed = textlabel_->isStreamedInForPlayer(*player_); + return streamed; +} + +OMP_CAPI(TextLabel_GetText, bool(objectPtr textlabel, OutputStringViewPtr output)) +{ + POOL_ENTITY_RET(textlabels, ITextLabel, textlabel, textlabel_, false); + auto result = textlabel_->getText(); + SET_CAPI_STRING_VIEW(output, result); + return true; +} + +OMP_CAPI(TextLabel_GetColor, uint32_t(objectPtr textlabel)) +{ + POOL_ENTITY_RET(textlabels, ITextLabel, textlabel, textlabel_, 0); + return textlabel_->getColour().RGBA(); +} + +OMP_CAPI(TextLabel_GetPos, bool(objectPtr textlabel, float* x, float* y, float* z)) +{ + POOL_ENTITY_RET(textlabels, ITextLabel, textlabel, textlabel_, false); + const Vector3& pos = textlabel_->getPosition(); + + *x = pos.x; + *y = pos.y; + *z = pos.z; + return true; +} + +OMP_CAPI(TextLabel_SetDrawDistance, bool(objectPtr textlabel, float distance)) +{ + POOL_ENTITY_RET(textlabels, ITextLabel, textlabel, textlabel_, false); + textlabel_->setDrawDistance(distance); + return true; +} + +OMP_CAPI(TextLabel_GetDrawDistance, float(objectPtr textlabel)) +{ + POOL_ENTITY_RET(textlabels, ITextLabel, textlabel, textlabel_, 0.0f); + auto distance = textlabel_->getDrawDistance(); + return distance; +} + +OMP_CAPI(TextLabel_GetLOS, bool(objectPtr textlabel)) +{ + POOL_ENTITY_RET(textlabels, ITextLabel, textlabel, textlabel_, false); + auto los = textlabel_->getTestLOS(); + return los; +} + +OMP_CAPI(TextLabel_SetLOS, bool(objectPtr textlabel, bool status)) +{ + POOL_ENTITY_RET(textlabels, ITextLabel, textlabel, textlabel_, false); + textlabel_->setTestLOS(status); + return true; +} + +OMP_CAPI(TextLabel_GetVirtualWorld, int(objectPtr textlabel)) +{ + POOL_ENTITY_RET(textlabels, ITextLabel, textlabel, textlabel_, 0); + auto virtualWorld = textlabel_->getVirtualWorld(); + return virtualWorld; +} + +OMP_CAPI(TextLabel_SetVirtualWorld, bool(objectPtr textlabel, int world)) +{ + POOL_ENTITY_RET(textlabels, ITextLabel, textlabel, textlabel_, false); + textlabel_->setVirtualWorld(world); + return true; +} + +OMP_CAPI(TextLabel_GetAttachedData, bool(objectPtr textlabel, int* attached_player, int* attached_vehicle)) +{ + POOL_ENTITY_RET(textlabels, ITextLabel, textlabel, textlabel_, false); + const TextLabelAttachmentData& data = textlabel_->getAttachmentData(); + + *attached_player = data.playerID; + *attached_vehicle = data.vehicleID; + + return true; +} + +/* + Per-Player TextLabel +*/ + +OMP_CAPI(PlayerTextLabel_Create, objectPtr(objectPtr player, StringCharPtr text, uint32_t color, float x, float y, float z, float drawDistance, objectPtr attachedPlayer, objectPtr attachedVehicle, bool los, int* id)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, nullptr); + IPlayerTextLabelData* labelData = queryExtension(player_); + if (labelData) + { + IPlayerTextLabel* textlabel = nullptr; + + if (attachedPlayer) + { + textlabel = labelData->create(text, Colour::FromRGBA(color), { x, y, z }, drawDistance, los, *reinterpret_cast(attachedPlayer)); + } + else if (attachedVehicle) + { + textlabel = labelData->create(text, Colour::FromRGBA(color), { x, y, z }, drawDistance, los, *reinterpret_cast(attachedVehicle)); + } + else + { + textlabel = labelData->create(text, Colour::FromRGBA(color), { x, y, z }, drawDistance, los); + } + + if (textlabel) + { + *id = textlabel->getID(); + return textlabel; + } + } + return nullptr; +} + +OMP_CAPI(PlayerTextLabel_Destroy, bool(objectPtr player, objectPtr textlabel)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextLabelData, IPlayerTextLabel, textlabel, textlabel_, false); + auto data = queryExtension(player_); + if (!data) + { + return false; + } + data->release(textlabel_->getID()); + return true; +} + +OMP_CAPI(PlayerTextLabel_FromID, objectPtr(objectPtr player, int textlabelid)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, nullptr); + IPlayerTextLabelData* labelData = queryExtension(player_); + if (labelData) + { + return labelData->get(textlabelid); + } + return nullptr; +} + +OMP_CAPI(PlayerTextLabel_GetID, int(objectPtr player, objectPtr textlabel)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextLabelData, IPlayerTextLabel, textlabel, textlabel_, false); + return textlabel_->getID(); +} + +OMP_CAPI(PlayerTextLabel_UpdateText, bool(objectPtr player, objectPtr textlabel, uint32_t color, StringCharPtr text)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextLabelData, IPlayerTextLabel, textlabel, textlabel_, false); + textlabel_->setColourAndText(Colour::FromRGBA(color), text); + return true; +} + +OMP_CAPI(PlayerTextLabel_IsValid, bool(objectPtr player, objectPtr textlabel, bool* valid)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextLabelData, IPlayerTextLabel, textlabel, textlabel_, false); + if (!playerData->get(textlabel_->getID())) + return false; + return true; +} + +OMP_CAPI(PlayerTextLabel_GetText, bool(objectPtr player, objectPtr textlabel, OutputStringViewPtr output)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextLabelData, IPlayerTextLabel, textlabel, textlabel_, false); + auto result = textlabel_->getText(); + SET_CAPI_STRING_VIEW(output, result); + return true; +} + +OMP_CAPI(PlayerTextLabel_GetColor, bool(objectPtr player, objectPtr textlabel, uint32_t* color)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextLabelData, IPlayerTextLabel, textlabel, textlabel_, false); + *color = textlabel_->getColour().RGBA(); + return true; +} + +OMP_CAPI(PlayerTextLabel_GetPos, bool(objectPtr player, objectPtr textlabel, float* x, float* y, float* z)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextLabelData, IPlayerTextLabel, textlabel, textlabel_, false); + const Vector3& pos = textlabel_->getPosition(); + + *x = pos.x; + *y = pos.y; + *z = pos.z; + return true; +} + +OMP_CAPI(PlayerTextLabel_SetDrawDistance, bool(objectPtr player, objectPtr textlabel, float distance)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextLabelData, IPlayerTextLabel, textlabel, textlabel_, false); + textlabel_->setDrawDistance(distance); + return true; +} + +OMP_CAPI(PlayerTextLabel_GetDrawDistance, float(objectPtr player, objectPtr textlabel)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0.0f); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextLabelData, IPlayerTextLabel, textlabel, textlabel_, 0.0f); + auto distance = textlabel_->getDrawDistance(); + return distance; +} + +OMP_CAPI(PlayerTextLabel_GetLOS, bool(objectPtr player, objectPtr textlabel)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextLabelData, IPlayerTextLabel, textlabel, textlabel_, false); + auto los = textlabel_->getTestLOS(); + return los; +} + +OMP_CAPI(PlayerTextLabel_SetLOS, bool(objectPtr player, objectPtr textlabel, bool status)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextLabelData, IPlayerTextLabel, textlabel, textlabel_, false); + textlabel_->setTestLOS(status); + return true; +} + +OMP_CAPI(PlayerTextLabel_GetVirtualWorld, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + return player_->getVirtualWorld(); +} + +OMP_CAPI(PlayerTextLabel_GetAttachedData, bool(objectPtr player, objectPtr textlabel, int* attached_player, int* attached_vehicle)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + PLAYER_POOL_ENTITY_RET(player_, IPlayerTextLabelData, IPlayerTextLabel, textlabel, textlabel_, false); + const TextLabelAttachmentData& data = textlabel_->getAttachmentData(); + + *attached_player = data.playerID; + *attached_vehicle = data.vehicleID; + + return true; +} diff --git a/Server/Components/CAPI/Impl/Vehicles/APIs.cpp b/Server/Components/CAPI/Impl/Vehicles/APIs.cpp new file mode 100644 index 000000000..0d3630f38 --- /dev/null +++ b/Server/Components/CAPI/Impl/Vehicles/APIs.cpp @@ -0,0 +1,782 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include "../ComponentManager.hpp" +#include +#include +#include +#include + +OMP_CAPI(Vehicle_Create, objectPtr(int modelid, float x, float y, float z, float rotation, int color1, int color2, int respawnDelay, bool addSiren, int* id)) +{ + IVehiclesComponent* vehicles = ComponentManager().Get()->vehicles; + if (vehicles) + { + IVehicle* vehicle = vehicles->create(false, modelid, { x, y, z }, rotation, color1, color2, Seconds(respawnDelay), addSiren); + if (vehicle) + { + *id = vehicle->getID(); + return vehicle; + } + } + return nullptr; +} + +OMP_CAPI(Vehicle_Destroy, bool(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicles->release(vehicle_->getID()); + return true; +} + +OMP_CAPI(Vehicle_FromID, objectPtr(int vehicleid)) +{ + IVehiclesComponent* component = ComponentManager::Get()->vehicles; + if (component) + { + return component->get(vehicleid); + } + return nullptr; +} + +OMP_CAPI(Vehicle_GetID, int(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + return vehicle_->getID(); +} + +OMP_CAPI(Vehicle_GetMaxPassengerSeats, int(int modelid)) +{ + int seats = Impl::getVehiclePassengerSeats(modelid); + return seats; +} + +OMP_CAPI(Vehicle_IsStreamedIn, bool(objectPtr vehicle, objectPtr player)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + return vehicle_->isStreamedInForPlayer(*player_); +} + +OMP_CAPI(Vehicle_GetPos, bool(objectPtr vehicle, float* x, float* y, float* z)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + const Vector3& pos = vehicle_->getPosition(); + + *x = pos.x; + *y = pos.y; + *z = pos.z; + return true; +} + +OMP_CAPI(Vehicle_SetPos, bool(objectPtr vehicle, float x, float y, float z)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->setPosition({ x, y, z }); + return true; +} + +OMP_CAPI(Vehicle_GetZAngle, float(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0.0f); + float angle = vehicle_->getZAngle(); + return angle; +} + +OMP_CAPI(Vehicle_GetRotationQuat, bool(objectPtr vehicle, float* w, float* x, float* y, float* z)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + glm::quat rotQuat = vehicle_->getRotation().q; + *w = rotQuat.w; + *x = rotQuat.x; + *y = rotQuat.y; + *z = rotQuat.z; + return true; +} + +OMP_CAPI(Vehicle_GetDistanceFromPoint, float(objectPtr vehicle, float x, float y, float z)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0.0f); + return glm::distance(vehicle_->getPosition(), { x, y, z }); +} + +OMP_CAPI(Vehicle_SetZAngle, bool(objectPtr vehicle, float angle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->setZAngle(angle); + return true; +} + +OMP_CAPI(Vehicle_SetParamsForPlayer, bool(objectPtr vehicle, objectPtr player, int objective, int doors)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + VehicleParams params = vehicle_->getParams(); + params.objective = objective; + params.doors = doors; + vehicle_->setParamsForPlayer(*player_, params); + return true; +} + +OMP_CAPI(Vehicle_UseManualEngineAndLights, bool()) +{ + *ComponentManager().Get()->core->getConfig().getBool("game.use_manual_engine_and_lights") = true; + return true; +} + +OMP_CAPI(Vehicle_SetParamsEx, bool(objectPtr vehicle, int engine, int lights, int alarm, int doors, int bonnet, int boot, int objective)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + VehicleParams params = vehicle_->getParams(); + params.engine = engine; + params.lights = lights; + params.alarm = alarm; + params.doors = doors; + params.bonnet = bonnet; + params.boot = boot; + params.objective = objective; + vehicle_->setParams(params); + return true; +} + +OMP_CAPI(Vehicle_GetParamsEx, bool(objectPtr vehicle, int* engine, int* lights, int* alarm, int* doors, int* bonnet, int* boot, int* objective)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + const VehicleParams& params = vehicle_->getParams(); + *engine = params.engine; + *lights = params.lights; + *alarm = params.alarm; + *doors = params.doors; + *bonnet = params.bonnet; + *boot = params.boot; + *objective = params.objective; + return true; +} + +OMP_CAPI(Vehicle_GetParamsSirenState, int(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0); + int state = vehicle_->getParams().siren; + return state; +} + +OMP_CAPI(Vehicle_SetParamsCarDoors, bool(objectPtr vehicle, int frontLeft, int frontRight, int rearLeft, int rearRight)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + VehicleParams params = vehicle_->getParams(); + params.doorDriver = frontLeft; + params.doorPassenger = frontRight; + params.doorBackLeft = rearLeft; + params.doorBackRight = rearRight; + vehicle_->setParams(params); + return true; +} + +OMP_CAPI(Vehicle_GetParamsCarDoors, bool(objectPtr vehicle, int* frontLeft, int* frontRight, int* rearLeft, int* rearRight)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + const VehicleParams& params = vehicle_->getParams(); + *frontLeft = params.doorDriver; + *frontRight = params.doorPassenger; + *rearLeft = params.doorBackLeft; + *rearRight = params.doorBackRight; + return true; +} + +OMP_CAPI(Vehicle_SetParamsCarWindows, bool(objectPtr vehicle, int frontLeft, int frontRight, int rearLeft, int rearRight)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + VehicleParams params = vehicle_->getParams(); + params.windowDriver = frontLeft; + params.windowPassenger = frontRight; + params.windowBackLeft = rearLeft; + params.windowBackRight = rearRight; + vehicle_->setParams(params); + return true; +} + +OMP_CAPI(Vehicle_GetParamsCarWindows, bool(objectPtr vehicle, int* frontLeft, int* frontRight, int* rearLeft, int* rearRight)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + const VehicleParams& params = vehicle_->getParams(); + *frontLeft = params.windowDriver; + *frontRight = params.windowPassenger; + *rearLeft = params.windowBackLeft; + *rearRight = params.windowBackRight; + return true; +} + +OMP_CAPI(Vehicle_SetToRespawn, bool(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->respawn(); + return true; +} + +OMP_CAPI(Vehicle_LinkToInterior, bool(objectPtr vehicle, int interiorid)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->setInterior(interiorid); + return true; +} + +OMP_CAPI(Vehicle_AddComponent, bool(objectPtr vehicle, int componentid)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->addComponent(componentid); + return true; +} + +OMP_CAPI(Vehicle_RemoveComponent, bool(objectPtr vehicle, int componentid)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->removeComponent(componentid); + return true; +} + +OMP_CAPI(Vehicle_ChangeColor, bool(objectPtr vehicle, int color1, int color2)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->setColour(color1, color2); + return true; +} + +OMP_CAPI(Vehicle_ChangePaintjob, bool(objectPtr vehicle, int paintjobid)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->setPaintJob(paintjobid); + return true; +} + +OMP_CAPI(Vehicle_SetHealth, bool(objectPtr vehicle, float health)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->setHealth(health); + return true; +} + +OMP_CAPI(Vehicle_GetHealth, float(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0.0f); + float health = vehicle_->getHealth(); + return health; +} + +OMP_CAPI(Vehicle_AttachTrailer, bool(objectPtr trailer, objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + ENTITY_CAST_RET(IVehicle, trailer, trailer_, false); + vehicle_->attachTrailer(*trailer_); + return true; +} + +OMP_CAPI(Vehicle_DetachTrailer, bool(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->detachTrailer(); + return true; +} + +OMP_CAPI(Vehicle_IsTrailerAttached, bool(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + return vehicle_->getTrailer() != nullptr; +} + +OMP_CAPI(Vehicle_GetTrailer, objectPtr(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, nullptr); + IVehicle* trailer = vehicle_->getTrailer(); + if (trailer) + { + return trailer; + } + return nullptr; +} + +OMP_CAPI(Vehicle_SetNumberPlate, bool(objectPtr vehicle, StringCharPtr numberPlate)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->setPlate(numberPlate); + return true; +} + +OMP_CAPI(Vehicle_GetModel, int(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0); + return vehicle_->getModel(); +} + +OMP_CAPI(Vehicle_GetComponentInSlot, int(objectPtr vehicle, int slot)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0); + return vehicle_->getComponentInSlot(slot); +} + +OMP_CAPI(Vehicle_GetComponentType, int(int componentid)) +{ + return Impl::getVehicleComponentSlot(componentid); +} + +OMP_CAPI(Vehicle_CanHaveComponent, bool(int modelid, int componentid)) +{ + return Impl::isValidComponentForVehicleModel(modelid, componentid); +} + +OMP_CAPI(Vehicle_GetRandomColorPair, bool(int modelid, int* color1, int* color2, int* color3, int* color4)) +{ + Impl::getRandomVehicleColour(modelid, *color1, *color2, *color3, *color4); + return true; +} + +OMP_CAPI(Vehicle_ColorIndexToColor, int(int colorIndex, int alpha)) +{ + return Impl::carColourIndexToColour(colorIndex, alpha); +} + +OMP_CAPI(Vehicle_Repair, bool(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->repair(); + return true; +} + +OMP_CAPI(Vehicle_GetVelocity, bool(objectPtr vehicle, float* x, float* y, float* z)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + const Vector3& velocity = vehicle_->getVelocity(); + *x = velocity.x; + *y = velocity.y; + *z = velocity.z; + return true; +} + +OMP_CAPI(Vehicle_SetVelocity, bool(objectPtr vehicle, float x, float y, float z)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->setVelocity({ x, y, z }); + return true; +} + +OMP_CAPI(Vehicle_SetAngularVelocity, bool(objectPtr vehicle, float x, float y, float z)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->setAngularVelocity({ x, y, z }); + return true; +} + +OMP_CAPI(Vehicle_GetDamageStatus, bool(objectPtr vehicle, int* panels, int* doors, int* lights, int* tires)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->getDamageStatus(*panels, *doors, *lights, *tires); + return true; +} + +OMP_CAPI(Vehicle_UpdateDamageStatus, bool(objectPtr vehicle, int panels, int doors, int lights, int tires)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->setDamageStatus(panels, doors, lights, tires); + return true; +} + +OMP_CAPI(Vehicle_GetModelInfo, bool(int vehiclemodel, int infotype, float* x, float* y, float* z)) +{ + Vector3 pos = {}; + Impl::getVehicleModelInfo(vehiclemodel, VehicleModelInfoType(infotype), pos); + *x = pos.x; + *y = pos.y; + *z = pos.z; + return true; +} + +OMP_CAPI(Vehicle_SetVirtualWorld, bool(objectPtr vehicle, int virtualWorld)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->setVirtualWorld(virtualWorld); + return true; +} + +OMP_CAPI(Vehicle_GetVirtualWorld, int(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0); + return vehicle_->getVirtualWorld(); +} + +OMP_CAPI(Vehicle_GetLandingGearState, int(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0); + int state = !vehicle_->getLandingGearState(); + return state; +} + +OMP_CAPI(Vehicle_IsValid, bool(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + if (!vehicles->get(vehicle_->getID())) + return false; + return true; +} + +OMP_CAPI(Vehicle_AddStatic, objectPtr(int modelid, float x, float y, float z, float angle, int color1, int color2, int* id)) +{ + IVehiclesComponent* vehicles = ComponentManager().Get()->vehicles; + if (vehicles) + { + IVehicle* vehicle = vehicles->create(true, modelid, { x, y, z }, angle, color1, color2, Seconds(120), false); + if (vehicle) + { + *id = vehicle->getID(); + return vehicle; + } + } + return nullptr; +} + +OMP_CAPI(Vehicle_AddStaticEx, objectPtr(int modelid, float x, float y, float z, float angle, int color1, int color2, int respawnDelay, bool addSiren, int* id)) +{ + IVehiclesComponent* vehicles = ComponentManager().Get()->vehicles; + if (vehicles) + { + IVehicle* vehicle = vehicles->create(true, modelid, { x, y, z }, angle, color1, color2, Seconds(respawnDelay), addSiren); + if (vehicle) + { + *id = vehicle->getID(); + return vehicle; + } + } + return nullptr; +} + +OMP_CAPI(Vehicle_EnableFriendlyFire, bool()) +{ + *ComponentManager::Get()->core->getConfig().getBool("game.use_vehicle_friendly_fire") = true; + return true; +} + +OMP_CAPI(Vehicle_GetSpawnInfo, bool(objectPtr vehicle, float* x, float* y, float* z, float* rotation, int* color1, int* color2)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + const VehicleSpawnData& data = vehicle_->getSpawnData(); + + *x = data.position.x; + *y = data.position.y; + *z = data.position.z; + *rotation = data.zRotation; + *color1 = data.colour1; + *color2 = data.colour2; + return true; +} + +OMP_CAPI(Vehicle_SetSpawnInfo, bool(objectPtr vehicle, int modelid, float x, float y, float z, float rotation, int color1, int color2, int respawn_time, int interior)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + const VehicleSpawnData& data = vehicle_->getSpawnData(); + + vehicle_->setSpawnData({ respawn_time >= -1 ? Seconds(respawn_time) : data.respawnDelay, modelid, { x, y, z }, rotation, color1, color2, data.siren, interior != -2 ? interior : data.interior }); + return true; +} + +OMP_CAPI(Vehicle_GetModelCount, int(int modelid)) +{ + if (modelid < 400 || modelid > 611) + return 0; + + auto& models = ComponentManager::Get()->vehicles->models(); + int count = models[modelid - 400]; + return count; +} + +OMP_CAPI(Vehicle_GetModelsUsed, int()) +{ + auto& vehicle_models = ComponentManager::Get()->vehicles->models(); + + int models = std::count_if(vehicle_models.begin(), vehicle_models.end(), [](uint8_t model_instances) + { + return model_instances > 0; + }); + return models; +} + +OMP_CAPI(Vehicle_GetPaintjob, int(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0); + return vehicle_->getPaintJob(); +} + +OMP_CAPI(Vehicle_GetColor, bool(objectPtr vehicle, int* color1, int* color2)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + Pair colors = vehicle_->getColour(); + + *color1 = colors.first; + *color2 = colors.second; + return true; +} + +OMP_CAPI(Vehicle_GetInterior, int(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0); + return vehicle_->getInterior(); +} + +OMP_CAPI(Vehicle_GetNumberPlate, bool(objectPtr vehicle, OutputStringViewPtr numberPlate)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0); + StringView plate = vehicle_->getPlate(); + SET_CAPI_STRING_VIEW(numberPlate, plate); + return true; +} + +OMP_CAPI(Vehicle_SetRespawnDelay, bool(objectPtr vehicle, int respawn_delay)) +{ + if (respawn_delay < -1) + { + return false; + } + + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->setRespawnDelay(Seconds(respawn_delay)); + return true; +} + +OMP_CAPI(Vehicle_GetRespawnDelay, int(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0); + int delay = vehicle_->getRespawnDelay().count(); + return delay; +} + +OMP_CAPI(Vehicle_GetCab, objectPtr(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, nullptr); + return vehicle_->getCab(); +} + +OMP_CAPI(Vehicle_GetTower, objectPtr(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, nullptr); + return vehicle_->getCab(); +} + +OMP_CAPI(Vehicle_GetOccupiedTick, int(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0); + int tick = std::chrono::duration_cast(Time::now() - vehicle_->getLastOccupiedTime()).count(); + return tick; +} + +OMP_CAPI(Vehicle_GetRespawnTick, int(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0); + int tick = std::chrono::duration_cast(Time::now() - vehicle_->getLastSpawnTime()).count(); + return tick; +} + +OMP_CAPI(Vehicle_HasBeenOccupied, bool(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + bool occupied = vehicle_->hasBeenOccupied(); + return occupied; +} + +OMP_CAPI(Vehicle_IsOccupied, bool(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + bool occupied = vehicle_->isOccupied(); + return occupied; +} + +OMP_CAPI(Vehicle_IsDead, bool(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + bool dead = vehicle_->isDead(); + return dead; +} + +OMP_CAPI(Vehicle_SetParamsSirenState, bool(objectPtr vehicle, bool siren_state)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + VehicleParams params = vehicle_->getParams(); + params.siren = siren_state; + + vehicle_->setParams(params); + return true; +} + +OMP_CAPI(Vehicle_ToggleSirenEnabled, bool(objectPtr vehicle, bool status)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + vehicle_->setSiren(status); + return true; +} + +OMP_CAPI(Vehicle_IsSirenEnabled, bool(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + bool enabled = vehicle_->getSpawnData().siren; + return enabled; +} + +OMP_CAPI(Vehicle_GetLastDriver, objectPtr(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, nullptr); + int lastDriver = vehicle_->getLastDriverPoolID(); + + auto players = ComponentManager::Get()->players; + if (players) + { + return players->get(lastDriver); + } + + return nullptr; +} + +OMP_CAPI(Vehicle_GetDriver, objectPtr(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, nullptr); + return vehicle_->getDriver(); +} + +OMP_CAPI(Player_IsInModShop, bool(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, false); + IPlayerVehicleData* data = queryExtension(player_); + if (data) + { + return data->isInModShop(); + } + return false; +} + +OMP_CAPI(Player_GetSirenState, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + IPlayerVehicleData* data = queryExtension(player_); + IVehicle* vehicle = data->getVehicle(); + if (vehicle) + { + return vehicle->getSirenState(); + } + return 0; +} + +OMP_CAPI(Player_GetLandingGearState, int(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + IPlayerVehicleData* data = queryExtension(player_); + IVehicle* vehicle = data->getVehicle(); + if (vehicle) + { + int state = vehicle->getLandingGearState(); + return state; + } + return 0; +} + +OMP_CAPI(Player_GetHydraReactorAngle, uint32_t(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0); + IPlayerVehicleData* data = queryExtension(player_); + IVehicle* vehicle = data->getVehicle(); + if (vehicle) + { + return vehicle->getHydraThrustAngle(); + } + return 0; +} + +OMP_CAPI(Player_GetTrainSpeed, float(objectPtr player)) +{ + POOL_ENTITY_RET(players, IPlayer, player, player_, 0.0f); + IPlayerVehicleData* data = queryExtension(player_); + IVehicle* vehicle = data->getVehicle(); + if (vehicle) + { + return vehicle->getTrainSpeed(); + } + return 0.0f; +} + +OMP_CAPI(Vehicle_GetSirenState, int(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0); + return vehicle_->getSirenState(); +} + +OMP_CAPI(Vehicle_GetHydraReactorAngle, uint32_t(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0); + return vehicle_->getHydraThrustAngle(); +} + +OMP_CAPI(Vehicle_GetTrainSpeed, float(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0.0f); + return vehicle_->getTrainSpeed(); +} + +OMP_CAPI(Vehicle_GetMatrix, bool(objectPtr vehicle, float* rightX, float* rightY, float* rightZ, float* upX, float* upY, float* upZ, float* atX, float* atY, float* atZ)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, false); + glm::mat3 mat = glm::transpose(glm::mat3_cast(vehicle_->getRotation().q)); + + *rightX = mat[0][0]; + *rightY = mat[0][1]; + *rightZ = mat[0][2]; + *upX = mat[1][0]; + *upY = mat[1][1]; + *upZ = mat[1][2]; + *atX = mat[2][0]; + *atY = mat[2][1]; + *atZ = mat[2][2]; + return true; +} + +OMP_CAPI(Vehicle_GetOccupant, objectPtr(objectPtr vehicle, int seat)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, nullptr); + IPlayer* driver = vehicle_->getDriver(); + // Looking for driver + if (seat == 0) + { + return driver; + } + // Looking for a passenger + else + { + const FlatHashSet& passengers = vehicle_->getPassengers(); + for (auto& passenger : passengers) + { + if (passenger) + { + IPlayerVehicleData* data = queryExtension(passenger); + if (data && data->getSeat() == seat) + { + return passenger; + } + } + } + } + return nullptr; +} + +OMP_CAPI(Vehicle_CountOccupants, int(objectPtr vehicle)) +{ + POOL_ENTITY_RET(vehicles, IVehicle, vehicle, vehicle_, 0); + IPlayer* driver = vehicle_->getDriver(); + const FlatHashSet& passengers = vehicle_->getPassengers(); + int occupants = 0; + + if (driver) + { + occupants++; + } + occupants += passengers.size(); + + return occupants; +} diff --git a/Server/Components/CAPI/Impl/Vehicles/Events.hpp b/Server/Components/CAPI/Impl/Vehicles/Events.hpp new file mode 100644 index 000000000..733a22a3c --- /dev/null +++ b/Server/Components/CAPI/Impl/Vehicles/Events.hpp @@ -0,0 +1,87 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#pragma once +#include "../ComponentManager.hpp" +#include "sdk.hpp" + +template +struct VehicleEvents : public VehicleEventHandler, public Singleton> +{ + void onVehicleStreamIn(IVehicle& vehicle, IPlayer& player) override + { + ComponentManager::Get()->CallEvent("onVehicleStreamIn", EventReturnHandler::None, &vehicle, &player); + } + + void onVehicleStreamOut(IVehicle& vehicle, IPlayer& player) override + { + ComponentManager::Get()->CallEvent("onVehicleStreamOut", EventReturnHandler::None, &vehicle, &player); + } + + void onVehicleDeath(IVehicle& vehicle, IPlayer& player) override + { + ComponentManager::Get()->CallEvent("onVehicleDeath", EventReturnHandler::None, &vehicle, &player); + } + + void onPlayerEnterVehicle(IPlayer& player, IVehicle& vehicle, bool passenger) override + { + ComponentManager::Get()->CallEvent("onPlayerEnterVehicle", EventReturnHandler::None, &player, &vehicle, passenger); + } + + void onPlayerExitVehicle(IPlayer& player, IVehicle& vehicle) override + { + ComponentManager::Get()->CallEvent("onPlayerExitVehicle", EventReturnHandler::None, &player, &vehicle); + } + + void onVehicleDamageStatusUpdate(IVehicle& vehicle, IPlayer& player) override + { + ComponentManager::Get()->CallEvent("onVehicleDamageStatusUpdate", EventReturnHandler::None, &vehicle, &player); + } + + bool onVehiclePaintJob(IPlayer& player, IVehicle& vehicle, int paintJob) override + { + return ComponentManager::Get()->CallEvent("onVehiclePaintJob", EventReturnHandler::StopAtFalse, &player, &vehicle, paintJob); + } + + bool onVehicleMod(IPlayer& player, IVehicle& vehicle, int component) override + { + return ComponentManager::Get()->CallEvent("onVehicleMod", EventReturnHandler::StopAtFalse, &player, &vehicle, component); + } + + bool onVehicleRespray(IPlayer& player, IVehicle& vehicle, int colour1, int colour2) override + { + return ComponentManager::Get()->CallEvent("onVehicleRespray", EventReturnHandler::StopAtFalse, &player, &vehicle, colour1, colour2); + } + + void onEnterExitModShop(IPlayer& player, bool enterexit, int interiorID) override + { + ComponentManager::Get()->CallEvent("onEnterExitModShop", EventReturnHandler::None, &player, enterexit, interiorID); + } + + void onVehicleSpawn(IVehicle& vehicle) override + { + ComponentManager::Get()->CallEvent("onVehicleSpawn", EventReturnHandler::None, &vehicle); + } + + bool onUnoccupiedVehicleUpdate(IVehicle& vehicle, IPlayer& player, const UnoccupiedVehicleUpdate updateData) override + { + return ComponentManager::Get()->CallEvent("onUnoccupiedVehicleUpdate", EventReturnHandler::StopAtFalse, &vehicle, &player, int(updateData.seat), + updateData.position.x, updateData.position.y, updateData.position.z, + updateData.velocity.x, updateData.velocity.y, updateData.velocity.z); + } + + bool onTrailerUpdate(IPlayer& player, IVehicle& trailer) override + { + return ComponentManager::Get()->CallEvent("onTrailerUpdate", EventReturnHandler::StopAtFalse, &player, &trailer); + } + + bool onVehicleSirenStateChange(IPlayer& player, IVehicle& vehicle, uint8_t sirenState) override + { + return ComponentManager::Get()->CallEvent("onVehicleSirenStateChange", EventReturnHandler::StopAtFalse, &player, &vehicle, int(sirenState)); + } +}; diff --git a/Server/Components/CAPI/Utils/MacroMagic.hpp b/Server/Components/CAPI/Utils/MacroMagic.hpp new file mode 100644 index 000000000..02842389b --- /dev/null +++ b/Server/Components/CAPI/Utils/MacroMagic.hpp @@ -0,0 +1,76 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#pragma once +#include +#include + +#define EXPORT_OMP_API extern "C" SDK_EXPORT + +using OutputStringViewPtr = CAPIStringView*; +using OutputStringBufferPtr = CAPIStringBuffer*; +using StringCharPtr = const char*; +using objectPtr = void*; +using voidPtr = void*; + +#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__) +#define PRIMITIVE_CAT(a, ...) a##__VA_ARGS__ + +#define EMPTY() +#define DEFER(id) id EMPTY() +#define OBSTRUCT(...) __VA_ARGS__ DEFER(EMPTY)() +#define EXPAND(...) __VA_ARGS__ + +#define OMP_CAPI__WITHOUT_PARAMS_size_t(...) size_t +#define OMP_CAPI__WITHOUT_PARAMS_unsigned(...) unsigned +#define OMP_CAPI__WITHOUT_PARAMS_int(...) int +#define OMP_CAPI__WITHOUT_PARAMS_float(...) float +#define OMP_CAPI__WITHOUT_PARAMS_bool(...) bool +#define OMP_CAPI__WITHOUT_PARAMS_void(...) void +#define OMP_CAPI__WITHOUT_PARAMS_cell(...) cell +#define OMP_CAPI__WITHOUT_PARAMS_id(...) id +#define OMP_CAPI__WITHOUT_PARAMS_int8_t(...) int8_t +#define OMP_CAPI__WITHOUT_PARAMS_int16_t(...) int16_t +#define OMP_CAPI__WITHOUT_PARAMS_int32_t(...) int32_t +#define OMP_CAPI__WITHOUT_PARAMS_int64_t(...) int64_t +#define OMP_CAPI__WITHOUT_PARAMS_uint8_t(...) uint8_t +#define OMP_CAPI__WITHOUT_PARAMS_uint16_t(...) uint16_t +#define OMP_CAPI__WITHOUT_PARAMS_uint32_t(...) uint32_t +#define OMP_CAPI__WITHOUT_PARAMS_uint64_t(...) uint64_t +#define OMP_CAPI__WITHOUT_PARAMS_objectPtr(...) objectPtr +#define OMP_CAPI__WITHOUT_PARAMS_voidPtr(...) voidPtr + +#define OMP_CAPI__WITHOUT_RETURN_size_t(...) __VA_ARGS__ +#define OMP_CAPI__WITHOUT_RETURN_unsigned(...) __VA_ARGS__ +#define OMP_CAPI__WITHOUT_RETURN_int(...) __VA_ARGS__ +#define OMP_CAPI__WITHOUT_RETURN_float(...) __VA_ARGS__ +#define OMP_CAPI__WITHOUT_RETURN_bool(...) __VA_ARGS__ +#define OMP_CAPI__WITHOUT_RETURN_void(...) __VA_ARGS__ +#define OMP_CAPI__WITHOUT_RETURN_cell(...) __VA_ARGS__ +#define OMP_CAPI__WITHOUT_RETURN_id(...) __VA_ARGS__ +#define OMP_CAPI__WITHOUT_RETURN_int8_t(...) __VA_ARGS__ +#define OMP_CAPI__WITHOUT_RETURN_int16_t(...) __VA_ARGS__ +#define OMP_CAPI__WITHOUT_RETURN_int32_t(...) __VA_ARGS__ +#define OMP_CAPI__WITHOUT_RETURN_int64_t(...) __VA_ARGS__ +#define OMP_CAPI__WITHOUT_RETURN_uint8_t(...) __VA_ARGS__ +#define OMP_CAPI__WITHOUT_RETURN_uint16_t(...) __VA_ARGS__ +#define OMP_CAPI__WITHOUT_RETURN_uint32_t(...) __VA_ARGS__ +#define OMP_CAPI__WITHOUT_RETURN_uint64_t(...) __VA_ARGS__ +#define OMP_CAPI__WITHOUT_RETURN_objectPtr(...) __VA_ARGS__ +#define OMP_CAPI__WITHOUT_RETURN_voidPtr(...) __VA_ARGS__ + +#define OMP_CAPI__RETURN(params) OMP_CAPI__WITHOUT_PARAMS_##params +#define OMP_CAPI__PARAMETERS(params) CAT(OMP_CAPI__WITHOUT_RETURN_, params) + +#define OMP_CAPI(name, params) \ + EXPORT_OMP_API OMP_CAPI__RETURN(params) name(OMP_CAPI__PARAMETERS(params)) + +#define RETURN_VALUE(x) returnValue(x) +#define RETURN_ERROR(x) returnError(x) +#define UNDEFINED_FAILED_RETURN(x) RETURN_ERROR("undefined error") +#define FUNCTION_FAIL_RETURN RETURN_ERROR(this->name_ + ": error while executing.") diff --git a/Server/Components/CAPI/main.cpp b/Server/Components/CAPI/main.cpp new file mode 100644 index 000000000..2ed468961 --- /dev/null +++ b/Server/Components/CAPI/main.cpp @@ -0,0 +1,87 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. + * + * The original code is copyright (c) 2024, open.mp team and contributors. + */ + +#include +#include "Impl/ComponentManager.hpp" + +class CAPIComponent final : public IComponent +{ +private: + ICore* core_ = nullptr; + +public: + PROVIDE_UID(0x10467DD8D4C56FC6) + + ~CAPIComponent() + { + ComponentManager::Get()->FreeEvents(); + } + + StringView componentName() const override + { + return "C-API"; + } + + SemanticVersion componentVersion() const override + { + return SemanticVersion(OMP_VERSION_MAJOR, OMP_VERSION_MINOR, OMP_VERSION_PATCH, BUILD_NUMBER); + } + + void onLoad(ICore* c) override + { + core_ = c; + } + + void onInit(IComponentList* components) override + { + ComponentManager::Get()->Init(core_, components); + + ComponentManager::Get()->InitializeEvents(); + } + + void onReady() override + { + } + + void onFree(IComponent* component) override + { +#define COMPONENT_UNLOADED(var) \ + if (component == var) \ + var = nullptr; + + auto mgr = ComponentManager::Get(); + + COMPONENT_UNLOADED(mgr->actors) + COMPONENT_UNLOADED(mgr->console) + COMPONENT_UNLOADED(mgr->checkpoints) + COMPONENT_UNLOADED(mgr->classes) + COMPONENT_UNLOADED(mgr->dialogs) + COMPONENT_UNLOADED(mgr->gangzones) + COMPONENT_UNLOADED(mgr->menus) + COMPONENT_UNLOADED(mgr->objects) + COMPONENT_UNLOADED(mgr->pickups) + COMPONENT_UNLOADED(mgr->textdraws) + COMPONENT_UNLOADED(mgr->textlabels) + COMPONENT_UNLOADED(mgr->vehicles) + COMPONENT_UNLOADED(mgr->models) + } + + void free() override + { + delete this; + } + + void reset() override + { + } +}; + +COMPONENT_ENTRY_POINT() +{ + return new CAPIComponent(); +} diff --git a/Server/Components/CMakeLists.txt b/Server/Components/CMakeLists.txt index 76b6021db..5353bfa2c 100644 --- a/Server/Components/CMakeLists.txt +++ b/Server/Components/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(Actors) +add_subdirectory(CAPI) add_subdirectory(Checkpoints) add_subdirectory(Classes) add_subdirectory(Console) diff --git a/Server/Components/Pawn/Manager/Manager.hpp b/Server/Components/Pawn/Manager/Manager.hpp index 5218db894..7ea41c70b 100644 --- a/Server/Components/Pawn/Manager/Manager.hpp +++ b/Server/Components/Pawn/Manager/Manager.hpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -43,7 +44,6 @@ #include "../PluginManager/PluginManager.hpp" #include "../Script/Script.hpp" -#include "../Singleton.hpp" using namespace Impl; diff --git a/Server/Components/Pawn/PluginManager/PluginManager.hpp b/Server/Components/Pawn/PluginManager/PluginManager.hpp index 57fd5fc28..2915a2701 100644 --- a/Server/Components/Pawn/PluginManager/PluginManager.hpp +++ b/Server/Components/Pawn/PluginManager/PluginManager.hpp @@ -13,7 +13,6 @@ #include #include "../Plugin/Plugin.h" -#include "../Singleton.hpp" using namespace Impl; diff --git a/Server/Components/Pawn/Scripting/Actor/Events.hpp b/Server/Components/Pawn/Scripting/Actor/Events.hpp index 042df7cef..fce87dd7b 100644 --- a/Server/Components/Pawn/Scripting/Actor/Events.hpp +++ b/Server/Components/Pawn/Scripting/Actor/Events.hpp @@ -8,7 +8,6 @@ #pragma once #include "../../Manager/Manager.hpp" -#include "../../Singleton.hpp" #include "sdk.hpp" struct ActorEvents : public ActorEventHandler, public Singleton diff --git a/Server/Components/Pawn/Scripting/Checkpoint/Events.hpp b/Server/Components/Pawn/Scripting/Checkpoint/Events.hpp index 9b315f839..7e583c951 100644 --- a/Server/Components/Pawn/Scripting/Checkpoint/Events.hpp +++ b/Server/Components/Pawn/Scripting/Checkpoint/Events.hpp @@ -8,7 +8,6 @@ #pragma once #include "../../Manager/Manager.hpp" -#include "../../Singleton.hpp" #include "sdk.hpp" struct CheckpointEvents : public PlayerCheckpointEventHandler, public Singleton diff --git a/Server/Components/Pawn/Scripting/Class/Events.hpp b/Server/Components/Pawn/Scripting/Class/Events.hpp index 8fea6918a..1120cdcbf 100644 --- a/Server/Components/Pawn/Scripting/Class/Events.hpp +++ b/Server/Components/Pawn/Scripting/Class/Events.hpp @@ -8,7 +8,6 @@ #pragma once #include "../../Manager/Manager.hpp" -#include "../../Singleton.hpp" #include "sdk.hpp" struct ClassEvents : public ClassEventHandler, public Singleton diff --git a/Server/Components/Pawn/Scripting/Core/Events.hpp b/Server/Components/Pawn/Scripting/Core/Events.hpp index 51ec2ea8e..b10ef9540 100644 --- a/Server/Components/Pawn/Scripting/Core/Events.hpp +++ b/Server/Components/Pawn/Scripting/Core/Events.hpp @@ -8,7 +8,6 @@ #pragma once #include "../../Manager/Manager.hpp" -#include "../../Singleton.hpp" #include "sdk.hpp" struct CoreEvents : public ConsoleEventHandler, public Singleton diff --git a/Server/Components/Pawn/Scripting/Core/Natives.cpp b/Server/Components/Pawn/Scripting/Core/Natives.cpp index 8a0952932..5ba997041 100644 --- a/Server/Components/Pawn/Scripting/Core/Natives.cpp +++ b/Server/Components/Pawn/Scripting/Core/Natives.cpp @@ -12,6 +12,7 @@ #include "../Types.hpp" #include "../../format.hpp" #include +#include #include #include #include @@ -348,164 +349,27 @@ SCRIPT_API(GameTextForAllf, bool(int time, int style, cell const* format)) return true; } -int getConfigOptionAsInt(std::string const& cvar) -{ - IConfig* config = PawnManager::Get()->config; - auto res = config->getNameFromAlias(cvar); - bool* v0 = nullptr; - int* v1 = nullptr; - if (!res.second.empty()) - { - if (res.first) - { - PawnManager::Get()->core->logLn(LogLevel::Warning, "Deprecated console variable \"%s\", use \"%.*s\" instead.", cvar.c_str(), PRINT_VIEW(res.second)); - } - if (!(v1 = config->getInt(res.second))) - { - v0 = config->getBool(res.second); - } - } - else - { - if (!(v1 = config->getInt(cvar))) - { - v0 = config->getBool(cvar); - } - } - if (v1) - { - return *v1; - } - else if (v0) - { - PawnManager::Get()->core->logLn(LogLevel::Warning, "Boolean console variable \"%s\" retreived as integer.", cvar.c_str()); - return *v0; - } - else - { - return 0; - } -} - -bool getConfigOptionAsBool(std::string const& cvar) -{ - IConfig* config = PawnManager::Get()->config; - auto res = config->getNameFromAlias(cvar); - bool* v0 = nullptr; - int* v1 = nullptr; - if (!res.second.empty()) - { - if (res.first) - { - PawnManager::Get()->core->logLn(LogLevel::Warning, "Deprecated console variable \"%s\", use \"%.*s\" instead.", cvar.c_str(), PRINT_VIEW(res.second)); - } - if (!(v0 = config->getBool(res.second))) - { - v1 = config->getInt(res.second); - } - } - else - { - if (!(v0 = config->getBool(cvar))) - { - v1 = config->getInt(cvar); - } - } - if (v0) - { - return *v0; - } - else if (v1) - { - PawnManager::Get()->core->logLn(LogLevel::Warning, "Integer console variable \"%s\" retreived as boolean.", cvar.c_str()); - return *v1 != 0; - } - else - { - return false; - } -} - -float getConfigOptionAsFloat(std::string const& cvar) -{ - IConfig* config = PawnManager::Get()->config; - auto res = config->getNameFromAlias(cvar); - float* var = nullptr; - if (!res.second.empty()) - { - if (res.first) - { - PawnManager::Get()->core->logLn(LogLevel::Warning, "Deprecated console variable \"%s\", use \"%.*s\" instead.", cvar.c_str(), PRINT_VIEW(res.second)); - } - var = config->getFloat(res.second); - } - else - { - var = config->getFloat(cvar); - } - if (var) - { - return *var; - } - else - { - return 0.0f; - } -} - -int getConfigOptionAsString(std::string const& cvar, OutputOnlyString& buffer) -{ - // Special case, converting `gamemode0` to `pawn.main_scripts[0]`. It is the only string to - // array change. - IConfig* config = PawnManager::Get()->config; - bool gm = cvar.substr(0, 8) == "gamemode"; - auto res = config->getNameFromAlias(gm ? "gamemode" : cvar); - if (!res.second.empty()) - { - if (res.first) - { - PawnManager::Get()->core->logLn(LogLevel::Warning, "Deprecated console variable \"%s\", use \"%.*s\" instead.", cvar.c_str(), PRINT_VIEW(res.second)); - } - if (gm) - { - size_t i = std::stoi("0" + cvar.substr(8)); - DynamicArray mainScripts(i + 1); - size_t n = config->getStrings(res.second, Span(mainScripts.data(), mainScripts.size())); - if (i < n) - { - buffer = mainScripts[i]; - } - } - else - { - buffer = config->getString(res.second); - } - } - else - { - buffer = config->getString(cvar); - } - return std::get(buffer).length(); -} - SCRIPT_API(GetConsoleVarAsBool, bool(std::string const& cvar)) { - return getConfigOptionAsBool(cvar); + return getConfigOptionAsBool(PawnManager::Get()->core, cvar); } SCRIPT_API(GetConsoleVarAsInt, int(std::string const& cvar)) { - return getConfigOptionAsInt(cvar); + return getConfigOptionAsInt(PawnManager::Get()->core, cvar); } SCRIPT_API(GetConsoleVarAsFloat, float(std::string const& cvar)) { - return getConfigOptionAsFloat(cvar); + return getConfigOptionAsFloat(PawnManager::Get()->core, cvar); } SCRIPT_API(GetConsoleVarAsString, int(std::string const& cvar, OutputOnlyString& buffer)) { - return getConfigOptionAsString(cvar, buffer); + Impl::String str_buffer; + auto result = getConfigOptionAsString(PawnManager::Get()->core, cvar, str_buffer); + buffer = str_buffer; + return result; } SCRIPT_API(GetNetworkStats, bool(OutputOnlyString& output)) @@ -582,22 +446,25 @@ SCRIPT_API(GetServerTickRate, int()) SCRIPT_API(GetServerVarAsBool, bool(std::string const& cvar)) { - return getConfigOptionAsBool(cvar); + return getConfigOptionAsBool(PawnManager::Get()->core, cvar); } SCRIPT_API(GetServerVarAsInt, int(std::string const& cvar)) { - return getConfigOptionAsInt(cvar); + return getConfigOptionAsInt(PawnManager::Get()->core, cvar); } SCRIPT_API(GetServerVarAsFloat, float(std::string const& cvar)) { - return getConfigOptionAsFloat(cvar); + return getConfigOptionAsFloat(PawnManager::Get()->core, cvar); } SCRIPT_API(GetServerVarAsString, int(std::string const& cvar, OutputOnlyString& buffer)) { - return getConfigOptionAsString(cvar, buffer); + Impl::String str_buffer; + auto result = getConfigOptionAsString(PawnManager::Get()->core, cvar, str_buffer); + buffer = str_buffer; + return result; } SCRIPT_API(GetWeaponName, int(int weaponid, OutputOnlyString& weapon)) diff --git a/Server/Components/Pawn/Scripting/CustomModels/Events.hpp b/Server/Components/Pawn/Scripting/CustomModels/Events.hpp index 4e76332fb..bcddfad6b 100644 --- a/Server/Components/Pawn/Scripting/CustomModels/Events.hpp +++ b/Server/Components/Pawn/Scripting/CustomModels/Events.hpp @@ -8,7 +8,6 @@ #pragma once #include "../../Manager/Manager.hpp" -#include "../../Singleton.hpp" #include "sdk.hpp" struct CustomModelsEvents : public PlayerModelsEventHandler, public Singleton diff --git a/Server/Components/Pawn/Scripting/Dialog/Events.hpp b/Server/Components/Pawn/Scripting/Dialog/Events.hpp index 487edc780..3c1925688 100644 --- a/Server/Components/Pawn/Scripting/Dialog/Events.hpp +++ b/Server/Components/Pawn/Scripting/Dialog/Events.hpp @@ -8,7 +8,6 @@ #pragma once #include "../../Manager/Manager.hpp" -#include "../../Singleton.hpp" #include "sdk.hpp" struct DialogEvents : public PlayerDialogEventHandler, public Singleton diff --git a/Server/Components/Pawn/Scripting/GangZone/Events.hpp b/Server/Components/Pawn/Scripting/GangZone/Events.hpp index 0568709c2..f724eb20d 100644 --- a/Server/Components/Pawn/Scripting/GangZone/Events.hpp +++ b/Server/Components/Pawn/Scripting/GangZone/Events.hpp @@ -8,7 +8,6 @@ #pragma once #include "../../Manager/Manager.hpp" -#include "../../Singleton.hpp" #include "sdk.hpp" struct GangZoneEvents : public GangZoneEventHandler, public Singleton diff --git a/Server/Components/Pawn/Scripting/Menu/Events.hpp b/Server/Components/Pawn/Scripting/Menu/Events.hpp index 562e44529..cbc8bf617 100644 --- a/Server/Components/Pawn/Scripting/Menu/Events.hpp +++ b/Server/Components/Pawn/Scripting/Menu/Events.hpp @@ -8,7 +8,6 @@ #pragma once #include "../../Manager/Manager.hpp" -#include "../../Singleton.hpp" #include "sdk.hpp" struct MenuEvents : public MenuEventHandler, public Singleton diff --git a/Server/Components/Pawn/Scripting/NPC/Events.hpp b/Server/Components/Pawn/Scripting/NPC/Events.hpp index 84f62ce3e..a86a179e1 100644 --- a/Server/Components/Pawn/Scripting/NPC/Events.hpp +++ b/Server/Components/Pawn/Scripting/NPC/Events.hpp @@ -8,7 +8,6 @@ #pragma once #include "../../Manager/Manager.hpp" -#include "../../Singleton.hpp" #include "sdk.hpp" struct NPCEvents : public NPCEventHandler, public Singleton diff --git a/Server/Components/Pawn/Scripting/Object/Events.hpp b/Server/Components/Pawn/Scripting/Object/Events.hpp index 36f7d0869..628f6b094 100644 --- a/Server/Components/Pawn/Scripting/Object/Events.hpp +++ b/Server/Components/Pawn/Scripting/Object/Events.hpp @@ -8,7 +8,6 @@ #pragma once #include "../../Manager/Manager.hpp" -#include "../../Singleton.hpp" #include "sdk.hpp" struct ObjectEvents : public ObjectEventHandler, public Singleton diff --git a/Server/Components/Pawn/Scripting/Pickup/Events.hpp b/Server/Components/Pawn/Scripting/Pickup/Events.hpp index dad1b4283..f85c02dd0 100644 --- a/Server/Components/Pawn/Scripting/Pickup/Events.hpp +++ b/Server/Components/Pawn/Scripting/Pickup/Events.hpp @@ -8,7 +8,6 @@ #pragma once #include "../../Manager/Manager.hpp" -#include "../../Singleton.hpp" #include "sdk.hpp" struct PickupEvents : public PickupEventHandler, public Singleton diff --git a/Server/Components/Pawn/Scripting/Player/Events.hpp b/Server/Components/Pawn/Scripting/Player/Events.hpp index 067892a02..eefffa1c6 100644 --- a/Server/Components/Pawn/Scripting/Player/Events.hpp +++ b/Server/Components/Pawn/Scripting/Player/Events.hpp @@ -8,7 +8,6 @@ #pragma once #include "../../Manager/Manager.hpp" -#include "../../Singleton.hpp" #include "sdk.hpp" class PlayerEvents : public PlayerSpawnEventHandler, public PlayerConnectEventHandler, public PlayerStreamEventHandler, public PlayerTextEventHandler, public PlayerShotEventHandler, public PlayerChangeEventHandler, public PlayerDamageEventHandler, public PlayerClickEventHandler, public PlayerCheckEventHandler, public PlayerUpdateEventHandler, public Singleton diff --git a/Server/Components/Pawn/Scripting/TextDraw/Events.hpp b/Server/Components/Pawn/Scripting/TextDraw/Events.hpp index 77e440c0b..1c044664f 100644 --- a/Server/Components/Pawn/Scripting/TextDraw/Events.hpp +++ b/Server/Components/Pawn/Scripting/TextDraw/Events.hpp @@ -8,7 +8,6 @@ #pragma once #include "../../Manager/Manager.hpp" -#include "../../Singleton.hpp" #include "sdk.hpp" struct TextDrawEvents : public TextDrawEventHandler, public Singleton diff --git a/Server/Components/Pawn/Scripting/Vehicle/Events.hpp b/Server/Components/Pawn/Scripting/Vehicle/Events.hpp index 0f898e70e..94eafe8e4 100644 --- a/Server/Components/Pawn/Scripting/Vehicle/Events.hpp +++ b/Server/Components/Pawn/Scripting/Vehicle/Events.hpp @@ -8,7 +8,6 @@ #pragma once #include "../../Manager/Manager.hpp" -#include "../../Singleton.hpp" #include "sdk.hpp" struct VehicleEvents : public VehicleEventHandler, public Singleton diff --git a/Server/Components/Pawn/Singleton.hpp b/Server/Components/Pawn/Singleton.hpp deleted file mode 100644 index 62fe0f09e..000000000 --- a/Server/Components/Pawn/Singleton.hpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public License, - * v. 2.0. If a copy of the MPL was not distributed with this file, You can - * obtain one at http://mozilla.org/MPL/2.0/. - * - * The original code is copyright (c) 2022, open.mp team and contributors. - */ - -#pragma once - -template -class Singleton -{ -protected: - static T* m_Instance; - -public: - Singleton() - { - } - virtual ~Singleton() - { - } - - inline static T* Get() - { - if (m_Instance == nullptr) - m_Instance = new T; - return m_Instance; - } - - inline static void Destroy() - { - if (m_Instance != nullptr) - { - delete m_Instance; - m_Instance = nullptr; - } - } -}; - -template -T* Singleton::m_Instance = nullptr;