add marketplace emulation

This commit is contained in:
quaK 2024-08-10 03:12:58 +03:00
parent d0bff32467
commit 1e9aee1227
20 changed files with 1481 additions and 48 deletions

View File

@ -501,7 +501,7 @@ namespace demonware
void post_unpack() override
{
#ifdef DW_DEBUG
//utils::hook::jump(0x141285040, bd_logger_stub, true);
utils::hook::jump(0x141285040, bd_logger_stub, true);
#endif
utils::hook::set<uint8_t>(0x14B5BB96F, 0x0); // CURLOPT_SSL_VERIFYPEER

View File

@ -267,7 +267,8 @@ namespace patches
dvars::override::register_int("dvl", 0, 0, 0, game::DVAR_FLAG_READ);
// killswitches
dvars::override::register_bool("killswitch_store", true, game::DVAR_FLAG_READ);
dvars::override::register_bool("mission_team_contracts_enabled", true, game::DVAR_FLAG_READ);
//dvars::override::register_bool("killswitch_store", true, game::DVAR_FLAG_READ);
dvars::override::register_bool("killswitch_matchID", true, game::DVAR_FLAG_READ);
// announcer packs

View File

@ -128,6 +128,24 @@ namespace demonware
return true;
}
bool byte_buffer::read_struct(void* output)
{
if (!this->read_data_type(BD_BB_STRUCTURED_DATA_TYPE))
{
return false;
}
unsigned int size;
this->read_uint32(&size);
auto data = const_cast<char*>(this->buffer_.data()) + this->current_byte_;
memcpy(output, data, size);
this->current_byte_ += size;
return true;
}
bool byte_buffer::read_data_type(const unsigned char expected)
{
if (!this->use_data_types_) return true;
@ -248,6 +266,14 @@ namespace demonware
return this->write(length, data);
}
bool byte_buffer::write_struct(void* data, const int length)
{
this->write_data_type(BD_BB_STRUCTURED_DATA_TYPE);
this->write_uint32(length);
return this->write(length, data);
}
bool byte_buffer::write_array_header(const unsigned char type, const unsigned int element_count,
const unsigned int element_size)
{

View File

@ -26,6 +26,7 @@ namespace demonware
bool read_string(std::string* output);
bool read_blob(char** output, int* length);
bool read_blob(std::string* output);
bool read_struct(void* output);
bool read_data_type(unsigned char expected);
bool read_array_header(unsigned char expected, unsigned int* element_count,
@ -46,6 +47,7 @@ namespace demonware
bool write_string(const std::string& data);
bool write_blob(const char* data, int length);
bool write_blob(const std::string& data);
bool write_struct(void* data, int length);
bool write_array_header(unsigned char type, unsigned int element_count, unsigned int element_size);

View File

@ -246,10 +246,10 @@ namespace demonware
class bdContextUserStorageFileInfo final : public bdTaskResult
{
public:
std::uint32_t create_time;
std::uint32_t modifed_time;
uint32_t create_time;
uint32_t modifed_time;
bool priv;
std::uint64_t owner_id;
uint64_t owner_id;
std::string account_type;
std::string filename;
@ -277,7 +277,7 @@ namespace demonware
class bdPublicProfileInfo final : public bdTaskResult
{
public:
std::uint64_t m_entityID;
uint64_t m_entityID;
std::string m_memberplayer_card;
void serialize(byte_buffer* buffer) override
@ -346,18 +346,87 @@ namespace demonware
{
public:
uint64_t user_id;
int64_t performance;
float performance;
void serialize(byte_buffer* buffer) override
{
buffer->write_uint64(this->user_id);
buffer->write_int64(this->performance);
buffer->write_float(this->performance);
}
void deserialize(byte_buffer* buffer) override
{
buffer->read_uint64(&this->user_id);
buffer->read_int64(&this->performance);
buffer->read_float(&this->performance);
}
};
class bdRewardEvent final : public bdTaskResult
{
public:
uint32_t push_type;
unsigned char r2;
uint64_t user_id;
std::string platform1;
std::string platform2;
int32_t rewardEventType;
uint32_t r7;
int32_t r8;
std::string json_buffer;
void serialize(byte_buffer* data) override
{
data->write_uint32(push_type);
data->write_ubyte(r2);
data->write_uint64(user_id);
data->write_string(platform1);
data->write_string(platform2);
data->write_int32(rewardEventType);
data->write_uint32(r7);
data->write_int32(r8);
data->write_string(json_buffer);
}
};
class bdMarketplaceInventory final : public bdTaskResult
{
public:
uint64_t m_playerId;
std::string unk;
uint32_t m_itemId;
uint32_t m_itemQuantity;
uint32_t m_itemXp;
std::string m_itemData;
uint32_t m_expireDateTime;
int64_t m_expiryDuration;
uint16_t m_collisionField;
uint32_t m_modDateTime;
void serialize(byte_buffer* data) override
{
data->write_uint64(m_playerId);
data->write_string(unk);
data->write_uint32(m_itemId);
data->write_uint32(m_itemQuantity);
data->write_uint32(m_itemXp);
data->write_blob(m_itemData);
data->write_uint32(m_expireDateTime);
data->write_int64(m_expiryDuration);
data->write_uint16(m_collisionField);
data->write_uint32(m_modDateTime);
}
};
class bdMarketplaceCurrency final : public bdTaskResult
{
public:
std::uint8_t m_currencyId;
std::uint32_t m_value;
void serialize(byte_buffer* data) override
{
data->write_ubyte(m_currencyId);
data->write_uint32(m_value);
}
};
}

View File

@ -1,6 +1,6 @@
#pragma once
//#define DW_DEBUG
#define DW_DEBUG
#include "game/types/demonware.hpp"
using namespace game::demonware;
@ -13,4 +13,6 @@ using namespace game::demonware;
#include "reply.hpp"
#include "service.hpp"
#include "loot/loot.hpp"
#include "services.hpp"

View File

@ -0,0 +1,621 @@
#include <std_include.hpp>
#include "../dw_include.hpp"
#include "game/game.hpp"
#include "utils/io.hpp"
#include "utils/json.hpp"
namespace demonware
{
namespace loot
{
const std::string& json_data_path = "iw7-mod/players2/user/loot/loot.json";
nlohmann::json json_buffer;
namespace csv
{
struct LootCsv
{
std::string file;
std::uint32_t index;
std::uint32_t quality;
std::uint32_t salvageReturned;
std::uint32_t cost;
};
struct LootCrateCsv
{
std::string file;
std::uint32_t index;
std::uint32_t cost;
std::uint32_t premiumCost;
std::uint32_t CODPointsSKU;
};
LootCsv weapon
{
.file = "mp/loot/iw7_weapon_loot_master.csv",
.index = 0,
.quality = 6,
.salvageReturned = 3,
.cost = 4,
};
LootCsv killstreak
{
.file = "mp/loot/iw7_killstreak_loot_master.csv",
.index = 0,
.quality = 2,
.salvageReturned = 3,
.cost = 4,
};
LootCsv consumable
{
.file = "mp/loot/iw7_consumable_loot_master.csv",
.index = 0,
.quality = 2,
.salvageReturned = 4,
.cost = 5,
};
LootCsv cosmetic_attachments
{
.file = "mp/loot/iw7_cosmetic_attachments_loot_master.csv",
.index = 0,
.quality = 2,
.salvageReturned = 4,
.cost = 5,
};
LootCsv cosmetic_calling_cards
{
.file = "mp/loot/iw7_cosmetic_calling_cards_loot_master.csv",
.index = 0,
.quality = 2,
.salvageReturned = 4,
.cost = 5,
};
LootCsv cosmetic_camos
{
.file = "mp/loot/iw7_cosmetic_camos_loot_master.csv",
.index = 0,
.quality = 2,
.salvageReturned = 4,
.cost = 5,
};
LootCsv cosmetic_emblems
{
.file = "mp/loot/iw7_cosmetic_emblems_loot_master.csv",
.index = 0,
.quality = 2,
.salvageReturned = 4,
.cost = 5,
};
LootCsv cosmetic_emotes
{
.file = "mp/loot/iw7_cosmetic_emotes_loot_master.csv",
.index = 0,
.quality = 2,
.salvageReturned = 4,
.cost = 5,
};
LootCsv cosmetic_heroes
{
.file = "mp/loot/iw7_cosmetic_heroes_loot_master.csv",
.index = 0,
.quality = 2,
.salvageReturned = 4,
.cost = 5,
};
LootCsv cosmetic_reticles
{
.file = "mp/loot/iw7_cosmetic_reticles_loot_master.csv",
.index = 0,
.quality = 2,
.salvageReturned = 4,
.cost = 5,
};
LootCsv cosmetic_rigs
{
.file = "mp/loot/iw7_cosmetic_rigs_loot_master.csv",
.index = 0,
.quality = 2,
.salvageReturned = 4,
.cost = 5,
};
LootCsv zombiefatefortune
{
.file = "cp/loot/iw7_zombiefatefortune_loot_master.csv",
.index = 0,
.quality = 2,
.salvageReturned = 0xFF,
.cost = 0xFF,
};
LootCrateCsv loot_crate
{
.file = "mp/loot/iw7_loot_crate_loot_master.csv",
.index = 0,
.cost = 3,
.premiumCost = 4,
.CODPointsSKU = 5,
};
}
std::vector<std::uint32_t> lootmap_weapon;
std::vector<std::uint32_t> lootmap_killstreak;
std::vector<std::uint32_t> lootmap_consumable;
std::vector<std::uint32_t> lootmap_cosmetic_attachments;
std::vector<std::uint32_t> lootmap_cosmetic_calling_cards;
std::vector<std::uint32_t> lootmap_cosmetic_camos;
std::vector<std::uint32_t> lootmap_cosmetic_emblems;
std::vector<std::uint32_t> lootmap_cosmetic_emotes;
std::vector<std::uint32_t> lootmap_cosmetic_heroes;
std::vector<std::uint32_t> lootmap_cosmetic_reticles;
std::vector<std::uint32_t> lootmap_cosmetic_rigs;
std::vector<std::uint32_t> lootmap_zombiefatefortune;
std::unordered_map<std::uint32_t, Item> m_lootmap;
struct LootCrate
{
std::uint32_t cost;
std::uint32_t premiumCost;
std::uint32_t salvageCost;
};
std::unordered_map<std::uint32_t, LootCrate> lootcrates;
void read_loot_csv(csv::LootCsv& csv, std::vector<std::uint32_t>& lootmap)
{
lootmap.clear();
const auto asset = game::DB_FindXAssetHeader(game::ASSET_TYPE_STRINGTABLE, csv.file.data(), 0).stringTable;
for (auto row = 0; row < asset->rowCount; row++)
{
const std::uint32_t id = std::atoi(game::StringTable_GetColumnValueForRow(asset, row, csv.index));
const std::uint32_t quality = std::atoi(game::StringTable_GetColumnValueForRow(asset, row, csv.quality));
std::uint32_t salvageReturned = 0;
std::uint32_t cost = 0;
if (csv.salvageReturned != 0xFF)
{
salvageReturned = std::atoi(game::StringTable_GetColumnValueForRow(asset, row, csv.salvageReturned));
}
if (csv.cost != 0xFF)
{
cost = std::atoi(game::StringTable_GetColumnValueForRow(asset, row, csv.cost));
}
const Item loot = { id, quality, salvageReturned, cost };
m_lootmap[id] = loot;
lootmap.push_back(id);
}
}
void read_weapon_csv()
{
read_loot_csv(csv::weapon, lootmap_weapon);
}
void read_killstreak_csv()
{
read_loot_csv(csv::killstreak, lootmap_killstreak);
}
void read_consumable_csv()
{
read_loot_csv(csv::consumable, lootmap_consumable);
}
void read_cosmetic_attachments_csv()
{
read_loot_csv(csv::cosmetic_attachments, lootmap_cosmetic_attachments);
}
void read_cosmetic_calling_cards_csv()
{
read_loot_csv(csv::cosmetic_calling_cards, lootmap_cosmetic_calling_cards);
}
void read_cosmetic_camos_csv()
{
read_loot_csv(csv::cosmetic_camos, lootmap_cosmetic_camos);
}
void read_cosmetic_emblems_csv()
{
read_loot_csv(csv::cosmetic_emblems, lootmap_cosmetic_emblems);
}
void read_cosmetic_emotes_csv()
{
read_loot_csv(csv::cosmetic_emotes, lootmap_cosmetic_emotes);
}
void read_cosmetic_heroes_csv()
{
read_loot_csv(csv::cosmetic_heroes, lootmap_cosmetic_heroes);
}
void read_cosmetic_reticles_csv()
{
read_loot_csv(csv::cosmetic_reticles, lootmap_cosmetic_reticles);
}
void read_cosmetic_rigs_csv()
{
read_loot_csv(csv::cosmetic_rigs, lootmap_cosmetic_rigs);
}
void read_zombiefatefortune_csv()
{
read_loot_csv(csv::zombiefatefortune, lootmap_zombiefatefortune);
}
void read_loot_crate_csv()
{
lootcrates.clear();
const auto asset = game::DB_FindXAssetHeader(game::ASSET_TYPE_STRINGTABLE, csv::loot_crate.file.data(), 0).stringTable;
for (auto row = 0; row < asset->rowCount; row++)
{
const std::uint32_t id = std::atoi(game::StringTable_GetColumnValueForRow(asset, row, csv::loot_crate.index));
const std::uint32_t cost = std::atoi(game::StringTable_GetColumnValueForRow(asset, row, csv::loot_crate.cost));
const std::uint32_t premiumCost = std::atoi(game::StringTable_GetColumnValueForRow(asset, row, csv::loot_crate.premiumCost));
const std::uint32_t CODPointsSKU = std::atoi(game::StringTable_GetColumnValueForRow(asset, row, csv::loot_crate.CODPointsSKU));
lootcrates[id] = { cost, premiumCost, CODPointsSKU };
}
}
void cache_loot()
{
static bool once = false;
if (once) return;
once = true;
m_lootmap.clear();
// mp
read_weapon_csv();
read_killstreak_csv();
read_consumable_csv();
read_cosmetic_attachments_csv();
read_cosmetic_calling_cards_csv();
read_cosmetic_camos_csv();
read_cosmetic_emblems_csv();
read_cosmetic_emotes_csv();
read_cosmetic_heroes_csv();
read_cosmetic_reticles_csv();
read_cosmetic_rigs_csv();
// cp
read_zombiefatefortune_csv();
// crates
read_loot_crate_csv();
}
// Brought to you by ChatGPT
std::vector<Item> get_random_loot_from_map(std::vector<std::uint32_t>& lootmap, const size_t itemAmount, const float luckFactor, std::uint32_t quaranteedQuality = 0)
{
// Edge case: if the vector is empty
if (lootmap.empty()) {
throw std::runtime_error("Lootmap is empty");
}
// Edge case: if itemAmount is more than the number of available items
if (itemAmount > lootmap.size()) {
throw std::runtime_error("Item amount exceeds the number of available items");
}
// Validate the luckFactor (should be positive)
if (luckFactor <= 0) {
throw std::invalid_argument("Luck factor must be greater than zero");
}
// Calculate the total weight based on adjusted rarity values
double totalWeight = 0;
std::vector<double> adjustedWeights(lootmap.size());
for (size_t i = 0; i < lootmap.size(); ++i) {
if (!get_loot(lootmap[i]).quality) continue;
// Calculate weight as inverse of rarity, adjusted by luckFactor
adjustedWeights[i] = 1.0 / std::pow(get_loot(lootmap[i]).quality, luckFactor);
totalWeight += adjustedWeights[i];
}
assert(adjustedWeights.size() >= itemAmount);
// Set up the random number generator
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<> dis(0.0, totalWeight);
std::vector<Item> selectedItems;
// Select items
while (selectedItems.size() < itemAmount) {
double randomValue = dis(gen);
double cumulativeWeight = 0;
// Find the item corresponding to the random value
for (size_t i = 0; i < lootmap.size(); ++i) {
cumulativeWeight += adjustedWeights[i];
if (randomValue < cumulativeWeight) {
if (quaranteedQuality && get_loot(lootmap[i]).quality < quaranteedQuality)
continue;
else
quaranteedQuality = 0;
// Add item to the result if it's not already selected
if (std::find(selectedItems.begin(), selectedItems.end(), get_loot(lootmap[i])) == selectedItems.end()) {
selectedItems.push_back(get_loot(lootmap[i]));
break;
}
}
}
}
return selectedItems;
}
std::vector<std::uint32_t> combined_mp_lootmaps;
std::vector<std::uint32_t> combine_mp_lootmaps()
{
std::vector<std::uint32_t>& lootmap = combined_mp_lootmaps;
if (!combined_mp_lootmaps.empty())
{
return combined_mp_lootmaps;
}
lootmap.insert(lootmap.end(), lootmap_weapon.begin(), lootmap_weapon.end());
lootmap.insert(lootmap.end(), lootmap_killstreak.begin(), lootmap_killstreak.end());
lootmap.insert(lootmap.end(), lootmap_consumable.begin(), lootmap_consumable.end());
lootmap.insert(lootmap.end(), lootmap_cosmetic_attachments.begin(), lootmap_cosmetic_attachments.end());
lootmap.insert(lootmap.end(), lootmap_cosmetic_calling_cards.begin(), lootmap_cosmetic_calling_cards.end());
lootmap.insert(lootmap.end(), lootmap_cosmetic_camos.begin(), lootmap_cosmetic_camos.end());
lootmap.insert(lootmap.end(), lootmap_cosmetic_emblems.begin(), lootmap_cosmetic_emblems.end());
lootmap.insert(lootmap.end(), lootmap_cosmetic_emotes.begin(), lootmap_cosmetic_emotes.end());
lootmap.insert(lootmap.end(), lootmap_cosmetic_heroes.begin(), lootmap_cosmetic_heroes.end());
lootmap.insert(lootmap.end(), lootmap_cosmetic_reticles.begin(), lootmap_cosmetic_reticles.end());
lootmap.insert(lootmap.end(), lootmap_cosmetic_rigs.begin(), lootmap_cosmetic_rigs.end());
return lootmap;
}
std::vector<std::uint32_t> get_all_lootmaps()
{
cache_loot();
std::vector<std::uint32_t> lootmap = combine_mp_lootmaps();
lootmap.insert(lootmap.end(), lootmap_zombiefatefortune.begin(), lootmap_zombiefatefortune.end());
return lootmap;
}
std::vector<Item> all_loot;
std::vector<Item> get_all_loot()
{
if (!all_loot.empty())
{
return all_loot;
}
std::vector<Item>& items = all_loot;
auto lootmap = get_all_lootmaps();
for (size_t i = 0; i < lootmap.size(); i++)
{
items.push_back(get_loot(lootmap[i]));
}
return items;
};
std::vector<Item> get_all_loot_owned()
{
auto lootmap = get_all_lootmaps();
std::vector<Item> items{};
for (size_t i = 0; i < lootmap.size(); i++)
{
if (get_item_balance(lootmap[i]))
{
items.push_back(get_loot(lootmap[i]));
}
}
return items;
}
Item get_loot(const std::uint32_t item_id)
{
if (m_lootmap.contains(item_id))
{
return m_lootmap[item_id];
}
return {};
}
std::vector<Item> get_random_loot_CommonCrate(const float scale = 1.0f, std::uint32_t quality = 0)
{
std::vector<std::uint32_t> lootmap = combine_mp_lootmaps();
return get_random_loot_from_map(lootmap, 3, scale, quality);
}
std::vector<Item> get_random_loot_RareCrate()
{
return get_random_loot_CommonCrate(0.34f, 2);
}
std::vector<Item> get_random_loot_ZombieCrate(const float scale = 1.0f, std::uint32_t quality = 0)
{
std::vector<std::uint32_t> lootmap = combine_mp_lootmaps();
auto mp = get_random_loot_from_map(lootmap, 2, scale);
auto cp = get_random_loot_from_map(lootmap_zombiefatefortune, 1, scale);
std::vector<Item> items;
items.push_back(mp[0]);
items.push_back(mp[1]);
items.push_back(cp[0]);
return items;
}
std::vector<Item> get_random_loot_ZombieRareCrate()
{
return get_random_loot_ZombieCrate(0.34f, 2);
}
std::vector<Item> get_random_loot_ZombieCardPack(const float scale = 1.0f, std::uint32_t quality = 0)
{
return get_random_loot_from_map(lootmap_zombiefatefortune, 3, scale, quality);
}
std::vector<Item> get_random_loot_ZombieRareCardPack()
{
return get_random_loot_ZombieCardPack(0.34f, 2);
}
std::vector<Item> get_random_loot(const std::uint32_t lootbox_id)
{
cache_loot();
switch (lootbox_id)
{
case 70000: // CommonCrate
return get_random_loot_CommonCrate();
case 70001: // RareCrate
return get_random_loot_RareCrate();
case 70002: // ZombieCrate
return get_random_loot_ZombieCrate();
case 70003: // ZombieRareCrate
return get_random_loot_ZombieRareCrate();
case 70004: // ZombieCardPack
return get_random_loot_ZombieCardPack();
case 70005: // ZombieRareCardPack
return get_random_loot_ZombieRareCardPack();
default:
printf("[DW]: Missing LootCrate logic for %d, using CommonCrate\n", lootbox_id);
return {};
}
}
std::uint32_t get_lootcrate_cost(const std::uint32_t id, const std::uint32_t type)
{
if (lootcrates.contains(id))
{
switch(type)
{
case CurrencyType::keys:
return lootcrates[id].cost;
break;
case CurrencyType::codpoints:
return lootcrates[id].premiumCost;
break;
case CurrencyType::salvage:
return lootcrates[id].salvageCost;
break;
}
}
return 0;
}
template <typename T> void json_put(nlohmann::json& field, T value)
{
field = value;
}
template <typename T> void json_add(nlohmann::json& field, T value)
{
if (!field.is_null())
{
field = field.get<T>();
}
else
{
field = value;
}
}
template <typename T> T json_read(nlohmann::json& field)
{
if (!field.is_null())
{
return field.get<T>();
}
else
{
return T{};
}
}
void save_json_data()
{
auto dump = json_buffer.dump(4);
utils::io::write_file(json_data_path, dump);
}
void read_json_data()
{
if (!json_buffer.empty())
{
return;
}
if (utils::io::file_exists(json_data_path))
{
auto data = utils::io::read_file(json_data_path);
json_buffer = nlohmann::json::parse(data);
return;
}
json_buffer = {};
set_currency_balance(CurrencyType::keys, 9999999);
set_currency_balance(CurrencyType::salvage, 9999);
set_currency_balance(CurrencyType::codpoints, 99999);
save_json_data();
}
void set_item_balance(const std::uint32_t item_id, const std::uint32_t amount)
{
json_put(json_buffer["Loot"][std::to_string(item_id)]["Balance"], amount);
}
std::uint32_t get_item_balance(const std::uint32_t item_id)
{
read_json_data();
return json_read<std::uint32_t>(json_buffer["Loot"][std::to_string(item_id)]["Balance"]);
}
void set_currency_balance(const std::uint32_t currency_id, const std::uint32_t amount)
{
json_put(json_buffer["Currency"][std::to_string(currency_id)]["Balance"], amount);
}
std::uint32_t get_currency_balance(const std::uint32_t currency_id)
{
read_json_data();
return json_read<std::uint32_t>(json_buffer["Currency"][std::to_string(currency_id)]["Balance"]);
}
void save()
{
save_json_data();
}
}
}

View File

@ -0,0 +1,42 @@
#pragma once
namespace demonware
{
namespace loot
{
struct Item
{
std::uint32_t id;
std::uint32_t quality;
std::uint32_t salvageReturned;
std::uint32_t cost;
// Define equality operator for Item
bool operator==(const Item& other) const {
return id == other.id && quality == other.quality && salvageReturned == other.salvageReturned && cost == other.cost;
}
};
enum CurrencyType
{
keys = 11,
salvage = 12,
codpoints = 20,
};
std::vector<Item> get_random_loot(const std::uint32_t lootbox_id);
std::vector<Item> get_all_loot();
std::vector<Item> get_all_loot_owned();
Item get_loot(const std::uint32_t item_id);
std::uint32_t get_lootcrate_cost(const std::uint32_t id, const std::uint32_t type);
void set_item_balance(const std::uint32_t item_id, const std::uint32_t amount);
std::uint32_t get_item_balance(const std::uint32_t item_id);
void set_currency_balance(const std::uint32_t currency_id, const std::uint32_t amount);
std::uint32_t get_currency_balance(const std::uint32_t currency_id);
void save();
}
}

View File

@ -5,6 +5,8 @@
namespace demonware
{
uint64_t service_reply::transaction_id = 0;
std::string unencrypted_reply::data()
{
byte_buffer result;

View File

@ -99,10 +99,11 @@ namespace demonware
{
}
static uint64_t transaction_id;
uint64_t send()
{
static uint64_t id = 0x0000000000000000;
const auto transaction_id = ++id;
transaction_id++;
byte_buffer buffer;
buffer.write_uint64(transaction_id);
@ -133,6 +134,36 @@ namespace demonware
return transaction_id;
}
uint64_t send_struct()
{
transaction_id++;
byte_buffer buffer;
buffer.write_uint64(transaction_id);
buffer.write_uint32(this->error_);
buffer.write_ubyte(this->type_);
if (!this->error_)
{
if (!this->objects_.empty())
{
for (auto& object : this->objects_)
{
object->serialize(&buffer);
}
this->objects_.clear();
}
}
else
{
buffer.write_uint64(transaction_id);
}
this->reply_.send(&buffer, true);
return transaction_id;
}
template<typename T>
void add(std::unique_ptr<T>& object)
{
@ -140,6 +171,11 @@ namespace demonware
this->objects_.emplace_back(std::move(object));
}
void set_error(uint32_t err)
{
this->error_ = err;
}
private:
uint8_t type_;
uint32_t error_;

View File

@ -53,7 +53,7 @@ namespace demonware
if (it != this->tasks_.end())
{
#ifdef DW_DEBUG
printf("[DW] %s: executing task '%d'\n", name_.data(), this->task_id_);
printf("[DW] %s: executing task '%d' (transaction ID: %llu)\n", name_.data(), this->task_id_, service_reply::transaction_id + 1);
#endif
it->second(server, &buffer);

View File

@ -7,28 +7,65 @@ namespace demonware
{
bdMarketingComms::bdMarketingComms() : service(104, "bdMarketingComms")
{
this->register_task(1, &bdMarketingComms::getMessages);
this->register_task(4, &bdMarketingComms::reportFullMessagesViewed);
this->register_task(6, &bdMarketingComms::unk6);
this->register_task(6, &bdMarketingComms::getMessages);
}
void bdMarketingComms::getMessages(service_server* server, byte_buffer* /*buffer*/) const
void bdMarketingComms::reportFullMessagesViewed(service_server* server, byte_buffer* buffer) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
reply.send();
}
void bdMarketingComms::reportFullMessagesViewed(service_server* server, byte_buffer* /*buffer*/) const
#pragma pack(push, 1)
struct bdCommsGetMessagesRequest
{
// TODO:
auto reply = server->create_reply(this->task_id());
reply.send();
}
char __pad0[23];
}; static_assert(sizeof(bdCommsGetMessagesRequest) == 23);
void bdMarketingComms::unk6(service_server* server, byte_buffer* /*buffer*/) const
struct unk_s
{
// TODO:
server->create_reply(this->task_id(), BD_NO_FILE).send();
char __pad0[17];
unsigned char unk;
};
struct bdCommsGetMessagesResponse
{
unk_s unk[1];
}; //static_assert(sizeof(bdCommsGetMessagesResponse) == 180);
#pragma pack(pop)
void bdMarketingComms::getMessages(service_server* server, byte_buffer* buffer) const
{
bdCommsGetMessagesRequest request{};
class bdCommsGetMessagesResult final : public bdTaskResult
{
public:
bdCommsGetMessagesResponse response;
void serialize(byte_buffer* data) override
{
data->write_struct(&response, sizeof(bdCommsGetMessagesResponse));
}
};
//buffer->read_struct(&request, sizeof(bdCommsGetMessagesRequest));
auto reply = server->create_reply(this->task_id());
auto info = std::make_unique<bdCommsGetMessagesResult>();
unsigned char unk[18]{ 0x0A, 0x10, 0x08, 0x00, 0x12, 0x00, 0x1A, 0x00, 0x22, 0x00, 0x2A, 0x00, 0x32, 0x00, 0x38, 0x00, 0x40, 0x01 };
for (auto i = 0; i < 1; i++)
{
memcpy(info->response.unk[i].__pad0, unk, 17);
}
info->response.unk[0].unk = 0x01;
reply.add(info);
reply.send_struct();
}
}

View File

@ -8,8 +8,7 @@ namespace demonware
bdMarketingComms();
private:
void getMessages(service_server* server, byte_buffer* buffer) const;
void reportFullMessagesViewed(service_server* server, byte_buffer* buffer) const;
void unk6(service_server* server, byte_buffer* buffer) const;
void getMessages(service_server* server, byte_buffer* buffer) const;
};
}

View File

@ -3,6 +3,10 @@
#include <utils/io.hpp>
#include "steam/steam.hpp"
#include "utils/json.hpp"
namespace demonware
{
bdMarketplace::bdMarketplace() : service(80, "bdMarketplace")
@ -14,26 +18,72 @@ namespace demonware
this->register_task(58, &bdMarketplace::validateInventoryItemsToken);
this->register_task(60, &bdMarketplace::steamProcessDurable);
this->register_task(85, &bdMarketplace::steamProcessDurableV2);
this->register_task(122, &bdMarketplace::purchaseSkus);
this->register_task(106, &bdMarketplace::purchaseSkus);
this->register_task(130, &bdMarketplace::getBalance);
this->register_task(132, &bdMarketplace::getBalanceV2);
this->register_task(165, &bdMarketplace::getInventoryPaginated);
this->register_task(193, &bdMarketplace::putPlayersInventoryItems);
this->register_task(199, &bdMarketplace::pawnItems);
this->register_task(232, &bdMarketplace::getEntitlements);
}
void bdMarketplace::startExchangeTransaction(service_server* server, byte_buffer* /*buffer*/) const
void bdMarketplace::startExchangeTransaction(service_server* server, byte_buffer* buffer) const
{
// TODO:
class bdStartExchangeTransactionResult final : public bdTaskResult
{
public:
std::string m_exchangeTransactionId;
void serialize(byte_buffer* data) override
{
data->write_string(m_exchangeTransactionId);
}
};
std::string platform;
buffer->read_string(&platform);
auto reply = server->create_reply(this->task_id());
auto info = std::make_unique<bdStartExchangeTransactionResult>();
info->m_exchangeTransactionId = std::to_string(service_reply::transaction_id);
reply.add(info);
reply.send();
}
void bdMarketplace::purchaseOnSteamInitialize(service_server* server, byte_buffer* /*buffer*/) const
void bdMarketplace::purchaseOnSteamInitialize(service_server* server, byte_buffer* buffer) const
{
// TODO:
/*class bdPurchaseInitializeResult final : public bdTaskResult
{
public:
std::uint64_t m_orderId;
void serialize(byte_buffer* data) override
{
data->write_uint64(m_orderId);
}
};
std::string platform;
std::string m_exchangeTransactionId;
std::uint32_t skuId;
std::string language;
std::uint64_t userID;
std::string contextUser;
buffer->read_string(&platform);
buffer->read_string(&m_exchangeTransactionId);
buffer->read_uint32(&skuId);
buffer->read_string(&language);
buffer->read_uint64(&userID);
buffer->read_string(&contextUser);
auto reply = server->create_reply(this->task_id());
reply.send();
auto info = std::make_unique<bdPurchaseInitializeResult>();
info->m_orderId = 0; // would need to emulate steam microtransaction stuff...
reply.add(info);
reply.send();*/
server->create_reply(this->task_id(), BD_MARKETPLACE_STEAM_NOT_APPROVED).send();
}
void bdMarketplace::purchaseOnSteamFinalize(service_server* server, byte_buffer* /*buffer*/) const
@ -64,18 +114,133 @@ namespace demonware
reply.send();
}
void bdMarketplace::steamProcessDurableV2(service_server* server, byte_buffer* /*buffer*/) const
void bdMarketplace::steamProcessDurableV2(service_server* server, byte_buffer* buffer) const
{
std::string platform;
buffer->read_string(&platform);
std::string unk1;
buffer->read_string(&unk1);
std::uint32_t unk2;
buffer->read_uint32(&unk2);
std::uint64_t unk3;
buffer->read_uint64(&unk3);
std::string unk4;
buffer->read_string(&unk4);
// TODO:
auto reply = server->create_reply(this->task_id());
reply.send();
}
void bdMarketplace::purchaseSkus(service_server* server, byte_buffer* /*buffer*/) const
void bdMarketplace::purchaseSkus(service_server* server, byte_buffer* buffer) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
reply.send();
std::string platform, clientTransactionId;
uint32_t numSkuIds;
uint32_t skuIds[4]{};
uint32_t numQuanities;
uint32_t skuQuanities[4]{};
uint32_t numMaxResults;
uint32_t numDiscounts;
uint32_t skuDiscounts[4]{};
uint32_t numCouponRecipients;
// coupon data here
uint32_t numCouponMetadata;
// coupon metadata here
uint8_t customSourceType;
bool ignoreEntitlements;
buffer->read_string(&platform);
buffer->read_string(&clientTransactionId);
buffer->read_uint32(&numSkuIds);
assert(numSkuIds < 4);
for (auto i = 0u; i < numSkuIds; i++)
{
buffer->read_uint32(&skuIds[i]);
}
buffer->read_uint32(&numQuanities);
assert(numQuanities < 4);
for (auto i = 0u; i < numQuanities; i++)
{
buffer->read_uint32(&skuQuanities[i]);
}
buffer->read_uint32(&numMaxResults);
buffer->read_uint32(&numDiscounts);
assert(numDiscounts < 4);
for (auto i = 0u; i < numDiscounts; i++)
{
buffer->read_uint32(&skuDiscounts[i]);
}
buffer->read_uint32(&numCouponRecipients);
assert(numCouponRecipients == 0);
buffer->read_uint32(&numCouponMetadata);
assert(numCouponMetadata == 0);
buffer->read_ubyte(&customSourceType);
buffer->read_bool(&ignoreEntitlements);
assert(numSkuIds == 1);
auto id = skuIds[0];
auto num = skuQuanities[0];
// schematic buy comes through here
if (id < 70000 || id > 75223)
{
const auto item = loot::get_loot(id);
const auto cost = item.cost;
const auto item_balance = loot::get_item_balance(id);
const auto currency_balance = loot::get_currency_balance(loot::CurrencyType::salvage);
if (cost > currency_balance)
{
server->create_reply(this->task_id(), BD_MARKETPLACE_ERROR).send();
return;
}
loot::set_currency_balance(loot::CurrencyType::salvage, currency_balance - cost);
loot::set_item_balance(id, item_balance + num);
loot::save();
server->create_reply(this->task_id(), BD_NO_ERROR).send();
return;
}
int currency_id = loot::CurrencyType::keys;
if (id > 70000 && (id - 5000) >= 70000)
{
id = id - 5000;
currency_id = loot::CurrencyType::codpoints;
}
const auto current_balance = loot::get_currency_balance(currency_id);
const auto cost = loot::get_lootcrate_cost(id, currency_id);
if (cost > current_balance)
{
server->create_reply(this->task_id(), BD_MARKETPLACE_ERROR).send();
return;
}
loot::set_currency_balance(currency_id, current_balance - cost);
loot::set_item_balance(id, loot::get_item_balance(id) + num);
loot::save();
server->create_reply(this->task_id(), BD_NO_ERROR).send();
}
void bdMarketplace::getBalance(service_server* server, byte_buffer* /*buffer*/) const
@ -85,17 +250,83 @@ namespace demonware
reply.send();
}
void bdMarketplace::getBalanceV2(service_server* server, byte_buffer* /*buffer*/) const
void bdMarketplace::getBalanceV2(service_server* server, byte_buffer* buffer) const
{
// TODO:
std::string platform;
buffer->read_string(&platform);
std::uint32_t maxNumResults;
buffer->read_uint32(&maxNumResults);
auto reply = server->create_reply(this->task_id());
std::uint32_t numResults = 0;
auto add_result = [&](std::uint8_t id, std::uint32_t value)
{
if (numResults >= maxNumResults) return;
auto info = std::make_unique<bdMarketplaceCurrency>();
info->m_currencyId = id;
info->m_value = value;
reply.add(info);
numResults++;
};
add_result(loot::CurrencyType::keys, loot::get_currency_balance(loot::CurrencyType::keys));
add_result(loot::CurrencyType::salvage, loot::get_currency_balance(loot::CurrencyType::salvage));
add_result(loot::CurrencyType::codpoints, loot::get_currency_balance(loot::CurrencyType::codpoints));
reply.send();
}
void bdMarketplace::getInventoryPaginated(service_server* server, byte_buffer* /*buffer*/) const
void bdMarketplace::getInventoryPaginated(service_server* server, byte_buffer* buffer) const
{
// TODO:
std::string platform;
std::uint32_t itemsPerPage, pageNum;
buffer->read_string(&platform);
buffer->read_uint32(&pageNum);
buffer->read_uint32(&itemsPerPage);
assert(itemsPerPage);
static unsigned int paginated_index = 0;
auto reply = server->create_reply(this->task_id());
auto all_loot = loot::get_all_loot_owned();
const unsigned int index = paginated_index;
for (size_t i = 0; i < all_loot.size() - index; i++)
{
const auto loot = all_loot[paginated_index];
const auto balance = loot::get_item_balance(loot.id);
assert(balance);
auto info = std::make_unique<bdMarketplaceInventory>();
info->m_playerId = steam::SteamUser()->GetSteamID().bits;
info->unk = "";
info->m_itemId = loot.id;
info->m_itemQuantity = balance;
info->m_itemXp = 0;
info->m_itemData = "";
info->m_expireDateTime = 0;
info->m_expiryDuration = 0;
info->m_collisionField = 0;
info->m_modDateTime = 0;
reply.add(info);
paginated_index++;
if (i >= itemsPerPage - 1)
{
reply.send();
return;
}
}
paginated_index = 0;
reply.send();
}
@ -106,6 +337,48 @@ namespace demonware
reply.send();
}
void bdMarketplace::pawnItems(service_server* server, byte_buffer* buffer) const
{
std::string platform, clientTransactionId;
std::uint32_t numItemsToPawn;
buffer->read_string(&platform);
buffer->read_string(&clientTransactionId);
auto currency = loot::get_currency_balance(loot::CurrencyType::salvage);
buffer->read_uint32(&numItemsToPawn);
for (auto i = 0u; i < numItemsToPawn; i++)
{
std::uint32_t id, nextBalance;
std::uint16_t unk2; // collision?
buffer->read_uint32(&id);
buffer->read_uint32(&nextBalance);
buffer->read_uint16(&unk2);
const auto loot = loot::get_loot(id);
const auto amount = loot::get_item_balance(loot.id);
if (amount == 1) // why are you trying to pawn when we only have 1...
{
continue;
}
loot::set_item_balance(loot.id, nextBalance);
currency += loot.salvageReturned;
#ifdef DW_DEBUG
printf("[DW]: pawning %d for %d salvage\n", loot.id, loot.salvageReturned);
#endif
}
loot::set_currency_balance(loot::CurrencyType::salvage, currency);
loot::save();
auto reply = server->create_reply(this->task_id());
reply.send();
}
void bdMarketplace::getEntitlements(service_server* server, byte_buffer* /*buffer*/) const
{
// TODO:

View File

@ -20,6 +20,7 @@ namespace demonware
void getBalanceV2(service_server* server, byte_buffer* buffer) const;
void getInventoryPaginated(service_server* server, byte_buffer* buffer) const;
void putPlayersInventoryItems(service_server* server, byte_buffer* buffer) const;
void pawnItems(service_server* server, byte_buffer* buffer) const;
void getEntitlements(service_server* server, byte_buffer* buffer) const;
};
}

