mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-15 08:17:13 +00:00
fix: construct a player in jetpack mode with its jetpack
LWOControllablePhysComponent::Deserialize (0x00c45770) reads the jetpack mode, effect, flying and bypass checks bits on construction, and LWOControllablePhysComponent::SendMessage (0x00d2f020) keeps the same from SetJetPackMode. DLU sent the message but never kept it, so a player who came into range of someone flying was told they had no jetpack. Live player constructions in jetpack mode carried effect 167, flying 0 and bypass checks 1, and a 0 after the pickup radius (the client reads that bit into the flying flag). DLU wrote the jetpack mode there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#include "JetPackBehavior.h"
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
|
||||
#include "BehaviorBranchContext.h"
|
||||
#include "GameMessages.h"
|
||||
@@ -21,6 +22,7 @@ void JetPackBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bit_st
|
||||
jetPackMode.fVertVel = this->m_VerticalVelocity;
|
||||
jetPackMode.iWarningEffectID = this->m_WarningEffectID;
|
||||
jetPackMode.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
if (auto* const physics = entity->GetComponent<ControllablePhysicsComponent>()) physics->SetJetPackMode(jetPackMode.bUse, jetPackMode.effectID, jetPackMode.bBypassChecks);
|
||||
|
||||
if (entity->IsPlayer()) {
|
||||
auto* character = entity->GetCharacter();
|
||||
@@ -39,6 +41,7 @@ void JetPackBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext bra
|
||||
jetPackMode.target = entity->GetObjectID();
|
||||
jetPackMode.bUse = false;
|
||||
jetPackMode.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
if (auto* const physics = entity->GetComponent<ControllablePhysicsComponent>()) physics->SetJetPackMode(jetPackMode.bUse, jetPackMode.effectID, jetPackMode.bBypassChecks);
|
||||
|
||||
if (entity->IsPlayer()) {
|
||||
auto* character = entity->GetCharacter();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
#include "RacingControlComponent.h"
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
|
||||
#include "CharacterComponent.h"
|
||||
#include "DestroyableComponent.h"
|
||||
@@ -276,6 +277,7 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player,
|
||||
jetPackMode.target = player->GetObjectID();
|
||||
jetPackMode.bUse = false;
|
||||
jetPackMode.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
if (auto* const physics = player->GetComponent<ControllablePhysicsComponent>()) physics->SetJetPackMode(jetPackMode.bUse, jetPackMode.effectID, jetPackMode.bBypassChecks);
|
||||
|
||||
// Set the vehicle's state.
|
||||
GameMessages::NotifyVehicleOfRacingObject notifyVehicle;
|
||||
|
||||
@@ -70,6 +70,7 @@ void PossessorComponent::Mount(Entity* mount) {
|
||||
jetPackMode.target = m_Parent->GetObjectID();
|
||||
jetPackMode.bUse = false;
|
||||
jetPackMode.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
if (auto* const physics = m_Parent->GetComponent<ControllablePhysicsComponent>()) physics->SetJetPackMode(jetPackMode.bUse, jetPackMode.effectID, jetPackMode.bBypassChecks);
|
||||
if (mount->GetComponent<HavokVehiclePhysicsComponent>()) {
|
||||
auto characterComponent = m_Parent->GetComponent<CharacterComponent>();
|
||||
if (characterComponent) characterComponent->SetIsRacing(true);
|
||||
|
||||
@@ -125,7 +125,9 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bo
|
||||
outBitStream.Write(m_DirtyEquippedItemInfo || bIsInitialUpdate);
|
||||
if (m_DirtyEquippedItemInfo || bIsInitialUpdate) {
|
||||
outBitStream.Write(m_PickupRadius);
|
||||
outBitStream.Write(m_InJetpackMode);
|
||||
// LWOControllablePhysComponent::Deserialize (0x00c45770) reads this into the same flag as the jetpack block's
|
||||
// flying bit; live wrote 0, also for players in jetpack mode (10,069 player constructions)
|
||||
outBitStream.Write(m_JetpackFlying);
|
||||
|
||||
if (!bIsInitialUpdate) m_DirtyEquippedItemInfo = false;
|
||||
}
|
||||
@@ -181,6 +183,12 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bo
|
||||
}
|
||||
}
|
||||
|
||||
void ControllablePhysicsComponent::SetJetPackMode(const bool use, const int32_t effectID, const bool bypassChecks) {
|
||||
m_InJetpackMode = use;
|
||||
m_JetpackEffectID = effectID;
|
||||
m_JetpackBypassChecks = bypassChecks;
|
||||
}
|
||||
|
||||
// A character's save, then where it comes into this world
|
||||
void ControllablePhysicsComponent::LoadFromSaveData(const tinyxml2::XMLDocument& doc) {
|
||||
ReadSaveData(doc);
|
||||
|
||||
@@ -150,6 +150,12 @@ public:
|
||||
*/
|
||||
const bool GetJetpackBypassChecks() const { return m_JetpackBypassChecks; }
|
||||
|
||||
/**
|
||||
* Keeps what a SetJetPackMode sent to the clients said, for the construction of this object: use, effect and
|
||||
* bypass checks, as LWOControllablePhysComponent::SendMessage (0x00d2f020) keeps them
|
||||
*/
|
||||
void SetJetPackMode(bool use, int32_t effectID, bool bypassChecks);
|
||||
|
||||
/**
|
||||
* Set the jetpack effect ID
|
||||
* @param effectID the effect to play while using the jetpack
|
||||
@@ -337,12 +343,12 @@ private:
|
||||
/**
|
||||
* Whether or not this entity is currently flying a jetpack
|
||||
*/
|
||||
bool m_JetpackFlying;
|
||||
bool m_JetpackFlying{ false };
|
||||
|
||||
/**
|
||||
* Bypass jetpack checks, currently unused
|
||||
* Bypass jetpack checks, from the last SetJetPackMode
|
||||
*/
|
||||
bool m_JetpackBypassChecks;
|
||||
bool m_JetpackBypassChecks{ false };
|
||||
|
||||
/**
|
||||
* The effect that plays while using the jetpack
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "GMGreaterThanZeroCommands.h"
|
||||
#include "ControllablePhysicsComponent.h"
|
||||
#include "ChatServerLink.h"
|
||||
#include "DashboardNotify.h"
|
||||
#include "SlashCommandHandler.h"
|
||||
@@ -206,6 +207,7 @@ namespace GMGreaterThanZeroCommands {
|
||||
jetPackMode.target = entity->GetObjectID();
|
||||
jetPackMode.bUse = false;
|
||||
jetPackMode.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
if (auto* const physics = entity->GetComponent<ControllablePhysicsComponent>()) physics->SetJetPackMode(jetPackMode.bUse, jetPackMode.effectID, jetPackMode.bBypassChecks);
|
||||
|
||||
character->SetIsFlying(false);
|
||||
} else {
|
||||
@@ -235,6 +237,7 @@ namespace GMGreaterThanZeroCommands {
|
||||
jetPackMode.fMaxAirspeed = maxAirSpeed;
|
||||
jetPackMode.fVertVel = verticalVelocity;
|
||||
jetPackMode.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
if (auto* const physics = entity->GetComponent<ControllablePhysicsComponent>()) physics->SetJetPackMode(jetPackMode.bUse, jetPackMode.effectID, jetPackMode.bBypassChecks);
|
||||
|
||||
character->SetIsFlying(true);
|
||||
}
|
||||
|
||||
@@ -173,6 +173,43 @@ TEST_F(ReplicaConstructionTest, CharacterActivityAndPvpChangesAreWrittenOnce) {
|
||||
player.SetCharacter(nullptr);
|
||||
}
|
||||
|
||||
// A player in jetpack mode is constructed in it: live player constructions carried the jetpack block (effect 167,
|
||||
// flying 0, bypass checks 1) and a 0 after the pickup radius. LWOControllablePhysComponent::Deserialize reads both.
|
||||
TEST_F(ReplicaConstructionTest, ControllablePhysicsConstructionCarriesJetPackMode) {
|
||||
Entity player(1152921506064087006, info);
|
||||
auto* const physics = player.AddComponent<ControllablePhysicsComponent>(-1);
|
||||
|
||||
const auto construct = [physics]() {
|
||||
RakNet::BitStream construction;
|
||||
physics->Serialize(construction, true);
|
||||
return Bits(construction);
|
||||
};
|
||||
const auto bits = [](const RakNet::BitStream& stream) { return Bits(stream); };
|
||||
|
||||
// No jetpack: one 0 bit, then the stun immunities
|
||||
const auto plain = construct();
|
||||
EXPECT_EQ(plain.substr(0, 2), "01");
|
||||
|
||||
physics->SetJetPackMode(true, 167, true);
|
||||
RakNet::BitStream head;
|
||||
head.Write1(); // jetpack
|
||||
head.Write<int32_t>(167); // effect
|
||||
head.Write0(); // flying
|
||||
head.Write1(); // bypass checks
|
||||
head.Write1(); // stun immunities
|
||||
const auto jetpack = construct();
|
||||
EXPECT_EQ(jetpack.substr(0, head.GetNumberOfBitsUsed()), bits(head));
|
||||
// After the 7 immunity counts: no cheats, then the pickup radius and a 0 (not the jetpack flag)
|
||||
const size_t afterImmunities = head.GetNumberOfBitsUsed() + 7 * 32;
|
||||
EXPECT_EQ(jetpack.substr(afterImmunities, 2), "01");
|
||||
EXPECT_EQ(jetpack[afterImmunities + 2 + 32], '0');
|
||||
// The rest is what a player without a jetpack gets
|
||||
EXPECT_EQ(jetpack.substr(head.GetNumberOfBitsUsed()), plain.substr(2));
|
||||
|
||||
physics->SetJetPackMode(false, -1, false);
|
||||
EXPECT_EQ(construct(), plain);
|
||||
}
|
||||
|
||||
// A guild change (docs/Guilds.md) is written once: the client reads guild id, a u8 length and 16-bit characters, and
|
||||
// redraws the name billboard every time it reads a name.
|
||||
TEST_F(ReplicaConstructionTest, CharacterGuildChangeIsWrittenOnce) {
|
||||
|
||||
Reference in New Issue
Block a user