Files
DarkflameServer/tests/dGameTests/dComponentsTests/RailActivatorComponentTests.cpp
Aaron Kimbrell b033655e90 fix: a rail that is off or whose preconditions fail is not ridden
LWORailActivatorComponent::msgCheckUseRequirements (0x00c2c5b0) refuses a
use, after the quick build check, when the row's preconditions fail or
rail_activator_active is off, so a client does not ask. The server started
the rail on any use request. The rows ask for an equipped item (44) or a
completed mission (283 to 286).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-10 15:14:44 -06:00

107 lines
4.2 KiB
C++

#include "GameDependencies.h"
#include "CDClientManager.h"
#include "CDComponentsRegistryTable.h"
#include "CDRailActivatorComponent.h"
#include "Entity.h"
#include "MovementMessages.h"
#include "ObjectMessages.h"
#include "dGameMessagesTests/GameMessageTestUtils.h"
#include "RailActivatorComponent.h"
#include <gtest/gtest.h>
// The rider flags come from the RailActivatorComponent row; a level key, when there, replaces the row's value.
class RailActivatorComponentTests : public GameDependenciesTest {
protected:
static constexpr int32_t COMPONENT_ID = 9999;
std::unique_ptr<Entity> entity;
void SetUp() override {
SetUpDependencies();
CDRailActivatorComponent row{};
row.id = COMPONENT_ID;
row.damageImmune = true;
row.noAggro = true;
row.showNameBillboard = true;
CDClientManager::GetEntriesMutable<CDRailActivatorComponentTable>().push_back(row);
// The entity's own LOT has no components, so it doesn't look anything up in a database
CDClientManager::GetEntriesMutable<CDComponentsRegistryTable>().insert_or_assign(static_cast<uint64_t>(info.lot), 0);
entity = std::make_unique<Entity>(1, info);
}
void TearDown() override {
entity.reset();
CDClientManager::GetEntriesMutable<CDRailActivatorComponentTable>().clear();
TearDownDependencies();
}
};
TEST_F(RailActivatorComponentTests, WithoutLevelKeysTheRowIsUsed) {
const auto* const rail = entity->AddComponent<RailActivatorComponent>(COMPONENT_ID);
EXPECT_TRUE(rail->GetDamageImmune());
EXPECT_TRUE(rail->GetNoAggro());
EXPECT_TRUE(rail->GetShowNameBillboard());
}
TEST_F(RailActivatorComponentTests, LevelKeysReplaceTheRow) {
entity->SetVar<bool>(u"rail_activator_damage_immune", false);
entity->SetVar<bool>(u"rail_no_aggro", false);
entity->SetVar<bool>(u"rail_show_name_billboard", false);
const auto* const rail = entity->AddComponent<RailActivatorComponent>(COMPONENT_ID);
EXPECT_FALSE(rail->GetDamageImmune());
EXPECT_FALSE(rail->GetNoAggro());
EXPECT_FALSE(rail->GetShowNameBillboard());
}
TEST_F(RailActivatorComponentTests, NoRowAndNoKeysIsNotImmune) {
const auto* const rail = entity->AddComponent<RailActivatorComponent>(COMPONENT_ID + 1);
EXPECT_FALSE(rail->GetDamageImmune());
EXPECT_FALSE(rail->GetNoAggro());
}
// Live answered the client's RequestRailActivatorState (1479, no payload; 152 packets) with
// NotifyRailActivatorStateChange (1478) to that client: every one of the 413 live packets was bActive = true (80).
TEST_F(RailActivatorComponentTests, RequestRailActivatorStateIsAnswered) {
using namespace GameMessageTestUtils;
entity->AddComponent<RailActivatorComponent>(COMPONENT_ID);
GameMessages::RequestRailActivatorState request;
const auto packets = Capture([&] { request.Handle(*entity, ClientAddress()); });
ASSERT_EQ(packets.size(), 1u);
EXPECT_EQ(packets[0].sysAddr, ClientAddress());
EXPECT_FALSE(packets[0].broadcast);
EXPECT_PACKET_EQ(FromHex("53 05 00 0c 00 00 00 00 01 00 00 00 00 00 00 00 c6 05 80", 145), FromCapture(packets[0]));
}
TEST_F(RailActivatorComponentTests, InactiveRailIsReportedInactive) {
using namespace GameMessageTestUtils;
entity->SetVar<bool>(u"rail_activator_active", false);
const auto* const rail = entity->AddComponent<RailActivatorComponent>(COMPONENT_ID);
EXPECT_FALSE(rail->GetActive());
GameMessages::NotifyRailActivatorStateChange notify;
notify.bActive = false;
EXPECT_FALSE(RoundTrip(notify).bActive);
}
// A rail that is off is not ridden: LWORailActivatorComponent::msgCheckUseRequirements (0x00c2c5b0) refuses the use, so a
// client never asks for it
TEST_F(RailActivatorComponentTests, ARailThatIsOffIsNotUsed) {
entity->SetVar<bool>(u"rail_activator_active", false);
auto* const rail = entity->AddComponent<RailActivatorComponent>(COMPONENT_ID);
Entity user(77, info);
GameMessages::RequestUse use;
GameMessages::RequestUseEvent event(use, user);
EXPECT_TRUE(rail->OnRequestUse(event));
EXPECT_TRUE(rail->GetEntitiesOnRail().empty());
}
TEST_F(RailActivatorComponentTests, ARailThatIsOnIsUsed) {
auto* const rail = entity->AddComponent<RailActivatorComponent>(COMPONENT_ID);
Entity user(78, info);
GameMessages::RequestUse use;
GameMessages::RequestUseEvent event(use, user);
EXPECT_TRUE(rail->OnRequestUse(event));
EXPECT_EQ(rail->GetEntitiesOnRail(), std::vector<LWOOBJID>{ 78 });
}