mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 19:03:43 +00:00
Live sends used_mouse false, no caster latency, no clicked position and a zero originator rotation (present, all four components 0) in every echo: 78,841 of 78,841 in the live captures, player and server casts alike. DLU forwarded the casting client's values and wrote the caster's facing for server casts. The client copies the rotation into its local StartSkill (LWOSkillComponent::msgEchoStartSkill), which is why a real rotation made power-up pickups jitter; the per-call override that worked around that is no longer needed and is removed. Check: with two players, attack, use a thrown or ranged item and pick up power-ups; the other player sees the same animations and the caster no longer snaps or jitters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
263 lines
8.6 KiB
C++
263 lines
8.6 KiB
C++
/*
|
|
* Darkflame Universe
|
|
* Copyright 2024
|
|
*/
|
|
|
|
#ifndef SKILLCOMPONENT_H
|
|
#define SKILLCOMPONENT_H
|
|
|
|
#include <map>
|
|
|
|
#include "BehaviorContext.h"
|
|
#include "BitStream.h"
|
|
#include "Component.h"
|
|
#include "Entity.h"
|
|
#include "Logger.h"
|
|
#include "eReplicaComponentType.h"
|
|
|
|
struct ProjectileSyncEntry {
|
|
LWOOBJID id = LWOOBJID_EMPTY;
|
|
|
|
bool calculation = false;
|
|
|
|
mutable float time = 0;
|
|
|
|
float maxTime = 0;
|
|
|
|
NiPoint3 startPosition{};
|
|
|
|
NiPoint3 lastPosition{};
|
|
|
|
NiPoint3 velocity{};
|
|
|
|
bool trackTarget = false;
|
|
|
|
float trackRadius = 0;
|
|
|
|
BehaviorContext* context = nullptr;
|
|
|
|
LOT lot = LOT_NULL;
|
|
|
|
BehaviorBranchContext branchContext{ 0, 0 };
|
|
|
|
int32_t skillId{ 0 };
|
|
|
|
explicit ProjectileSyncEntry();
|
|
};
|
|
|
|
struct SkillExecutionResult {
|
|
bool success;
|
|
|
|
float skillTime;
|
|
};
|
|
|
|
/**
|
|
* The SkillComponent of an entity. This manages both player and AI skills, such as attacks and consumables.
|
|
* There are two sets of skill methods: one for player skills and one for server-side calculations.
|
|
*
|
|
* Skills are a built up by a tree of behaviors. See dGame/dBehaviors/ for a list of behaviors.
|
|
*
|
|
* This system is very convoluted and still has a lot of unknowns.
|
|
*/
|
|
class SkillComponent final : public Component {
|
|
public:
|
|
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::SKILL;
|
|
|
|
explicit SkillComponent(Entity* parent, const int32_t componentID);
|
|
~SkillComponent() override;
|
|
|
|
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
|
|
|
|
/**
|
|
* Computes skill updates. Invokes CalculateUpdate.
|
|
*/
|
|
void Update(float deltaTime) override;
|
|
|
|
/**
|
|
* Loads the cooldown groups still running at the last save: <skil sc="group:seconds;group:seconds;"/>
|
|
*/
|
|
void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
|
|
|
|
/**
|
|
* Saves the running cooldown groups the way live did and the client reads them (LWOSkillComponent::LoadFromSaveData
|
|
* 0x00c2a9c0 splits sc on ';' and ':' into a cooldown group and the seconds left)
|
|
*/
|
|
void UpdateXml(tinyxml2::XMLDocument& doc) override;
|
|
|
|
/**
|
|
* Starts the cooldown of a skill's cooldown group (SkillBehavior.cooldowngroup, cooldown) when the player casts it
|
|
*/
|
|
void StartCooldown(uint32_t skillID);
|
|
|
|
/**
|
|
* The cooldown groups running and the seconds left on each
|
|
*/
|
|
const std::map<int32_t, float>& GetCooldownGroups() const { return m_CooldownGroups; }
|
|
|
|
/**
|
|
* Computes server-side skill updates.
|
|
*/
|
|
void CalculateUpdate(float deltaTime);
|
|
|
|
/**
|
|
* Resets all skills, projectiles, and other calculations.
|
|
*/
|
|
void Reset();
|
|
|
|
/**
|
|
* Interrupts active skills.
|
|
*/
|
|
void Interrupt();
|
|
|
|
/**
|
|
* Ends a skill this entity is running: drops its behaviors (and their pending hits and projectiles) with that
|
|
* skill handle
|
|
* @param skillUid the skill handle
|
|
*/
|
|
void EndSkill(uint32_t skillUid);
|
|
|
|
/**
|
|
* @return whether this entity runs a skill with that handle
|
|
*/
|
|
bool HasSkill(uint32_t skillUid) const { return m_managedBehaviors.contains(skillUid); }
|
|
|
|
/**
|
|
* Starts a player skill. Should only be called when the server receives a start skill message from the client.
|
|
* @param behaviorId the root behavior ID of the skill
|
|
* @param skillUid the unique ID of the skill given by the client
|
|
* @param bitStream the bitSteam given by the client to determine the behavior path
|
|
* @param target the explicit target of the skill
|
|
*/
|
|
bool CastPlayerSkill(uint32_t behaviorId, uint32_t skillUid, RakNet::BitStream& bitStream, LWOOBJID target, uint32_t skillID = 0);
|
|
|
|
/**
|
|
* Continues a player skill. Should only be called when the server receives a sync message from the client.
|
|
* @param skillUid the unique ID of the skill given by the client
|
|
* @param syncId the unique sync ID of the skill given by the client
|
|
* @param bitStream the bitSteam given by the client to determine the behavior path
|
|
*/
|
|
void SyncPlayerSkill(uint32_t skillUid, uint32_t syncId, RakNet::BitStream& bitStream);
|
|
|
|
/**
|
|
* Continues a player projectile calculation. Should only be called when the server receives a projectile sync message from the client.
|
|
* @param projectileId the unique ID of the projectile given by the client
|
|
* @param bitStream the bitSteam given by the client to determine the behavior path
|
|
* @param target the explicit target of the target
|
|
*/
|
|
void SyncPlayerProjectile(LWOOBJID projectileId, RakNet::BitStream& bitStream, LWOOBJID target);
|
|
|
|
/**
|
|
* Registers a player projectile. Should only be called when the server is computing a player projectile.
|
|
* @param projectileId the unique ID of the projectile given by the client
|
|
* @param context the current behavior context of the active skill
|
|
* @param branch the current behavior branch context of the active skill
|
|
* @param lot the LOT of the projectile
|
|
*/
|
|
void RegisterPlayerProjectile(LWOOBJID projectileId, BehaviorContext* context, const BehaviorBranchContext& branch, LOT lot);
|
|
|
|
/**
|
|
* Wrapper for CalculateBehavior that mimics the call structure in scripts and helps reduce magic numbers
|
|
* @param skillId the skill to cast
|
|
* @param target the target of the skill
|
|
* @param optionalOriginatorID change the originator of the skill
|
|
* @return if the case succeeded
|
|
*/
|
|
bool CastSkill(const uint32_t skillId, LWOOBJID target = LWOOBJID_EMPTY, const LWOOBJID optionalOriginatorID = LWOOBJID_EMPTY, const int32_t castType = 0);
|
|
|
|
/**
|
|
* Initializes a server-side skill calculation.
|
|
* @param skillId the skill ID
|
|
* @param behaviorId the root behavior ID of the skill
|
|
* @param target the explicit target of the skill
|
|
* @param ignoreTarget continue the skill calculation even if the target is invalid or no target is found
|
|
* @param clientInitalized indicates if the skill calculation was initiated by a client skill, ignores some checks
|
|
* @param originatorOverride an override for the originator of the skill calculation
|
|
* @return the result of the skill calculation
|
|
*/
|
|
SkillExecutionResult CalculateBehavior(uint32_t skillId, uint32_t behaviorId, LWOOBJID target, bool ignoreTarget = false, bool clientInitalized = false, LWOOBJID originatorOverride = LWOOBJID_EMPTY, const int32_t castType = 0);
|
|
|
|
/**
|
|
* Register a server-side projectile.
|
|
* @param projectileId the unique ID of the projectile
|
|
* @param context the current behavior context of the active skill
|
|
* @param branch the current behavior branch context of the active skill
|
|
* @param lot the LOT of the projectile
|
|
* @param maxTime the maximum travel time of the projectile
|
|
* @param startPosition the start position of the projectile
|
|
* @param velocity the velocity of the projectile
|
|
* @param trackTarget whether the projectile should track the target
|
|
* @param trackRadius the radius of the tracking circle
|
|
*/
|
|
void RegisterCalculatedProjectile(
|
|
LWOOBJID projectileId,
|
|
BehaviorContext* context,
|
|
const BehaviorBranchContext& branch,
|
|
LOT lot,
|
|
const float maxTime,
|
|
const NiPoint3& startPosition,
|
|
const NiPoint3& velocity,
|
|
bool trackTarget,
|
|
float TrackRadius);
|
|
|
|
/**
|
|
* Computes a server-side skill calculation without an associated entity.
|
|
* @param behaviorId the root behavior ID of the skill
|
|
* @param target the explicit target of the skill
|
|
* @param source the explicit source of the skill
|
|
*/
|
|
static void HandleUnmanaged(uint32_t behaviorId, LWOOBJID target, LWOOBJID source = LWOOBJID_EMPTY);
|
|
|
|
/**
|
|
* Calculates a behavior on the server only, without syncing it to clients, for effects the
|
|
* clients already run on their own (e.g. item set DarkInspiration status effects).
|
|
* @param behaviorId the root behavior ID of the skill
|
|
* @param caster the entity the behavior is cast by
|
|
* @param target the entity the behavior targets
|
|
*/
|
|
static void CalculateUnmanaged(uint32_t behaviorId, LWOOBJID caster, LWOOBJID target);
|
|
|
|
/**
|
|
* Computes a server-side skill uncast calculation without an associated entity.
|
|
* @param behaviorId the root behavior ID of the skill
|
|
* @param target the explicit target of the skill
|
|
*/
|
|
static void HandleUnCast(uint32_t behaviorId, LWOOBJID target);
|
|
|
|
/**
|
|
* @returns a unique ID for the next skill calculation
|
|
*/
|
|
uint32_t GetUniqueSkillId();
|
|
|
|
private:
|
|
/**
|
|
* All of the active skills mapped by their unique ID.
|
|
*/
|
|
std::multimap<uint32_t, BehaviorContext*> m_managedBehaviors;
|
|
|
|
/**
|
|
* All active projectiles.
|
|
*/
|
|
std::vector<ProjectileSyncEntry> m_managedProjectiles;
|
|
|
|
/**
|
|
* Unique ID counter.
|
|
*/
|
|
uint32_t m_skillUid;
|
|
|
|
// Cooldown group -> seconds left, for the player's save
|
|
std::map<int32_t, float> m_CooldownGroups;
|
|
|
|
/**
|
|
* Cache for looking up a behavior id via a skill ID
|
|
*/
|
|
static std::unordered_map<uint32_t, uint32_t> m_skillBehaviorCache;
|
|
|
|
/**
|
|
* Sync a server-side projectile calculation.
|
|
* @param entry the projectile information
|
|
*/
|
|
void SyncProjectileCalculation(const ProjectileSyncEntry& entry) const;
|
|
};
|
|
|
|
#endif // SKILLCOMPONENT_H
|