mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-14 07:47:13 +00:00
fix: trigger volume effects go out as live sent them
A trigger's SetPhysicsVolumeEffect is "type,amount,x,y,z,use distance,
min,max". In live phantom physics constructions:
- the direction has the amount as its length: "Push,50,0.91,-0.34,-0.22"
went out as 45.68, -17.07, -11.04. DLU multiplied the arguments as
they were, so a direction that isn't a unit vector pushed harder;
- a zero direction ("Repulse,150,0,0,0,true,25,100") is no direction;
- the distances go out when use distance is "true" ("true,150,175" as 1
and 175, "true,20,50" as 1 and 50) and not when it is "1"
("Push,40.0,0,1,0,1,20,50"). DLU never wrote them.
LWOPhantomPhysicsComponent::Deserialize (0x00c34db0) reads the two
distances as floats and keeps the least of the min and 1, and a max of at
least that.
Seven arguments no longer read an eighth.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
* Copyright 2018
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
@@ -136,13 +137,13 @@ void PhantomPhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bI
|
||||
outBitStream.Write(m_EffectType);
|
||||
outBitStream.Write(m_DirectionalMultiplier);
|
||||
|
||||
// forgive me father for i have sinned
|
||||
outBitStream.Write0();
|
||||
//outBitStream.Write(m_MinMax);
|
||||
//if (m_MinMax) {
|
||||
//outBitStream.Write(m_Min);
|
||||
//outBitStream.Write(m_Max);
|
||||
//}
|
||||
// Live wrote the distances as the client keeps them: "true,150,175" went out as 1 and 175
|
||||
outBitStream.Write(m_MinMax);
|
||||
if (m_MinMax) {
|
||||
const float min = std::min(m_Min, 1.0f);
|
||||
outBitStream.Write(min);
|
||||
outBitStream.Write(std::max(m_Max, min));
|
||||
}
|
||||
|
||||
outBitStream.Write(m_IsDirectional);
|
||||
if (m_IsDirectional) {
|
||||
@@ -244,18 +245,50 @@ void PhantomPhysicsComponent::SetEffectType(ePhysicsEffectType type) {
|
||||
m_EffectInfoDirty = true;
|
||||
}
|
||||
|
||||
void PhantomPhysicsComponent::SetMin(uint32_t min) {
|
||||
void PhantomPhysicsComponent::SetMin(const float min) {
|
||||
m_Min = min;
|
||||
m_MinMax = true;
|
||||
m_EffectInfoDirty = true;
|
||||
}
|
||||
|
||||
void PhantomPhysicsComponent::SetMax(uint32_t max) {
|
||||
void PhantomPhysicsComponent::SetMax(const float max) {
|
||||
m_Max = max;
|
||||
m_MinMax = true;
|
||||
m_EffectInfoDirty = true;
|
||||
}
|
||||
|
||||
void PhantomPhysicsComponent::SetVolumeEffect(std::vector<std::string> args) {
|
||||
for (auto& arg : args) {
|
||||
arg.erase(0, arg.find_first_not_of(' '));
|
||||
arg.erase(arg.find_last_not_of(' ') + 1);
|
||||
}
|
||||
|
||||
SetPhysicsEffectActive(true);
|
||||
ePhysicsEffectType effectType = ePhysicsEffectType::PUSH;
|
||||
if (!args.empty()) {
|
||||
std::transform(args.at(0).begin(), args.at(0).end(), args.at(0).begin(), ::tolower);
|
||||
if (args.at(0) == "push") effectType = ePhysicsEffectType::PUSH;
|
||||
else if (args.at(0) == "attract") effectType = ePhysicsEffectType::ATTRACT;
|
||||
else if (args.at(0) == "repulse") effectType = ePhysicsEffectType::REPULSE;
|
||||
else if (args.at(0) == "gravity") effectType = ePhysicsEffectType::GRAVITY_SCALE;
|
||||
else if (args.at(0) == "friction") effectType = ePhysicsEffectType::FRICTION;
|
||||
}
|
||||
|
||||
SetEffectType(effectType);
|
||||
if (args.size() > 1) SetDirectionalMultiplier(GeneralUtils::TryParse(args.at(1), 0.0f));
|
||||
if (args.size() > 4) {
|
||||
// Live sent the direction with the amount as its length ("Push,50,0.91,-0.34,-0.22" as 45.68, -17.07, -11.04)
|
||||
// and none when it was zero ("Repulse,150,0,0,0,true,25,100")
|
||||
const NiPoint3 direction = GeneralUtils::TryParse(args.at(2), args.at(3), args.at(4), NiPoint3Constant::ZERO);
|
||||
if (direction != NiPoint3Constant::ZERO) SetDirection(direction.Unitize());
|
||||
}
|
||||
// Live sent the distances for "true" only: volumes with "1,20,50" went out without them
|
||||
if (args.size() > 7 && args.at(5) == "true") {
|
||||
SetMin(GeneralUtils::TryParse(args.at(6), 0.0f));
|
||||
SetMax(GeneralUtils::TryParse(args.at(7), 0.0f));
|
||||
}
|
||||
}
|
||||
|
||||
void PhantomPhysicsComponent::SetPosition(const NiPoint3& pos) {
|
||||
PhysicsComponent::SetPosition(pos);
|
||||
if (m_dpEntity) m_dpEntity->SetPosition(pos);
|
||||
@@ -281,8 +314,8 @@ bool PhantomPhysicsComponent::OnGetObjectReportInfo(GameMessages::GetObjectRepor
|
||||
|
||||
if (m_MinMax) {
|
||||
auto& minMaxInfo = info.PushDebug("Min Max Info");
|
||||
minMaxInfo.PushDebug<AMFIntValue>("Min") = m_Min;
|
||||
minMaxInfo.PushDebug<AMFIntValue>("Max") = m_Max;
|
||||
minMaxInfo.PushDebug<AMFDoubleValue>("Min") = m_Min;
|
||||
minMaxInfo.PushDebug<AMFDoubleValue>("Max") = m_Max;
|
||||
}
|
||||
|
||||
if (m_IsRespawnVolume) {
|
||||
|
||||
@@ -120,14 +120,16 @@ public:
|
||||
void SpawnVertices() const;
|
||||
|
||||
/**
|
||||
* Legacy stuff no clue what this does
|
||||
* The distances the effect scales between; written only once one of them is set.
|
||||
* LWOPhantomPhysicsComponent::Deserialize (0x00c34db0) keeps the least of the min and 1, and a max of at least that.
|
||||
*/
|
||||
void SetMin(uint32_t min);
|
||||
void SetMin(float min);
|
||||
void SetMax(float max);
|
||||
|
||||
/**
|
||||
* Legacy stuff no clue what this does
|
||||
* A trigger's SetPhysicsVolumeEffect: type, amount, direction x, y, z, use distance, min, max
|
||||
*/
|
||||
void SetMax(uint32_t max);
|
||||
void SetVolumeEffect(std::vector<std::string> args);
|
||||
|
||||
private:
|
||||
bool OnGetObjectReportInfo(GameMessages::GetObjectReportInfo& reportInfo);
|
||||
@@ -158,8 +160,8 @@ private:
|
||||
float m_DirectionalMultiplier;
|
||||
|
||||
bool m_MinMax;
|
||||
uint32_t m_Min;
|
||||
uint32_t m_Max;
|
||||
float m_Min;
|
||||
float m_Max;
|
||||
|
||||
/**
|
||||
* Whether or not this physics object is pointed in some direction
|
||||
|
||||
@@ -407,34 +407,7 @@ void TriggerComponent::HandleSetPhysicsVolumeEffect(Entity* targetEntity, std::v
|
||||
LOG_DEBUG("Phantom Physics component not found!");
|
||||
return;
|
||||
}
|
||||
phantomPhysicsComponent->SetPhysicsEffectActive(true);
|
||||
ePhysicsEffectType effectType = ePhysicsEffectType::PUSH;
|
||||
if (!argArray.empty()) {
|
||||
std::transform(argArray.at(0).begin(), argArray.at(0).end(), argArray.at(0).begin(), ::tolower); //Transform to lowercase
|
||||
if (argArray.at(0) == "push") effectType = ePhysicsEffectType::PUSH;
|
||||
else if (argArray.at(0) == "attract") effectType = ePhysicsEffectType::ATTRACT;
|
||||
else if (argArray.at(0) == "repulse") effectType = ePhysicsEffectType::REPULSE;
|
||||
else if (argArray.at(0) == "gravity") effectType = ePhysicsEffectType::GRAVITY_SCALE;
|
||||
else if (argArray.at(0) == "friction") effectType = ePhysicsEffectType::FRICTION;
|
||||
}
|
||||
|
||||
phantomPhysicsComponent->SetEffectType(effectType);
|
||||
if (argArray.size() > 1) {
|
||||
phantomPhysicsComponent->SetDirectionalMultiplier(GeneralUtils::TryParse(argArray.at(1), 0.0f));
|
||||
}
|
||||
if (argArray.size() > 4) {
|
||||
const NiPoint3 direction =
|
||||
GeneralUtils::TryParse(argArray.at(2), argArray.at(3), argArray.at(4), NiPoint3Constant::ZERO);
|
||||
|
||||
phantomPhysicsComponent->SetDirection(direction);
|
||||
}
|
||||
if (argArray.size() > 6) {
|
||||
const uint32_t min = GeneralUtils::TryParse(argArray.at(6), 0);
|
||||
phantomPhysicsComponent->SetMin(min);
|
||||
|
||||
const uint32_t max = GeneralUtils::TryParse(argArray.at(7), 0);
|
||||
phantomPhysicsComponent->SetMax(max);
|
||||
}
|
||||
phantomPhysicsComponent->SetVolumeEffect(argArray);
|
||||
|
||||
Game::entityManager->SerializeEntity(targetEntity);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "Item.h"
|
||||
#include "ModelComponent.h"
|
||||
#include "MovingPlatformComponent.h"
|
||||
#include "PhantomPhysicsComponent.h"
|
||||
#include "SimplePhysicsComponent.h"
|
||||
#include "User.h"
|
||||
#include "eGameActivity.h"
|
||||
@@ -210,6 +211,111 @@ TEST_F(ReplicaConstructionTest, ControllablePhysicsConstructionCarriesJetPackMod
|
||||
EXPECT_EQ(construct(), plain);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct VolumeEffect {
|
||||
uint32_t type{};
|
||||
float amount{};
|
||||
bool hasDistance{};
|
||||
float min{};
|
||||
float max{};
|
||||
bool isDirectional{};
|
||||
NiPoint3 direction{};
|
||||
};
|
||||
|
||||
// A phantom volume looks its LOT up in the registry when it is made
|
||||
void ConnectRegistry() {
|
||||
if (CDClientDatabase::isConnected) return;
|
||||
CDClientDatabase::Connect(":memory:");
|
||||
CDClientDatabase::ExecuteDML("CREATE TABLE ComponentsRegistry (id INTEGER, component_type INTEGER, component_id INTEGER);");
|
||||
}
|
||||
|
||||
// The effect block of a phantom physics update made from a trigger's SetPhysicsVolumeEffect arguments
|
||||
VolumeEffect ReadVolumeEffect(Entity& volume, const std::vector<std::string>& args) {
|
||||
auto* const phantom = volume.AddComponent<PhantomPhysicsComponent>(-1);
|
||||
phantom->SetVolumeEffect(args);
|
||||
RakNet::BitStream stream;
|
||||
phantom->Serialize(stream, false);
|
||||
|
||||
VolumeEffect effect;
|
||||
bool flag{};
|
||||
stream.Read(flag);
|
||||
if (flag) stream.IgnoreBits(7 * 32); // position and rotation
|
||||
stream.Read(flag);
|
||||
EXPECT_TRUE(flag); // the effect block
|
||||
stream.Read(flag);
|
||||
EXPECT_TRUE(flag); // active
|
||||
stream.Read(effect.type);
|
||||
stream.Read(effect.amount);
|
||||
stream.Read(effect.hasDistance);
|
||||
if (effect.hasDistance) {
|
||||
stream.Read(effect.min);
|
||||
stream.Read(effect.max);
|
||||
}
|
||||
stream.Read(effect.isDirectional);
|
||||
if (effect.isDirectional) {
|
||||
stream.Read(effect.direction.x);
|
||||
stream.Read(effect.direction.y);
|
||||
stream.Read(effect.direction.z);
|
||||
}
|
||||
EXPECT_EQ(stream.GetNumberOfUnreadBits(), 0u);
|
||||
return effect;
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger volume effects as live sent them (phantom physics constructions in the live captures; the client reads
|
||||
// them in LWOPhantomPhysicsComponent::Deserialize): the direction has the amount as its length, the distances go out
|
||||
// for "true" only and as 1 and the max, and a zero direction is no direction.
|
||||
TEST_F(ReplicaConstructionTest, PhantomPhysicsVolumeEffectsAsLive) {
|
||||
ConnectRegistry();
|
||||
Entity pushWithDistance(1001, info);
|
||||
auto effect = ReadVolumeEffect(pushWithDistance, { "Push", "50", "0.91", "-0.34", "-0.22", "true", "20", "50" });
|
||||
EXPECT_EQ(effect.type, 0u);
|
||||
EXPECT_EQ(effect.amount, 50.0f);
|
||||
EXPECT_TRUE(effect.hasDistance);
|
||||
EXPECT_EQ(effect.min, 1.0f);
|
||||
EXPECT_EQ(effect.max, 50.0f);
|
||||
EXPECT_TRUE(effect.isDirectional);
|
||||
EXPECT_NEAR(effect.direction.x, 45.68f, 0.01f);
|
||||
EXPECT_NEAR(effect.direction.y, -17.07f, 0.01f);
|
||||
EXPECT_NEAR(effect.direction.z, -11.04f, 0.01f);
|
||||
|
||||
Entity pushFar(1002, info);
|
||||
effect = ReadVolumeEffect(pushFar, { "Push", "200", "1", "0", "0", "true", "150", "175" });
|
||||
EXPECT_TRUE(effect.hasDistance);
|
||||
EXPECT_EQ(effect.min, 1.0f);
|
||||
EXPECT_EQ(effect.max, 175.0f);
|
||||
EXPECT_EQ(effect.direction, NiPoint3(200.0f, 0.0f, 0.0f));
|
||||
|
||||
// "1" is not "true": no distances
|
||||
Entity updraft(1003, info);
|
||||
effect = ReadVolumeEffect(updraft, { "Push", "40.0", "0", "1", "0", "1", "20", "50" });
|
||||
EXPECT_EQ(effect.amount, 40.0f);
|
||||
EXPECT_FALSE(effect.hasDistance);
|
||||
EXPECT_TRUE(effect.isDirectional);
|
||||
EXPECT_EQ(effect.direction, NiPoint3(0.0f, 40.0f, 0.0f));
|
||||
|
||||
Entity repulse(1004, info);
|
||||
effect = ReadVolumeEffect(repulse, { "Repulse", "150", "0", "0", "0", "true", "25", "100" });
|
||||
EXPECT_EQ(effect.type, 2u);
|
||||
EXPECT_EQ(effect.amount, 150.0f);
|
||||
EXPECT_TRUE(effect.hasDistance);
|
||||
EXPECT_EQ(effect.min, 1.0f);
|
||||
EXPECT_EQ(effect.max, 100.0f);
|
||||
EXPECT_FALSE(effect.isDirectional);
|
||||
|
||||
Entity slanted(1005, info);
|
||||
effect = ReadVolumeEffect(slanted, { "Push", " 35", " .79", " 0", " .61" });
|
||||
EXPECT_FALSE(effect.hasDistance);
|
||||
EXPECT_NEAR(effect.direction.x, 27.70f, 0.01f);
|
||||
EXPECT_NEAR(effect.direction.z, 21.39f, 0.01f);
|
||||
|
||||
Entity friction(1006, info);
|
||||
effect = ReadVolumeEffect(friction, { "Friction", "0" });
|
||||
EXPECT_EQ(effect.type, 4u);
|
||||
EXPECT_FALSE(effect.hasDistance);
|
||||
EXPECT_FALSE(effect.isDirectional);
|
||||
}
|
||||
|
||||
// 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