Bug fixes, more refactoring

This commit is contained in:
pigreco 2024-09-28 13:23:41 +02:00
parent 35a94d1240
commit 195dbc4e1e
33 changed files with 693 additions and 332 deletions

View file

@ -30,6 +30,7 @@
<ClInclude Include="include\ConstantDatabase\Structures\CdbCapsuleDisplay.h" />
<ClInclude Include="include\ConstantDatabase\Structures\CdbCapsuleInfo.h" />
<ClInclude Include="include\ConstantDatabase\Structures\CdbItemInfo.h" />
<ClInclude Include="include\ConstantDatabase\Structures\CdbItemsInfo.h" />
<ClInclude Include="include\ConstantDatabase\Structures\CdbMapInfo.h" />
<ClInclude Include="include\ConstantDatabase\Structures\CdbUpgradeInfo.h" />
<ClInclude Include="include\ConstantDatabase\Structures\CdbWeaponsInfo.h" />

View file

@ -111,6 +111,9 @@
<ClInclude Include="include\ConstantDatabase\Structures\CdbCapsuleDisplay.h">
<Filter>File di intestazione</Filter>
</ClInclude>
<ClInclude Include="include\ConstantDatabase\Structures\CdbItemsInfo.h">
<Filter>File di intestazione</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\Cryptography\Crypt.cpp">

View file

@ -4,6 +4,7 @@
#include "Cdb.h"
#include <string>
#include "Structures/CdbItemInfo.h"
#include "Structures/CdbItemsInfo.h"
#include "Structures/CdbWeaponsInfo.h"
namespace Common
@ -15,7 +16,6 @@ namespace Common
{
private:
inline static Cdb<T> m_cdb{};
inline static std::unordered_map<std::uint32_t, std::vector<std::uint32_t>> m_gambleItems; // [type][itemIds]
inline static std::unordered_map<std::uint32_t, std::uint32_t> m_itemByType; // [itemId][Type]
public:
@ -24,61 +24,30 @@ namespace Common
return m_cdb;
}
static const auto& getGambleItems()
{
return m_gambleItems;
}
static std::uint32_t getItemType(std::uint32_t itemId)
{
return m_itemByType[itemId];
}
static void initialize(const std::string& filePath, const std::string& fileName)
requires std::same_as<T, Common::ConstantDatabase::CdbWeaponInfo>
{
m_cdb.parse(filePath, fileName);
auto entries = m_cdb.getEntries();
for (const auto& [id, structType] : entries)
{
if ((strcmp(structType.ii_name_time, "Unlimited") == 0) && structType.ii_is_trade && structType.ii_upgradable)
{
m_gambleItems[structType.ii_type].push_back(static_cast<std::uint32_t>(structType.ii_id));
}
}
}
static void initializeItemTypes(const std::string& filePath, const std::string& weaponFileName, const std::string& itemFileName)
requires std::same_as<T, Common::ConstantDatabase::CdbItemInfo>
requires std::same_as<T, Common::ConstantDatabase::CdbItemsInfoCMV>
{
/*
Cdb<CdbWeaponInfo> cdbWeapon{};
cdbWeapon.parse(filePath, weaponFileName);
auto entries = cdbWeapon.getEntries();
for (const auto& [id, structType] : entries)
{
m_itemByType[structType.ii_id] = structType.ii_type;
}
}*/
Cdb<CdbItemInfo> cdbItem{};
Cdb<Common::ConstantDatabase::CdbItemsInfoCMV> cdbItem{};
cdbItem.parse(filePath, itemFileName);
auto entries2 = cdbItem.getEntries();
for (const auto& [id, structType] : entries2)
{
m_itemByType[structType.ii_id] = structType.ii_type;
}
}
static void initialize(const std::string& filePath, const std::string& fileName)
requires std::same_as<T, Common::ConstantDatabase::CdbItemInfo>
{
m_cdb.parse(filePath, fileName);
auto entries = m_cdb.getEntries();
auto entries = cdbItem.getEntries();
for (const auto& [id, structType] : entries)
{
if ((strcmp(structType.ii_name_time, "Unlimited") == 0) && structType.ii_is_trade)
{
m_gambleItems[structType.ii_type].push_back(static_cast<std::uint32_t>(structType.ii_id));
}
m_itemByType[structType.ii_id] = structType.ii_type;
}
}

View file

