From 43778356bc4f8a1f6c558139a7206c26c5ceea52 Mon Sep 17 00:00:00 2001 From: iAmir Date: Wed, 17 Apr 2024 04:34:26 +0330 Subject: [PATCH 01/32] continue onReceive event calls if Recording component has failure --- .../Components/Recordings/recordings_main.cpp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Server/Components/Recordings/recordings_main.cpp b/Server/Components/Recordings/recordings_main.cpp index 0a884b595..75d70060b 100644 --- a/Server/Components/Recordings/recordings_main.cpp +++ b/Server/Components/Recordings/recordings_main.cpp @@ -85,16 +85,16 @@ class RecordingsComponent final : public IRecordingsComponent, public PlayerConn bool onReceive(IPlayer& peer, NetworkBitStream& bs) override { - NetCode::Packet::PlayerFootSync footSync; - if (!footSync.read(bs)) + PlayerRecordingData* data = queryExtension(peer); + if (!data) { - return false; + return true; } - PlayerRecordingData* data = queryExtension(peer); - if (!data) + NetCode::Packet::PlayerFootSync footSync; + if (!footSync.read(bs)) { - return false; + return true; } // Write on foot recording data @@ -135,16 +135,16 @@ class RecordingsComponent final : public IRecordingsComponent, public PlayerConn bool onReceive(IPlayer& peer, NetworkBitStream& bs) override { - NetCode::Packet::PlayerVehicleSync vehicleSync; - if (!vehicleSync.read(bs)) + PlayerRecordingData* data = queryExtension(peer); + if (!data) { - return false; + return true; } - PlayerRecordingData* data = queryExtension(peer); - if (!data) + NetCode::Packet::PlayerVehicleSync vehicleSync; + if (!vehicleSync.read(bs)) { - return false; + return true; } // Write driver recording data From e930b1c0580abbe82796092f32dbd0e2e75b2a3c Mon Sep 17 00:00:00 2001 From: iAmir Date: Fri, 18 Oct 2024 08:14:14 +0330 Subject: [PATCH 02/32] create NPCs component --- SDK | 2 +- Server/Components/CMakeLists.txt | 1 + Server/Components/NPCs/CMakeLists.txt | 2 + Server/Components/NPCs/NPC/npc.cpp | 232 ++++++++++++++++++ Server/Components/NPCs/NPC/npc.hpp | 77 ++++++ .../Components/NPCs/Network/npcs_network.hpp | 80 ++++++ Server/Components/NPCs/npcs_impl.cpp | 181 ++++++++++++++ Server/Components/NPCs/npcs_impl.hpp | 85 +++++++ 8 files changed, 659 insertions(+), 1 deletion(-) create mode 100644 Server/Components/NPCs/CMakeLists.txt create mode 100644 Server/Components/NPCs/NPC/npc.cpp create mode 100644 Server/Components/NPCs/NPC/npc.hpp create mode 100644 Server/Components/NPCs/Network/npcs_network.hpp create mode 100644 Server/Components/NPCs/npcs_impl.cpp create mode 100644 Server/Components/NPCs/npcs_impl.hpp diff --git a/SDK b/SDK index 3dc9cf854..c4aa3138a 160000 --- a/SDK +++ b/SDK @@ -1 +1 @@ -Subproject commit 3dc9cf854c2a002a683baf50d5845f396c634e2c +Subproject commit c4aa3138a16973916fae276747460a60794bfd88 diff --git a/Server/Components/CMakeLists.txt b/Server/Components/CMakeLists.txt index b949f3c48..76b6021db 100644 --- a/Server/Components/CMakeLists.txt +++ b/Server/Components/CMakeLists.txt @@ -20,6 +20,7 @@ add_subdirectory(TextLabels) add_subdirectory(Timers) add_subdirectory(Variables) add_subdirectory(Vehicles) +add_subdirectory(NPCs) # Pawn if(BUILD_PAWN_COMPONENT) diff --git a/Server/Components/NPCs/CMakeLists.txt b/Server/Components/NPCs/CMakeLists.txt new file mode 100644 index 000000000..09bd36528 --- /dev/null +++ b/Server/Components/NPCs/CMakeLists.txt @@ -0,0 +1,2 @@ +get_filename_component(ProjectId ${CMAKE_CURRENT_SOURCE_DIR} NAME) +add_server_component(${ProjectId}) diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp new file mode 100644 index 000000000..4d40fa905 --- /dev/null +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -0,0 +1,232 @@ +/* + * 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. + */ + +#include "npc.hpp" +#include +#define _USE_MATH_DEFINES +#include +#include "../npcs_impl.hpp" + +namespace utils +{ +float getAngleOfLine(float x, float y) +{ + float angle = atan2(y, x) * (180.0f / M_PI) + 270.0f; + if (angle >= 360.0f) + { + angle -= 360.0f; + } + else if (angle < 0.0f) + { + angle += 360.0f; + } + return angle; +} +} + +NPC::NPC(NPCComponent* component, IPlayer* playerPtr) +{ + // Keep a handle of NPC copmonent instance internally + npcComponent = component; + // We created a player instance for it, let's keep a handle of it internally + player = playerPtr; + + // Initial entity values + Vector3 initialPosition = { 0.0f, 0.0f, 3.5f }; + GTAQuat initialRotation = { 0.960891485f, 0.0f, 0.0f, 0.276925147f }; + moving = false; + velocity = { 0.0f, 0.0f, 0.0f }; + moveType = NPCMoveType_None; + + // Initial values for foot sync values + footSync.LeftRight = 0; + footSync.UpDown = 0; + footSync.Keys = 0; + footSync.Position = initialPosition; + footSync.Velocity = velocity; + footSync.Rotation = initialRotation; + footSync.WeaponAdditionalKey = 0; + footSync.HealthArmour = { 100.0f, 0.0f }; + footSync.SpecialAction = 0; + footSync.AnimationID = 0; + footSync.AnimationFlags = 0; + footSync.SurfingData.type = PlayerSurfingData::Type::None; + footSync.SurfingData.ID = 0; + footSync.SurfingData.offset = { 0.0f, 0.0f, 0.0f }; +} + +Vector3 NPC::getPosition() const +{ + return player->getPosition(); +} + +void NPC::setPosition(Vector3 pos) +{ + footSync.Position = pos; +} + +GTAQuat NPC::getRotation() const +{ + return player->getRotation(); +} + +void NPC::setRotation(GTAQuat rot) +{ + footSync.Rotation = rot; +} + +int NPC::getVirtualWorld() const +{ + return player->getVirtualWorld(); +} + +void NPC::setVirtualWorld(int vw) +{ + player->setVirtualWorld(vw); +} + +void NPC::spawn() +{ + NetworkBitStream requestClassBS; + NetworkBitStream emptyBS; + + requestClassBS.writeUINT16(0); + npcComponent->emulateRPCIn(this, NetCode::RPC::PlayerRequestClass::PacketID, requestClassBS); + + npcComponent->emulateRPCIn(this, NetCode::RPC::PlayerRequestSpawn::PacketID, emptyBS); + npcComponent->emulateRPCIn(this, NetCode::RPC::PlayerSpawn::PacketID, emptyBS); +} + +bool NPC::move(Vector3 pos, NPCMoveType moveType) +{ + // Set up everything to start moving in next tick + auto position = getPosition(); + float distance = glm::distance(position, pos); + Vector3 newVelocity; + + if (!(distance <= 0.0f)) + { + newVelocity = (pos - position) / distance; + } + + // Determine which speed to use based on moving type + float speed = 0.0f; + if (moveType != NPCMoveType_None) + { + if (moveType == NPCMoveType_Sprint) + { + speed = NPC_MOVE_SPEED_SPRINT; + footSync.Keys |= Key::SPRINT; + } + else if (moveType == NPCMoveType_Jog) + { + speed = NPC_MOVE_SPEED_JOG; + } + else + { + speed = NPC_MOVE_SPEED_WALK; + footSync.Keys |= Key::WALK; + } + + footSync.UpDown = static_cast(Key::ANALOG_UP); + } + + // Calculate velocity to use on tick + newVelocity *= (speed / 100.0f); + velocity = newVelocity; + + // Calculate front vector and player's facing angle: + Vector3 front; + if (!(std::fabs(distance) < DBL_EPSILON)) + { + front = (pos - position) / distance; + } + + auto rotation = getRotation().ToEuler(); + rotation.z = utils::getAngleOfLine(front.x, front.y); + setRotation(rotation); + + // Set internal variables + moveSpeed = speed; + targetPosition = pos; + moving = true; + lastMove = Time::now(); + return true; +} + +void NPC::sendFootSync() +{ + NetworkBitStream bs; + + auto& quat = footSync.Rotation.q; + + bs.writeUINT16(footSync.LeftRight); + bs.writeUINT16(footSync.UpDown); + bs.writeUINT16(footSync.Keys); + bs.writeVEC3(footSync.Position); + bs.writeVEC4(Vector4(quat.w, quat.x, quat.y, quat.z)); + bs.writeUINT8(uint8_t(footSync.HealthArmour.x)); + bs.writeUINT8(uint8_t(footSync.HealthArmour.y)); + bs.writeUINT8(footSync.WeaponAdditionalKey); + bs.writeUINT8(footSync.SpecialAction); + bs.writeVEC3(footSync.Velocity); + bs.writeVEC3(footSync.SurfingData.offset); + bs.writeUINT16(footSync.SurfingData.ID); + bs.writeUINT16(footSync.AnimationID); + bs.writeUINT16(footSync.AnimationFlags); + + npcComponent->emulatePacketIn(this, footSync.PacketID, bs); +} + +void NPC::advance(TimePoint now) +{ + auto position = getPosition(); + Milliseconds difference = duration_cast(now.time_since_epoch()) - duration_cast(lastMove.time_since_epoch()); + const float remainingDistance = glm::distance(position, targetPosition); + const float travelledDistance = glm::length(velocity * static_cast(difference.count())); + + if (travelledDistance >= remainingDistance) + { + moving = false; + moveSpeed = 0.0f; + targetPosition = { 0.0f, 0.0f, 0.0f }; + + footSync.Keys &= Key::SPRINT; + footSync.Keys &= Key::WALK; + footSync.UpDown = 0; + npcComponent->getEventDispatcher_internal().dispatch(&NPCEventHandler::onNPCFinishMove, *this); + } + else + { + position += velocity * static_cast(difference.count()); + footSync.Velocity = velocity; + } + + lastMove = Time::now(); + setPosition(position); +} + +void NPC::tick(Microseconds elapsed, TimePoint now) +{ + static auto footSyncRate = npcComponent->getCore()->getConfig().getInt("network.on_foot_sync_rate"); + + // Only process the NPC if it is spawned + if (player && (player->getState() == PlayerState_OnFoot || player->getState() == PlayerState_Driver || player->getState() == PlayerState_Passenger || player->getState() == PlayerState_Spawned)) + { + if (moving) + { + advance(now); + } + + if ((now - lastUpdate).count() > *footSyncRate) + { + sendFootSync(); + lastUpdate = now; + } + } +} diff --git a/Server/Components/NPCs/NPC/npc.hpp b/Server/Components/NPCs/NPC/npc.hpp new file mode 100644 index 000000000..aab8f39ff --- /dev/null +++ b/Server/Components/NPCs/NPC/npc.hpp @@ -0,0 +1,77 @@ +/* + * 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 + +#include +#include +#include + +class NPCComponent; + +class NPC : public INPC, public PoolIDProvider, public NoCopy +{ +public: + NPC(NPCComponent* npcComponent, IPlayer* playerPtr); + + Vector3 getPosition() const override; + + void setPosition(Vector3 position) override; + + GTAQuat getRotation() const override; + + void setRotation(GTAQuat rotation) override; + + int getVirtualWorld() const override; + + void setVirtualWorld(int vw) override; + + void spawn() override; + + bool move(Vector3 position, NPCMoveType moveType) override; + + void sendFootSync(); + + void tick(Microseconds elapsed, TimePoint now); + + void advance(TimePoint now); + + int getID() const override + { + return poolID; + } + + IPlayer* getPlayer() override + { + return player; + } + + void setPlayer(IPlayer* plyr) + { + player = plyr; + } + +private: + // The NPC's player pointer. + IPlayer* player; + TimePoint lastUpdate; + + // Movements + NPCMoveType moveType; + TimePoint lastMove; + TimePoint moveStart; + float moveSpeed; + Vector3 targetPosition; + Vector3 velocity; + bool moving; + + // Packets + NetCode::Packet::PlayerFootSync footSync; + + NPCComponent* npcComponent; +}; diff --git a/Server/Components/NPCs/Network/npcs_network.hpp b/Server/Components/NPCs/Network/npcs_network.hpp new file mode 100644 index 000000000..ddbb05414 --- /dev/null +++ b/Server/Components/NPCs/Network/npcs_network.hpp @@ -0,0 +1,80 @@ +/* + * 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. + */ + +#include +#include + +struct NPCNetwork : public Impl::Network +{ + ICore* core; + void init(ICore* core) + { + this->core = core; + } + + ENetworkType getNetworkType() const override + { + return ENetworkType(3); + } + + bool sendPacket(IPlayer& peer, Span data, int channel, bool dispatchEvents = true) override + { + //core->logLn(LogLevel::Error, "[npc network] sendPacket(\"%.*s\", data, %i, %i)\n", peer.getName().length(), peer.getName().data(), channel, dispatchEvents); + return true; + } + + bool broadcastPacket(Span data, int channel, const IPlayer* exceptPeer = nullptr, bool dispatchEvents = true) override + { + //core->logLn(LogLevel::Error, "[npc network] broadcastPacket(data, %i, \"%.*s\", %i)\n", channel, exceptPeer == nullptr ? 0 : exceptPeer->getName().length(), exceptPeer == nullptr ? "" : exceptPeer->getName().data(), dispatchEvents); + return true; + } + + bool sendRPC(IPlayer& peer, int id, Span data, int channel, bool dispatchEvents = true) override + { + //core->logLn(LogLevel::Error, "[npc network] sendRpc(\"%.*s\", %i, data, %i, %i)\n", peer.getName().length(), peer.getName().data(), id, channel, dispatchEvents); + return true; + } + + bool broadcastRPC(int id, Span data, int channel, const IPlayer* exceptPeer = nullptr, bool dispatchEvents = true) override + { + //core->logLn(LogLevel::Error, "[npc network] broadcastRPC(%i, data, %i, \"%.*s\", %i)\n", id, channel, exceptPeer == nullptr ? 0 : exceptPeer->getName().length(), exceptPeer == nullptr ? "" : exceptPeer->getName().data(), dispatchEvents); + return true; + } + + NetworkStats getStatistics(IPlayer* player = nullptr) override + { + return NetworkStats(); + } + + unsigned getPing(const IPlayer& peer) override + { + return 0; + } + + void disconnect(const IPlayer& peer) override + { + } + + void ban(const BanEntry& entry, Milliseconds expire = Milliseconds(0)) override + { + } + + void unban(const BanEntry& entry) override + { + } + + void update() override + { + } + + NPCNetwork() + : Network(256, 256) + { + } + ~NPCNetwork() { } +}; \ No newline at end of file diff --git a/Server/Components/NPCs/npcs_impl.cpp b/Server/Components/NPCs/npcs_impl.cpp new file mode 100644 index 000000000..cf6cbe946 --- /dev/null +++ b/Server/Components/NPCs/npcs_impl.cpp @@ -0,0 +1,181 @@ +/* + * 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. + */ + +#include "./npcs_impl.hpp" +#include + +void NPCComponent::onLoad(ICore* c) +{ + core = c; +} + +void NPCComponent::onInit(IComponentList* components) +{ + npcNetwork.init(core); + core->getEventDispatcher().addEventHandler(this); +} + +void NPCComponent::free() +{ + core->getEventDispatcher().removeEventHandler(this); + delete this; +} + +INetwork* NPCComponent::getNetwork() +{ + return &npcNetwork; +} + +IEventDispatcher& NPCComponent::getEventDispatcher() +{ + return eventDispatcher; +} + +IEventDispatcher>& NPCComponent::getPoolEventDispatcher() +{ + return storage.getEventDispatcher(); +} + +const FlatPtrHashSet& NPCComponent::entries() +{ + return storage._entries(); +} + +Pair NPCComponent::bounds() const +{ + return std::make_pair(storage.Lower, storage.Upper); +} + +INPC* NPCComponent::get(int index) +{ + if (index == -1) + { + return nullptr; + } + return storage.get(index); +} + +void NPCComponent::release(int index) +{ + auto ptr = storage.get(index); + if (ptr) + { + // Call disconnect events for both NPC and player. This way NPC's player instance is going to be handled and cleared properly. + eventDispatcher.dispatch(&NPCEventHandler::onNPCDisconnect, *ptr); + npcNetwork.networkEventDispatcher.dispatch(&NetworkEventHandler::onPeerDisconnect, *ptr->getPlayer(), PeerDisconnectReason_Quit); + storage.release(index, false); + } +} + +void NPCComponent::lock(int index) +{ + storage.lock(index); +} + +bool NPCComponent::unlock(int index) +{ + return storage.unlock(index); +} + +void NPCComponent::onTick(Microseconds elapsed, TimePoint now) +{ + for (auto& npc : storage) + { + static_cast(npc)->tick(elapsed, now); + } +} + +INPC* NPCComponent::create(StringView name) +{ + // Reserve a random ephemeral port for our NPC client + // Ephemeral ports: https://en.wikipedia.org/wiki/Ephemeral_port + uint16_t port = 0; + std::random_device rd; + std::mt19937 gen(rd()); + port = std::uniform_int_distribution(49152, 65535)(gen); + + PeerNetworkData data; + data.network = getNetwork(); + data.networkID.address.v4 = 16777343; // Set ipv4 to 127.0.0.1 + data.networkID.port = port; // Set our randomly generated port + + PeerRequestParams request; + request.bot = true; // Mark as an NPC + request.name = name; + + Pair newConnectionResult { NewConnectionResult_Ignore, nullptr }; + newConnectionResult = core->getPlayers().requestPlayer(data, request); + + if (newConnectionResult.first == NewConnectionResult_NoPlayerSlot) + { + core->logLn(LogLevel::Error, "[NPC] NPC creation failed. Server is either full or max_bots in config is not enough!"); + return nullptr; + } + else if (newConnectionResult.first == NewConnectionResult_BadName) + { + core->logLn(LogLevel::Error, "[NPC] NPC has a bad name!"); + return nullptr; + } + + // Hint newly initialized player's ID as our pool ID in NPC pool. This way they're going to have identical IDs + auto npcId = storage.claimHint(newConnectionResult.second->getID(), this, newConnectionResult.second); + + auto npc = storage.get(npcId); + if (npc) + { + // Call connect events for both NPC and player, this way it can get initialized properly in player pool too + eventDispatcher.dispatch(&NPCEventHandler::onNPCConnect, *npc); + npcNetwork.networkEventDispatcher.dispatch(&NetworkEventHandler::onPeerConnect, *npc->getPlayer()); + } + + return npc; +} + +void NPCComponent::emulateRPCIn(INPC* npc, int rpcId, NetworkBitStream& bs) +{ + auto player = npc->getPlayer(); + + const bool res = npcNetwork.inEventDispatcher.stopAtFalse([player, rpcId, &bs](NetworkInEventHandler* handler) + { + return handler->onReceiveRPC(*player, rpcId, bs); + }); + + if (res) + { + npcNetwork.rpcInEventDispatcher.stopAtFalse(rpcId, [player, &bs](SingleNetworkInEventHandler* handler) + { + bs.resetReadPointer(); + return handler->onReceive(*player, bs); + }); + } +} + +void NPCComponent::emulatePacketIn(INPC* npc, int type, NetworkBitStream& bs) +{ + auto player = npc->getPlayer(); + + const bool res = npcNetwork.inEventDispatcher.stopAtFalse([player, type, &bs](NetworkInEventHandler* handler) + { + bs.resetReadPointer(); + return handler->onReceivePacket(*player, type, bs); + }); + + if (res) + { + npcNetwork.packetInEventDispatcher.stopAtFalse(type, [player, &bs](SingleNetworkInEventHandler* handler) + { + bs.resetReadPointer(); + return handler->onReceive(*player, bs); + }); + } +} + +COMPONENT_ENTRY_POINT() +{ + return new NPCComponent(); +} diff --git a/Server/Components/NPCs/npcs_impl.hpp b/Server/Components/NPCs/npcs_impl.hpp new file mode 100644 index 000000000..5152c7de1 --- /dev/null +++ b/Server/Components/NPCs/npcs_impl.hpp @@ -0,0 +1,85 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include "./Network/npcs_network.hpp" +#include "./NPC/npc.hpp" + +using namespace Impl; + +class NPCComponent final : public INPCComponent, public CoreEventHandler +{ +public: + StringView componentName() const override + { + return "Controllable NPCs"; + } + + SemanticVersion componentVersion() const override + { + return SemanticVersion(0, 0, 1, 0); + } + + void onLoad(ICore* c) override; + + void onInit(IComponentList* components) override; + + void onReady() override { } + + void free() override; + + void reset() override + { + } + + INetwork* getNetwork() override; + + IEventDispatcher& getEventDispatcher() override; + + IEventDispatcher>& getPoolEventDispatcher() override; + + const FlatPtrHashSet& entries() override; + + Pair bounds() const override; + + INPC* get(int index) override; + + void release(int index) override; + + void lock(int index) override; + + bool unlock(int index) override; + + void onTick(Microseconds elapsed, TimePoint now) override; + + INPC* create(StringView name) override; + + void emulateRPCIn(INPC* npc, int rpcId, NetworkBitStream& bs); + + void emulatePacketIn(INPC* npc, int type, NetworkBitStream& bs); + + ICore* getCore() + { + return core; + } + + DefaultEventDispatcher& getEventDispatcher_internal() + { + return eventDispatcher; + } + +private: + ICore* core = nullptr; + NPCNetwork npcNetwork; + DefaultEventDispatcher eventDispatcher; + MarkedDynamicPoolStorage storage; +}; From 949a40e509b5bfa5c8e0396f2a447053fb88987d Mon Sep 17 00:00:00 2001 From: iAmir Date: Wed, 17 Apr 2024 05:33:57 +0330 Subject: [PATCH 03/32] create pawn scripting api for current NPC functions --- SDK | 2 +- Server/Components/Pawn/Manager/Manager.hpp | 1 + Server/Components/Pawn/Scripting/Impl.cpp | 9 ++ .../Components/Pawn/Scripting/NPC/Events.hpp | 30 +++++++ .../Components/Pawn/Scripting/NPC/Natives.cpp | 85 +++++++++++++++++++ Server/Components/Pawn/main.cpp | 1 + 6 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 Server/Components/Pawn/Scripting/NPC/Events.hpp create mode 100644 Server/Components/Pawn/Scripting/NPC/Natives.cpp diff --git a/SDK b/SDK index c4aa3138a..43cd1ef09 160000 --- a/SDK +++ b/SDK @@ -1 +1 @@ -Subproject commit c4aa3138a16973916fae276747460a60794bfd88 +Subproject commit 43cd1ef099b70b4f417d0cd34c8cba7ad50771bd diff --git a/Server/Components/Pawn/Manager/Manager.hpp b/Server/Components/Pawn/Manager/Manager.hpp index 0f1b606b5..5218db894 100644 --- a/Server/Components/Pawn/Manager/Manager.hpp +++ b/Server/Components/Pawn/Manager/Manager.hpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include diff --git a/Server/Components/Pawn/Scripting/Impl.cpp b/Server/Components/Pawn/Scripting/Impl.cpp index 4ccde9462..4c569b757 100644 --- a/Server/Components/Pawn/Scripting/Impl.cpp +++ b/Server/Components/Pawn/Scripting/Impl.cpp @@ -21,6 +21,7 @@ #include "Vehicle/Events.hpp" #include "GangZone/Events.hpp" #include "CustomModels/Events.hpp" +#include "NPC/Events.hpp" Scripting::~Scripting() { @@ -88,6 +89,10 @@ Scripting::~Scripting() { mgr->models->getEventDispatcher().removeEventHandler(CustomModelsEvents::Get()); } + if (mgr->npcs) + { + mgr->npcs->getEventDispatcher().removeEventHandler(NPCEvents::Get()); + } } void Scripting::addEvents() const @@ -156,4 +161,8 @@ void Scripting::addEvents() const { mgr->models->getEventDispatcher().addEventHandler(CustomModelsEvents::Get()); } + if (mgr->npcs) + { + mgr->npcs->getEventDispatcher().addEventHandler(NPCEvents::Get()); + } } diff --git a/Server/Components/Pawn/Scripting/NPC/Events.hpp b/Server/Components/Pawn/Scripting/NPC/Events.hpp new file mode 100644 index 000000000..757ee2fe8 --- /dev/null +++ b/Server/Components/Pawn/Scripting/NPC/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) 2022, open.mp team and contributors. + */ + +#pragma once +#include "../../Manager/Manager.hpp" +#include "../../Singleton.hpp" +#include "sdk.hpp" + +struct NPCEvents : public NPCEventHandler, public Singleton +{ + void onNPCFinishMove(INPC& npc) override + { + PawnManager::Get()->CallAllInEntryFirst("OnNPCFinishMove", DefaultReturnValue_True, npc.getID()); + } + + void onNPCConnect(INPC& npc) override + { + PawnManager::Get()->CallAllInEntryFirst("OnNPCConnect", DefaultReturnValue_True, npc.getID()); + } + + void onNPCDisconnect(INPC& npc) override + { + PawnManager::Get()->CallAllInEntryFirst("OnNPCDisconnect", DefaultReturnValue_True, npc.getID()); + } +}; diff --git a/Server/Components/Pawn/Scripting/NPC/Natives.cpp b/Server/Components/Pawn/Scripting/NPC/Natives.cpp new file mode 100644 index 000000000..9c76e3fec --- /dev/null +++ b/Server/Components/Pawn/Scripting/NPC/Natives.cpp @@ -0,0 +1,85 @@ +/* + * 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. + */ + +#include "../Types.hpp" +#include "sdk.hpp" +#include +#include "../../format.hpp" + +SCRIPT_API(NPC_Create, int(const String& name)) +{ + auto component = PawnManager::Get()->npcs; + if (component) + { + auto npc = component->create(name.c_str()); + if (npc) + { + return npc->getID(); + } + } + return INVALID_PLAYER_ID; +} + +SCRIPT_API(NPC_Destroy, bool(INPC& npc)) +{ + PawnManager::Get()->npcs->release(npc.getID()); + return true; +} + +SCRIPT_API(NPC_IsValid, bool(INPC* npc)) +{ + return npc != nullptr; +} + +SCRIPT_API(NPC_Spawn, bool(INPC& npc)) +{ + npc.spawn(); + return true; +} + +SCRIPT_API(NPC_SetPos, bool(INPC& npc, Vector3 position)) +{ + npc.setPosition(position); + return true; +} + +SCRIPT_API(NPC_GetPos, bool(INPC& npc, Vector3& position)) +{ + position = npc.getPosition(); + return true; +} + +SCRIPT_API(NPC_SetRot, bool(INPC& npc, Vector3 rotation)) +{ + npc.setRotation(rotation); + return true; +} + +SCRIPT_API(NPC_GetRot, bool(INPC& npc, Vector3& rotation)) +{ + rotation = npc.getRotation().ToEuler(); + return true; +} + +SCRIPT_API(NPC_SetVirtualWorld, bool(INPC& npc, int virtualWorld)) +{ + npc.setVirtualWorld(virtualWorld); + return true; +} + +SCRIPT_API(NPC_GetVirtualWorld, bool(INPC& npc, int& virtualWorld)) +{ + virtualWorld = npc.getVirtualWorld(); + return true; +} + +SCRIPT_API(NPC_Move, bool(INPC& npc, Vector3 targetPos, int moveType)) +{ + npc.move(targetPos, NPCMoveType(moveType)); + return true; +} diff --git a/Server/Components/Pawn/main.cpp b/Server/Components/Pawn/main.cpp index a054d1e16..8c99558ac 100644 --- a/Server/Components/Pawn/main.cpp +++ b/Server/Components/Pawn/main.cpp @@ -167,6 +167,7 @@ class PawnComponent final : public IPawnComponent, public CoreEventHandler, publ mgr->vars = components->queryComponent(); mgr->vehicles = components->queryComponent(); mgr->models = components->queryComponent(); + mgr->npcs = components->queryComponent(); scriptingInstance.addEvents(); From 1d9bac3c407edbabd5d23e43c0bfdef642383046 Mon Sep 17 00:00:00 2001 From: iAmir Date: Wed, 17 Apr 2024 05:36:16 +0330 Subject: [PATCH 04/32] force internal updates on position and rotation change --- Server/Components/NPCs/NPC/npc.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index 4d40fa905..cb4de8b79 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -68,6 +68,9 @@ Vector3 NPC::getPosition() const void NPC::setPosition(Vector3 pos) { footSync.Position = pos; + + // Let it update for all players and internally in open.mp + sendFootSync(); } GTAQuat NPC::getRotation() const @@ -78,6 +81,9 @@ GTAQuat NPC::getRotation() const void NPC::setRotation(GTAQuat rot) { footSync.Rotation = rot; + + // Let it update for all players and internally in open.mp + sendFootSync(); } int NPC::getVirtualWorld() const From 32331ff36de4c77d9a1f257c66b4e1687dc13dd1 Mon Sep 17 00:00:00 2001 From: iAmir Date: Wed, 17 Apr 2024 05:39:24 +0330 Subject: [PATCH 05/32] calculate travelled distance once --- Server/Components/NPCs/NPC/npc.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index cb4de8b79..256570546 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -193,10 +193,10 @@ void NPC::advance(TimePoint now) { auto position = getPosition(); Milliseconds difference = duration_cast(now.time_since_epoch()) - duration_cast(lastMove.time_since_epoch()); - const float remainingDistance = glm::distance(position, targetPosition); - const float travelledDistance = glm::length(velocity * static_cast(difference.count())); + float remainingDistance = glm::distance(position, targetPosition); + Vector3 travelled = velocity * static_cast(difference.count()); - if (travelledDistance >= remainingDistance) + if (glm::length(travelled) >= remainingDistance) { moving = false; moveSpeed = 0.0f; @@ -209,7 +209,7 @@ void NPC::advance(TimePoint now) } else { - position += velocity * static_cast(difference.count()); + position += travelled; footSync.Velocity = velocity; } From 3e9a7bc1babf2bd340bdec1b05acb35cd3315bc3 Mon Sep 17 00:00:00 2001 From: iAmir Date: Wed, 17 Apr 2024 06:19:22 +0330 Subject: [PATCH 06/32] add NPC_StopMove --- SDK | 2 +- Server/Components/NPCs/NPC/npc.cpp | 13 +++++++++++++ Server/Components/NPCs/NPC/npc.hpp | 2 ++ Server/Components/Pawn/Scripting/NPC/Natives.cpp | 7 ++++++- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/SDK b/SDK index 43cd1ef09..338c0020a 160000 --- a/SDK +++ b/SDK @@ -1 +1 @@ -Subproject commit 43cd1ef099b70b4f417d0cd34c8cba7ad50771bd +Subproject commit 338c0020a5263092278e13cefae031af05fee7fe diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index 256570546..9f567d6b3 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -165,6 +165,19 @@ bool NPC::move(Vector3 pos, NPCMoveType moveType) return true; } +void NPC::stopMove() +{ + moving = false; + moveSpeed = 0.0f; + targetPosition = { 0.0f, 0.0f, 0.0f }; + velocity = { 0.0f, 0.0f, 0.0f }; + moveType = NPCMoveType_None; + + footSync.Keys &= Key::SPRINT; + footSync.Keys &= Key::WALK; + footSync.UpDown = 0; +} + void NPC::sendFootSync() { NetworkBitStream bs; diff --git a/Server/Components/NPCs/NPC/npc.hpp b/Server/Components/NPCs/NPC/npc.hpp index aab8f39ff..cd2cf9aa1 100644 --- a/Server/Components/NPCs/NPC/npc.hpp +++ b/Server/Components/NPCs/NPC/npc.hpp @@ -35,6 +35,8 @@ class NPC : public INPC, public PoolIDProvider, public NoCopy bool move(Vector3 position, NPCMoveType moveType) override; + void stopMove() override; + void sendFootSync(); void tick(Microseconds elapsed, TimePoint now); diff --git a/Server/Components/Pawn/Scripting/NPC/Natives.cpp b/Server/Components/Pawn/Scripting/NPC/Natives.cpp index 9c76e3fec..4d10f67f3 100644 --- a/Server/Components/Pawn/Scripting/NPC/Natives.cpp +++ b/Server/Components/Pawn/Scripting/NPC/Natives.cpp @@ -80,6 +80,11 @@ SCRIPT_API(NPC_GetVirtualWorld, bool(INPC& npc, int& virtualWorld)) SCRIPT_API(NPC_Move, bool(INPC& npc, Vector3 targetPos, int moveType)) { - npc.move(targetPos, NPCMoveType(moveType)); + return npc.move(targetPos, NPCMoveType(moveType)); +} + +SCRIPT_API(NPC_StopMove, bool(INPC& npc)) +{ + npc.stopMove(); return true; } From a265c888073f7fa75269e27773c41c501fabb19e Mon Sep 17 00:00:00 2001 From: iAmir Date: Wed, 17 Apr 2024 06:19:55 +0330 Subject: [PATCH 07/32] restart npc move when pos or rot is reset --- Server/Components/NPCs/NPC/npc.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index 9f567d6b3..50d42420a 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -71,6 +71,11 @@ void NPC::setPosition(Vector3 pos) // Let it update for all players and internally in open.mp sendFootSync(); + + if (moving) + { + move(targetPosition, moveType); + } } GTAQuat NPC::getRotation() const @@ -84,6 +89,11 @@ void NPC::setRotation(GTAQuat rot) // Let it update for all players and internally in open.mp sendFootSync(); + + if (moving) + { + move(targetPosition, moveType); + } } int NPC::getVirtualWorld() const From 43e64cdb560dd4c07779354911d2bd2c7b52f600 Mon Sep 17 00:00:00 2001 From: iAmir Date: Wed, 17 Apr 2024 06:20:16 +0330 Subject: [PATCH 08/32] only start npc move if move type is valid --- Server/Components/NPCs/NPC/npc.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index 50d42420a..7e7f4e1e6 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -120,6 +120,11 @@ void NPC::spawn() bool NPC::move(Vector3 pos, NPCMoveType moveType) { + if (moveType_ == NPCMoveType_None) + { + return false; + } + // Set up everything to start moving in next tick auto position = getPosition(); float distance = glm::distance(position, pos); From 8cb40058cd63a998d7eb8ff250e5c1bb2fb05dcb Mon Sep 17 00:00:00 2001 From: iAmir Date: Wed, 17 Apr 2024 06:20:39 +0330 Subject: [PATCH 09/32] set internal rot and pos values during move to avoid recursion --- Server/Components/NPCs/NPC/npc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index 7e7f4e1e6..0b91b8c28 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -170,7 +170,7 @@ bool NPC::move(Vector3 pos, NPCMoveType moveType) auto rotation = getRotation().ToEuler(); rotation.z = utils::getAngleOfLine(front.x, front.y); - setRotation(rotation); + footSync.Rotation = rotation; // Do this directly, if you use NPC::setRotation it's going to cause recursion // Set internal variables moveSpeed = speed; @@ -242,7 +242,7 @@ void NPC::advance(TimePoint now) } lastMove = Time::now(); - setPosition(position); + footSync.Position = position; // Do this directly, if you use NPC::setPosition it's going to cause recursion } void NPC::tick(Microseconds elapsed, TimePoint now) From c1db6c929926ebdcb5c2b9169292c4e476eb8dd0 Mon Sep 17 00:00:00 2001 From: iAmir Date: Wed, 17 Apr 2024 06:20:56 +0330 Subject: [PATCH 10/32] use stopMove when npc move is finished --- Server/Components/NPCs/NPC/npc.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index 0b91b8c28..82f75af7c 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -226,13 +226,7 @@ void NPC::advance(TimePoint now) if (glm::length(travelled) >= remainingDistance) { - moving = false; - moveSpeed = 0.0f; - targetPosition = { 0.0f, 0.0f, 0.0f }; - - footSync.Keys &= Key::SPRINT; - footSync.Keys &= Key::WALK; - footSync.UpDown = 0; + stopMove(); npcComponent->getEventDispatcher_internal().dispatch(&NPCEventHandler::onNPCFinishMove, *this); } else From 658292092dd497a0b74fa7c96cad2dad6c87d6fe Mon Sep 17 00:00:00 2001 From: iAmir Date: Wed, 17 Apr 2024 06:21:12 +0330 Subject: [PATCH 11/32] only send NPC foot sync if player is spawned --- Server/Components/NPCs/NPC/npc.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index 82f75af7c..9c41d2f09 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -195,6 +195,12 @@ void NPC::stopMove() void NPC::sendFootSync() { + // Only send foot sync if player is spawned + if (!(player->getState() == PlayerState_OnFoot || player->getState() == PlayerState_Driver || player->getState() == PlayerState_Passenger || player->getState() == PlayerState_Spawned)) + { + return; + } + NetworkBitStream bs; auto& quat = footSync.Rotation.q; From 589a6cf800395d992a40578659a26f7dda2f0255 Mon Sep 17 00:00:00 2001 From: iAmir Date: Wed, 17 Apr 2024 06:25:08 +0330 Subject: [PATCH 12/32] use underscore suffixed class variables for NPC class --- Server/Components/NPCs/NPC/npc.cpp | 186 ++++++++++++++--------------- Server/Components/NPCs/NPC/npc.hpp | 28 ++--- 2 files changed, 106 insertions(+), 108 deletions(-) diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index 9c41d2f09..e80ae5f81 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -32,78 +32,78 @@ float getAngleOfLine(float x, float y) NPC::NPC(NPCComponent* component, IPlayer* playerPtr) { // Keep a handle of NPC copmonent instance internally - npcComponent = component; + npcComponent_ = component; // We created a player instance for it, let's keep a handle of it internally - player = playerPtr; + player_ = playerPtr; // Initial entity values Vector3 initialPosition = { 0.0f, 0.0f, 3.5f }; GTAQuat initialRotation = { 0.960891485f, 0.0f, 0.0f, 0.276925147f }; - moving = false; - velocity = { 0.0f, 0.0f, 0.0f }; - moveType = NPCMoveType_None; + moving_ = false; + velocity_ = { 0.0f, 0.0f, 0.0f }; + moveType_ = NPCMoveType_None; // Initial values for foot sync values - footSync.LeftRight = 0; - footSync.UpDown = 0; - footSync.Keys = 0; - footSync.Position = initialPosition; - footSync.Velocity = velocity; - footSync.Rotation = initialRotation; - footSync.WeaponAdditionalKey = 0; - footSync.HealthArmour = { 100.0f, 0.0f }; - footSync.SpecialAction = 0; - footSync.AnimationID = 0; - footSync.AnimationFlags = 0; - footSync.SurfingData.type = PlayerSurfingData::Type::None; - footSync.SurfingData.ID = 0; - footSync.SurfingData.offset = { 0.0f, 0.0f, 0.0f }; + footSync_.LeftRight = 0; + footSync_.UpDown = 0; + footSync_.Keys = 0; + footSync_.Position = initialPosition; + footSync_.Velocity = velocity_; + footSync_.Rotation = initialRotation; + footSync_.WeaponAdditionalKey = 0; + footSync_.HealthArmour = { 100.0f, 0.0f }; + footSync_.SpecialAction = 0; + footSync_.AnimationID = 0; + footSync_.AnimationFlags = 0; + footSync_.SurfingData.type = PlayerSurfingData::Type::None; + footSync_.SurfingData.ID = 0; + footSync_.SurfingData.offset = { 0.0f, 0.0f, 0.0f }; } Vector3 NPC::getPosition() const { - return player->getPosition(); + return player_->getPosition(); } void NPC::setPosition(Vector3 pos) { - footSync.Position = pos; + footSync_.Position = pos; // Let it update for all players and internally in open.mp sendFootSync(); - if (moving) + if (moving_) { - move(targetPosition, moveType); + move(targetPosition_, moveType_); } } GTAQuat NPC::getRotation() const { - return player->getRotation(); + return player_->getRotation(); } void NPC::setRotation(GTAQuat rot) { - footSync.Rotation = rot; + footSync_.Rotation = rot; // Let it update for all players and internally in open.mp sendFootSync(); - if (moving) + if (moving_) { - move(targetPosition, moveType); + move(targetPosition_, moveType_); } } int NPC::getVirtualWorld() const { - return player->getVirtualWorld(); + return player_->getVirtualWorld(); } void NPC::setVirtualWorld(int vw) { - player->setVirtualWorld(vw); + player_->setVirtualWorld(vw); } void NPC::spawn() @@ -112,15 +112,15 @@ void NPC::spawn() NetworkBitStream emptyBS; requestClassBS.writeUINT16(0); - npcComponent->emulateRPCIn(this, NetCode::RPC::PlayerRequestClass::PacketID, requestClassBS); + npcComponent_->emulateRPCIn(this, NetCode::RPC::PlayerRequestClass::PacketID, requestClassBS); - npcComponent->emulateRPCIn(this, NetCode::RPC::PlayerRequestSpawn::PacketID, emptyBS); - npcComponent->emulateRPCIn(this, NetCode::RPC::PlayerSpawn::PacketID, emptyBS); + npcComponent_->emulateRPCIn(this, NetCode::RPC::PlayerRequestSpawn::PacketID, emptyBS); + npcComponent_->emulateRPCIn(this, NetCode::RPC::PlayerSpawn::PacketID, emptyBS); } bool NPC::move(Vector3 pos, NPCMoveType moveType) { - if (moveType_ == NPCMoveType_None) + if (moveType == NPCMoveType_None) { return false; } @@ -137,29 +137,26 @@ bool NPC::move(Vector3 pos, NPCMoveType moveType) // Determine which speed to use based on moving type float speed = 0.0f; - if (moveType != NPCMoveType_None) + if (moveType == NPCMoveType_Sprint) { - if (moveType == NPCMoveType_Sprint) - { - speed = NPC_MOVE_SPEED_SPRINT; - footSync.Keys |= Key::SPRINT; - } - else if (moveType == NPCMoveType_Jog) - { - speed = NPC_MOVE_SPEED_JOG; - } - else - { - speed = NPC_MOVE_SPEED_WALK; - footSync.Keys |= Key::WALK; - } - - footSync.UpDown = static_cast(Key::ANALOG_UP); + speed = NPC_MOVE_SPEED_SPRINT; + footSync_.Keys |= Key::SPRINT; } + else if (moveType == NPCMoveType_Jog) + { + speed = NPC_MOVE_SPEED_JOG; + } + else + { + speed = NPC_MOVE_SPEED_WALK; + footSync_.Keys |= Key::WALK; + } + + footSync_.UpDown = static_cast(Key::ANALOG_UP); // Calculate velocity to use on tick newVelocity *= (speed / 100.0f); - velocity = newVelocity; + velocity_ = newVelocity; // Calculate front vector and player's facing angle: Vector3 front; @@ -170,97 +167,98 @@ bool NPC::move(Vector3 pos, NPCMoveType moveType) auto rotation = getRotation().ToEuler(); rotation.z = utils::getAngleOfLine(front.x, front.y); - footSync.Rotation = rotation; // Do this directly, if you use NPC::setRotation it's going to cause recursion + footSync_.Rotation = rotation; // Do this directly, if you use NPC::setRotation it's going to cause recursion // Set internal variables - moveSpeed = speed; - targetPosition = pos; - moving = true; - lastMove = Time::now(); + moveSpeed_ = speed; + targetPosition_ = pos; + moving_ = true; + moveType_ = moveType; + lastMove_ = Time::now(); return true; } void NPC::stopMove() { - moving = false; - moveSpeed = 0.0f; - targetPosition = { 0.0f, 0.0f, 0.0f }; - velocity = { 0.0f, 0.0f, 0.0f }; - moveType = NPCMoveType_None; - - footSync.Keys &= Key::SPRINT; - footSync.Keys &= Key::WALK; - footSync.UpDown = 0; + moving_ = false; + moveSpeed_ = 0.0f; + targetPosition_ = { 0.0f, 0.0f, 0.0f }; + velocity_ = { 0.0f, 0.0f, 0.0f }; + moveType_ = NPCMoveType_None; + + footSync_.Keys &= Key::SPRINT; + footSync_.Keys &= Key::WALK; + footSync_.UpDown = 0; } void NPC::sendFootSync() { // Only send foot sync if player is spawned - if (!(player->getState() == PlayerState_OnFoot || player->getState() == PlayerState_Driver || player->getState() == PlayerState_Passenger || player->getState() == PlayerState_Spawned)) + if (!(player_->getState() == PlayerState_OnFoot || player_->getState() == PlayerState_Driver || player_->getState() == PlayerState_Passenger || player_->getState() == PlayerState_Spawned)) { return; } NetworkBitStream bs; - auto& quat = footSync.Rotation.q; + auto& quat = footSync_.Rotation.q; - bs.writeUINT16(footSync.LeftRight); - bs.writeUINT16(footSync.UpDown); - bs.writeUINT16(footSync.Keys); - bs.writeVEC3(footSync.Position); + bs.writeUINT16(footSync_.LeftRight); + bs.writeUINT16(footSync_.UpDown); + bs.writeUINT16(footSync_.Keys); + bs.writeVEC3(footSync_.Position); bs.writeVEC4(Vector4(quat.w, quat.x, quat.y, quat.z)); - bs.writeUINT8(uint8_t(footSync.HealthArmour.x)); - bs.writeUINT8(uint8_t(footSync.HealthArmour.y)); - bs.writeUINT8(footSync.WeaponAdditionalKey); - bs.writeUINT8(footSync.SpecialAction); - bs.writeVEC3(footSync.Velocity); - bs.writeVEC3(footSync.SurfingData.offset); - bs.writeUINT16(footSync.SurfingData.ID); - bs.writeUINT16(footSync.AnimationID); - bs.writeUINT16(footSync.AnimationFlags); - - npcComponent->emulatePacketIn(this, footSync.PacketID, bs); + bs.writeUINT8(uint8_t(footSync_.HealthArmour.x)); + bs.writeUINT8(uint8_t(footSync_.HealthArmour.y)); + bs.writeUINT8(footSync_.WeaponAdditionalKey); + bs.writeUINT8(footSync_.SpecialAction); + bs.writeVEC3(footSync_.Velocity); + bs.writeVEC3(footSync_.SurfingData.offset); + bs.writeUINT16(footSync_.SurfingData.ID); + bs.writeUINT16(footSync_.AnimationID); + bs.writeUINT16(footSync_.AnimationFlags); + + npcComponent_->emulatePacketIn(this, footSync_.PacketID, bs); } void NPC::advance(TimePoint now) { auto position = getPosition(); - Milliseconds difference = duration_cast(now.time_since_epoch()) - duration_cast(lastMove.time_since_epoch()); - float remainingDistance = glm::distance(position, targetPosition); - Vector3 travelled = velocity * static_cast(difference.count()); + Milliseconds difference = duration_cast(now.time_since_epoch()) - duration_cast(lastMove_.time_since_epoch()); + float remainingDistance = glm::distance(position, targetPosition_); + Vector3 travelled = velocity_ * static_cast(difference.count()); if (glm::length(travelled) >= remainingDistance) { stopMove(); - npcComponent->getEventDispatcher_internal().dispatch(&NPCEventHandler::onNPCFinishMove, *this); + npcComponent_->getEventDispatcher_internal().dispatch(&NPCEventHandler::onNPCFinishMove, *this); } else { position += travelled; - footSync.Velocity = velocity; + footSync_.Velocity = velocity_; } - lastMove = Time::now(); - footSync.Position = position; // Do this directly, if you use NPC::setPosition it's going to cause recursion + lastMove_ = Time::now(); + footSync_.Position = position; // Do this directly, if you use NPC::setPosition it's going to cause recursion } void NPC::tick(Microseconds elapsed, TimePoint now) { - static auto footSyncRate = npcComponent->getCore()->getConfig().getInt("network.on_foot_sync_rate"); + static auto footSyncRate = npcComponent_->getCore()->getConfig().getInt("network.on_foot_sync_rate"); // Only process the NPC if it is spawned - if (player && (player->getState() == PlayerState_OnFoot || player->getState() == PlayerState_Driver || player->getState() == PlayerState_Passenger || player->getState() == PlayerState_Spawned)) + if (player_ && (player_->getState() == PlayerState_OnFoot || player_->getState() == PlayerState_Driver || player_->getState() == PlayerState_Passenger || player_->getState() == PlayerState_Spawned)) { - if (moving) + if (moving_) { advance(now); } - if ((now - lastUpdate).count() > *footSyncRate) + if ((now - lastUpdate_).count() > *footSyncRate) { sendFootSync(); - lastUpdate = now; + lastUpdate_ = now; } } } diff --git a/Server/Components/NPCs/NPC/npc.hpp b/Server/Components/NPCs/NPC/npc.hpp index cd2cf9aa1..f4740e075 100644 --- a/Server/Components/NPCs/NPC/npc.hpp +++ b/Server/Components/NPCs/NPC/npc.hpp @@ -50,30 +50,30 @@ class NPC : public INPC, public PoolIDProvider, public NoCopy IPlayer* getPlayer() override { - return player; + return player_; } - void setPlayer(IPlayer* plyr) + void setPlayer(IPlayer* player) { - player = plyr; + player_ = player; } private: // The NPC's player pointer. - IPlayer* player; - TimePoint lastUpdate; + IPlayer* player_; + TimePoint lastUpdate_; // Movements - NPCMoveType moveType; - TimePoint lastMove; - TimePoint moveStart; - float moveSpeed; - Vector3 targetPosition; - Vector3 velocity; - bool moving; + NPCMoveType moveType_; + TimePoint lastMove_; + TimePoint moveStart_; + float moveSpeed_; + Vector3 targetPosition_; + Vector3 velocity_; + bool moving_; // Packets - NetCode::Packet::PlayerFootSync footSync; + NetCode::Packet::PlayerFootSync footSync_; - NPCComponent* npcComponent; + NPCComponent* npcComponent_; }; From 07b1d1630edbf709288af51365811ad1adcdfb59 Mon Sep 17 00:00:00 2001 From: iAmir Date: Wed, 17 Apr 2024 06:34:58 +0330 Subject: [PATCH 13/32] fix formatting --- Server/Components/NPCs/Network/npcs_network.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Server/Components/NPCs/Network/npcs_network.hpp b/Server/Components/NPCs/Network/npcs_network.hpp index ddbb05414..edf4d14b7 100644 --- a/Server/Components/NPCs/Network/npcs_network.hpp +++ b/Server/Components/NPCs/Network/npcs_network.hpp @@ -24,25 +24,25 @@ struct NPCNetwork : public Impl::Network bool sendPacket(IPlayer& peer, Span data, int channel, bool dispatchEvents = true) override { - //core->logLn(LogLevel::Error, "[npc network] sendPacket(\"%.*s\", data, %i, %i)\n", peer.getName().length(), peer.getName().data(), channel, dispatchEvents); + // core->logLn(LogLevel::Error, "[npc network] sendPacket(\"%.*s\", data, %i, %i)\n", peer.getName().length(), peer.getName().data(), channel, dispatchEvents); return true; } bool broadcastPacket(Span data, int channel, const IPlayer* exceptPeer = nullptr, bool dispatchEvents = true) override { - //core->logLn(LogLevel::Error, "[npc network] broadcastPacket(data, %i, \"%.*s\", %i)\n", channel, exceptPeer == nullptr ? 0 : exceptPeer->getName().length(), exceptPeer == nullptr ? "" : exceptPeer->getName().data(), dispatchEvents); + // core->logLn(LogLevel::Error, "[npc network] broadcastPacket(data, %i, \"%.*s\", %i)\n", channel, exceptPeer == nullptr ? 0 : exceptPeer->getName().length(), exceptPeer == nullptr ? "" : exceptPeer->getName().data(), dispatchEvents); return true; } bool sendRPC(IPlayer& peer, int id, Span data, int channel, bool dispatchEvents = true) override { - //core->logLn(LogLevel::Error, "[npc network] sendRpc(\"%.*s\", %i, data, %i, %i)\n", peer.getName().length(), peer.getName().data(), id, channel, dispatchEvents); + // core->logLn(LogLevel::Error, "[npc network] sendRpc(\"%.*s\", %i, data, %i, %i)\n", peer.getName().length(), peer.getName().data(), id, channel, dispatchEvents); return true; } bool broadcastRPC(int id, Span data, int channel, const IPlayer* exceptPeer = nullptr, bool dispatchEvents = true) override { - //core->logLn(LogLevel::Error, "[npc network] broadcastRPC(%i, data, %i, \"%.*s\", %i)\n", id, channel, exceptPeer == nullptr ? 0 : exceptPeer->getName().length(), exceptPeer == nullptr ? "" : exceptPeer->getName().data(), dispatchEvents); + // core->logLn(LogLevel::Error, "[npc network] broadcastRPC(%i, data, %i, \"%.*s\", %i)\n", id, channel, exceptPeer == nullptr ? 0 : exceptPeer->getName().length(), exceptPeer == nullptr ? "" : exceptPeer->getName().data(), dispatchEvents); return true; } From 6ee33c1878915764856fb2c35660b9dc1c2e9456 Mon Sep 17 00:00:00 2001 From: iAmir Date: Sat, 20 Apr 2024 05:34:35 +0330 Subject: [PATCH 14/32] rename npc connect events to entity create and destroy --- SDK | 2 +- Server/Components/NPCs/npcs_impl.cpp | 17 +++++++++++++---- Server/Components/Pawn/Scripting/NPC/Events.hpp | 8 ++++---- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/SDK b/SDK index 338c0020a..118809c0a 160000 --- a/SDK +++ b/SDK @@ -1 +1 @@ -Subproject commit 338c0020a5263092278e13cefae031af05fee7fe +Subproject commit 118809c0a8a4236f1b36440481b7cb0797bb0a76 diff --git a/Server/Components/NPCs/npcs_impl.cpp b/Server/Components/NPCs/npcs_impl.cpp index cf6cbe946..d01a1cac3 100644 --- a/Server/Components/NPCs/npcs_impl.cpp +++ b/Server/Components/NPCs/npcs_impl.cpp @@ -66,8 +66,13 @@ void NPCComponent::release(int index) if (ptr) { // Call disconnect events for both NPC and player. This way NPC's player instance is going to be handled and cleared properly. - eventDispatcher.dispatch(&NPCEventHandler::onNPCDisconnect, *ptr); - npcNetwork.networkEventDispatcher.dispatch(&NetworkEventHandler::onPeerDisconnect, *ptr->getPlayer(), PeerDisconnectReason_Quit); + ScopedPoolReleaseLock lock(*this, ptr->getID()); + if (lock.entry) + { + eventDispatcher.dispatch(&NPCEventHandler::onNPCDestroy, *lock.entry); + npcNetwork.networkEventDispatcher.dispatch(&NetworkEventHandler::onPeerDisconnect, *lock.entry->getPlayer(), PeerDisconnectReason_Quit); + } + storage.release(index, false); } } @@ -129,8 +134,12 @@ INPC* NPCComponent::create(StringView name) if (npc) { // Call connect events for both NPC and player, this way it can get initialized properly in player pool too - eventDispatcher.dispatch(&NPCEventHandler::onNPCConnect, *npc); - npcNetwork.networkEventDispatcher.dispatch(&NetworkEventHandler::onPeerConnect, *npc->getPlayer()); + ScopedPoolReleaseLock lock(*this, npc->getID()); + if (lock.entry) + { + eventDispatcher.dispatch(&NPCEventHandler::onNPCCreate, *lock.entry); + npcNetwork.networkEventDispatcher.dispatch(&NetworkEventHandler::onPeerConnect, *lock.entry->getPlayer()); + } } return npc; diff --git a/Server/Components/Pawn/Scripting/NPC/Events.hpp b/Server/Components/Pawn/Scripting/NPC/Events.hpp index 757ee2fe8..d932fd0db 100644 --- a/Server/Components/Pawn/Scripting/NPC/Events.hpp +++ b/Server/Components/Pawn/Scripting/NPC/Events.hpp @@ -18,13 +18,13 @@ struct NPCEvents : public NPCEventHandler, public Singleton PawnManager::Get()->CallAllInEntryFirst("OnNPCFinishMove", DefaultReturnValue_True, npc.getID()); } - void onNPCConnect(INPC& npc) override + void onNPCCreate(INPC& npc) override { - PawnManager::Get()->CallAllInEntryFirst("OnNPCConnect", DefaultReturnValue_True, npc.getID()); + PawnManager::Get()->CallAllInEntryFirst("OnNPCCreate", DefaultReturnValue_True, npc.getID()); } - void onNPCDisconnect(INPC& npc) override + void onNPCDestroy(INPC& npc) override { - PawnManager::Get()->CallAllInEntryFirst("OnNPCDisconnect", DefaultReturnValue_True, npc.getID()); + PawnManager::Get()->CallAllInEntryFirst("OnNPCDestroy", DefaultReturnValue_True, npc.getID()); } }; From 34172858ea1d396df1b67bf07e43235cf332230f Mon Sep 17 00:00:00 2001 From: iAmir Date: Sat, 20 Apr 2024 05:35:45 +0330 Subject: [PATCH 15/32] mark to be destroyed npcs to be process next tick --- .../Components/NPCs/Network/npcs_network.hpp | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/Server/Components/NPCs/Network/npcs_network.hpp b/Server/Components/NPCs/Network/npcs_network.hpp index edf4d14b7..1af0f8b60 100644 --- a/Server/Components/NPCs/Network/npcs_network.hpp +++ b/Server/Components/NPCs/Network/npcs_network.hpp @@ -7,14 +7,28 @@ */ #include +#include #include -struct NPCNetwork : public Impl::Network +using namespace Impl; + +class NPCNetwork : public Impl::Network { +private: ICore* core; - void init(ICore* core) + INPCComponent* npcComponent; + DynamicArray markedToBeKicked; + +public: + void init(ICore* c, INPCComponent* comp) + { + core = c; + npcComponent = comp; + } + + DynamicArray& getMarkedForKickNPCs() { - this->core = core; + return markedToBeKicked; } ENetworkType getNetworkType() const override @@ -58,6 +72,12 @@ struct NPCNetwork : public Impl::Network void disconnect(const IPlayer& peer) override { + auto id = peer.getID(); + auto npc = npcComponent->get(id); + if (npc) + { + markedToBeKicked.push_back(npc->getID()); + } } void ban(const BanEntry& entry, Milliseconds expire = Milliseconds(0)) override @@ -76,5 +96,6 @@ struct NPCNetwork : public Impl::Network : Network(256, 256) { } + ~NPCNetwork() { } -}; \ No newline at end of file +}; From 95d5522563f279da94119f6a60c62fe6f2faac0d Mon Sep 17 00:00:00 2001 From: iAmir Date: Sat, 20 Apr 2024 05:36:55 +0330 Subject: [PATCH 16/32] destroy NPCs safely --- SDK | 2 +- Server/Components/NPCs/npcs_impl.cpp | 16 +++++++++++++++- Server/Components/NPCs/npcs_impl.hpp | 3 ++- Server/Components/Pawn/Scripting/NPC/Natives.cpp | 2 +- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/SDK b/SDK index 118809c0a..b1f6b7e09 160000 --- a/SDK +++ b/SDK @@ -1 +1 @@ -Subproject commit 118809c0a8a4236f1b36440481b7cb0797bb0a76 +Subproject commit b1f6b7e0988a566844db0823729b68d14a877a95 diff --git a/Server/Components/NPCs/npcs_impl.cpp b/Server/Components/NPCs/npcs_impl.cpp index d01a1cac3..0c9fceb23 100644 --- a/Server/Components/NPCs/npcs_impl.cpp +++ b/Server/Components/NPCs/npcs_impl.cpp @@ -89,6 +89,16 @@ bool NPCComponent::unlock(int index) void NPCComponent::onTick(Microseconds elapsed, TimePoint now) { + // Go through NPCs ready to be destroyed/kicked + auto markedForKick = npcNetwork.getMarkedForKickNPCs(); + for (auto& npc : markedForKick) + { + release(npc); + } + + // Clean this pool because it is now processed + markedForKick.clear(); + for (auto& npc : storage) { static_cast(npc)->tick(elapsed, now); @@ -145,7 +155,11 @@ INPC* NPCComponent::create(StringView name) return npc; } -void NPCComponent::emulateRPCIn(INPC* npc, int rpcId, NetworkBitStream& bs) +void NPCComponent::destroy(INPC& npc) +{ + npcNetwork.disconnect(*npc.getPlayer()); +} + { auto player = npc->getPlayer(); diff --git a/Server/Components/NPCs/npcs_impl.hpp b/Server/Components/NPCs/npcs_impl.hpp index 5152c7de1..2e9f2ec92 100644 --- a/Server/Components/NPCs/npcs_impl.hpp +++ b/Server/Components/NPCs/npcs_impl.hpp @@ -63,7 +63,8 @@ class NPCComponent final : public INPCComponent, public CoreEventHandler INPC* create(StringView name) override; - void emulateRPCIn(INPC* npc, int rpcId, NetworkBitStream& bs); + void destroy(INPC& npc) override; + void emulatePacketIn(INPC* npc, int type, NetworkBitStream& bs); diff --git a/Server/Components/Pawn/Scripting/NPC/Natives.cpp b/Server/Components/Pawn/Scripting/NPC/Natives.cpp index 4d10f67f3..f75b58fb2 100644 --- a/Server/Components/Pawn/Scripting/NPC/Natives.cpp +++ b/Server/Components/Pawn/Scripting/NPC/Natives.cpp @@ -27,7 +27,7 @@ SCRIPT_API(NPC_Create, int(const String& name)) SCRIPT_API(NPC_Destroy, bool(INPC& npc)) { - PawnManager::Get()->npcs->release(npc.getID()); + PawnManager::Get()->npcs->destroy(npc); return true; } From de9320f07ae0148eac80ac10341200a549c95cfa Mon Sep 17 00:00:00 2001 From: iAmir Date: Sat, 20 Apr 2024 05:37:18 +0330 Subject: [PATCH 17/32] use NPC ref instead of ptr in emulate functions --- Server/Components/NPCs/NPC/npc.cpp | 8 ++++---- Server/Components/NPCs/npcs_impl.cpp | 9 +++++---- Server/Components/NPCs/npcs_impl.hpp | 3 ++- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index e80ae5f81..d81bdbbb2 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -112,10 +112,10 @@ void NPC::spawn() NetworkBitStream emptyBS; requestClassBS.writeUINT16(0); - npcComponent_->emulateRPCIn(this, NetCode::RPC::PlayerRequestClass::PacketID, requestClassBS); + npcComponent_->emulateRPCIn(*this, NetCode::RPC::PlayerRequestClass::PacketID, requestClassBS); - npcComponent_->emulateRPCIn(this, NetCode::RPC::PlayerRequestSpawn::PacketID, emptyBS); - npcComponent_->emulateRPCIn(this, NetCode::RPC::PlayerSpawn::PacketID, emptyBS); + npcComponent_->emulateRPCIn(*this, NetCode::RPC::PlayerRequestSpawn::PacketID, emptyBS); + npcComponent_->emulateRPCIn(*this, NetCode::RPC::PlayerSpawn::PacketID, emptyBS); } bool NPC::move(Vector3 pos, NPCMoveType moveType) @@ -218,7 +218,7 @@ void NPC::sendFootSync() bs.writeUINT16(footSync_.AnimationID); bs.writeUINT16(footSync_.AnimationFlags); - npcComponent_->emulatePacketIn(this, footSync_.PacketID, bs); + npcComponent_->emulatePacketIn(*this, footSync_.PacketID, bs); } void NPC::advance(TimePoint now) diff --git a/Server/Components/NPCs/npcs_impl.cpp b/Server/Components/NPCs/npcs_impl.cpp index 0c9fceb23..1dc559b63 100644 --- a/Server/Components/NPCs/npcs_impl.cpp +++ b/Server/Components/NPCs/npcs_impl.cpp @@ -16,7 +16,7 @@ void NPCComponent::onLoad(ICore* c) void NPCComponent::onInit(IComponentList* components) { - npcNetwork.init(core); + npcNetwork.init(core, this); core->getEventDispatcher().addEventHandler(this); } @@ -160,8 +160,9 @@ void NPCComponent::destroy(INPC& npc) npcNetwork.disconnect(*npc.getPlayer()); } +void NPCComponent::emulateRPCIn(INPC& npc, int rpcId, NetworkBitStream& bs) { - auto player = npc->getPlayer(); + auto player = npc.getPlayer(); const bool res = npcNetwork.inEventDispatcher.stopAtFalse([player, rpcId, &bs](NetworkInEventHandler* handler) { @@ -178,9 +179,9 @@ void NPCComponent::destroy(INPC& npc) } } -void NPCComponent::emulatePacketIn(INPC* npc, int type, NetworkBitStream& bs) +void NPCComponent::emulatePacketIn(INPC& npc, int type, NetworkBitStream& bs) { - auto player = npc->getPlayer(); + auto player = npc.getPlayer(); const bool res = npcNetwork.inEventDispatcher.stopAtFalse([player, type, &bs](NetworkInEventHandler* handler) { diff --git a/Server/Components/NPCs/npcs_impl.hpp b/Server/Components/NPCs/npcs_impl.hpp index 2e9f2ec92..227879d5b 100644 --- a/Server/Components/NPCs/npcs_impl.hpp +++ b/Server/Components/NPCs/npcs_impl.hpp @@ -65,8 +65,9 @@ class NPCComponent final : public INPCComponent, public CoreEventHandler void destroy(INPC& npc) override; + void emulateRPCIn(INPC& npc, int rpcId, NetworkBitStream& bs); - void emulatePacketIn(INPC* npc, int type, NetworkBitStream& bs); + void emulatePacketIn(INPC& npc, int type, NetworkBitStream& bs); ICore* getCore() { From 4c7916c7d10c51a8485bb59af8d84e62e85fb7d2 Mon Sep 17 00:00:00 2001 From: iAmir Date: Tue, 23 Apr 2024 15:28:27 +0330 Subject: [PATCH 18/32] store footSyncRate config value in npc component --- Server/Components/NPCs/NPC/npc.cpp | 2 +- Server/Components/NPCs/npcs_impl.cpp | 1 + Server/Components/NPCs/npcs_impl.hpp | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index d81bdbbb2..752cd16ad 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -255,7 +255,7 @@ void NPC::tick(Microseconds elapsed, TimePoint now) advance(now); } - if ((now - lastUpdate_).count() > *footSyncRate) + if ((now - lastUpdate_).count() > npcComponent_->getFootSyncRate()) { sendFootSync(); lastUpdate_ = now; diff --git a/Server/Components/NPCs/npcs_impl.cpp b/Server/Components/NPCs/npcs_impl.cpp index 1dc559b63..425125e22 100644 --- a/Server/Components/NPCs/npcs_impl.cpp +++ b/Server/Components/NPCs/npcs_impl.cpp @@ -12,6 +12,7 @@ void NPCComponent::onLoad(ICore* c) { core = c; + footSyncRate = c->getConfig().getInt("network.on_foot_sync_rate"); } void NPCComponent::onInit(IComponentList* components) diff --git a/Server/Components/NPCs/npcs_impl.hpp b/Server/Components/NPCs/npcs_impl.hpp index 227879d5b..a3a865fd1 100644 --- a/Server/Components/NPCs/npcs_impl.hpp +++ b/Server/Components/NPCs/npcs_impl.hpp @@ -79,9 +79,15 @@ class NPCComponent final : public INPCComponent, public CoreEventHandler return eventDispatcher; } + int getFootSyncRate() const + { + return *footSyncRate; + } + private: ICore* core = nullptr; NPCNetwork npcNetwork; DefaultEventDispatcher eventDispatcher; MarkedDynamicPoolStorage storage; + int* footSyncRate = nullptr; }; From 2b9450836f7429e856ea9ee3d4417092b56d344c Mon Sep 17 00:00:00 2001 From: iAmir Date: Tue, 23 Apr 2024 15:28:47 +0330 Subject: [PATCH 19/32] calculate estimated arrival time and use it to detect finish move --- Server/Components/NPCs/NPC/npc.cpp | 36 +++++++++++++++------------- Server/Components/NPCs/NPC/npc.hpp | 1 + Server/Components/NPCs/npcs_impl.hpp | 2 +- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index 752cd16ad..e8a62c9f5 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -128,12 +128,6 @@ bool NPC::move(Vector3 pos, NPCMoveType moveType) // Set up everything to start moving in next tick auto position = getPosition(); float distance = glm::distance(position, pos); - Vector3 newVelocity; - - if (!(distance <= 0.0f)) - { - newVelocity = (pos - position) / distance; - } // Determine which speed to use based on moving type float speed = 0.0f; @@ -154,10 +148,6 @@ bool NPC::move(Vector3 pos, NPCMoveType moveType) footSync_.UpDown = static_cast(Key::ANALOG_UP); - // Calculate velocity to use on tick - newVelocity *= (speed / 100.0f); - velocity_ = newVelocity; - // Calculate front vector and player's facing angle: Vector3 front; if (!(std::fabs(distance) < DBL_EPSILON)) @@ -169,6 +159,18 @@ bool NPC::move(Vector3 pos, NPCMoveType moveType) rotation.z = utils::getAngleOfLine(front.x, front.y); footSync_.Rotation = rotation; // Do this directly, if you use NPC::setRotation it's going to cause recursion + // Calculate velocity to use on tick + velocity_ *= (speed / 100.0f); + + if (glm::length(velocity_) != 0.0f) + { + estimatedArrivalTimeNS_ = Time::now().time_since_epoch().count() + (static_cast(distance / glm::length(velocity_)) * ((npcComponent_->getFootSyncRate() * 10000) + 1000000)); + } + else + { + estimatedArrivalTimeNS_ = 0; + } + // Set internal variables moveSpeed_ = speed; targetPosition_ = pos; @@ -185,6 +187,7 @@ void NPC::stopMove() targetPosition_ = { 0.0f, 0.0f, 0.0f }; velocity_ = { 0.0f, 0.0f, 0.0f }; moveType_ = NPCMoveType_None; + estimatedArrivalTimeNS_ = 0; footSync_.Keys &= Key::SPRINT; footSync_.Keys &= Key::WALK; @@ -224,29 +227,28 @@ void NPC::sendFootSync() void NPC::advance(TimePoint now) { auto position = getPosition(); - Milliseconds difference = duration_cast(now.time_since_epoch()) - duration_cast(lastMove_.time_since_epoch()); - float remainingDistance = glm::distance(position, targetPosition_); - Vector3 travelled = velocity_ * static_cast(difference.count()); - if (glm::length(travelled) >= remainingDistance) + if (estimatedArrivalTimeNS_ <= Time::now().time_since_epoch().count()) { + footSync_.Position = targetPosition_; stopMove(); npcComponent_->getEventDispatcher_internal().dispatch(&NPCEventHandler::onNPCFinishMove, *this); } else { + Milliseconds difference = duration_cast(now.time_since_epoch()) - duration_cast(lastMove_.time_since_epoch()); + Vector3 travelled = velocity_ * static_cast(difference.count()); + position += travelled; footSync_.Velocity = velocity_; + footSync_.Position = position; // Do this directly, if you use NPC::setPosition it's going to cause recursion } lastMove_ = Time::now(); - footSync_.Position = position; // Do this directly, if you use NPC::setPosition it's going to cause recursion } void NPC::tick(Microseconds elapsed, TimePoint now) { - static auto footSyncRate = npcComponent_->getCore()->getConfig().getInt("network.on_foot_sync_rate"); - // Only process the NPC if it is spawned if (player_ && (player_->getState() == PlayerState_OnFoot || player_->getState() == PlayerState_Driver || player_->getState() == PlayerState_Passenger || player_->getState() == PlayerState_Spawned)) { diff --git a/Server/Components/NPCs/NPC/npc.hpp b/Server/Components/NPCs/NPC/npc.hpp index f4740e075..e707f2a3f 100644 --- a/Server/Components/NPCs/NPC/npc.hpp +++ b/Server/Components/NPCs/NPC/npc.hpp @@ -66,6 +66,7 @@ class NPC : public INPC, public PoolIDProvider, public NoCopy // Movements NPCMoveType moveType_; TimePoint lastMove_; + long long estimatedArrivalTimeNS_; TimePoint moveStart_; float moveSpeed_; Vector3 targetPosition_; diff --git a/Server/Components/NPCs/npcs_impl.hpp b/Server/Components/NPCs/npcs_impl.hpp index a3a865fd1..86a7b4f87 100644 --- a/Server/Components/NPCs/npcs_impl.hpp +++ b/Server/Components/NPCs/npcs_impl.hpp @@ -79,7 +79,7 @@ class NPCComponent final : public INPCComponent, public CoreEventHandler return eventDispatcher; } - int getFootSyncRate() const + int getFootSyncRate() const { return *footSyncRate; } From 84b176ca4ad53579cdec5c873c626544bd7a4e98 Mon Sep 17 00:00:00 2001 From: iAmir Date: Tue, 23 Apr 2024 17:19:57 +0330 Subject: [PATCH 20/32] fix velocity calculation --- Server/Components/NPCs/NPC/npc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index e8a62c9f5..206bc5a45 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -160,9 +160,9 @@ bool NPC::move(Vector3 pos, NPCMoveType moveType) footSync_.Rotation = rotation; // Do this directly, if you use NPC::setRotation it's going to cause recursion // Calculate velocity to use on tick - velocity_ *= (speed / 100.0f); + velocity_ = front * (speed / 100.0f); - if (glm::length(velocity_) != 0.0f) + if (!(std::fabs(glm::length(velocity_)) < DBL_EPSILON)) { estimatedArrivalTimeNS_ = Time::now().time_since_epoch().count() + (static_cast(distance / glm::length(velocity_)) * ((npcComponent_->getFootSyncRate() * 10000) + 1000000)); } From 827d3e6baf2c5b11f25a5cabfdecd0df9f16777b Mon Sep 17 00:00:00 2001 From: iAmir Date: Tue, 23 Apr 2024 17:20:09 +0330 Subject: [PATCH 21/32] add NPC_SetFacingAngle and GetFacingAngle --- Server/Components/Pawn/Scripting/NPC/Natives.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Server/Components/Pawn/Scripting/NPC/Natives.cpp b/Server/Components/Pawn/Scripting/NPC/Natives.cpp index f75b58fb2..20ba04563 100644 --- a/Server/Components/Pawn/Scripting/NPC/Natives.cpp +++ b/Server/Components/Pawn/Scripting/NPC/Natives.cpp @@ -66,6 +66,20 @@ SCRIPT_API(NPC_GetRot, bool(INPC& npc, Vector3& rotation)) return true; } +SCRIPT_API(NPC_SetFacingAngle, bool(INPC& npc, float angle)) +{ + auto rotation = npc.getRotation().ToEuler(); + rotation.z = angle; + return true; +} + +SCRIPT_API(NPC_GetFacingAngle, bool(INPC& npc, float& angle)) +{ + auto rotation = npc.getRotation().ToEuler(); + angle = rotation.z; + return true; +} + SCRIPT_API(NPC_SetVirtualWorld, bool(INPC& npc, int virtualWorld)) { npc.setVirtualWorld(virtualWorld); From c474539474bb5596cd28f576ed575c37fab4d036 Mon Sep 17 00:00:00 2001 From: iAmir Date: Wed, 24 Apr 2024 04:39:30 +0330 Subject: [PATCH 22/32] another attempt to fix npc movements --- Server/Components/NPCs/NPC/npc.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index 206bc5a45..99e799b34 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -164,7 +164,7 @@ bool NPC::move(Vector3 pos, NPCMoveType moveType) if (!(std::fabs(glm::length(velocity_)) < DBL_EPSILON)) { - estimatedArrivalTimeNS_ = Time::now().time_since_epoch().count() + (static_cast(distance / glm::length(velocity_)) * ((npcComponent_->getFootSyncRate() * 10000) + 1000000)); + estimatedArrivalTimeNS_ = Time::now().time_since_epoch().count() + (static_cast(distance / glm::length(velocity_)) * (/* (npcComponent_->getFootSyncRate() * 10000) +*/ 1000000)); } else { @@ -228,10 +228,11 @@ void NPC::advance(TimePoint now) { auto position = getPosition(); - if (estimatedArrivalTimeNS_ <= Time::now().time_since_epoch().count()) + if (estimatedArrivalTimeNS_ <= Time::now().time_since_epoch().count() || glm::distance(position, targetPosition_) <= 0.1f) { - footSync_.Position = targetPosition_; + auto pos = targetPosition_; stopMove(); + setPosition(pos); npcComponent_->getEventDispatcher_internal().dispatch(&NPCEventHandler::onNPCFinishMove, *this); } else From 13daf56ff0a5ed39e0e38274a77a4e65daf5e044 Mon Sep 17 00:00:00 2001 From: iAmir Date: Thu, 9 May 2024 01:11:44 +0330 Subject: [PATCH 23/32] add NPC_SetSkin --- SDK | 2 +- Server/Components/NPCs/NPC/npc.cpp | 5 +++++ Server/Components/NPCs/NPC/npc.hpp | 2 ++ Server/Components/Pawn/Scripting/NPC/Natives.cpp | 6 ++++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/SDK b/SDK index b1f6b7e09..f36dc2e59 160000 --- a/SDK +++ b/SDK @@ -1 +1 @@ -Subproject commit b1f6b7e0988a566844db0823729b68d14a877a95 +Subproject commit f36dc2e591a505b5eb6cf1f7a11b34d0089c715a diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index 99e799b34..cda853166 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -194,6 +194,11 @@ void NPC::stopMove() footSync_.UpDown = 0; } +void NPC::setSkin(int model) +{ + player_->setSkin(model); +} + void NPC::sendFootSync() { // Only send foot sync if player is spawned diff --git a/Server/Components/NPCs/NPC/npc.hpp b/Server/Components/NPCs/NPC/npc.hpp index e707f2a3f..1113bd95a 100644 --- a/Server/Components/NPCs/NPC/npc.hpp +++ b/Server/Components/NPCs/NPC/npc.hpp @@ -37,6 +37,8 @@ class NPC : public INPC, public PoolIDProvider, public NoCopy void stopMove() override; + void setSkin(int model) override; + void sendFootSync(); void tick(Microseconds elapsed, TimePoint now); diff --git a/Server/Components/Pawn/Scripting/NPC/Natives.cpp b/Server/Components/Pawn/Scripting/NPC/Natives.cpp index 20ba04563..098161d5c 100644 --- a/Server/Components/Pawn/Scripting/NPC/Natives.cpp +++ b/Server/Components/Pawn/Scripting/NPC/Natives.cpp @@ -102,3 +102,9 @@ SCRIPT_API(NPC_StopMove, bool(INPC& npc)) npc.stopMove(); return true; } + +SCRIPT_API(NPC_SetSkin, bool(INPC& npc, int model)) +{ + npc.setSkin(model); + return true; +} From 7c369e81cfaab00385273fa347e08bc4c9145a9e Mon Sep 17 00:00:00 2001 From: iAmir Date: Mon, 13 May 2024 11:04:23 +0330 Subject: [PATCH 24/32] add streamed in check natives for NPCs --- SDK | 2 +- Server/Components/NPCs/NPC/npc.cpp | 15 +++++++++++++++ Server/Components/NPCs/NPC/npc.hpp | 4 ++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/SDK b/SDK index f36dc2e59..2f1dade8f 160000 --- a/SDK +++ b/SDK @@ -1 +1 @@ -Subproject commit f36dc2e591a505b5eb6cf1f7a11b34d0089c715a +Subproject commit 2f1dade8f6acf86cb14ad8b730ba1ba833458aa8 diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index cda853166..b68654da8 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -199,6 +199,21 @@ void NPC::setSkin(int model) player_->setSkin(model); } +bool NPC::isStreamedInForPlayer(const IPlayer& other) const +{ + if (player_) + { + return player_->isStreamedInForPlayer(other); + } + + return false; +} + +const FlatPtrHashSet& NPC::streamedForPlayers() const +{ + return player_->streamedForPlayers(); +} + void NPC::sendFootSync() { // Only send foot sync if player is spawned diff --git a/Server/Components/NPCs/NPC/npc.hpp b/Server/Components/NPCs/NPC/npc.hpp index 1113bd95a..08ed1de9a 100644 --- a/Server/Components/NPCs/NPC/npc.hpp +++ b/Server/Components/NPCs/NPC/npc.hpp @@ -39,6 +39,10 @@ class NPC : public INPC, public PoolIDProvider, public NoCopy void setSkin(int model) override; + bool isStreamedInForPlayer(const IPlayer& other) const override; + + const FlatPtrHashSet& streamedForPlayers() const override; + void sendFootSync(); void tick(Microseconds elapsed, TimePoint now); From e68672533db76fd9cb5f734e4ccfdaefb43d5963 Mon Sep 17 00:00:00 2001 From: iAmir Date: Mon, 13 May 2024 11:04:34 +0330 Subject: [PATCH 25/32] add NPC_GetAll native --- .../Components/Pawn/Scripting/NPC/Natives.cpp | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Server/Components/Pawn/Scripting/NPC/Natives.cpp b/Server/Components/Pawn/Scripting/NPC/Natives.cpp index 098161d5c..afeb38e22 100644 --- a/Server/Components/Pawn/Scripting/NPC/Natives.cpp +++ b/Server/Components/Pawn/Scripting/NPC/Natives.cpp @@ -108,3 +108,41 @@ SCRIPT_API(NPC_SetSkin, bool(INPC& npc, int model)) npc.setSkin(model); return true; } + +SCRIPT_API(NPC_IsStreamedIn, bool(INPC& npc, IPlayer& player)) +{ + return npc.isStreamedInForPlayer(player); +} + +SCRIPT_API(NPC_IsAnyStreamedIn, bool(INPC& npc)) +{ + auto streamedIn = npc.streamedForPlayers(); + return streamedIn.size() > 0; +} + +SCRIPT_API(NPC_GetAll, int(DynamicArray& outputNPCs)) +{ + int index = -1; + auto npcs = PawnManager::Get()->npcs; + if (npcs) + { + if (outputNPCs.size() < npcs->count()) + { + PawnManager::Get()->core->printLn( + "There are %zu NPCs in your server but array size used in `NPC_GetAll` is %zu; Use a bigger size in your script.", + npcs->count(), + outputNPCs.size()); + } + + for (INPC* npc : *npcs) + { + index++; + if (index >= outputNPCs.size()) + { + break; + } + outputNPCs[index] = npc->getID(); + } + } + return index + 1; +} From 10599d50c75f179c83f9dd892ef76be7f974c668 Mon Sep 17 00:00:00 2001 From: iAmir Date: Sat, 18 May 2024 04:17:28 +0330 Subject: [PATCH 26/32] npc interior natives --- SDK | 2 +- Server/Components/NPCs/NPC/npc.cpp | 18 ++++++++++++++++++ Server/Components/NPCs/NPC/npc.hpp | 4 ++++ .../Components/Pawn/Scripting/NPC/Natives.cpp | 16 +++++++++++++--- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/SDK b/SDK index 2f1dade8f..d578dfc3c 160000 --- a/SDK +++ b/SDK @@ -1 +1 @@ -Subproject commit 2f1dade8f6acf86cb14ad8b730ba1ba833458aa8 +Subproject commit d578dfc3cdc91b7c8fedee280994b067e11ab7b5 diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index b68654da8..e83d61133 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -214,6 +214,24 @@ const FlatPtrHashSet& NPC::streamedForPlayers() const return player_->streamedForPlayers(); } +void NPC::setInterior(unsigned int interior) +{ + if (player_) + { + player_->setInterior(interior); + } +} + +unsigned int NPC::getInterior() const +{ + if (player_) + { + return player_->getInterior(); + } + + return 0; +} + void NPC::sendFootSync() { // Only send foot sync if player is spawned diff --git a/Server/Components/NPCs/NPC/npc.hpp b/Server/Components/NPCs/NPC/npc.hpp index 08ed1de9a..6b3f2409f 100644 --- a/Server/Components/NPCs/NPC/npc.hpp +++ b/Server/Components/NPCs/NPC/npc.hpp @@ -43,6 +43,10 @@ class NPC : public INPC, public PoolIDProvider, public NoCopy const FlatPtrHashSet& streamedForPlayers() const override; + void setInterior(unsigned int interior) override; + + unsigned int getInterior() const override; + void sendFootSync(); void tick(Microseconds elapsed, TimePoint now); diff --git a/Server/Components/Pawn/Scripting/NPC/Natives.cpp b/Server/Components/Pawn/Scripting/NPC/Natives.cpp index afeb38e22..f1806b2a6 100644 --- a/Server/Components/Pawn/Scripting/NPC/Natives.cpp +++ b/Server/Components/Pawn/Scripting/NPC/Natives.cpp @@ -86,10 +86,9 @@ SCRIPT_API(NPC_SetVirtualWorld, bool(INPC& npc, int virtualWorld)) return true; } -SCRIPT_API(NPC_GetVirtualWorld, bool(INPC& npc, int& virtualWorld)) +SCRIPT_API(NPC_GetVirtualWorld, int(INPC& npc)) { - virtualWorld = npc.getVirtualWorld(); - return true; + return npc.getVirtualWorld(); } SCRIPT_API(NPC_Move, bool(INPC& npc, Vector3 targetPos, int moveType)) @@ -146,3 +145,14 @@ SCRIPT_API(NPC_GetAll, int(DynamicArray& outputNPCs)) } return index + 1; } + +SCRIPT_API(NPC_SetInterior, bool(INPC& npc, int interior)) +{ + npc.setInterior(interior); + return true; +} + +SCRIPT_API(NPC_GetInterior, int(INPC& npc)) +{ + return npc.getInterior(); +} From eb1a3f462249cabc595dc5f6f92fb7388edbe720 Mon Sep 17 00:00:00 2001 From: iAmir Date: Sat, 18 May 2024 06:23:16 +0330 Subject: [PATCH 27/32] proper class variable initialization in constructor --- Server/Components/NPCs/NPC/npc.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index e83d61133..bd36d6b2a 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -30,6 +30,12 @@ float getAngleOfLine(float x, float y) } NPC::NPC(NPCComponent* component, IPlayer* playerPtr) + : moveType_(NPCMoveType_None) + , estimatedArrivalTimeNS_(0) + , moveSpeed_(0.0f) + , targetPosition_({ 0.0f, 0.0f, 0.0f }) + , velocity_({ 0.0f, 0.0f, 0.0f }) + , moving_(false) { // Keep a handle of NPC copmonent instance internally npcComponent_ = component; @@ -39,9 +45,6 @@ NPC::NPC(NPCComponent* component, IPlayer* playerPtr) // Initial entity values Vector3 initialPosition = { 0.0f, 0.0f, 3.5f }; GTAQuat initialRotation = { 0.960891485f, 0.0f, 0.0f, 0.276925147f }; - moving_ = false; - velocity_ = { 0.0f, 0.0f, 0.0f }; - moveType_ = NPCMoveType_None; // Initial values for foot sync values footSync_.LeftRight = 0; From 4bf88e545d3b411b258dab60b5461653d0f3806e Mon Sep 17 00:00:00 2001 From: iAmir Date: Sat, 18 May 2024 06:47:16 +0330 Subject: [PATCH 28/32] add set/get velocity for NPCs --- SDK | 2 +- Server/Components/NPCs/NPC/npc.cpp | 23 +++++++++++++++++++++++ Server/Components/NPCs/NPC/npc.hpp | 6 +++++- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/SDK b/SDK index d578dfc3c..b01f59e89 160000 --- a/SDK +++ b/SDK @@ -1 +1 @@ -Subproject commit d578dfc3cdc91b7c8fedee280994b067e11ab7b5 +Subproject commit b01f59e89905f8d03690a94983876ab964b49f78 diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index bd36d6b2a..169bdc22a 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -36,6 +36,7 @@ NPC::NPC(NPCComponent* component, IPlayer* playerPtr) , targetPosition_({ 0.0f, 0.0f, 0.0f }) , velocity_({ 0.0f, 0.0f, 0.0f }) , moving_(false) + , needsVelocityUpdate_(false) { // Keep a handle of NPC copmonent instance internally npcComponent_ = component; @@ -235,6 +236,22 @@ unsigned int NPC::getInterior() const return 0; } +Vector3 NPC::getVelocity() const +{ + return player_->getPosition(); +} + +void NPC::setVelocity(Vector3 velocity, bool update) +{ + if (moving_ && !update) + { + velocity_ = velocity; + footSync_.Velocity = velocity; + } + + needsVelocityUpdate_ = update; +} + void NPC::sendFootSync() { // Only send foot sync if player is spawned @@ -294,6 +311,12 @@ void NPC::tick(Microseconds elapsed, TimePoint now) // Only process the NPC if it is spawned if (player_ && (player_->getState() == PlayerState_OnFoot || player_->getState() == PlayerState_Driver || player_->getState() == PlayerState_Passenger || player_->getState() == PlayerState_Spawned)) { + if (needsVelocityUpdate_) + { + setPosition(getPosition() + velocity_); + setVelocity({ 0.0f, 0.0f, 0.0f }, false); + } + if (moving_) { advance(now); diff --git a/Server/Components/NPCs/NPC/npc.hpp b/Server/Components/NPCs/NPC/npc.hpp index 6b3f2409f..bda6af35a 100644 --- a/Server/Components/NPCs/NPC/npc.hpp +++ b/Server/Components/NPCs/NPC/npc.hpp @@ -47,6 +47,10 @@ class NPC : public INPC, public PoolIDProvider, public NoCopy unsigned int getInterior() const override; + Vector3 getVelocity() const override; + + void setVelocity(Vector3 position, bool update = false) override; + void sendFootSync(); void tick(Microseconds elapsed, TimePoint now); @@ -82,7 +86,7 @@ class NPC : public INPC, public PoolIDProvider, public NoCopy Vector3 targetPosition_; Vector3 velocity_; bool moving_; - + bool needsVelocityUpdate_; // Packets NetCode::Packet::PlayerFootSync footSync_; From 5ad4db9b9d3f1f7182df068369b9f5cf5cd2d44e Mon Sep 17 00:00:00 2001 From: iAmir Date: Fri, 18 Oct 2024 08:52:17 +0330 Subject: [PATCH 29/32] fix build --- SDK | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SDK b/SDK index b01f59e89..308487558 160000 --- a/SDK +++ b/SDK @@ -1 +1 @@ -Subproject commit b01f59e89905f8d03690a94983876ab964b49f78 +Subproject commit 3084875585c3724b35bdcf99b6f41c6690e42215 From 5980d0f348e3d3c529903bc2a40401193c90f629 Mon Sep 17 00:00:00 2001 From: iAmir Date: Fri, 18 Oct 2024 19:39:52 +0330 Subject: [PATCH 30/32] fix NPC_SetFacingAngle --- Server/Components/Pawn/Scripting/NPC/Natives.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Server/Components/Pawn/Scripting/NPC/Natives.cpp b/Server/Components/Pawn/Scripting/NPC/Natives.cpp index f1806b2a6..0a83985f3 100644 --- a/Server/Components/Pawn/Scripting/NPC/Natives.cpp +++ b/Server/Components/Pawn/Scripting/NPC/Natives.cpp @@ -70,6 +70,7 @@ SCRIPT_API(NPC_SetFacingAngle, bool(INPC& npc, float angle)) { auto rotation = npc.getRotation().ToEuler(); rotation.z = angle; + npc.setRotation(rotation); return true; } From 3e101db7a69c90d9226b79e4ee1839388214b25e Mon Sep 17 00:00:00 2001 From: iAmir Date: Sun, 20 Oct 2024 06:57:45 +0330 Subject: [PATCH 31/32] add health, armour and animation natives --- SDK | 2 +- Server/Components/NPCs/NPC/npc.cpp | 20 +++++++++++++ Server/Components/NPCs/NPC/npc.hpp | 8 +++++ .../Components/Pawn/Scripting/NPC/Natives.cpp | 29 +++++++++++++++++++ 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/SDK b/SDK index 308487558..2ab854da8 160000 --- a/SDK +++ b/SDK @@ -1 +1 @@ -Subproject commit 3084875585c3724b35bdcf99b6f41c6690e42215 +Subproject commit 2ab854da82117ddae5c24089440699cae9e61f6b diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index 169bdc22a..e6c526bb5 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -252,6 +252,26 @@ void NPC::setVelocity(Vector3 velocity, bool update) needsVelocityUpdate_ = update; } +void NPC::setHealth(float health) +{ + footSync_.HealthArmour.x = health; +} + +float NPC::getHealth() const +{ + return footSync_.HealthArmour.x; +} + +void NPC::setArmour(float armour) +{ + footSync_.HealthArmour.y = armour; +} + +float NPC::getArmour() const +{ + return footSync_.HealthArmour.y; +} + void NPC::sendFootSync() { // Only send foot sync if player is spawned diff --git a/Server/Components/NPCs/NPC/npc.hpp b/Server/Components/NPCs/NPC/npc.hpp index bda6af35a..2afc7c656 100644 --- a/Server/Components/NPCs/NPC/npc.hpp +++ b/Server/Components/NPCs/NPC/npc.hpp @@ -51,6 +51,14 @@ class NPC : public INPC, public PoolIDProvider, public NoCopy void setVelocity(Vector3 position, bool update = false) override; + void setHealth(float health) override; + + float getHealth() const override; + + void setArmour(float armour) override; + + float getArmour() const override; + void sendFootSync(); void tick(Microseconds elapsed, TimePoint now); diff --git a/Server/Components/Pawn/Scripting/NPC/Natives.cpp b/Server/Components/Pawn/Scripting/NPC/Natives.cpp index 0a83985f3..91ca746a8 100644 --- a/Server/Components/Pawn/Scripting/NPC/Natives.cpp +++ b/Server/Components/Pawn/Scripting/NPC/Natives.cpp @@ -157,3 +157,32 @@ SCRIPT_API(NPC_GetInterior, int(INPC& npc)) { return npc.getInterior(); } + +SCRIPT_API(NPC_SetHealth, bool(INPC& npc, float health)) +{ + npc.setHealth(health); + return true; +} + +SCRIPT_API(NPC_GetHealth, float(INPC& npc)) +{ + return npc.getHealth(); +} + +SCRIPT_API(NPC_SetArmour, bool(INPC& npc, float armour)) +{ + npc.setArmour(armour); + return true; +} + +SCRIPT_API(NPC_GetArmour, float(INPC& npc)) +{ + return npc.getArmour(); +} + +SCRIPT_API(NPC_ApplyAnimation, bool(INPC& npc, const std::string& animlib, const std::string& animname, float delta, bool loop, bool lockX, bool lockY, bool freeze, uint32_t time, int sync)) +{ + const AnimationData animationData(delta, loop, lockX, lockY, freeze, time, animlib, animname); + npc.getPlayer()->applyAnimation(animationData, PlayerAnimationSyncType_SyncOthers); + return true; +} From 0ee0f1ab52f41576dfe817bcf96507a9b5651b9f Mon Sep 17 00:00:00 2001 From: iAmir Date: Sun, 20 Oct 2024 08:05:48 +0330 Subject: [PATCH 32/32] validity check for setHealth and setArmour --- Server/Components/NPCs/NPC/npc.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Server/Components/NPCs/NPC/npc.cpp b/Server/Components/NPCs/NPC/npc.cpp index e6c526bb5..08eb5fef5 100644 --- a/Server/Components/NPCs/NPC/npc.cpp +++ b/Server/Components/NPCs/NPC/npc.cpp @@ -254,7 +254,14 @@ void NPC::setVelocity(Vector3 velocity, bool update) void NPC::setHealth(float health) { - footSync_.HealthArmour.x = health; + if (health < 0.0f) + { + footSync_.HealthArmour.x = 0.0f; + } + else + { + footSync_.HealthArmour.x = health; + } } float NPC::getHealth() const @@ -264,7 +271,14 @@ float NPC::getHealth() const void NPC::setArmour(float armour) { - footSync_.HealthArmour.y = armour; + if (armour < 0.0f) + { + footSync_.HealthArmour.y = 0.0f; + } + else + { + footSync_.HealthArmour.y = armour; + } } float NPC::getArmour() const