mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
fix(currency): SetCurrency names its source the way live did
Live filled SetCurrency's source fields per source type (16,816 decoded live SetCurrency): pickups carry the position the client picked the coins up at (the one in its PickupCurrency) and no source; mission, achievement and activity rewards name the player (object and LOT 1); vendor buys, sells and buybacks name the vendor and its LOT; trades carry the trade ID; death, mail and everything else name nothing. No live SetCurrency sets loot_type. DLU wrote a present source LOT of 0, no object and a zero position for all of them. The position is the one the client uses: for pickups it projects it to the screen as the start of the coin counter animation (LWOCharacterComponent::SetCurrency), so it started from the world origin before. PickupCurrency now reads that position (the client always sends it). The trade ID is an 8-byte object ID in the client; DLU declared it 4 bytes, which only never broke because it was never set. Check: pick up coins and watch the coin counter animate from where the coins were; buy and sell at a vendor, finish a mission and trade coins with another player, and the coin total updates each time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ set(DGAMETEST_SOURCES
|
||||
"UgcManifestTests.cpp"
|
||||
"SceneGhostingTests.cpp"
|
||||
"HolidayEventsTests.cpp"
|
||||
"SetCurrencySourceTests.cpp"
|
||||
)
|
||||
|
||||
add_subdirectory(dComponentsTests)
|
||||
|
||||
55
tests/dGameTests/SetCurrencySourceTests.cpp
Normal file
55
tests/dGameTests/SetCurrencySourceTests.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "GameDependencies.h"
|
||||
#include "dGameMessagesTests/GameMessageTestUtils.h"
|
||||
|
||||
#include "Character.h"
|
||||
#include "Entity.h"
|
||||
#include "PlayerMessages.h"
|
||||
#include "eLootSourceType.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace GameMessageTestUtils;
|
||||
|
||||
// SetCurrency source fields per source type, as live sent them. Each expected packet is a live SetCurrency with the
|
||||
// player ID replaced by 0x1000000000000001 and any other object or trade ID by 0x0102030405060708.
|
||||
class SetCurrencySourceTests : public GameDependenciesTest {
|
||||
protected:
|
||||
void SetUp() override { SetUpDependencies(); }
|
||||
void TearDown() override { TearDownDependencies(); }
|
||||
|
||||
static constexpr LWOOBJID PLAYER = 0x1000000000000001LL;
|
||||
static constexpr LWOOBJID OTHER = 0x0102030405060708LL;
|
||||
};
|
||||
|
||||
TEST_F(SetCurrencySourceTests, PickupCarriesThePickupPosition) {
|
||||
const auto msg = Character::MakeSetCurrency(PLAYER, 253493, eLootSourceType::PICKUP, CoinSource::Position(NiPoint3(-142.26820373535156f, 69.87815856933594f, 391.2568359375f)));
|
||||
EXPECT_PACKET_EQ(FromHex("53 05 00 0c 00 00 00 00 01 00 00 00 00 00 00 10 85 00 35 de 03 00 00 00 00 00 54 a2 07 61 cf 60 c5 a1 70 50 61 a1 88 58 00 00 00", 341), StructPacket(msg));
|
||||
}
|
||||
|
||||
TEST_F(SetCurrencySourceTests, MissionAndAchievementNameThePlayer) {
|
||||
auto playerInfo = info;
|
||||
playerInfo.lot = 1;
|
||||
Entity player(PLAYER, playerInfo);
|
||||
EXPECT_PACKET_EQ(FromHex("53 05 00 0c 00 00 00 00 01 00 00 00 00 00 00 10 85 00 02 25 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 40 00 00 20 20 00 00 00 00 00 02 08 10 00 00 00", 437),
|
||||
StructPacket(Character::MakeSetCurrency(PLAYER, 206082, eLootSourceType::MISSION, CoinSource::Object(player))));
|
||||
EXPECT_PACKET_EQ(FromHex("53 05 00 0c 00 00 00 00 01 00 00 00 00 00 00 10 85 00 0a ef 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 40 00 00 20 20 00 00 00 00 00 02 08 28 00 00 00", 437),
|
||||
StructPacket(Character::MakeSetCurrency(PLAYER, 257802, eLootSourceType::ACHIEVEMENT, CoinSource::Object(player))));
|
||||
}
|
||||
|
||||
TEST_F(SetCurrencySourceTests, VendorNamesTheVendor) {
|
||||
auto vendorInfo = info;
|
||||
vendorInfo.lot = 8212;
|
||||
Entity vendor(OTHER, vendorInfo);
|
||||
EXPECT_PACKET_EQ(FromHex("53 05 00 0c 00 00 00 00 01 00 00 00 00 00 00 10 85 00 18 39 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 45 08 00 00 21 00 e0 c0 a0 80 60 40 28 48 00 00 00", 437),
|
||||
StructPacket(Character::MakeSetCurrency(PLAYER, 14616, eLootSourceType::VENDOR, CoinSource::Object(vendor))));
|
||||
}
|
||||
|
||||
TEST_F(SetCurrencySourceTests, TradeCarriesAnEightByteTradeId) {
|
||||
EXPECT_PACKET_EQ(FromHex("53 05 00 0c 00 00 00 00 01 00 00 00 00 00 00 10 85 00 34 66 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 80 70 60 50 40 30 20 18 30 00 00 00", 405),
|
||||
StructPacket(Character::MakeSetCurrency(PLAYER, 222772, eLootSourceType::TRADE, CoinSource::Trade(OTHER))));
|
||||
}
|
||||
|
||||
TEST_F(SetCurrencySourceTests, DeathNamesNothing) {
|
||||
EXPECT_PACKET_EQ(FromHex("53 05 00 0c 00 00 00 00 01 00 00 00 00 00 00 10 85 00 61 59 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 40 00 00 00", 341),
|
||||
StructPacket(Character::MakeSetCurrency(PLAYER, 22881, eLootSourceType::DELETION, {})));
|
||||
}
|
||||
@@ -408,9 +408,9 @@ TEST_F(RemainingMessagesTests, PlayerMessagesMatchLegacy) {
|
||||
currency.lootType = smallValue;
|
||||
currency.sourceID = smallValue;
|
||||
currency.sourceLOT = smallValue;
|
||||
currency.sourceTradeID = smallValue;
|
||||
currency.sourceType = source;
|
||||
ExpectSameSends([&] { LegacyGameMessages::SendSetCurrency(&entity, value, smallValue, smallValue, smallValue, smallValue, true, source); }, [&] { currency.SendToClient(entity.GetSystemAddress()); });
|
||||
// The legacy sender wrote a set trade ID as 4 bytes; the client reads 8 (see SetCurrencyTradeIdIsAnObjectId).
|
||||
ExpectSameSends([&] { LegacyGameMessages::SendSetCurrency(&entity, value, smallValue, smallValue, smallValue, 0, true, source); }, [&] { currency.SendToClient(entity.GetSystemAddress()); });
|
||||
RoundTrip(currency);
|
||||
}
|
||||
}
|
||||
@@ -427,7 +427,16 @@ TEST_F(RemainingMessagesTests, InboundPlayerMessagesMatchLegacy) {
|
||||
for (const uint32_t amount : { 0u, 1u, 0xffffffffu }) {
|
||||
GameMessages::PickupCurrency pickup;
|
||||
pickup.currency = amount;
|
||||
EXPECT_EQ(ReadWithLegacy<unsigned int>(pickup, LegacyGameMessages::ReadPickupCurrency), amount);
|
||||
pickup.position = NiPoint3(1.5f, -2.0f, 300.25f);
|
||||
// The old reader stopped after the amount; the client also sends where it picked the coins up.
|
||||
RakNet::BitStream wire;
|
||||
pickup.Serialize(wire);
|
||||
RakNet::BitStream legacyStream(wire.GetData(), wire.GetNumberOfBytesUsed(), false);
|
||||
EXPECT_EQ(LegacyGameMessages::ReadPickupCurrency(legacyStream), amount);
|
||||
const auto copy = RoundTrip(pickup);
|
||||
EXPECT_EQ(copy.currency, amount);
|
||||
EXPECT_EQ(copy.position, pickup.position);
|
||||
EXPECT_EQ(wire.GetNumberOfBitsUsed(), 16u * 8u);
|
||||
ExpectTruncatedFails(pickup);
|
||||
}
|
||||
for (const auto& name : g_WStrings) {
|
||||
|
||||
Reference in New Issue
Block a user