@ -3,6 +3,7 @@
#include "visit_struct/visit_struct.hpp"
// This is used for MVSurge, refer to "CdbItemsInfo" for CMV
namespace Common
{
namespace ConstantDatabase

View file

@ -0,0 +1,81 @@
#ifndef CDB_ITEMSINFOS_STRUCT_H
#define CDB_ITEMSINFOS_STRUCT_H
#include "visit_struct/visit_struct.hpp"
// This is for CMV, equivalent to ItemInfo + WeaponInfo on Surge (for CMV they both were placed in a single file)
namespace Common
{
namespace ConstantDatabase
{
#pragma pack(push, 1)
struct CdbItemsInfoCMV
{
int ii_id;
char ii_name[50];
char ii_name_option[50];
char ii_name_time[50];
int ii_type;
int ii_type_inven;
bool ii_inven_usable;
int ii_type_pcbang;
int ii_package_result;
bool ii_dress;
bool ii_hide_hair;
bool ii_hide_face;
bool ii_hide_back;
bool ii_class_a;
bool ii_class_b;
bool ii_class_c;
bool ii_class_d;
bool ii_class_e;
int ii_grade;
int ii_stocks;
bool ii_usable;
bool ii_upgradable;
bool ii_consumable;
int ii_weaponinfo;
int ii_durable_value;
int ii_durable_factor;
int ii_durable_repair_type;
bool ii_limited_mod;
int ii_limited_time;
int ii_buy_coupon;
int ii_buy_cash;
int ii_buy_point;
int ii_sell_point;
int ii_bonus_point;
int ii_bonus_pack;
int ii_dioramainfo;
int ii_dummyinfo;
int ii_icon;
int ii_iconsmall;
char ii_meshfilename[50];
char ii_nodename[50];
char ii_color_ambient[50];
char ii_color_diffuse[50];
char ii_color_specular[50];
char ii_color_emittance[50];
int ii_sfx;
int ef_effect_1;
int ef_target_1;
int ef_effect_2;
int ef_target_2;
int ef_effect_3;
int ef_target_3;
char ii_desc[200];
};
#pragma pack(pop)
}
}
VISITABLE_STRUCT(Common::ConstantDatabase::CdbItemsInfoCMV, ii_id, ii_name, ii_name_option, ii_name_time, ii_type, ii_type_inven, ii_inven_usable, ii_type_pcbang, ii_package_result, ii_dress, ii_hide_hair, ii_hide_face, ii_hide_back, ii_class_a, ii_class_b,
ii_class_c, ii_class_d, ii_class_e, ii_grade, ii_stocks, ii_usable,
ii_upgradable, ii_consumable, ii_weaponinfo, ii_durable_value, ii_durable_factor, ii_durable_repair_type, ii_limited_mod, ii_limited_time, ii_buy_coupon, ii_buy_cash, ii_buy_point,
ii_sell_point, ii_bonus_point, ii_bonus_pack, ii_dioramainfo, ii_dummyinfo, ii_icon, ii_iconsmall, ii_meshfilename, ii_nodename, ii_color_ambient, ii_color_diffuse, ii_color_specular, ii_color_emittance,
ii_sfx, ef_effect_1, ef_target_1, ef_effect_2, ef_target_2, ef_effect_3, ef_target_3, ii_desc
);
#endif

View file

@ -4,6 +4,7 @@
#include "visit_struct/visit_struct.hpp"
// This is used for MVSurge, refer to "CdbItemsInfo" for CMV
namespace Common
{
namespace ConstantDatabase

View file

@ -178,6 +178,7 @@
<ClCompile Include="src\ChatCommands\SimpleCommands\Shutdown.cpp" />
<ClCompile Include="src\ChatCommands\SimpleRoomCommands\BreakroomCommand.cpp" />
<ClCompile Include="src\ChatCommands\SimpleRoomCommands\UnmuteRoom.cpp" />
<ClCompile Include="src\Classes\CapsuleManager.cpp" />
<ClCompile Include="src\Classes\Player.cpp" />
<ClCompile Include="src\Classes\Room.cpp" />
<ClCompile Include="src\Classes\RoomsManager.cpp" />
@ -214,6 +215,7 @@
<ClInclude Include="include\ChatCommands\SimpleCommands\Shutdown.h" />
<ClInclude Include="include\ChatCommands\SimpleRoomCommands\BreakroomCommand.h" />
<ClInclude Include="include\ChatCommands\SimpleRoomCommands\UnmuteRoom.h" />
<ClInclude Include="include\Classes\CapsuleManager.h" />
<ClInclude Include="include\Classes\Player.h" />
<ClInclude Include="include\Classes\RoomsManager.h" />
<ClInclude Include="include\Handlers\CapsuleReqHandler.h" />
@ -233,6 +235,7 @@
<ClInclude Include="include\Handlers\Room\EliminationNextRoundHandler.h" />
<ClInclude Include="include\Handlers\Room\MatchLeaveHandler.h" />
<ClInclude Include="include\Handlers\Room\RoomChangeHostHandler.h" />
<ClInclude Include="include\Handlers\Room\RoomInviteJoin.h" />
<ClInclude Include="include\Handlers\Room\RoomJoinHandler.h" />
<ClInclude Include="include\Handlers\Player\LobbyAccountInfoHandler.h" />
<ClInclude Include="include\Handlers\Player\Mailbox\MailboxDeleteHandler.h" />

View file

@ -34,6 +34,7 @@
<ClCompile Include="src\ChatCommands\SimpleRoomCommands\Muteroom.cpp" />
<ClCompile Include="src\ChatCommands\SimpleRoomCommands\UnmuteRoom.cpp" />
<ClCompile Include="src\ChatCommands\ComplexCommands\OneArgumentCommands\ChangeHost.cpp" />
<ClCompile Include="src\Classes\CapsuleManager.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\ChatCommands\AllCommandsIncludes.h" />
@ -135,5 +136,7 @@
<ClInclude Include="include\Handlers\CapsuleReqHandler.h" />
<ClInclude Include="include\Structures\ClientData\Structures.h" />
<ClInclude Include="include\Handlers\Item\ItemAndCapsuleHandler.h" />
<ClInclude Include="include\Handlers\Room\RoomInviteJoin.h" />
<ClInclude Include="include\Classes\CapsuleManager.h" />
</ItemGroup>
</Project>

View file

@ -30,8 +30,8 @@ namespace Main
std::uint32_t itemId{};
std::uint32_t m_maxCapsuleItems{};
using cdbItems = Common::ConstantDatabase::CdbSingleton<Common::ConstantDatabase::CdbItemInfo>;
using cdbWeapons = Common::ConstantDatabase::CdbSingleton<Common::ConstantDatabase::CdbWeaponInfo>;
using cdbItems = Common::ConstantDatabase::CdbSingleton<Common::ConstantDatabase::CdbItemsInfoCMV>;
//using cdbWeapons = Common::ConstantDatabase::CdbSingleton<Common::ConstantDatabase::CdbWeaponInfo>;
using cdbUpgrades = Common::ConstantDatabase::CdbSingleton<Common::ConstantDatabase::CdbUpgradeInfo>;
using cdbCapsuleInfos = Common::ConstantDatabase::CdbSingleton<Common::ConstantDatabase::CdbCapsuleInfo>;
using cdbCapsulePackageInfos = Common::ConstantDatabase::CdbSingleton<Common::ConstantDatabase::CdbCapsulePackageInfo>;
@ -68,32 +68,17 @@ namespace Main
std::optional<ItemTypePricePair> getItemPrice() const
{
const auto itemInfoMapPrice = getItemPriceInternal(cdbItems::getInstance());
if (itemInfoMapPrice == std::nullopt)
{
return getItemPriceInternal(cdbWeapons::getInstance());
}
return itemInfoMapPrice;
return getItemPriceInternal(cdbItems::getInstance());
}
std::optional<std::uint16_t> getItemDurability() const
{
const auto itemDurability = getDurabilityInternal(cdbItems::getInstance());
if (itemDurability == std::nullopt)
{
return getDurabilityInternal(cdbWeapons::getInstance());
}
return itemDurability;
return getDurabilityInternal(cdbItems::getInstance());
}
std::optional<std::uint16_t> getItemRefundPrice() const
{
const auto refundValue = getRefundValue(cdbItems::getInstance());
if (refundValue == std::nullopt)
{
return getRefundValue(cdbWeapons::getInstance());
}
return refundValue;
return getRefundValue(cdbItems::getInstance());
}
std::optional<Common::ConstantDatabase::CdbRewardInfo> getRewardInfoForMode(std::uint32_t mode) const
@ -116,34 +101,14 @@ namespace Main
return entry;
}
std::optional<bool> isImmediatelySet() const
{
const auto isItemImmediatelySet = isImmediatelySetInternal(cdbItems::getInstance());
if (isItemImmediatelySet == std::nullopt)
{
return isImmediatelySetInternal(cdbWeapons::getInstance());
}
return isItemImmediatelySet;
}
std::optional<std::uint32_t> getItemDuration() const
{
const auto itemInfoMapExpiration = getItemDurationInternal(cdbItems::getInstance());
if (itemInfoMapExpiration == std::nullopt)
{
return getItemDurationInternal(cdbWeapons::getInstance());
}
return itemInfoMapExpiration;
return getItemDurationInternal(cdbItems::getInstance());
}
std::optional<std::uint32_t> getItemType() const
{
const auto itemType = getItemTypeInternal(cdbItems::getInstance());
if (itemType == std::nullopt)
{
return getItemTypeInternal(cdbWeapons::getInstance());
}
return itemType;
return getItemTypeInternal(cdbItems::getInstance());
}
std::optional<std::uint32_t> getBatteryNeededForUpgrade() const

View file

@ -0,0 +1,29 @@
#ifndef CAPSULE_MANAGER_H
#define CAPSULE_MANAGER_H
#include <cstdint>
#include "../Persistence/MainScheduler.h"
namespace Main
{
namespace Classes
{
class CapsuleManager
{
private:
std::uint32_t m_jackpot{};
Main::Persistence::MainScheduler& m_scheduler;
Main::Persistence::PersistentDatabase& database;
public:
explicit CapsuleManager(Main::Persistence::MainScheduler& m_scheduler, Main::Persistence::PersistentDatabase& database);
std::uint32_t getJackpot();
void addJackpot(uint32_t value);
void subJackpot(uint32_t value);
void setJackpot(uint32_t jackpot);
};
}
}
#endif

View file

@ -1,11 +1,10 @@
#ifndef CAPSULE_REQ_HANDLER_H
#define CAPSULE_REQ_HANDLER_H
#include "../Network/MainSession.h"
#include "../Network/MainSessionManager.h"
#include "Network/Packet.h"
#include <ConstantDatabase/Structures/SetItemInfo.h>
#include <Utils/Utils.h>
#include "../Classes/CapsuleManager.h"
#include "../Utilities.h"
@ -13,13 +12,13 @@ namespace Main
{
namespace Handlers
{
inline void handleCapsuleReq(const Common::Network::Packet& request, Main::Network::Session& session, Main::Classes::RoomsManager& roomsManager)
inline void handleCapsuleReq(const Common::Network::Packet& request, Main::Network::Session& session, Main::Classes::CapsuleManager& capsule)
{
// capsules are all completely client sided
Common::Network::Packet response;
response.setTcpHeader(request.getSession(), Common::Enums::USER_LARGE_ENCRYPTION);
response.setOrder(83);
std::uint32_t mpJackpot = 0;
std::uint32_t mpJackpot = capsule.getJackpot();
response.setData(reinterpret_cast<std::uint8_t*>(&mpJackpot), sizeof(mpJackpot));
session.asyncWrite(response);
}

View file

@ -5,6 +5,7 @@
#include "../../include/Structures/AccountInfo/MainAccountInfo.h"
#include "Network/Packet.h"
#include "../Structures/Capsule/CapsuleSpin.h"
#include "../Classes/CapsuleManager.h"
#include <random>
namespace Main
@ -30,33 +31,43 @@ namespace Main
NORMAL_SPIN = 1
};
enum CapsuleSpinLuckyRewards
{
GoldLuck = 7304,
SilverLuck = 7305,
BronzeLuck = 7306,
SemiLuck = 7307,
NotSoLuck = 7308,
ToughLuck = 7309
};
inline std::pair<std::uint32_t, std::uint32_t> itemSelectionAlgorithm(const Main::ConstantDatabase::CdbUtil& cdbUtil, std::uint32_t gi_itemid,
std::uint32_t gi_price, std::uint32_t gi_type)
std::uint32_t gi_price, std::uint32_t gi_type)
{
static std::array<double, 3> averageSpinCostByCurrency = cdbUtil.getAverageSpinCostByCurrency();
auto items = cdbUtil.getAllEntriesWhereId(gi_itemid);
constexpr const std::uint32_t maxProbability = 200'000;
constexpr const double priceFactor = 0.2;
constexpr const double priceFactor = 0.2;
std::vector<std::pair<std::pair<std::uint32_t, std::uint32_t>, double>> itemProbabilities;
for (const auto& item : items)
for (const auto& item : items)
{
if (!item.pi_type) continue;
double probability = static_cast<double>(item.pi_prob) / maxProbability * 100.0;
if (gi_price > averageSpinCostByCurrency[gi_type] && item.pi_type == 2) // Increase chance for rare
{
probability *= (1.0 + priceFactor);
probability *= (1.0 + priceFactor);
}
else if (gi_price < averageSpinCostByCurrency[gi_type] && item.pi_type == 2) // decrease chance for rare
{
probability *= (1.0 - priceFactor);
probability *= (1.0 - priceFactor);
}
itemProbabilities.emplace_back(std::pair{ item.pi_valueA, item.pi_type }, probability);
}
std::vector<double> probabilities;
for (const auto& pair : itemProbabilities)
for (const auto& pair : itemProbabilities)
{
probabilities.push_back(pair.second);
}
@ -64,8 +75,42 @@ namespace Main
std::discrete_distribution<int> itemDistribution(probabilities.begin(), probabilities.end());
static std::random_device rd;
static std::mt19937 gen(rd());
int randomIndex = itemDistribution(gen);
return itemProbabilities[itemDistribution(gen)].first;
return itemProbabilities[randomIndex].first;
}
inline Common::ConstantDatabase::CdbCapsulePackageInfo luckySelectionAlgorithm(const ConstantDatabase::CdbUtil& cdbUtil, std::uint32_t gi_itemid,
std::uint32_t gi_type, Classes::CapsuleManager capsuleManager)
{
auto items = cdbUtil.getAllEntriesWhereId(gi_itemid);
constexpr const std::uint32_t maxProbability = 860400;
std::vector<std::pair<Common::ConstantDatabase::CdbCapsulePackageInfo, double>> itemProbabilities;
for (const auto& item : items)
{
double probability = static_cast<double>(item.pi_prob) / maxProbability * 100.0;
itemProbabilities.emplace_back(item, probability);
}
std::vector<double> probabilities;
for (const auto& pair : itemProbabilities)
{
probabilities.push_back(pair.second);
}
std::discrete_distribution<int> itemDistribution(probabilities.begin(), probabilities.end());
static std::random_device rd;
static std::mt19937 gen(rd());
int randomIndex = itemDistribution(gen);
for (int i = 0; i < itemProbabilities.size(); i++)
{
if (itemProbabilities[i].first.pi_id == SilverLuck)
{
randomIndex = i;
}
}
return itemProbabilities[randomIndex].first;
}
inline void removeCurrencyByCapsuleType(Main::Network::Session& session, const Main::Structures::AccountInfo& accountInfo, CapsuleCurrencyType capsuleCurrencyType,
@ -81,30 +126,10 @@ namespace Main
}
}
inline void handleCapsuleSpin(const Common::Network::Packet& request, Main::Network::Session& session)
inline void handleItemSpin(const Common::Network::Packet& request, Network::Session& session, Common::Network::Packet& response,
ConstantDatabase::CdbUtil& cdbUtil, Common::ConstantDatabase::CdbCapsuleInfo capsuleInfo, Structures::AccountInfo accountInfo,
Classes::CapsuleManager& capsuleManager)
{
auto response = request;
response.setOption(1); // number of items per spin in newer versions
Main::ConstantDatabase::CdbUtil cdbUtil;
const auto& capsuleInfo = cdbUtil.getCapsuleInfoById(request.getOption());
if (capsuleInfo == std::nullopt)
{
// Apparently there's no error message in this case in CMV, just resend the packet given by the client...
session.asyncWrite(response);
return;
}
const auto& accountInfo = session.getAccountInfo();
if (capsuleInfo->gi_type == CapsuleCurrencyType::ROCKTOTENS && accountInfo.rockTotens < capsuleInfo->gi_pay_cash * request.getOption()
|| capsuleInfo->gi_type == CapsuleCurrencyType::MICROPOINTS && accountInfo.microPoints < capsuleInfo->gi_pay_point * request.getOption())
{
response.setExtra(CapsuleSpinExtra::NOT_ENOUGH_CURRENCY);
response.setOption(capsuleInfo->gi_type);
session.asyncWrite(response);
return;
}
if (!session.hasEnoughInventorySpace(request.getOption()))
{
response.setExtra(CapsuleSpinExtra::INVENTORY_FULL);
@ -112,37 +137,26 @@ namespace Main
return;
}
constexpr const std::uint32_t maxLuckySpin = 1000;
std::size_t i = 0;
Main::Structures::CapsuleSpin capsuleSpin;
if (session.getLuckyPoints() > maxLuckySpin)
{
session.setLuckyPoints(0);
//response.setMission(CapsuleSpinMission::LUCKY_SPIN);
}
else
{
//response.setMission(CapsuleSpinMission::NORMAL_SPIN);
}
capsuleSpin.itemSerialInfo.itemNumber = session.getLatestItemNumber() + 1;
session.setLatestItemNumber(capsuleSpin.itemSerialInfo.itemNumber);
// Unsure how to handle this for CMV, currently only RT capsules are enabled, the rest are prices = 0
// Here I'm assuming that to enable e.g. the MP capsule, we just need to
std::uint32_t price = 0;
if (capsuleInfo->gi_type == CapsuleCurrencyType::ROCKTOTENS)
if (capsuleInfo.gi_type == CapsuleCurrencyType::ROCKTOTENS) // rt
{
price = capsuleInfo->gi_pay_cash;
price = capsuleInfo.gi_pay_cash;
}
else if (capsuleInfo->gi_type == CapsuleCurrencyType::MICROPOINTS)
else if (capsuleInfo.gi_type == CapsuleCurrencyType::MICROPOINTS) // mp
{
price = capsuleInfo->gi_pay_point;
}
price = capsuleInfo.gi_pay_point;
}
const std::pair<std::uint32_t, std::uint32_t> wonItemIdAndType = itemSelectionAlgorithm(cdbUtil, capsuleInfo->gi_itemid, price, capsuleInfo->gi_type);
const std::pair<std::uint32_t, std::uint32_t> wonItemIdAndType = itemSelectionAlgorithm(cdbUtil, capsuleInfo.gi_itemid, price, capsuleInfo.gi_type);
capsuleSpin.winItemId = wonItemIdAndType.first;
cdbUtil.setItemId(capsuleSpin.winItemId);
if (cdbUtil.getItemDurability() == std::nullopt)
{
// Apparently there's no error message in this case in CMV, just resend the packet given by the client...
@ -154,17 +168,105 @@ namespace Main
item.expirationDate = *cdbUtil.getItemDuration();
item.serialInfo = capsuleSpin.itemSerialInfo;
item.id = capsuleSpin.winItemId;
response.setData(reinterpret_cast<std::uint8_t*>(&capsuleSpin), sizeof(Main::Structures::CapsuleSpin));
session.asyncWrite(response);
session.addItem(item);
removeCurrencyByCapsuleType(session, accountInfo, static_cast<CapsuleCurrencyType>(capsuleInfo->gi_type), price);
removeCurrencyByCapsuleType(session, accountInfo, static_cast<CapsuleCurrencyType>(capsuleInfo.gi_type), price);
capsuleManager.addJackpot(400);
if (response.getMission() != 0)
{
session.addLuckyPoints(capsuleInfo->gi_bonus_lucky);
session.addLuckyPoints(capsuleInfo.gi_bonus_lucky);
}
}
inline void handleLuckySpin(const Common::Network::Packet& request, Network::Session& session, Common::Network::Packet& response,
ConstantDatabase::CdbUtil& cdbUtil, Common::ConstantDatabase::CdbCapsuleInfo capsuleInfo, Structures::AccountInfo accountInfo,
Classes::CapsuleManager& capsuleManager, Network::SessionsManager sessionsManager)
{
const auto selectedItem = luckySelectionAlgorithm(cdbUtil, capsuleInfo.gi_itemid, capsuleInfo.gi_type, capsuleManager);
std::uint32_t mpAmountToReturn;
std::uint32_t mpAmountToAdd;
auto const jackpotValue = capsuleManager.getJackpot();
std::string winMessage;
switch (selectedItem.pi_id)
{
case GoldLuck:
mpAmountToReturn = jackpotValue;
mpAmountToAdd = jackpotValue;
winMessage = std::string("[") + session.getPlayerName() + "] won a Gold Luck Charm and earned " + std::to_string(mpAmountToAdd) + " MP!";
break;
case SilverLuck:
mpAmountToReturn = selectedItem.pi_valueA * 1000; // % of the jackpot we won, 300000 = 30%, pi_valueA = 300
mpAmountToAdd = jackpotValue * (selectedItem.pi_valueA / 10) / 100; // 30% of the jackpot
winMessage = std::string("[") + session.getPlayerName() + "] won a Silver Luck Charm and earned " + std::to_string(mpAmountToAdd) + " MP!";
break;
case BronzeLuck:
mpAmountToReturn = jackpotValue; // 10% of this value results in the MP amount we get
mpAmountToAdd = jackpotValue * (selectedItem.pi_valueA / 10) / 100; // 10% of the jackpot
winMessage = std::string("[") + session.getPlayerName() + "] won a Copper Luck Charm and earned " + std::to_string(mpAmountToAdd) + " MP!";
break;
case SemiLuck:
case NotSoLuck:
case ToughLuck:
mpAmountToReturn = selectedItem.pi_valueB;
mpAmountToAdd = selectedItem.pi_valueB;
break;
}
Structures::CapsuleSpin capsuleSpin;
capsuleSpin.winItemId = selectedItem.pi_id;
capsuleSpin.mpAmount = mpAmountToReturn;
session.setAccountMicroPoints(accountInfo.microPoints + mpAmountToAdd);
capsuleManager.subJackpot(mpAmountToAdd);
response.setExtra(37);
response.setData(reinterpret_cast<std::uint8_t*>(&capsuleSpin), sizeof(Main::Structures::CapsuleSpin));
session.asyncWrite(response);
session.setLuckyPoints(0);
sessionsManager.broadcastMessageExceptSelf(session.getSessionId(), winMessage);
}
inline void handleCapsuleSpin(const Common::Network::Packet& request, Main::Network::Session& session, Classes::CapsuleManager& capsuleManager, Network::SessionsManager sessionsManager)
{
auto response = request;
response.setOption(1); // number of items per spin (e.g 2 if we want to add addiitonal coupon)
Main::ConstantDatabase::CdbUtil cdbUtil;
const auto& capsuleInfo = cdbUtil.getCapsuleInfoById(request.getOption());
if (capsuleInfo == std::nullopt)
{
// Apparently there's no error message in this case in CMV, just resend the packet given by the client...
session.asyncWrite(response);
return;
}
const auto& accountInfo = session.getAccountInfo();
if (capsuleInfo->gi_type == CapsuleCurrencyType::COINS &&
capsuleInfo->gi_type == CapsuleCurrencyType::ROCKTOTENS && accountInfo.rockTotens < capsuleInfo->gi_pay_cash * request.getOption()
|| capsuleInfo->gi_type == CapsuleCurrencyType::MICROPOINTS && accountInfo.microPoints < capsuleInfo->gi_pay_point * request.getOption())
{
response.setExtra(CapsuleSpinExtra::NOT_ENOUGH_CURRENCY);
response.setOption(capsuleInfo->gi_type);
session.asyncWrite(response);
return;
}
if (session.getLuckyPoints() >= 1000)
{
handleLuckySpin(request, session, response, cdbUtil, capsuleInfo.value(), accountInfo, capsuleManager, sessionsManager);
}
else
{
handleItemSpin(request, session, response, cdbUtil, capsuleInfo.value(), accountInfo, capsuleManager);
}
}
}
}
#endif
#endif