View File

@ -71,7 +71,7 @@ namespace demonware
{
auto result = std::make_unique<bdPerformanceValue>();
result->user_id = steam::SteamUser()->GetSteamID().bits;
result->performance = 10;
result->performance = 10.0f;
auto reply = server->create_reply(this->task_id());
reply.add(result);

View File

@ -1,6 +1,13 @@
#include <std_include.hpp>
#include "../dw_include.hpp"
#include "steam/steam.hpp"
#include "utils/json.hpp"
#include "../loot/loot.hpp"
//#define VERIFY_CRATE_AMOUNT
namespace demonware
{
bdReward::bdReward() : service(139, "bdReward")
@ -32,10 +39,200 @@ namespace demonware
reply.send();
}
void bdReward::reportRewardEvents(service_server* server, byte_buffer* /*buffer*/) const
void bdReward::reportRewardEvents(service_server* server, byte_buffer* buffer) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
reply.send();
std::string platform;
unsigned short numEvents;
std::int32_t rewardEventType;
std::string json_buffer;
buffer->read_string(&platform);
buffer->read_uint16(&numEvents);
buffer->read_int32(&rewardEventType); // none 0, json 1
assert(numEvents == 1);
buffer->read_string(&json_buffer);
const auto send = [&](nlohmann::json& json_reply)
{
auto result = std::make_unique<bdRewardEvent>();
result->push_type = BD_REWARD_EVENT_MESSAGE;
result->r2 = 1;
result->user_id = steam::SteamUser()->GetSteamID().bits;
result->platform1 = "steam";
result->platform2 = platform;
result->rewardEventType = rewardEventType;
result->r7 = 1;
result->r8 = 1;
result->json_buffer = json_reply.dump(0);
byte_buffer reply_buffer;
result->serialize(&reply_buffer);
auto reply = server->create_message(BD_LOBBY_SERVICE_PUSH_MESSAGE);
reply.send(&reply_buffer, true);
};
nlohmann::json json;
json = json.parse(json_buffer);
const auto action = json["Action"].get<std::string>();
if (action == "DailyLogin")
{
#ifdef DW_DEBUG
printf("[DW]: daily login requested...\n");
#endif
nlohmann::json json_reply;
json_reply["Action"] = "DailyLoginResponse";
json_reply["LoginDayCountSP"] = -1;
json_reply["FirstTimeTodaySP"] = false;
json_reply["LoginDayCount"] = 1;
json_reply["FirstTimeToday"] = false;
// Packs
json_reply["BasicPacks"] = nlohmann::json::value_type::array();
json_reply["BasicPacks"][0]["Currencies"] = nlohmann::json::value_type::object();
json_reply["BasicPacks"][0]["Currencies"]["11"] = 0;
json_reply["BasicPacks"][0]["Currencies"]["12"] = 0;
json_reply["BasicPacks"][0]["Currencies"]["20"] = 0;
json_reply["BasicPacks"][0]["Items"] = nlohmann::json::value_type::array();
json_reply["BasicPacks"][0]["Id"] = 200018;
json_reply["SeasonPassPacks"] = nlohmann::json::value_type::array();
// Extra Items
json_reply["ExtraItems"] = nlohmann::json::value_type::array();
// Items
json_reply["Items"] = nlohmann::json::value_type::array();
// mp/loot/iw7_loot_crate_loot_master.csv
json_reply["Items"][0]["ItemId"] = 70005; // ZombieRareCardPack
json_reply["Items"][0]["Collision"] = 0; // not sure what this does
json_reply["Items"][0]["Balance"] = 999;
loot::set_item_balance(70005, 999);
// Currencies
json_reply["Currencies"] = nlohmann::json::value_type::array();
// Keys
json_reply["Currencies"][0]["CurrencyId"] = loot::CurrencyType::keys;
json_reply["Currencies"][0]["Balance"] = loot::get_currency_balance(loot::CurrencyType::keys);
// Salvage
json_reply["Currencies"][1]["CurrencyId"] = loot::CurrencyType::salvage;
json_reply["Currencies"][1]["Balance"] = loot::get_currency_balance(loot::CurrencyType::salvage);
// CodPoints
json_reply["Currencies"][2]["CurrencyId"] = loot::CurrencyType::codpoints;
json_reply["Currencies"][2]["Balance"] = loot::get_currency_balance(loot::CurrencyType::codpoints);
json_reply["ClientTx"] = json["ClientTx"];
send(json_reply);
}
else if (action == "ClaimLootCrates")
{
#ifdef DW_DEBUG
printf("[DW]: supply drop open requested...\n");
#endif
nlohmann::json json_reply;
json_reply["Action"] = "ClaimLootCratesResponse";
json_reply["Packs"] = nlohmann::json::value_type::array();
json_reply["Items"] = nlohmann::json::value_type::array();
json_reply["Currencies"] = nlohmann::json::value_type::array();
// Items, Packs
const auto rule_id = json["RuleId"].is_number_integer() ? json["RuleId"].get<int>() : 0;
const auto crate_id = 70000 + rule_id;
int itemidx = 0;
auto crate_balance = loot::get_item_balance(crate_id);
#ifdef VERIFY_CRATE_AMOUNT
if (!crate_balance)
{
server->create_reply(this->task_id(), BD_MARKETPLACE_ERROR).send();
return;
}
#else
if (!crate_balance)
{
crate_balance = 1;
}
#endif
const auto new_crate_balance = crate_balance - 1;
json_reply["Items"][itemidx]["ItemId"] = crate_id;
json_reply["Items"][itemidx]["Collision"] = 0; // not sure what this does
json_reply["Items"][itemidx]["Balance"] = new_crate_balance;
itemidx++;
loot::set_item_balance(crate_id, new_crate_balance);
auto loot = loot::get_random_loot(crate_id);
for (auto i = 0; i < loot.size(); i++)
{
const auto item_id = loot[i].id;
json_reply["Packs"][i] = item_id;
const auto balance = loot::get_item_balance(item_id) + 1;
loot::set_item_balance(item_id, balance);
json_reply["Items"][i + itemidx]["ItemId"] = item_id;
json_reply["Items"][i + itemidx]["Collision"] = 0;
json_reply["Items"][i + itemidx]["Balance"] = balance;
#ifdef DW_DEBUG
printf("[DW]: loot %d\n", item_id);
#endif
}
loot::save();
json_reply["Error"] = "";
json_reply["ClientTx"] = json["ClientTx"];
send(json_reply);
}
else if (action == "StartMission")
{
printf("%s\n", json_buffer.data());
//const auto match_id = json["MatchId"].get<int>();
//const auto mission_set_instance_id = json["MissionSetInstanceId"].get<unsigned int>();
}
else if (action == "StartMissionSet")
{
printf("%s\n", json_buffer.data());
//const auto mission_set_id = json["MissionSetId"].get<unsigned int>();
}
else if (action == "EndMission")
{
printf("%s\n", json_buffer.data());
}
else if (action == "EndMissionSet")
{
printf("%s\n", json_buffer.data());
}
else if (action == "ResetMissions")
{
printf("%s\n", json_buffer.data());
}
else
{
printf("[DW]: unhandled reward action \"%s\"...\n", action.data());
}
server->create_reply(this->task_id(), BD_NO_ERROR).send();
}
}

