mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-12 07:43:43 +00:00
refactor: death, stun, buff and knockback game messages as structs
Converts the combat messages to NetGameMsgs in CombatMessages.{h,cpp}:
Die, Resurrect, SetResurrectRestoreValues, SetPlayerAllowedRespawn
(SendToClient, as before), Knockback, SetStunned, SetStunImmunity,
SetStatusImmunity, AddBuff, RemoveBuff, AddRunSpeedModifier,
RemoveRunSpeedModifier and DeactivateBubbleBuffFromServer, and the
received RequestDie, RequestSmashPlayer, RequestResurrect, Resurrect,
ActivateBubbleBuff and DeactivateBubbleBuff. Smash and UnSmash move
here from GameMessages.h (Smash's ghostCapacity is ghostOpacity, the
client's name) and gain a Deserialize.
The callers (DestroyableComponent, BuffComponent,
ControllablePhysicsComponent, QuickBuildComponent, RacingControl,
RailActivator and the scripts) build the structs. SendResurrect's
respawn timer moves to DestroyableComponent::Resurrect. The received
messages are registered in GameMessageHandler's map and their switch
cases are deleted, along with the unused HandleRequestDie overload and
the Send* functions nothing called (SendSmash, SendUnSmash, the run
speed modifiers, SendActivateBubbleBuffFromServer). Handler logic is
unchanged.
No wire change and no change in recipients. Verified byte for byte
against a frozen verbatim copy of the old functions over an input grid
(every optional field, bool patterns, empty and long strings), received
messages compared with the old handlers' read sequences and truncated
payloads rejected, hand computed golden bytes, round trips, and a
deliberate width mutation made the tests fail.
Known difference from the client, not changed here: DLU writes
SetStatusImmunity's nine flags in its own order; the client reads them
alphabetically (GameMessage::SetStatusImmunity::Serialize @ 00d8f140).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
#include "Logger.h"
|
||||
#include "GameMessages.h"
|
||||
#include "EffectsMessages.h"
|
||||
#include "CombatMessages.h"
|
||||
#include "SkillComponent.h"
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
#include "EntityManager.h"
|
||||
@@ -157,9 +158,22 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
|
||||
addedByTeammate = std::count(team->members.begin(), team->members.end(), m_Parent->GetObjectID()) > 0;
|
||||
}
|
||||
|
||||
GameMessages::SendAddBuff(const_cast<LWOOBJID&>(m_Parent->GetObjectID()), source, static_cast<uint32_t>(id),
|
||||
static_cast<uint32_t>(duration) * 1000, addImmunity, cancelOnDamaged, cancelOnDeath,
|
||||
cancelOnLogout, cancelOnRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone, addedByTeammate, applyOnTeammates);
|
||||
GameMessages::AddBuff addBuff;
|
||||
addBuff.target = m_Parent->GetObjectID();
|
||||
addBuff.bAddedByTeammate = addedByTeammate;
|
||||
addBuff.bApplyOnTeammates = applyOnTeammates;
|
||||
addBuff.bCancelOnDamaged = cancelOnDamaged;
|
||||
addBuff.bCancelOnDeath = cancelOnDeath;
|
||||
addBuff.bCancelOnLogOut = cancelOnLogout;
|
||||
addBuff.bCancelOnRemoveBuff = cancelOnRemoveBuff;
|
||||
addBuff.bCancelOnUI = cancelOnUi;
|
||||
addBuff.bCancelOnUnEquip = cancelOnUnequip;
|
||||
addBuff.bCancelOnZone = cancelOnZone;
|
||||
addBuff.bIsImmunity = addImmunity;
|
||||
addBuff.i64AddedBy = source;
|
||||
addBuff.uiBuffID = static_cast<uint32_t>(id);
|
||||
addBuff.uiDurationMS = static_cast<uint32_t>(duration) * 1000;
|
||||
addBuff.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
float tick = 0;
|
||||
float stacks = 0;
|
||||
@@ -223,7 +237,12 @@ void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity
|
||||
}
|
||||
}
|
||||
|
||||
GameMessages::SendRemoveBuff(m_Parent, fromUnEquip, removeImmunity, id);
|
||||
GameMessages::RemoveBuff removeBuff;
|
||||
removeBuff.target = m_Parent->GetObjectID();
|
||||
removeBuff.bFromUnEquip = fromUnEquip;
|
||||
removeBuff.bRemoveImmunity = removeImmunity;
|
||||
removeBuff.uiBuffID = static_cast<uint32_t>(id);
|
||||
removeBuff.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
m_BuffsToRemove.insert(id);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
#include "CombatMessages.h"
|
||||
#include "Entity.h"
|
||||
#include "BitStream.h"
|
||||
#include "Logger.h"
|
||||
@@ -347,16 +348,18 @@ void ControllablePhysicsComponent::SetStunImmunity(
|
||||
if (bImmuneToStunUseItem) m_ImmuneToStunUseItemCount += 1;
|
||||
}
|
||||
|
||||
GameMessages::SendSetStunImmunity(
|
||||
m_Parent->GetObjectID(), state, m_Parent->GetSystemAddress(), originator,
|
||||
bImmuneToStunAttack,
|
||||
bImmuneToStunEquip,
|
||||
bImmuneToStunInteract,
|
||||
bImmuneToStunJump,
|
||||
bImmuneToStunMove,
|
||||
bImmuneToStunTurn,
|
||||
bImmuneToStunUseItem
|
||||
);
|
||||
GameMessages::SetStunImmunity stunImmunity;
|
||||
stunImmunity.target = m_Parent->GetObjectID();
|
||||
stunImmunity.Caster = originator;
|
||||
stunImmunity.StateChangeType = state;
|
||||
stunImmunity.bImmuneToStunAttack = bImmuneToStunAttack;
|
||||
stunImmunity.bImmuneToStunEquip = bImmuneToStunEquip;
|
||||
stunImmunity.bImmuneToStunInteract = bImmuneToStunInteract;
|
||||
stunImmunity.bImmuneToStunJump = bImmuneToStunJump;
|
||||
stunImmunity.bImmuneToStunMove = bImmuneToStunMove;
|
||||
stunImmunity.bImmuneToStunTurn = bImmuneToStunTurn;
|
||||
stunImmunity.bImmuneToStunUseItem = bImmuneToStunUseItem;
|
||||
stunImmunity.Send(m_Parent->GetSystemAddress());
|
||||
}
|
||||
|
||||
bool ControllablePhysicsComponent::OnGetObjectReportInfo(GameMessages::GetObjectReportInfo& reportInfo) {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "DestroyableComponent.h"
|
||||
#include "CombatMessages.h"
|
||||
#include "LevelProgressionComponent.h"
|
||||
#include "EconomyLedger.h"
|
||||
#include "BitStream.h"
|
||||
#include "Logger.h"
|
||||
@@ -779,7 +781,15 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
|
||||
|
||||
const auto isPlayer = m_Parent->IsPlayer();
|
||||
|
||||
GameMessages::SendDie(m_Parent, source, source, true, killType, deathType, 0, 0, 0, isPlayer, true, 1);
|
||||
GameMessages::Die die;
|
||||
die.target = m_Parent->GetObjectID();
|
||||
die.bClientDeath = isPlayer;
|
||||
die.bSpawnLoot = true;
|
||||
die.deathType = deathType;
|
||||
die.killType = killType;
|
||||
die.killerID = source;
|
||||
die.lootOwnerID = source;
|
||||
die.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
//NANI?!
|
||||
if (!isPlayer) {
|
||||
@@ -870,18 +880,19 @@ void DestroyableComponent::SetStatusImmunity(
|
||||
if (bImmuneToPullToPoint) m_ImmuneToPullToPointCount += 1;
|
||||
}
|
||||
|
||||
GameMessages::SendSetStatusImmunity(
|
||||
m_Parent->GetObjectID(), state, m_Parent->GetSystemAddress(),
|
||||
bImmuneToBasicAttack,
|
||||
bImmuneToDamageOverTime,
|
||||
bImmuneToKnockback,
|
||||
bImmuneToInterrupt,
|
||||
bImmuneToSpeed,
|
||||
bImmuneToImaginationGain,
|
||||
bImmuneToImaginationLoss,
|
||||
bImmuneToQuickbuildInterrupt,
|
||||
bImmuneToPullToPoint
|
||||
);
|
||||
GameMessages::SetStatusImmunity statusImmunity;
|
||||
statusImmunity.target = m_Parent->GetObjectID();
|
||||
statusImmunity.StateChangeType = state;
|
||||
statusImmunity.bImmuneToBasicAttack = bImmuneToBasicAttack;
|
||||
statusImmunity.bImmuneToDOT = bImmuneToDamageOverTime;
|
||||
statusImmunity.bImmuneToKnockback = bImmuneToKnockback;
|
||||
statusImmunity.bImmuneToInterrupt = bImmuneToInterrupt;
|
||||
statusImmunity.bImmuneToSpeed = bImmuneToSpeed;
|
||||
statusImmunity.bImmuneToImaginationGain = bImmuneToImaginationGain;
|
||||
statusImmunity.bImmuneToImaginationLoss = bImmuneToImaginationLoss;
|
||||
statusImmunity.bImmuneToQuickbuildInterrupt = bImmuneToQuickbuildInterrupt;
|
||||
statusImmunity.bImmuneToPullToPoint = bImmuneToPullToPoint;
|
||||
statusImmunity.Send(m_Parent->GetSystemAddress());
|
||||
}
|
||||
|
||||
void DestroyableComponent::FixStats() {
|
||||
@@ -1222,3 +1233,36 @@ bool DestroyableComponent::OnIsDead(GameMessages::IsDead& isDead) {
|
||||
isDead.bDead = m_IsDead || (GetHealth() == 0 && GetArmor() == 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DestroyableComponent::Resurrect(Entity& entityRef) {
|
||||
Entity* entity = &entityRef;
|
||||
// Restore the players health after the animation for respawning has finished.
|
||||
// This is when the health appered back in live, not immediately upon requesting respawn
|
||||
// Add a half second in case someone decides to cheat and move during the death animation
|
||||
// and just make sure the client has time to be ready.
|
||||
constexpr float respawnTime = 3.66700005531311f + 0.5f;
|
||||
entity->AddCallbackTimer(respawnTime, [=]() {
|
||||
GameMessages::PlayerResurrectionFinished msg;
|
||||
entity->NotifyPlayerResurrectionFinished(msg);
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
|
||||
if (destroyableComponent != nullptr && entity->GetLOT() == 1) {
|
||||
destroyableComponent->SetIsDead(false);
|
||||
auto* levelComponent = entity->GetComponent<LevelProgressionComponent>();
|
||||
if (levelComponent) {
|
||||
int32_t healthToRestore = levelComponent->GetLevel() >= 45 ? 8 : 4;
|
||||
if (healthToRestore > destroyableComponent->GetMaxHealth()) healthToRestore = destroyableComponent->GetMaxHealth();
|
||||
destroyableComponent->SetHealth(healthToRestore);
|
||||
|
||||
int32_t imaginationToRestore = levelComponent->GetLevel() >= 45 ? 20 : 6;
|
||||
if (imaginationToRestore > destroyableComponent->GetMaxImagination()) imaginationToRestore = destroyableComponent->GetMaxImagination();
|
||||
destroyableComponent->SetImagination(imaginationToRestore);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
GameMessages::Resurrect resurrect;
|
||||
resurrect.target = entity->GetObjectID();
|
||||
resurrect.bRezImmediately = false;
|
||||
resurrect.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
|
||||
@@ -404,6 +404,12 @@ public:
|
||||
*/
|
||||
void Smash(LWOOBJID source, eKillType killType = eKillType::VIOLENT, const std::u16string& deathType = u"", uint32_t skillID = 0);
|
||||
|
||||
/**
|
||||
* Brings entity back to life: tells every client (Resurrect), then, once the respawn animation is done,
|
||||
* finishes the resurrection and restores a player's health and imagination.
|
||||
*/
|
||||
static void Resurrect(Entity& entity);
|
||||
|
||||
/**
|
||||
* Push or Pop a layer of status immunity to this entity
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ModelComponent.h"
|
||||
#include "CombatMessages.h"
|
||||
#include "Entity.h"
|
||||
|
||||
#include "Game.h"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "DestroyableComponent.h"
|
||||
#include "GameMessages.h"
|
||||
#include "EffectsMessages.h"
|
||||
#include "CombatMessages.h"
|
||||
#include "EntityManager.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
@@ -132,7 +133,12 @@ void QuickBuildComponent::Update(float deltaTime) {
|
||||
if (m_TimerIncomplete >= m_TimeBeforeSmash) {
|
||||
m_Builder = LWOOBJID_EMPTY;
|
||||
|
||||
GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true);
|
||||
GameMessages::Die die;
|
||||
die.target = m_Parent->GetObjectID();
|
||||
die.bClientDeath = false;
|
||||
die.bSpawnLoot = true;
|
||||
die.killerID = LWOOBJID_EMPTY;
|
||||
die.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
ResetQuickBuild(false);
|
||||
}
|
||||
@@ -154,7 +160,13 @@ void QuickBuildComponent::Update(float deltaTime) {
|
||||
|
||||
if (m_Timer >= m_ResetTime) {
|
||||
|
||||
GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 7.0f, false, true);
|
||||
GameMessages::Die die;
|
||||
die.target = m_Parent->GetObjectID();
|
||||
die.bClientDeath = false;
|
||||
die.bSpawnLoot = true;
|
||||
die.directionRelative_Force = 7.0f;
|
||||
die.killerID = LWOOBJID_EMPTY;
|
||||
die.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
ResetQuickBuild(false);
|
||||
}
|
||||
@@ -215,7 +227,12 @@ void QuickBuildComponent::Update(float deltaTime) {
|
||||
if (m_TimerIncomplete >= m_TimeBeforeSmash) {
|
||||
m_Builder = LWOOBJID_EMPTY;
|
||||
|
||||
GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true);
|
||||
GameMessages::Die die;
|
||||
die.target = m_Parent->GetObjectID();
|
||||
die.bClientDeath = false;
|
||||
die.bSpawnLoot = true;
|
||||
die.killerID = LWOOBJID_EMPTY;
|
||||
die.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
ResetQuickBuild(false);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "DestroyableComponent.h"
|
||||
#include "EntityManager.h"
|
||||
#include "GameMessages.h"
|
||||
#include "CombatMessages.h"
|
||||
#include "ActivityMessages.h"
|
||||
#include "RacingMessages.h"
|
||||
#include "InventoryComponent.h"
|
||||
@@ -83,7 +84,18 @@ void RacingControlComponent::OnPlayerLoaded(Entity* player) {
|
||||
m_LoadedPlayers++;
|
||||
|
||||
// not live accurate to stun the player but prevents them from using skills during the race that are not meant to be used.
|
||||
GameMessages::SendSetStunned(player->GetObjectID(), eStateChangeType::PUSH, player->GetSystemAddress(), LWOOBJID_EMPTY, true, true, true, true, true, true, true, true, true);
|
||||
GameMessages::SetStunned stun;
|
||||
stun.target = player->GetObjectID();
|
||||
stun.StateChangeType = eStateChangeType::PUSH;
|
||||
stun.bCantAttack = true;
|
||||
stun.bCantEquip = true;
|
||||
stun.bCantInteract = true;
|
||||
stun.bCantJump = true;
|
||||
stun.bCantMove = true;
|
||||
stun.bCantTurn = true;
|
||||
stun.bCantUseItem = true;
|
||||
stun.bDontTerminateInteract = true;
|
||||
stun.Send(player->GetSystemAddress());
|
||||
|
||||
LOG("Loading player %i",
|
||||
m_LoadedPlayers);
|
||||
@@ -316,8 +328,14 @@ void RacingControlComponent::OnRequestDie(Entity* player, const std::u16string&
|
||||
if (!racingPlayer.noSmashOnReload) {
|
||||
racingPlayer.smashedTimes++;
|
||||
LOG("Death type %s", GeneralUtils::UTF16ToWTF8(deathType).c_str());
|
||||
GameMessages::SendDie(vehicle, vehicle->GetObjectID(), LWOOBJID_EMPTY, true,
|
||||
eKillType::VIOLENT, deathType, 0, 0, 90.0f, false, true, 0);
|
||||
GameMessages::Die die;
|
||||
die.target = vehicle->GetObjectID();
|
||||
die.bClientDeath = false;
|
||||
die.bSpawnLoot = true;
|
||||
die.deathType = deathType;
|
||||
die.directionRelative_Force = 90.0f;
|
||||
die.killerID = vehicle->GetObjectID();
|
||||
die.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
auto* destroyableComponent = vehicle->GetComponent<DestroyableComponent>();
|
||||
int32_t respawnImagination = 0;
|
||||
@@ -325,7 +343,10 @@ void RacingControlComponent::OnRequestDie(Entity* player, const std::u16string&
|
||||
// Do not actually change the value yet. Do that on respawn.
|
||||
if (destroyableComponent) {
|
||||
respawnImagination = static_cast<int32_t>(ceil(destroyableComponent->GetImagination() / 2.0f / 10.0f)) * 10.0f;
|
||||
GameMessages::SendSetResurrectRestoreValues(vehicle, -1, -1, respawnImagination);
|
||||
GameMessages::SetResurrectRestoreValues restoreValues;
|
||||
restoreValues.target = vehicle->GetObjectID();
|
||||
restoreValues.iImaginationRestore = respawnImagination;
|
||||
restoreValues.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
|
||||
// Respawn the player in 2 seconds, as was done in live. Not sure if this value is in a setting somewhere else...
|
||||
@@ -350,7 +371,7 @@ void RacingControlComponent::OnRequestDie(Entity* player, const std::u16string&
|
||||
resetInfo.upcomingPlane = racingPlayer.respawnIndex + 1;
|
||||
resetInfo.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
GameMessages::SendResurrect(vehicle);
|
||||
DestroyableComponent::Resurrect(*vehicle);
|
||||
auto* destroyableComponent = vehicle->GetComponent<DestroyableComponent>();
|
||||
// Reset imagination to half its current value, rounded up to the nearest value divisible by 10, as it was done in live.
|
||||
if (destroyableComponent) destroyableComponent->SetImagination(respawnImagination);
|
||||
@@ -771,9 +792,12 @@ void RacingControlComponent::Update(float deltaTime) {
|
||||
// If the player is this far below the map, safe to assume they should
|
||||
// be smashed by death plane
|
||||
if (vehiclePosition.y < -500) {
|
||||
GameMessages::SendDie(vehicle, m_Parent->GetObjectID(),
|
||||
LWOOBJID_EMPTY, true, eKillType::VIOLENT, u"", 0, 0, 0,
|
||||
true, false, 0);
|
||||
GameMessages::Die die;
|
||||
die.target = vehicle->GetObjectID();
|
||||
die.bClientDeath = true;
|
||||
die.bSpawnLoot = false;
|
||||
die.killerID = m_Parent->GetObjectID();
|
||||
die.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
OnRequestDie(playerEntity);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Entity.h"
|
||||
#include "GameMessages.h"
|
||||
#include "EffectsMessages.h"
|
||||
#include "CombatMessages.h"
|
||||
#include "QuickBuildComponent.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
@@ -83,9 +84,17 @@ void RailActivatorComponent::OnUse(Entity* originator) {
|
||||
|
||||
void RailActivatorComponent::OnRailMovementReady(Entity* originator) const {
|
||||
// Stun the originator
|
||||
GameMessages::SendSetStunned(originator->GetObjectID(), eStateChangeType::PUSH, originator->GetSystemAddress(), LWOOBJID_EMPTY,
|
||||
true, true, true, true, true, true, true
|
||||
);
|
||||
GameMessages::SetStunned stun;
|
||||
stun.target = originator->GetObjectID();
|
||||
stun.StateChangeType = eStateChangeType::PUSH;
|
||||
stun.bCantAttack = true;
|
||||
stun.bCantEquip = true;
|
||||
stun.bCantInteract = true;
|
||||
stun.bCantJump = true;
|
||||
stun.bCantMove = true;
|
||||
stun.bCantTurn = true;
|
||||
stun.bCantUseItem = true;
|
||||
stun.Send(originator->GetSystemAddress());
|
||||
|
||||
if (std::find(m_EntitiesOnRail.begin(), m_EntitiesOnRail.end(), originator->GetObjectID()) != m_EntitiesOnRail.end()) {
|
||||
// Stop the initial effects
|
||||
@@ -110,9 +119,17 @@ void RailActivatorComponent::OnRailMovementReady(Entity* originator) const {
|
||||
|
||||
void RailActivatorComponent::OnCancelRailMovement(Entity* originator) {
|
||||
// Remove the stun from the originator
|
||||
GameMessages::SendSetStunned(originator->GetObjectID(), eStateChangeType::POP, originator->GetSystemAddress(), LWOOBJID_EMPTY,
|
||||
true, true, true, true, true, true, true
|
||||
);
|
||||
GameMessages::SetStunned stun;
|
||||
stun.target = originator->GetObjectID();
|
||||
stun.StateChangeType = eStateChangeType::POP;
|
||||
stun.bCantAttack = true;
|
||||
stun.bCantEquip = true;
|
||||
stun.bCantInteract = true;
|
||||
stun.bCantJump = true;
|
||||
stun.bCantMove = true;
|
||||
stun.bCantTurn = true;
|
||||
stun.bCantUseItem = true;
|
||||
stun.Send(originator->GetSystemAddress());
|
||||
|
||||
auto* quickBuildComponent = m_Parent->GetComponent<QuickBuildComponent>();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user