View file

@ -16,11 +16,12 @@ namespace Main
namespace Handlers
{
inline void handleGeneralItem(const Common::Network::Packet& request, Main::Network::Session& session, Main::Persistence::PersistentDatabase& db,
const std::unordered_map<std::uint32_t, std::unique_ptr<Main::Box::IBox>>& m_boxes, const Main::Structures::ItemSerialInfo& openedItemSerialInfo)
const std::unordered_map<std::uint32_t, std::unique_ptr<Main::Box::IBox>>& m_boxes, const Main::Structures::ItemSerialInfo& openedItemSerialInfo,
Classes::CapsuleManager capsuleManager, Network::SessionsManager sessionsManager)
{
if (request.getMission() == 2) // capsule spin
{
Main::Handlers::handleCapsuleSpin(request, session);
Main::Handlers::handleCapsuleSpin(request, session, capsuleManager, sessionsManager);
}
else
{

View file

@ -93,7 +93,7 @@ namespace Main
session.setAccountMicroPoints(accountInfo.microPoints - *mpNeededForUpgrade);
session.sendCurrency();
}
else if (request.getExtra() == 55)
else if (request.getExtra() == 53)
{
// Item Reset
}

View file

@ -0,0 +1,145 @@
#ifndef ROOM_INVITE_JOIN_HANDLER_H
#define ROOM_INVITE_JOIN_HANDLER_H
#include "../../Network/MainSession.h"
#include "../../../include/Structures/AccountInfo/MainAccountInfo.h"
#include "../../../include/Structures/Room/RoomSettingsUpdate.h"
#include "Network/Packet.h"
#include <span>
#include "../Room/RoomJoinHandler.h"
#include "../Room/RoomLeaveHandler.h"
#include "../../Utilities.h"
#include "../../Structures/ClientData/Structures.h"
namespace Main
{
namespace Handlers
{
enum RoomInviteJoinExtra
{
USER_INVITING_FRIEND = 0,
USER_READY = 5,
USER_OFFLINE = 0xD,
ROOM_FULL = 0xE,
ROOM_SENDINVITE_TOTARGET = 44,
USER_JOINING_FRIEND = 28,
};
// Room invites/join, not fully implemented, also not working in certain scenarios, recheck + refactor this function
inline void handleJoinAndInvites(const Common::Network::Packet& request, Main::Network::Session& session, Main::Classes::RoomsManager& roomsManager,
Main::Network::SessionsManager& sessionsManager)
{
auto response = request;
if (request.getExtra() == RoomInviteJoinExtra::USER_JOINING_FRIEND)
{
std::uint32_t targetAccountId;
std::memcpy(&targetAccountId, reinterpret_cast<std::uint8_t*>(const_cast<std::uint8_t*>(request.getData())), sizeof(targetAccountId));
if (auto* targetSession = sessionsManager.getSessionByAccountId(targetAccountId))
{
const std::uint32_t targetRoomNum = targetSession->getRoomNumber();
const std::uint32_t selfRoomNum = session.getRoomNumber();
if (targetRoomNum)
{
if (!session.isInLobby() && !selfRoomNum)
{ // User is neither in lobby and in room, join should not work in this case
Main::Details::sendMessage("You can only join a friend's room while being in a different room or in the lobby!", session);
return;
}
else if (targetRoomNum == selfRoomNum)
{ // User attempting to join the same room, disallow this
Main::Details::sendMessage("You cannot join this friend as you're already in their room.", session);
return;
}
else if (selfRoomNum)
{ // if the player is in a room, leave it first
response.setCommand(137, 0, 0, 0);
Main::Handlers::handleRoomLeave(response, session, sessionsManager, roomsManager, 4, Details::parseData<Main::Structures::UniqueId>(response));
}
Main::ClientData::RoomInfo joinInfo{ targetRoomNum - 1 };
response.setCommand(136, 0, 0, 0);
response.setData(reinterpret_cast<std::uint8_t*>(&joinInfo), sizeof(joinInfo));
if (Main::Classes::Room* room = roomsManager.getRoomByNumber(targetRoomNum))
{
Main::Handlers::handleRoomJoin(response, session, roomsManager, joinInfo, !room->getPassword().empty());
}
else
{
Main::Details::sendMessage("Room not found.", session);
}
}
else
{
Main::Details::sendMessage("This friend is currently not inside a room.", session);
return;
}
}
else
{
Main::Details::sendMessage("The friend you're trying to join went offline.", session);
return;
}
}
else if (request.getExtra() == RoomInviteJoinExtra::USER_INVITING_FRIEND && request.getOption() == 2) // Currently doesn't work, something's wrong here
{
char targetNickname[16];
std::ranges::copy(std::span(reinterpret_cast<const char*>(request.getData()), sizeof(targetNickname)), targetNickname);
Main::Network::Session* targetSession = sessionsManager.findSessionByName(targetNickname);
if (targetSession)
{
if (!targetSession->isInLobby())
{
Details::sendMessage("This player is currently not in the lobby.", session);
}
else if (targetSession->getRoomNumber() == session.getRoomNumber())
{
Details::sendMessage("The player you are trying to invite is already in your room.", session);
}
if (Main::Classes::Room* room = roomsManager.getRoomByNumber(session.getRoomNumber()))
{
response.setCommand(319, 0, 0, 0);
response.setSession(targetSession->getId());
#pragma pack(push, 1)
struct Response
{
std::uint32_t serverId = 4; // unsure whether this really is the server's id
char sourceNickname[16]{}; // OK
std::uint16_t roomNumber{};
std::uint16_t unknown{ 2 };
char roomTitle[32]{};
};
#pragma pack(pop)
Response responseStruct;
responseStruct.roomNumber = session.getRoomNumber() - 1; // correct
std::ranges::copy(std::span(room->getRoomTitle().c_str(), sizeof(responseStruct.roomTitle) - 1), responseStruct.roomTitle);
responseStruct.roomTitle[sizeof(responseStruct.roomTitle) - 1] = '\0';
std::ranges::copy(std::span(session.getAccountInfo().nickname, sizeof(responseStruct.sourceNickname) - 1), responseStruct.sourceNickname);
responseStruct.sourceNickname[sizeof(responseStruct.sourceNickname) - 1] = '\0';
response.setData(reinterpret_cast<std::uint8_t*>(&responseStruct), sizeof(responseStruct));
targetSession->asyncWrite(response);
}
else
{
Details::sendMessage("You must be in a room to invite a player.", session);
}
}
else
{
Details::sendMessage("The player you invited is offline.", session);
}
}
else if (session.getRoomNumber())
{
roomsManager.broadcastToRoom(session.getRoomNumber(), const_cast<Common::Network::Packet&>(request));
}
}
}
}
#endif

View file

@ -42,7 +42,6 @@ namespace Main
inline void handleRoomLeave(const Common::Network::Packet& request, Main::Network::Session& session, Main::Network::SessionsManager& sessionsManager,
Main::Classes::RoomsManager& roomsManager, std::uint32_t serverId, const Main::Structures::UniqueId& uniqueId)
{
auto roomOpt = roomsManager.getRoomByNumber(session.getRoomNumber());
if (Main::Classes::Room* room = roomsManager.getRoomByNumber(session.getRoomNumber()))
{
if (request.getExtra() == ClientExtra::KICK_PLAYER)

View file

@ -5,7 +5,6 @@
#include "../../../include/Structures/AccountInfo/MainAccountInfo.h"
#include "../../../include/Structures/Room/RoomSettingsUpdate.h"
#include "Network/Packet.h"
#include <span>
#include "../Room/RoomJoinHandler.h"
#include "../Room/RoomLeaveHandler.h"
@ -14,136 +13,6 @@ namespace Main
{
namespace Handlers
{
enum RoomInviteJoinExtra
{
USER_INVITING_FRIEND = 0,
USER_READY = 5,
USER_OFFLINE = 0xD,
ROOM_FULL = 0xE,
ROOM_SENDINVITE_TOTARGET = 44,
USER_JOINING_FRIEND = 28,
};
// Room invites/join, not fully implemented, also not working in certain scenarios, recheck + refactor this function
inline void unknown(const Common::Network::Packet& request, Main::Network::Session& session, Main::Classes::RoomsManager& roomsManager,
Main::Network::SessionsManager& sessionsManager)
{
auto response = request;
if (request.getExtra() == RoomInviteJoinExtra::USER_JOINING_FRIEND)
{
response.setOrder(136);
std::uint32_t targetAccountId;
std::memcpy(&targetAccountId, reinterpret_cast<std::uint8_t*>(const_cast<std::uint8_t*>(request.getData())), sizeof(targetAccountId));
auto* targetSession = sessionsManager.getSessionByAccountId(targetAccountId);
if (targetSession)
{
const std::uint32_t targetRoomNum = targetSession->getRoomNumber();
const std::uint32_t selfRoomNum = session.getRoomNumber();
if (targetRoomNum)
{
if (!session.isInLobby() && !selfRoomNum)
{ // User is neither in lobby and in room, join should not work in this case
Main::Details::sendMessage("You can only join a friend's room while being in a different room or in the lobby!", session);
return;
}
else if (targetRoomNum == selfRoomNum)
{ // User attempting to join the same room, disallow this
Main::Details::sendMessage("You cannot join this friend as you're already in their room.", session);
return;
}
else if (selfRoomNum)
{ // if the player is in a room, leave it first
Common::Network::Packet response;
response.setTcpHeader(session.getId(), Common::Enums::USER_LARGE_ENCRYPTION);
response.setOrder(137);
Main::Handlers::handleRoomLeave(response, session, sessionsManager, roomsManager, 4, Details::parseData<Main::Structures::UniqueId>(response));
}
struct JoinInfo
{
std::uint16_t roomNum = 0;
std::uint16_t unknown = 2; // seemingly always 2 for some reason
};
JoinInfo joinInfo{ targetRoomNum - 1 };
response.setOption(0);
response.setExtra(0);
response.setMission(0);
response.setData(reinterpret_cast<std::uint8_t*>(&joinInfo), sizeof(joinInfo));
if (Main::Classes::Room* room = roomsManager.getRoomByNumber(session.getRoomNumber()))
{
// Currently passworded room not handled!
//Main::Handlers::handleRoomJoin(response, session, roomsManager, !roomOpt->get().getPassword().empty());
}
else
{
Main::Details::sendMessage("Room not found.", session);
}
}
else
{
Main::Details::sendMessage("This friend is currently not inside a room.", session);
return;
}
}
else
{
Main::Details::sendMessage("The friend you're trying to join went offline.", session);
return;
}
}
else if (request.getExtra() == RoomInviteJoinExtra::USER_INVITING_FRIEND) // Currently doesn't work, something's wrong here
{
char targetNickname[16];
std::ranges::copy(std::span(reinterpret_cast<const char*>(request.getData()), sizeof(targetNickname)), targetNickname);
Main::Network::Session* targetSession = sessionsManager.findSessionByName(targetNickname);
if (targetSession)
{
if (Main::Classes::Room* room = roomsManager.getRoomByNumber(session.getRoomNumber()))
{
response.setOrder(319); // Room invite request
response.setExtra(44); // Room invite request
response.setOption(0);
response.setSession(targetSession->getId());
#pragma pack(push, 1)
struct Response
{
std::uint32_t serverId = 4; // unsure
char sourceNickname[16]{};
std::uint16_t roomNumber{};
std::uint16_t unknown{ 2 }; // same as for RoomJoin structure, always 2
char roomTitle[30]{};
std::uint16_t padding = 0;
char password[8]{};
std::uint64_t padding2 = 0;
};
#pragma pack(pop)
Response responseStruct;
responseStruct.roomNumber = session.getRoomNumber() - 1;
std::ranges::copy(std::span(room->getRoomTitle().c_str(), sizeof(responseStruct.roomTitle) - 1), responseStruct.roomTitle);
responseStruct.roomTitle[sizeof(responseStruct.roomTitle) - 1] = '\0';
std::ranges::copy(std::span(session.getAccountInfo().nickname, sizeof(responseStruct.sourceNickname) - 1), responseStruct.sourceNickname);
responseStruct.sourceNickname[sizeof(responseStruct.sourceNickname) - 1] = '\0';
response.setData(reinterpret_cast<std::uint8_t*>(&responseStruct), sizeof(responseStruct));
targetSession->asyncWrite(response);
}
}
else
{
}
}
else if (session.getRoomNumber())
{
roomsManager.broadcastToRoom(session.getRoomNumber(), const_cast<Common::Network::Packet&>(request));
}
}
// Takes care of settings that are inside the "Room Settings" button + switching team
inline void handleRoomMiscellaneous(const Common::Network::Packet& request, Main::Network::Session& session, Main::Classes::RoomsManager& roomsManager,
std::uint64_t m_latestServerRestart)

View file

@ -11,12 +11,6 @@ namespace Main
{
namespace Handlers
{
std::uint64_t getUtcTimeMs()
{
const auto durationSinceEpoch = std::chrono::system_clock::now().time_since_epoch();
return static_cast<std::uint64_t>(duration_cast<std::chrono::milliseconds>(durationSinceEpoch).count());
}
inline void handleRoomStart(const Common::Network::Packet& request, Main::Network::Session& session, Main::Classes::RoomsManager& roomsManager,
std::uint64_t timeSinceLastServerRestart)
{
@ -26,6 +20,7 @@ namespace Main
response.setTcpHeader(request.getSession(), Common::Enums::USER_LARGE_ENCRYPTION);
auto selfUniqueId = session.getAccountInfo().uniqueId;
// Do NOT touch this part, or you risk breaking the match-start mechanism!
if (request.getExtra() == 38) // host or non-host clicks on "start" button (n.b: SingleWave's extra is 6)
{
response.setCommand(request.getOrder(), 0, 38, room->getRoomSettings().map);
@ -37,6 +32,11 @@ namespace Main
Utils::MapInfo mapInfo{ room->getRoomSettings().map, selfUniqueId.session };
Utils::IPCManager::ipc_mainToCast(mapInfo, std::to_string(room->getRoomNumber()), "map_info");
}
else if (room->hasMatchStarted())
{
room->setStateFor(selfUniqueId, static_cast<Common::Enums::PlayerState>(11));
session.setIsInMatch(true);
}
}
else if (request.getExtra() == 41)
{
@ -44,11 +44,11 @@ namespace Main
if (room->isHost(selfUniqueId)) // broadcast the tick to the room
{
std::uint64_t roomTick = getUtcTimeMs() - timeSinceLastServerRestart;
std::uint64_t roomTick = Details::getUtcTimeMs() - timeSinceLastServerRestart;
response.setCommand(258, 0, 1, 0);
response.setData(reinterpret_cast<std::uint8_t*>(&roomTick), sizeof(roomTick));
room->broadcastToRoom(response);
room->startMatch(selfUniqueId);
room->startMatch(selfUniqueId); // don't move this anywhere else!
}
else // Tell the other players in the match that we joined
{

View file

@ -9,6 +9,7 @@
#include "Network/MainSessionManager.h"
#include "ChatCommands/ChatCommands.h"
#include "Classes/RoomsManager.h"
#include "Classes/CapsuleManager.h"
#include <iostream>
#include "Boxes/BoxBase.h"
@ -27,12 +28,14 @@ namespace Main
std::uint16_t m_serverId;
Main::Persistence::PersistentDatabase m_database;
Main::Persistence::MainScheduler m_scheduler;
Main::Classes::CapsuleManager m_capsuleManager;
Main::Network::SessionsManager m_sessionsManager;
Main::Classes::RoomsManager m_roomsManager;
Main::Command::ChatCommands m_chatCommands;
std::unordered_map<std::uint32_t, std::unique_ptr<Main::Box::IBox>> m_boxes;
std::uint64_t m_timeSinceLastRestart{};
// For auth server communication
tcp::acceptor m_authServerAcceptor;
std::optional<tcp::socket> m_authSocket;

View file

@ -38,6 +38,12 @@ namespace Main
public:
explicit Session(Main::Persistence::MainScheduler& scheduler, tcp::socket&& socket, std::function<void(std::size_t)> fnct);
~Session()
{
m_scheduler.immediatePersist(m_player.getAccountID(), &Main::Persistence::PersistentDatabase::updateOfflineStatus, m_player.getAccountID());
m_scheduler.persistFor(m_player.getAccountID());
}
std::size_t getSessionId() const;
void onPacket(std::vector<std::uint8_t>& data) override;

View file

@ -18,6 +18,8 @@ namespace Main
std::vector<Main::Network::Session*> m_sessionsVector{};
Main::Classes::RoomsManager* roomsManager;
Common::Network::Packet prepareMessage(const std::string& message) const;
public:
void setRoomsManager(Main::Classes::RoomsManager* roomsManager);
@ -37,6 +39,9 @@ namespace Main
void broadcastToClan(std::uint64_t selfSessionId, const Common::Network::Packet& message) const;
void broadcastMessage(const std::string& message) const;
void broadcastMessageExceptSelf(std::size_t selfSessionId, const std::string& message) const;
Main::Network::Session* findSessionByName(const char* nickname);
Main::Network::Session* getSessionByAccountId(std::uint32_t aid);

View file

@ -109,6 +109,7 @@ namespace Main
playerInfoStructure.accountKey = static_cast<std::uint32_t>(query.getColumn("AccountKey").getInt());
playerInfoStructure.totalKills = static_cast<std::uint32_t>(query.getColumn("Kills").getInt());
playerInfoStructure.deaths = static_cast<std::uint32_t>(query.getColumn("Deaths").getInt());
//playerInfoStructure.assists = static_cast<std::uint32_t>(query.getColumn("Assists").getInt());
playerInfoStructure.wins = static_cast<std::uint32_t>(query.getColumn("Wins").getInt());
playerInfoStructure.losses = static_cast<std::uint32_t>(query.getColumn("Loses").getInt());
playerInfoStructure.draws = static_cast<std::uint32_t>(query.getColumn("Draws").getInt());
@ -127,6 +128,7 @@ namespace Main
playerInfoStructure.playerLevel = static_cast<std::uint64_t>(query.getColumn("Level").getInt()) + 1;
playerInfoStructure.battery = static_cast<std::uint64_t>(query.getColumn("Battery").getInt());
playerInfoStructure.luckyPoints = static_cast<std::uint64_t>(query.getColumn("LuckyPoints").getInt());
//playerInfoStructure.coins = static_cast<std::uint64_t>(query.getColumn("Coins").getInt());
playerInfoStructure.playerGrade = static_cast<std::uint64_t>(query.getColumn("Grade").getInt());
playerInfoStructure.experience = static_cast<std::uint32_t>(query.getColumn("Experience").getInt());
playerInfoStructure.microPoints = static_cast<std::uint64_t>(query.getColumn("MicroPoints").getInt64());
@ -137,6 +139,7 @@ namespace Main
playerInfoStructure.singleWaveAttempts = static_cast<std::uint32_t>(query.getColumn("SingleWaveAttempts").getInt());
playerInfoStructure.highestSinglewaveStage = static_cast<std::uint32_t>(query.getColumn("SingleWaveAttempts").getInt());
playerInfoStructure.highestSingleWaveScore = static_cast<std::uint32_t>(query.getColumn("HighestSinglewaveScore").getInt());
//playerInfoStructure.vipExperience = static_cast<std::uint32_t>(query.getColumn("VipExperience").getInt());
playerInfoStructure.clanContribution = static_cast<std::uint64_t>(query.getColumn("ClanContribution").getInt64());
playerInfoStructure.clanLogoFrontId = static_cast<std::uint64_t>(query.getColumn("ClanFrontIcon").getInt());
playerInfoStructure.clanLogoBackId = static_cast<std::uint64_t>(query.getColumn("ClanBackIcon").getInt());
@ -207,7 +210,7 @@ namespace Main
SQLite::Transaction transaction(db);
SQLite::Statement query(db, queryStr);
query.bind(1, nickname);
if (!query.exec())
{
std::cerr << "[Main::Database::UnbanPlayer] Error executing query: " << query.getExpandedSQL() << '\n';
@ -256,6 +259,7 @@ namespace Main
}
// Returing const& causes exceptions later...?
auto getPlayerItems(std::uint32_t playerID) const
-> std::pair<std::vector<Item>, std::unordered_map<std::uint16_t, std::vector<EquippedItem>>>
{
@ -281,7 +285,7 @@ namespace Main
item.serialInfo.itemNumber = ++itemNum;
SQLite::Statement updateItemNumberQuery(db, "UPDATE UserItems SET ItemNumber = :itemNumber WHERE rowid = :rowid");
updateItemNumberQuery.bind(":itemNumber", static_cast<std::int64_t>(item.serialInfo.itemNumber));
updateItemNumberQuery.bind(":rowid" ,rowId);
updateItemNumberQuery.bind(":rowid", rowId);
if (!updateItemNumberQuery.exec())
{
std::cerr << "ItemNumberUpdate error in getPlayerItems(): " << updateItemNumberQuery.getExtendedErrorCode() << '\n';
@ -302,8 +306,44 @@ namespace Main
}
item.expirationDate = static_cast<__time32_t>(newExpDate);
}
// REMOVE THIS
//Main::ConstantDatabase::CdbUtil cdbUtil(item.id);
//auto durability = *(cdbUtil.getItemDurability());
//auto duration = *(cdbUtil.getItemDuration());
////
item.durability = static_cast<std::uint16_t>(allItemsQuery.getColumn("durability").getInt());
item.energy = static_cast<std::uint16_t>(allItemsQuery.getColumn("energy").getInt());
/*item.isSealed = static_cast<std::uint32_t>(allItemsQuery.getColumn("isSealed").getInt());
item.sealLevel = static_cast<std::uint32_t>(allItemsQuery.getColumn("sealLevel").getInt());
item.experienceEnhancement = static_cast<std::uint32_t>(allItemsQuery.getColumn("expEnhancement").getInt());
item.mpEnhancement = static_cast<std::uint32_t>(allItemsQuery.getColumn("mpEnhancement").getInt());
item.unknown = static_cast<std::uint32_t>(allItemsQuery.getColumn("unknown"));
*/
// REMOVE THIS
/*
SQLite::Statement updateDurabilityQuery(db, "UPDATE UserItems SET durability = :durability WHERE rowid = :rowid");
updateDurabilityQuery.bind(":durability", durability);
updateDurabilityQuery.bind(":rowid", rowId);
if (!updateDurabilityQuery.exec())
{
std::cerr << "DurabilityUpdate error in getPlayerItems(): " << updateDurabilityQuery.getExtendedErrorCode() << '\n';
std::cerr << "Error message: " << updateDurabilityQuery.getErrorMsg() << '\n';
}
SQLite::Statement updateDuration(db, "UPDATE UserItems SET ItemDuration = :duration WHERE rowid = :rowid");
updateDuration.bind(":duration", duration);
updateDuration.bind(":rowid", rowId);
if (!updateDuration.exec())
{
std::cerr << "DurabilityUpdate error in getPlayerItems(): " << updateDuration.getExtendedErrorCode() << '\n';
std::cerr << "Error message: " << updateDuration.getErrorMsg() << '\n';
}*/
////
if (allItemsQuery.getColumn("IsEquipped").getInt() == 1)
{
Main::Structures::EquippedItem equippedItem{ item };
@ -332,7 +372,7 @@ namespace Main
}
}
void replaceItem(std::uint32_t accountID, std::uint32_t itemNum, const Main::Structures::ItemSerialInfo& newSerialInfo,
void replaceItem(std::uint32_t accountID, std::uint32_t itemNum, const Main::Structures::ItemSerialInfo& newSerialInfo,
std::uint64_t newExpiration)
{
try
@ -572,8 +612,8 @@ namespace Main
try
{
SQLite::Transaction transaction(db);
SQLite::Statement query(db, "UPDATE Users SET MeleeKills = ?, RifleKills = ?, ShotgunKills = ?, SniperKills = ?, GatlingKills = ?, "
"BazookaKills = ? , GrenadeKills = ? , HighestKillstreak = ? , Kills = ? , Deaths = ? , Headshots = ? , Assists = ?, "
SQLite::Statement query(db, "UPDATE Users SET MeleeKills = ?, RifleKills = ?, ShotgunKills = ?, SniperKills = ?, GatlingKills = ?, "
"BazookaKills = ? , GrenadeKills = ? , HighestKillstreak = ? , Kills = ? , Deaths = ? , Headshots = ? , Assists = ?, "
" Experience = ?, MicroPoints = ?, Wins = ?, Loses = ?, Draws = ?, Level = ? WHERE AccountID = ?");
query.bind(1, updatedAccountInfo.meleeKills);
@ -587,6 +627,7 @@ namespace Main
query.bind(9, updatedAccountInfo.totalKills);
query.bind(10, updatedAccountInfo.deaths);
query.bind(11, static_cast<std::uint32_t>(updatedAccountInfo.headshots));
// query.bind(12, updatedAccountInfo.assists);
query.bind(13, updatedAccountInfo.experience);
query.bind(14, static_cast<std::uint32_t>(updatedAccountInfo.microPoints));
query.bind(15, updatedAccountInfo.wins);
@ -1152,7 +1193,7 @@ namespace Main
std::cerr << "[Main::Database::addPendingFriendRequestFor] Error message: " << findPlayerByAccountId.getErrorMsg() << '\n';
return Main::Enums::AddFriendServerExtra::TARGET_NOT_FOUND;
}
// 2. Check if the target player has less than 30 friends.
SQLite::Statement findTotalFriends(db, "SELECT * FROM Friendlist WHERE AccountID = ?");
findTotalFriends.bind(1, targetAid);
@ -1163,7 +1204,7 @@ namespace Main
std::cerr << "[Main::Database::addPendingFriendRequestFor::findTotalFriends] Error message: " << findTotalFriends.getErrorMsg() << '\n';
totalCount = 0;
}
else
else
{
totalCount = 1;
while (findTotalFriends.executeStep())
@ -1313,14 +1354,14 @@ namespace Main
{
try
{
std::uint32_t accountId = 0;
std::uint32_t accountId = 0;
const std::string retrieveAccountIdQuery = "SELECT AccountID FROM Users WHERE Nickname = ?";
SQLite::Statement retrieveQuery(db, retrieveAccountIdQuery);
retrieveQuery.bind(1, mailbox.nickname);
if (retrieveQuery.executeStep())
{
accountId = retrieveQuery.getColumn("AccountID").getInt();
accountId = retrieveQuery.getColumn("AccountID").getInt();
}
else
{
@ -1337,7 +1378,7 @@ namespace Main
//query.bind(3, mailbox.uniqueId);
query.bind(4, senderNickname);
query.bind(5, mailbox.message);
query.bind(6, false);
query.bind(6, false);
query.bind(7, true);
if (!query.exec())
@ -1491,8 +1532,75 @@ namespace Main
}
}
void updatePlayerLuckyPoints(std::uint32_t accountID, std::uint32_t luckyPoints)
{
try
{
std::string updateLevelQuery = "UPDATE Users SET LuckyPoints = ? WHERE AccountID = ?";
SQLite::Transaction transaction(db);
SQLite::Statement query(db, updateLevelQuery);
query.bind(1, luckyPoints);
query.bind(2, accountID);
if (!query.exec())
{
std::cerr << "[Main::Database::updatePlayerLuckyPoints] Error executing query: " << query.getExpandedSQL() << '\n';
std::cerr << "[Main::Database::updatePlayerLuckyPoints] Error message: " << query.getErrorMsg() << '\n';
return;
}
transaction.commit();
}
catch (const std::exception& e)
{
std::cerr << "[Main::Database::updatePlayerLuckyPoints] SQLite exception: " << e.what() << '\n';
}
}
uint32_t getCapsuleJackpot()
{
uint32_t jackpot = 0;
try
{
SQLite::Statement query(db, "SELECT Value FROM GameParameters WHERE Key = \"Jackpot\"");
if (query.executeStep())
{
jackpot = static_cast<std::uint32_t>(query.getColumn("Value").getInt());
}
}
catch (const std::exception& e)
{
std::cerr << "[Main::Database::getCapsuleJackpot] SQLite exception: " << e.what() << '\n';
}
return jackpot;
}
void updateCapsuleJackpot(uint32_t jackpotValue)
{
try
{
std::string updateJackpotQuery("UPDATE GameParameters SET Value = ? WHERE Key = \"Jackpot\"");
SQLite::Transaction transaction(db);
SQLite::Statement query(db, updateJackpotQuery);
query.bind(1, jackpotValue);
if (!query.exec())
{
std::cerr << "[Main::Database::updateCapsuleJackpot] Error executing query: " << query.getExpandedSQL() << '\n';
std::cerr << "[Main::Database::updateCapsuleJackpot] Error message: " << query.getErrorMsg() << '\n';
return;
}
transaction.commit();
}
catch (const std::exception& e)
{
std::cerr << "[Main::Database::updateCapsuleJackpot] SQLite exception: " << e.what() << '\n';
}
}
};
}
}
#endif
#endif

View file

@ -13,7 +13,7 @@ namespace Main
struct CapsuleSpin
{
std::uint32_t winItemId{};
std::uint32_t unknown1{0}; // was 1 originally, try to change this and check what it does
std::uint32_t mpAmount{};
Main::Structures::ItemSerialInfo itemSerialInfo{};
CapsuleSpin()

View file

@ -44,7 +44,7 @@ namespace Main
struct RoomInfo
{
std::uint16_t roomNumber{};
std::uint16_t unknown{};
std::uint16_t unknown{2}; // seemingly always 2 for some reason
char password[8]{};
};
#pragma pack(pop)

View file

@ -7,6 +7,8 @@
#include "Classes/RoomsManager.h"
#include "Network/Packet.h"
#include <chrono>
namespace Main
{
namespace Details
@ -46,13 +48,19 @@ namespace Main
session.asyncWrite(response);
}
inline void sendPlayerState(Main::Network::Session& session, Main::Structures::UniqueId uniqueId)
inline void sendPlayerState(Main::Network::Session& session, Main::Structures::UniqueId uniqueId, Main::Classes::Room& room, std::uint32_t state = 11)
{
Common::Network::Packet response;
response.setTcpHeader(session.getId(), Common::Enums::USER_LARGE_ENCRYPTION);
response.setCommand(312, 0, 0, 2);
response.setCommand(312, 0, 0, state);
response.setData(reinterpret_cast<std::uint8_t*>(&uniqueId), sizeof(uniqueId));
session.asyncWrite(response);
room.broadcastToRoom(response);
}
inline std::uint64_t getUtcTimeMs()
{
const auto durationSinceEpoch = std::chrono::system_clock::now().time_since_epoch();
return static_cast<std::uint64_t>(duration_cast<std::chrono::milliseconds>(durationSinceEpoch).count());
}
inline void broadcastPlayerItems(Main::Classes::RoomsManager& roomsManager, Main::Network::Session& session, const Common::Network::Packet& request)

View file

@ -0,0 +1,35 @@
#include "../../include/Classes/CapsuleManager.h"
namespace Main
{
namespace Classes
{
CapsuleManager::CapsuleManager(Main::Persistence::MainScheduler& m_scheduler, Main::Persistence::PersistentDatabase& database)
: m_scheduler{ m_scheduler }
, database{ database }
{
}
std::uint32_t CapsuleManager::getJackpot()
{
m_jackpot = database.getCapsuleJackpot();
return m_jackpot;
}
void CapsuleManager::addJackpot(std::uint32_t value)
{
setJackpot(m_jackpot + value);
}
void CapsuleManager::subJackpot(std::uint32_t value)
{
setJackpot(m_jackpot - value);
}
void CapsuleManager::setJackpot(std::uint32_t value)
{
m_jackpot = value < 300000 ? 300000 : value;
m_scheduler.immediatePersist(0, &Main::Persistence::PersistentDatabase::updateCapsuleJackpot, m_jackpot);
}
}
}

View file

@ -232,6 +232,7 @@ namespace Main
m_players[bestMsPlayerIdxInMatch].second->asyncWrite(removePlayerFromRoomServerRequest);
m_players[bestMsPlayerIdxInMatch].second->leaveRoom();
broadcastToRoomExceptSelf(removePlayerFromMatchServerRequest, originalHostUniqueId);
// NOTE: This decrements all player indexes inside the client! Call it ONLY AFTER sending the packet to change the host!
Main::Handlers::notifyRoomPlayerLeaves(originalHostUniqueId, *this);
@ -372,12 +373,13 @@ namespace Main
// Remove the player from the room
playerIter->second->asyncWrite(removePlayerFromRoomServerRequest);
playerIter->second->leaveRoom();
playerIter->second->leaveRoom();
// NOTE: This decrements all player indexes in the client! Do it always after sending the change host packet!
Main::Handlers::notifyRoomPlayerLeaves(playerIter->second->getAccountInfo().uniqueId, *this);
m_players.erase(playerIter);
// unmap original host
// unmap
m_playerSessionIdToVecIdx.erase(originalSessionId);
logger.log("Removed player from room. " + playerIter->second->getPlayerInfoAsString(),
@ -649,7 +651,7 @@ namespace Main
bool Room::isHost(const Main::Structures::UniqueId& uniqueId) const
{
return m_players[0].first.uniqueId == uniqueId;
return m_players[0].first.uniqueId.session == uniqueId.session;
}
void Room::setSpecificSetting(std::uint8_t setting)
@ -784,6 +786,7 @@ namespace Main
void Room::setStateFor(const Main::Structures::UniqueId& uniqueId, const Common::Enums::PlayerState& playerState)
{
std::cout << "Changing Player State of SessionID: " << uniqueId.session << " to state: " << (uint32_t)playerState << '\n';
auto playerIt = m_playerSessionIdToVecIdx.find(uniqueId.session);
if (playerIt != m_playerSessionIdToVecIdx.end())
{
@ -801,36 +804,23 @@ namespace Main
}
}
// ABSOLUTELY DO NOT MODIFY THIS!
void Room::startMatch(const Main::Structures::UniqueId& uniqueId)
{
if (m_hasMatchStarted)
m_players[0].second->setIsInMatch(true);
setStateFor(uniqueId, static_cast<Common::Enums::PlayerState>(11));
Details::sendPlayerState(*m_players[0].second, m_players[0].second->getAccountInfo().uniqueId, *this);
for (auto& [roomInfo, session] : ranges::views::concat(m_players, m_observerPlayers))
{
for (auto& [roomInfo, session] : ranges::views::concat(m_players, m_observerPlayers))
if (roomInfo.state == Common::Enums::STATE_READY)
{
if (roomInfo.uniqueId == uniqueId)
{
Main::Details::sendPlayerState(*session, uniqueId);
setStateFor(uniqueId, static_cast<Common::Enums::PlayerState>(2));
session->setIsInMatch(true);
return;
}
Main::Details::sendPlayerState(*session, uniqueId, *this);
setStateFor(roomInfo.uniqueId, static_cast<Common::Enums::PlayerState>(11));
session->setIsInMatch(true);
}
}
else
{
m_players[0].first.state = Common::Enums::STATE_PLAYING;
m_players[0].second->setIsInMatch(true);
for (auto& [roomInfo, session] : ranges::views::concat(m_players, m_observerPlayers))
{
if (roomInfo.state == Common::Enums::STATE_READY)
{
Main::Details::sendPlayerState(*session, uniqueId);
setStateFor(uniqueId, static_cast<Common::Enums::PlayerState>(2));
session->setIsInMatch(true);
}
}
m_hasMatchStarted = true;
}
m_hasMatchStarted = true;
}
bool Room::isObserverFull() const

View file

@ -48,6 +48,7 @@
#include "../include/Boxes/MpBox.h"
#include "../include/Network/AuthSession.h"
#include "../include/Handlers/Item/ItemAndCapsuleHandler.h"
#include "../include/Handlers/Room/RoomInviteJoin.h"
namespace Main
@ -58,7 +59,9 @@ namespace Main
, m_authServerAcceptor{ io_context, tcp::endpoint(tcp::v4(), authPort) }
, m_serverId{ serverId }
, m_database{ "../ExternalLibraries/Database/GameDatabase.db" }
, m_scheduler{ 5, m_database }
, m_scheduler{ 300, m_database }
, m_capsuleManager{ m_scheduler, m_database }
{
const auto durationSinceEpoch = std::chrono::system_clock::now().time_since_epoch();
m_timeSinceLastRestart = static_cast<std::uint64_t>(duration_cast<std::chrono::milliseconds>(durationSinceEpoch).count());
@ -89,6 +92,8 @@ namespace Main
Main::Network::Session& session) { Main::Handlers::handlePing(request, session, m_roomsManager, Details::parseData<Main::ClientData::Ping>(request)); });
Common::Network::Session::addCallback<Main::Network::Session>(74, [&](const Common::Network::Packet& request,
Main::Network::Session& session) { Main::Handlers::handleCharacterSelection(request, session, m_roomsManager); });
Common::Network::Session::addCallback<Main::Network::Session>(83, [&](const Common::Network::Packet& request,
Main::Network::Session& session) { Main::Handlers::handleCapsuleReq(request, session, m_capsuleManager); });
Common::Network::Session::addCallback<Main::Network::Session>(84, [&](const Common::Network::Packet& request,
Main::Network::Session& session) { Main::Handlers::handleLobbyUserList(request, session, m_sessionsManager); });
Common::Network::Session::addCallback<Main::Network::Session>(85, [&](const Common::Network::Packet& request,
@ -106,7 +111,8 @@ namespace Main
Common::Network::Session::addCallback<Main::Network::Session>(97, [&](const Common::Network::Packet& request,
Main::Network::Session& session) { Main::Handlers::handleItemUpgrade(request, session, Details::parseData<Main::ClientData::ItemUpgrade>(request)); });
Common::Network::Session::addCallback<Main::Network::Session>(98, [&](const Common::Network::Packet& request,
Main::Network::Session& session) { Main::Handlers::handleGeneralItem(request, session, m_database, m_boxes, Details::parseData<Main::Structures::ItemSerialInfo>(request)); });
Main::Network::Session& session) { Main::Handlers::handleGeneralItem(request, session, m_database, m_boxes, Details::parseData<Main::Structures::ItemSerialInfo>(request),
m_capsuleManager, m_sessionsManager); });
Common::Network::Session::addCallback<Main::Network::Session>(99, [&](const Common::Network::Packet& request,
Main::Network::Session& session) { Main::Handlers::handleMailboxDelete(request, session, Details::parseData<std::uint32_t>(request, 4)); });
Common::Network::Session::addCallback<Main::Network::Session>(100, [&](const Common::Network::Packet& request,
@ -174,12 +180,11 @@ namespace Main
Main::Network::Session& session) { Main::Handlers::handleMapEvents(request, session, m_database); });
*/
// These 2 following handlers must be fully implemented still. Also check whether they're related to Bomb Battle (for host side)!
// Also requires to be refactored
// Invites are currently not working correctly, + refactor this handler
Common::Network::Session::addCallback<Main::Network::Session>(156, [&](const Common::Network::Packet& request,
Main::Network::Session& session) { Main::Handlers::unknown(request, session, m_roomsManager, m_sessionsManager); });
Main::Network::Session& session) { Main::Handlers::handleJoinAndInvites(request, session, m_roomsManager, m_sessionsManager); });
Common::Network::Session::addCallback<Main::Network::Session>(159, [&](const Common::Network::Packet& request,
Main::Network::Session& session) { Main::Handlers::unknown(request, session, m_roomsManager, m_sessionsManager); });
Main::Network::Session& session) { Main::Handlers::handleJoinAndInvites(request, session, m_roomsManager, m_sessionsManager); });
// This handler also needs to refactored + checked (not fully working, e.g. for level up, different match ending scoreboards, etc)

View file

@ -38,9 +38,9 @@ void initializeCdbFiles()
const std::string cdbRewardInfo = "rewardinfo.cdb";
const std::string cdbGradeInfo = "gradeinfo.cdb";
using cdbItems = Common::ConstantDatabase::CdbSingleton<Common::ConstantDatabase::CdbItemInfo>;
using cdbItems = Common::ConstantDatabase::CdbSingleton<Common::ConstantDatabase::CdbItemsInfoCMV>;
using setItems = Common::ConstantDatabase::CdbSingleton<Common::ConstantDatabase::SetItemInfo>;
using cdbWeapons = Common::ConstantDatabase::CdbSingleton<Common::ConstantDatabase::CdbWeaponInfo>;
//using cdbWeapons = Common::ConstantDatabase::CdbSingleton<Common::ConstantDatabase::CdbWeaponInfo>;
using upgradeInfos = Common::ConstantDatabase::CdbSingleton<Common::ConstantDatabase::CdbUpgradeInfo>;
using capsulePackageInfos = Common::ConstantDatabase::CdbSingleton<Common::ConstantDatabase::CdbCapsulePackageInfo>;
using capsuleInfos = Common::ConstantDatabase::CdbSingleton<Common::ConstantDatabase::CdbCapsuleInfo>;
@ -52,7 +52,7 @@ void initializeCdbFiles()
cdbItems::initialize(cdbItemInfoPath, cdbItemInfoName);
cdbItems::initializeItemTypes(cdbItemInfoPath, cdbWeaponItemInfoName, cdbItemInfoName);
setItems::initialize(cdbItemInfoPath, cdbSetItemInfoName);
cdbWeapons::initialize(cdbItemInfoPath, cdbWeaponItemInfoName);
//cdbWeapons::initialize(cdbItemInfoPath, cdbWeaponItemInfoName);
upgradeInfos::initialize(cdbItemInfoPath, cdbUpgradeInfoName);
capsuleInfos::initialize(cdbItemInfoPath, cdbCapsuleInfoName);
capsulePackageInfos::initialize(cdbItemInfoPath, cdbCapsulePackageInfoName);

View file

@ -540,11 +540,13 @@ namespace Main
void Session::addLuckyPoints(std::uint32_t points)
{
m_player.addLuckyPoints(points);
m_scheduler.immediatePersist(m_player.getAccountID(), &Persistence::PersistentDatabase::updatePlayerLuckyPoints, m_player.getAccountID(), m_player.getLuckyPoints());
}
void Session::setLuckyPoints(std::uint32_t points)
{
m_player.setLuckyPoints(points);
m_scheduler.immediatePersist(m_player.getAccountID(), &Persistence::PersistentDatabase::updatePlayerLuckyPoints, m_player.getAccountID(), m_player.getLuckyPoints());
}
std::uint32_t Session::getLuckyPoints() const

View file

@ -67,9 +67,7 @@ namespace Main
roomsManager->removeRoom(room->getRoomNumber());
}
}
m_sessionsBySessionId[sessionId]->leaveRoom();
}
m_sessionsBySessionId[sessionId]->clear();
m_sessionsBySessionId.erase(sessionId);
auto it = std::find_if(m_sessionsVector.begin(), m_sessionsVector.end(),
@ -183,5 +181,35 @@ namespace Main
}
return false;
}
Common::Network::Packet SessionsManager::prepareMessage(const std::string& message) const
{
Common::Network::Packet response;
response.setOrder(316);
response.setExtra(1);
std::string m_confirmationMessage{ std::string(16, '0') };
m_confirmationMessage += message;
response.setData(reinterpret_cast<std::uint8_t*>(m_confirmationMessage.data()), m_confirmationMessage.size());
return response;
}
void SessionsManager::broadcastMessage(const std::string& message) const
{
auto response = prepareMessage(message);
for (auto& currentSession : m_sessionsVector)
{
currentSession->asyncWrite(response);
}
}
void SessionsManager::broadcastMessageExceptSelf(std::size_t selfSessionId, const std::string& message) const
{
auto response = prepareMessage(message);
for (auto& currentSession : m_sessionsVector)
{
if (currentSession->getId() == selfSessionId) continue;
currentSession->asyncWrite(response);
}
}
};
}