View File

@ -20,7 +20,7 @@ namespace demonware
this->register_task(14, &bdStats::writeServerValidatedStats);
}
void bdStats::writeStats(service_server* server, byte_buffer* /*buffer*/) const
void bdStats::writeStats(service_server* server, byte_buffer* buffer) const
{
// TODO:
auto reply = server->create_reply(this->task_id());
@ -104,8 +104,11 @@ namespace demonware
reply.send();
}
void bdStats::writeServerValidatedStats(service_server* server, byte_buffer* /*buffer*/) const
void bdStats::writeServerValidatedStats(service_server* server, byte_buffer* buffer) const
{
std::string blob;
buffer->read_blob(&blob);
// TODO:
auto reply = server->create_reply(this->task_id());
reply.send();

View File

@ -21,6 +21,125 @@ namespace game::demonware
DW_NET_STARTED_ONLINE = 0x6,
};
enum bdEventType
{
BD_NEW_NOTIFICATION = 0x1,
BD_FRIENDSHIP_PROPOSAL = 0x2,
BD_TEAM_PROPOSAL = 0x3,
BD_FRIEND_CONNECTED = 0x4,
BD_FRIEND_DISCONNECTED = 0x5,
BD_SESSION_INVITATION = 0x6,
BD_CHANNEL_CHAT_BROADCAST_MSG = 0x7,
BD_CHANNEL_CHAT_WHISPER_MSG = 0x8,
BD_CHANNEL_USER_SUBSCRIBED = 0x9,
BD_CHANNEL_USER_UNSUBSCRIBED = 0xA,
BD_TEAMMEMBER_CONNECTED = 0xB,
BD_TEAMMEMBER_DISCONNECTED = 0xC,
BD_FRIEND_RICH_PRESENCE_UPDATED = 0xD,
BD_FRIEND_CHAT_MSG = 0xF,
BD_TEAM_CHAT_MSG = 0x10,
BD_NOTIFY_LEAVE = 0x11,
BD_NEW_MAIL = 0x12,
BD_CHALLENGES_RECEIVED = 0x13,
BD_ASYNCHRONOUS_RESULT = 0x14,
BD_GLOBAL_INSTANT_MESSAGE = 0x15,
BD_CHANNEL_CHAT_BROADCAST_MSG_V2 = 0x16,
BD_CHANNEL_CHAT_WHISPER_MSG_V2 = 0x17,
BD_CHANNEL_USER_SUBSCRIBED_V2 = 0x18,
BD_CHANNEL_USER_UNSUBSCRIBED_V2 = 0x19,
BD_CHANNEL_USER_MUTED_V2 = 0x1A,
BD_CHANNEL_USER_PROMOTED_V2 = 0x1B,
BD_CHANNEL_USER_KICKED_V2 = 0x1C,
BD_MULTIPLE_LOGONS = 0x1D,
BD_PLAYER_BANNED = 0x1E,
BD_CHANNEL_USER_PROMOTED = 0x1F,
BD_CHANNEL_USER_KICKED = 0x20,
BD_FEATURE_BAN = 0x21,
BD_GMSG_GROUP_MESSAGE = 0x22,
BD_GMSG_BROADCAST = 0x23,
BD_TENCENT_AAS_RECORD = 0x24,
BD_NOT_WHITE_LISTED = 0x26,
BD_CHANNEL_USER_MUTED = 0x27,
BD_STABILISED = 0x28,
BD_CONSOLE_BANNED = 0x29,
BD_MULTIPLE_LINKED_ACCOUNT_LOGONS = 0x2A,
BD_LINKED_ACCOUNT_STATUS_CHANGE = 0x2B,
BD_QUEUED_MATCHING_DATA = 0x2C,
BD_EVENT_LOG_FILTERED_CATEGORIES = 0x2D,
BD_MARKETPLACE_COUPONS_GRANTED = 0x2E,
BD_TOTP_CHALLENGE = 0x2F,
BD_MARKETPLACE_ITEMS_EXPIRED = 0x30,
BD_MARKETPLACE_ITEMS_UPDATED = 0x31,
BD_MARKETPLACE_COUPONS_UPDATED = 0x32,
BD_REWARD_ACHIEVEMENT_CLAIMED = 0x33,
BD_MARKETPLACE_BALANCE_UPDATED = 0x34,
BD_MARKETPLACE_DEPOSIT_GRANTED = 0x35,
BD_MARKETPLACE_ITEMS_GRANTED = 0x36,
BD_MARKETPLACE_ENTITLEMENTS_GRANTED = 0x37,
BD_STORAGE_WEBSERVICE_WRITE = 0x38,
BD_REWARD_EVENT_MESSAGE = 0x39,
BD_MARKETING_COMMS_ASSIGNMENTS_AVAILABLE = 0x3A,
BD_PUBLISHER_VARIABLES_UPDATE_MESSAGE = 0x3B,
BD_MARKETPLACE_COUPONS_UPDATED_V2 = 0x3C,
BD_MARKETPLACE_ITEMS_GRANTED_WITH_INVENTORY_QUANTITY = 0x3D,
BD_USER_PRIVATE_PROFILE_UPDATED = 0x3E,
BD_TEAM_MEMBER_RICH_PRESENCE_UPDATED = 0x3F,
BD_TITLE_VERSION_DISABLED = 0x40,
BD_TEAM_MEMBER_USER_NAME_UPDATED = 0x41,
BD_MARKETPLACE_COUPONS_UPDATED_V3 = 0x42,
BD_REWARD_ACHIEVEMENT_MESSAGE = 0x43,
BD_PLAYER_DISCONNECTED = 0x44,
BD_CODO_TEAM_MARKETPLACE_LEVEL_CHANGED = 0x45,
BD_SUBSCRIBED_RICH_PRESENCE_UPDATED = 0x46,
BD_PLAYER_LOGON_TIME_PROHIBITED = 0x47,
BD_PLAYER_LOGON_TIME_PROHIBITED_WARNING = 0x48,
BD_MARKETPLACE_COUPONS_UPDATED_V4 = 0x49,
BD_NOT_WHITE_LISTED_WITH_MESSAGE = 0x4A,
BD_ACHIEVEMENTS_UPDATED = 0x4B,
BD_MARKETPLACE_BALANCE_UPDATED_V2 = 0x4C,
BD_MW4_CLANS_MEMBERSHIP_PROPOSAL = 0x4D,
BD_MW4_CLANS_MEMBER_ADDED = 0x4E,
BD_MW4_CLANS_PROPOSAL_REMOVED = 0x4F,
BD_MW4_CLANS_MEMBER_REMOVED = 0x50,
BD_MW4_CLANS_GROUP_DISBANDED = 0x51,
BD_MW4_CLANS_ACTIVE_GROUP_CHANGED = 0x52,
BD_MW4_CLANS_GROUP_UPDATED = 0x53,
BD_MW4_CLANS_MEMBER_UPDATED = 0x54,
BD_DEMONATA_PUSH_MESSAGE = 0xC7,
BD_DEMONATA_PROTOBUF_PUSH_MESSAGE = 0xC8,
BD_TENCENT_ANTIBOT_DATA = 0x3E9,
BD_TENCENT_ANTIBOT_PUNISH = 0x3EA,
BD_TENCENT_REWARD = 0x3EB,
BD_TENCENT_ANTIBOT_SERVER_READY = 0x3EC,
BD_TENCENT_NAME_CHANGED = 0x3ED,
BD_TENCENT_USER_LEAVE_REASON = 0x3EE,
BD_TENCENT_LOUDSPEAKER_MESSAGE = 0x3EF,
BD_TENCENT_NO_REWARD = 0x3F0,
BD_TENCENT_SECURITY_RATING = 0x3F1,
BD_QOS_HOSTS = 0x7D0,
BD_JOIN_LOBBY = 0x7D1,
BD_LOBBY_DISBANDED = 0x7D2,
BD_MATCHMAKING_SEARCH_STATUS = 0x7D3,
BD_LOBBY_NOT_FOUND = 0x7D4,
BD_CREATE_NEW_LOBBY = 0x7D5,
BD_UPDATED_LOBBY_DOCUMENT = 0x7D6,
BD_EXPECT_GAME = 0x7D7,
BD_MERGE_INTO_LOBBY = 0x7D8,
BD_JOIN_TOURNAMENT = 0x7D9,
BD_UPDATE_TOURNAMENT = 0x7DA,
BD_TOURNAMENT_FORMATION_LOBBY_DISBANDED = 0x7DB,
BD_TOURNAMENT_DISBANDED = 0x7DC,
};
enum bdLobbyServiceType : std::int32_t
{
BD_LOBBY_SERVICE_TASK_REPLY = 1,
BD_LOBBY_SERVICE_PUSH_MESSAGE = 2,
BD_LSG_SERVICE_ERROR = 3,
BD_LSG_SERVICE_CONNECTION = 4,
BD_LSG_SERVICE_TASK_REPLY = 5,
};
enum bdLobbyErrorCode : std::int32_t
{
BD_NO_ERROR = 0x0,

View File

@ -74,8 +74,11 @@
#include <functional>
#include <sstream>
#include <optional>
#include <set>
#include <unordered_set>
#include <variant>
#include <random>
#include <numeric>
#include <gsl/gsl>
#include <udis86.h>