mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-01 01:17:13 +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:
@@ -590,7 +590,38 @@ const NiPoint3& Character::GetRespawnPoint(LWOMAPID map) const {
|
|||||||
return pair->second;
|
return pair->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Character::SetCoins(int64_t newCoins, eLootSourceType lootSource) {
|
CoinSource CoinSource::Object(const Entity& source) {
|
||||||
|
CoinSource coinSource;
|
||||||
|
coinSource.object = source.GetObjectID();
|
||||||
|
coinSource.lot = source.GetLOT();
|
||||||
|
return coinSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoinSource CoinSource::Position(const NiPoint3& position) {
|
||||||
|
CoinSource coinSource;
|
||||||
|
coinSource.position = position;
|
||||||
|
return coinSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoinSource CoinSource::Trade(const LWOOBJID tradeID) {
|
||||||
|
CoinSource coinSource;
|
||||||
|
coinSource.tradeID = tradeID;
|
||||||
|
return coinSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameMessages::SetCurrency Character::MakeSetCurrency(const LWOOBJID player, const int64_t coins, const eLootSourceType coinSource, const CoinSource& source) {
|
||||||
|
GameMessages::SetCurrency setCurrency;
|
||||||
|
setCurrency.target = player;
|
||||||
|
setCurrency.currency = coins;
|
||||||
|
setCurrency.position = source.position;
|
||||||
|
setCurrency.sourceLOT = source.lot;
|
||||||
|
setCurrency.sourceID = source.object;
|
||||||
|
setCurrency.sourceTradeID = source.tradeID;
|
||||||
|
setCurrency.sourceType = coinSource;
|
||||||
|
return setCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Character::SetCoins(int64_t newCoins, eLootSourceType lootSource, const CoinSource& source) {
|
||||||
if (newCoins < 0) {
|
if (newCoins < 0) {
|
||||||
newCoins = 0;
|
newCoins = 0;
|
||||||
}
|
}
|
||||||
@@ -600,15 +631,7 @@ void Character::SetCoins(int64_t newCoins, eLootSourceType lootSource) {
|
|||||||
m_Coins = newCoins;
|
m_Coins = newCoins;
|
||||||
|
|
||||||
auto* entity = Game::entityManager->GetEntity(m_ObjectID);
|
auto* entity = Game::entityManager->GetEntity(m_ObjectID);
|
||||||
GameMessages::SetCurrency setCurrency;
|
MakeSetCurrency(entity->GetObjectID(), m_Coins, lootSource, source).SendToClient(entity->GetSystemAddress());
|
||||||
setCurrency.target = entity->GetObjectID();
|
|
||||||
setCurrency.currency = m_Coins;
|
|
||||||
setCurrency.lootType = 0;
|
|
||||||
setCurrency.sourceLOT = 0;
|
|
||||||
setCurrency.sourceID = 0;
|
|
||||||
setCurrency.sourceTradeID = 0;
|
|
||||||
setCurrency.sourceType = lootSource;
|
|
||||||
setCurrency.SendToClient(entity->GetSystemAddress());
|
|
||||||
|
|
||||||
// Live followed every coin gain with CurrencyCollected (the amount gained); losses had none
|
// Live followed every coin gain with CurrencyCollected (the amount gained); losses had none
|
||||||
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
||||||
|
|||||||
@@ -17,6 +17,26 @@ class Entity;
|
|||||||
enum class ePermissionMap : uint64_t;
|
enum class ePermissionMap : uint64_t;
|
||||||
enum class eGameMasterLevel : uint8_t;
|
enum class eGameMasterLevel : uint8_t;
|
||||||
enum class eLootSourceType : uint32_t;
|
enum class eLootSourceType : uint32_t;
|
||||||
|
namespace GameMessages {
|
||||||
|
struct SetCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Where a coin change came from, as live filled SetCurrency (18,417 live SetCurrency):
|
||||||
|
* pickups carry the position the client picked the coins up at (the client starts the coin animation there);
|
||||||
|
* mission, achievement and activity rewards name the player (LOT 1); vendors name the vendor and its LOT;
|
||||||
|
* trades carry the trade ID. Everything else (death, mail) names nothing.
|
||||||
|
*/
|
||||||
|
struct CoinSource {
|
||||||
|
NiPoint3 position{ NiPoint3Constant::ZERO };
|
||||||
|
LWOOBJID object{ LWOOBJID_EMPTY };
|
||||||
|
LOT lot{ LOT_NULL };
|
||||||
|
LWOOBJID tradeID{ LWOOBJID_EMPTY };
|
||||||
|
|
||||||
|
static CoinSource Object(const Entity& source);
|
||||||
|
static CoinSource Position(const NiPoint3& position);
|
||||||
|
static CoinSource Trade(LWOOBJID tradeID);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Meta information about a character, like their name and style
|
* Meta information about a character, like their name and style
|
||||||
@@ -352,7 +372,12 @@ public:
|
|||||||
* @param newCoins the amount of coins to update by
|
* @param newCoins the amount of coins to update by
|
||||||
* @param coinSource The source of the loot
|
* @param coinSource The source of the loot
|
||||||
*/
|
*/
|
||||||
void SetCoins(int64_t newCoins, eLootSourceType coinSource);
|
void SetCoins(int64_t newCoins, eLootSourceType coinSource, const CoinSource& source = {});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The SetCurrency live sent for a coin change.
|
||||||
|
*/
|
||||||
|
static GameMessages::SetCurrency MakeSetCurrency(LWOOBJID player, int64_t coins, eLootSourceType coinSource, const CoinSource& source);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the entity this character belongs to
|
* Get the entity this character belongs to
|
||||||
|
|||||||
@@ -177,8 +177,8 @@ void Trade::Complete() {
|
|||||||
|
|
||||||
// Now actually do the trade. Nothing is created or destroyed, so the economy ledger records transfers instead.
|
// Now actually do the trade. Nothing is created or destroyed, so the economy ledger records transfers instead.
|
||||||
EconomyLedger::ScopedItemTransfer transfer;
|
EconomyLedger::ScopedItemTransfer transfer;
|
||||||
characterA->SetCoins(characterA->GetCoins() - m_CoinsA + m_CoinsB, eLootSourceType::TRADE);
|
characterA->SetCoins(characterA->GetCoins() - m_CoinsA + m_CoinsB, eLootSourceType::TRADE, CoinSource::Trade(m_TradeId));
|
||||||
characterB->SetCoins(characterB->GetCoins() - m_CoinsB + m_CoinsA, eLootSourceType::TRADE);
|
characterB->SetCoins(characterB->GetCoins() - m_CoinsB + m_CoinsA, eLootSourceType::TRADE, CoinSource::Trade(m_TradeId));
|
||||||
|
|
||||||
const auto zone = Game::server ? Game::server->GetZoneID() : 0;
|
const auto zone = Game::server ? Game::server->GetZoneID() : 0;
|
||||||
const auto recordCoins = [zone](const uint64_t coins, const LWOOBJID from, const LWOOBJID to) {
|
const auto recordCoins = [zone](const uint64_t coins, const LWOOBJID from, const LWOOBJID to) {
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ void VendorComponent::Buy(Entity* buyer, LOT lot, uint32_t count) {
|
|||||||
inventoryComponent->RemoveItem(itemComp.currencyLOT, altCurrencyCost, eInventoryType::ALL);
|
inventoryComponent->RemoveItem(itemComp.currencyLOT, altCurrencyCost, eInventoryType::ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
character->SetCoins(character->GetCoins() - (coinCost), eLootSourceType::VENDOR);
|
character->SetCoins(character->GetCoins() - (coinCost), eLootSourceType::VENDOR, CoinSource::Object(*m_Parent));
|
||||||
inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR);
|
inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR);
|
||||||
SendTransactionResult(buyer->GetObjectID(), buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS);
|
SendTransactionResult(buyer->GetObjectID(), buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS);
|
||||||
}
|
}
|
||||||
@@ -278,7 +278,7 @@ void VendorComponent::SellToVendor(Entity& player, const SystemAddress& sysAddr,
|
|||||||
|
|
||||||
MakeRoomInBuyback(*inv, *item, static_cast<uint32_t>(std::max(count, 0)));
|
MakeRoomInBuyback(*inv, *item, static_cast<uint32_t>(std::max(count, 0)));
|
||||||
inv->MoveItemToInventory(item, eInventoryType::VENDOR_BUYBACK, count, true, false, true);
|
inv->MoveItemToInventory(item, eInventoryType::VENDOR_BUYBACK, count, true, false, true);
|
||||||
character->SetCoins(std::floor(character->GetCoins() + (static_cast<uint32_t>(itemComp.baseValue * sellScalar) * count)), eLootSourceType::VENDOR);
|
character->SetCoins(std::floor(character->GetCoins() + (static_cast<uint32_t>(itemComp.baseValue * sellScalar) * count)), eLootSourceType::VENDOR, CoinSource::Object(*m_Parent));
|
||||||
SendTransactionResult(m_Parent->GetObjectID(), sysAddr, eVendorTransactionResult::SELL_SUCCESS);
|
SendTransactionResult(m_Parent->GetObjectID(), sysAddr, eVendorTransactionResult::SELL_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,7 +316,7 @@ void VendorComponent::BuybackFromVendor(Entity& player, const SystemAddress& sys
|
|||||||
|
|
||||||
//inv->RemoveItem(count, -1, iObjID);
|
//inv->RemoveItem(count, -1, iObjID);
|
||||||
inv->MoveItemToInventory(item, Inventory::FindInventoryTypeForLot(item->GetLot()), count, true, false);
|
inv->MoveItemToInventory(item, Inventory::FindInventoryTypeForLot(item->GetLot()), count, true, false);
|
||||||
character->SetCoins(character->GetCoins() - cost, eLootSourceType::VENDOR);
|
character->SetCoins(character->GetCoins() - cost, eLootSourceType::VENDOR, CoinSource::Object(*m_Parent));
|
||||||
//Game::entityManager->SerializeEntity(player); // so inventory updates
|
//Game::entityManager->SerializeEntity(player); // so inventory updates
|
||||||
SendTransactionResult(m_Parent->GetObjectID(), sysAddr, eVendorTransactionResult::PURCHASE_SUCCESS);
|
SendTransactionResult(m_Parent->GetObjectID(), sysAddr, eVendorTransactionResult::PURCHASE_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace GameMessages {
|
|||||||
bitStream.Write(position.z);
|
bitStream.Write(position.z);
|
||||||
BitStreamUtils::WriteOptional(bitStream, sourceLOT, LOT_NULL);
|
BitStreamUtils::WriteOptional(bitStream, sourceLOT, LOT_NULL);
|
||||||
BitStreamUtils::WriteOptional(bitStream, sourceID, LWOOBJID_EMPTY);
|
BitStreamUtils::WriteOptional(bitStream, sourceID, LWOOBJID_EMPTY);
|
||||||
BitStreamUtils::WriteOptional(bitStream, sourceTradeID, 0);
|
BitStreamUtils::WriteOptional(bitStream, sourceTradeID, LWOOBJID_EMPTY);
|
||||||
BitStreamUtils::WriteOptional(bitStream, sourceType, eLootSourceType::NONE);
|
BitStreamUtils::WriteOptional(bitStream, sourceType, eLootSourceType::NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ namespace GameMessages {
|
|||||||
VALIDATE_READ(bitStream.Read(position.z));
|
VALIDATE_READ(bitStream.Read(position.z));
|
||||||
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, sourceLOT, LOT_NULL));
|
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, sourceLOT, LOT_NULL));
|
||||||
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, sourceID, LWOOBJID_EMPTY));
|
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, sourceID, LWOOBJID_EMPTY));
|
||||||
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, sourceTradeID, 0));
|
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, sourceTradeID, LWOOBJID_EMPTY));
|
||||||
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, sourceType, eLootSourceType::NONE));
|
VALIDATE_READ(BitStreamUtils::ReadOptional(bitStream, sourceType, eLootSourceType::NONE));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -115,10 +115,17 @@ namespace GameMessages {
|
|||||||
|
|
||||||
void PickupCurrency::Serialize(RakNet::BitStream& bitStream) const {
|
void PickupCurrency::Serialize(RakNet::BitStream& bitStream) const {
|
||||||
bitStream.Write(currency);
|
bitStream.Write(currency);
|
||||||
|
bitStream.Write(position.x);
|
||||||
|
bitStream.Write(position.y);
|
||||||
|
bitStream.Write(position.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PickupCurrency::Deserialize(RakNet::BitStream& bitStream) {
|
bool PickupCurrency::Deserialize(RakNet::BitStream& bitStream) {
|
||||||
return bitStream.Read(currency);
|
VALIDATE_READ(bitStream.Read(currency));
|
||||||
|
VALIDATE_READ(bitStream.Read(position.x));
|
||||||
|
VALIDATE_READ(bitStream.Read(position.y));
|
||||||
|
VALIDATE_READ(bitStream.Read(position.z));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PickupCurrency::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
void PickupCurrency::Handle(Entity& entity, const SystemAddress& sysAddr) {
|
||||||
@@ -126,7 +133,7 @@ namespace GameMessages {
|
|||||||
|
|
||||||
auto* ch = entity.GetCharacter();
|
auto* ch = entity.GetCharacter();
|
||||||
if (ch && entity.PickupCoins(currency)) {
|
if (ch && entity.PickupCoins(currency)) {
|
||||||
ch->SetCoins(ch->GetCoins() + currency, eLootSourceType::PICKUP);
|
ch->SetCoins(ch->GetCoins() + currency, eLootSourceType::PICKUP, CoinSource::Position(position));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,8 +53,7 @@ namespace GameMessages {
|
|||||||
NiPoint3 position{ NiPoint3Constant::ZERO };
|
NiPoint3 position{ NiPoint3Constant::ZERO };
|
||||||
LOT sourceLOT{ LOT_NULL }; // optional
|
LOT sourceLOT{ LOT_NULL }; // optional
|
||||||
LWOOBJID sourceID{ LWOOBJID_EMPTY }; // optional
|
LWOOBJID sourceID{ LWOOBJID_EMPTY }; // optional
|
||||||
// optional (default 0). DLU writes it as an int32_t; lu_packets has an object ID (8 bytes). Only ever 0.
|
LWOOBJID sourceTradeID{ LWOOBJID_EMPTY }; // optional; the client reads an object ID (8 bytes)
|
||||||
int32_t sourceTradeID{ 0 };
|
|
||||||
eLootSourceType sourceType{ eLootSourceType::NONE }; // optional
|
eLootSourceType sourceType{ eLootSourceType::NONE }; // optional
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -109,6 +108,7 @@ namespace GameMessages {
|
|||||||
void Handle(Entity& entity, const SystemAddress& sysAddr) override;
|
void Handle(Entity& entity, const SystemAddress& sysAddr) override;
|
||||||
|
|
||||||
uint32_t currency{};
|
uint32_t currency{};
|
||||||
|
NiPoint3 position{ NiPoint3Constant::ZERO }; // where the client picked the coins up
|
||||||
};
|
};
|
||||||
|
|
||||||
// Client -> server.
|
// Client -> server.
|
||||||
|
|||||||
@@ -528,7 +528,7 @@ void Mission::YieldRewards() {
|
|||||||
|
|
||||||
if (info.reward_currency_repeatable > 0 || coinsToSend > 0) {
|
if (info.reward_currency_repeatable > 0 || coinsToSend > 0) {
|
||||||
eLootSourceType lootSource = info.isMission ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT;
|
eLootSourceType lootSource = info.isMission ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT;
|
||||||
character->SetCoins(character->GetCoins() + info.reward_currency_repeatable + coinsToSend, lootSource);
|
character->SetCoins(character->GetCoins() + info.reward_currency_repeatable + coinsToSend, lootSource, CoinSource::Object(*entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -556,7 +556,7 @@ void Mission::YieldRewards() {
|
|||||||
|
|
||||||
if (info.reward_currency > 0 || coinsToSend > 0) {
|
if (info.reward_currency > 0 || coinsToSend > 0) {
|
||||||
eLootSourceType lootSource = info.isMission ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT;
|
eLootSourceType lootSource = info.isMission ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT;
|
||||||
character->SetCoins(character->GetCoins() + info.reward_currency + coinsToSend, lootSource);
|
character->SetCoins(character->GetCoins() + info.reward_currency + coinsToSend, lootSource, CoinSource::Object(*entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.reward_maxinventory > 0) {
|
if (info.reward_maxinventory > 0) {
|
||||||
|
|||||||
@@ -570,7 +570,7 @@ void Loot::GiveActivityLoot(Entity* player, const LWOOBJID source, uint32_t acti
|
|||||||
|
|
||||||
auto* character = player->GetCharacter();
|
auto* character = player->GetCharacter();
|
||||||
|
|
||||||
character->SetCoins(character->GetCoins() + coins, eLootSourceType::ACTIVITY);
|
character->SetCoins(character->GetCoins() + coins, eLootSourceType::ACTIVITY, CoinSource::Object(*player));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loot::DropLoot(Entity* player, const LWOOBJID source, uint32_t matrixIndex, uint32_t minCoins, uint32_t maxCoins) {
|
void Loot::DropLoot(Entity* player, const LWOOBJID source, uint32_t matrixIndex, uint32_t minCoins, uint32_t maxCoins) {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ set(DGAMETEST_SOURCES
|
|||||||
"UgcManifestTests.cpp"
|
"UgcManifestTests.cpp"
|
||||||
"SceneGhostingTests.cpp"
|
"SceneGhostingTests.cpp"
|
||||||
"HolidayEventsTests.cpp"
|
"HolidayEventsTests.cpp"
|
||||||
|
"SetCurrencySourceTests.cpp"
|
||||||
)
|
)
|
||||||
|
|
||||||
add_subdirectory(dComponentsTests)
|
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.lootType = smallValue;
|
||||||
currency.sourceID = smallValue;
|
currency.sourceID = smallValue;
|
||||||
currency.sourceLOT = smallValue;
|
currency.sourceLOT = smallValue;
|
||||||
currency.sourceTradeID = smallValue;
|
|
||||||
currency.sourceType = source;
|
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);
|
RoundTrip(currency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -427,7 +427,16 @@ TEST_F(RemainingMessagesTests, InboundPlayerMessagesMatchLegacy) {
|
|||||||
for (const uint32_t amount : { 0u, 1u, 0xffffffffu }) {
|
for (const uint32_t amount : { 0u, 1u, 0xffffffffu }) {
|
||||||
GameMessages::PickupCurrency pickup;
|
GameMessages::PickupCurrency pickup;
|
||||||
pickup.currency = amount;
|
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);
|
ExpectTruncatedFails(pickup);
|
||||||
}
|
}
|
||||||
for (const auto& name : g_WStrings) {
|
for (const auto& name : g_WStrings) {
|
||||||
|
|||||||
Reference in New Issue
Block a user