Skip to content

Commit

Permalink
Merge pull request #250 from DarkflameUniverse/main
Browse files Browse the repository at this point in the history
[pull] main from DarkflameUniverse:main
  • Loading branch information
leolion3 authored Sep 5, 2024
2 parents 843857c + 94b9731 commit 7f0faeb
Show file tree
Hide file tree
Showing 16 changed files with 339 additions and 27 deletions.
9 changes: 9 additions & 0 deletions dCommon/dEnums/eInventoryType.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
#define __EINVENTORYTYPE__H__

#include <cstdint>

#include "magic_enum.hpp"

static const uint8_t NUMBER_OF_INVENTORIES = 17;
/**
* Represents the different types of inventories an entity may have
Expand Down Expand Up @@ -56,4 +59,10 @@ class InventoryType {
};
};

template <>
struct magic_enum::customize::enum_range<eInventoryType> {
static constexpr int min = 0;
static constexpr int max = 16;
};

#endif //!__EINVENTORYTYPE__H__
3 changes: 3 additions & 0 deletions dDatabase/GameDatabase/ITables/IAccounts.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ class IAccounts {

// Add a new account to the database.
virtual void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) = 0;

// Update the GameMaster level of an account.
virtual void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) = 0;
};

#endif //!__IACCOUNTS__H__
1 change: 1 addition & 0 deletions dDatabase/GameDatabase/MySQL/MySQLDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ class MySQLDatabase : public GameDatabase {
void AddBehavior(const IBehaviors::Info& info) override;
std::string GetBehavior(const int32_t behaviorId) override;
void RemoveBehavior(const int32_t characterId) override;
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
private:

// Generic query functions that can be used for any query.
Expand Down
4 changes: 4 additions & 0 deletions dDatabase/GameDatabase/MySQL/Tables/Accounts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@ void MySQLDatabase::UpdateAccountPassword(const uint32_t accountId, const std::s
void MySQLDatabase::InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) {
ExecuteInsert("INSERT INTO accounts (name, password, gm_level) VALUES (?, ?, ?);", username, bcryptpassword, static_cast<int32_t>(eGameMasterLevel::OPERATOR));
}

void MySQLDatabase::UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) {
ExecuteUpdate("UPDATE accounts SET gm_level = ? WHERE id = ?;", static_cast<int32_t>(gmLevel), accountId);
}
20 changes: 20 additions & 0 deletions dGame/dBehaviors/BehaviorContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,26 @@ void BehaviorContext::UpdatePlayerSyncs(float deltaTime) {
i++;
continue;
}

if (this->skillUId != 0 && !clientInitalized) {
EchoSyncSkill echo;
echo.bDone = true;
echo.uiSkillHandle = this->skillUId;
echo.uiBehaviorHandle = entry.handle;

RakNet::BitStream bitStream{};
entry.behavior->SyncCalculation(this, bitStream, entry.branchContext);

echo.sBitStream.assign(reinterpret_cast<char*>(bitStream.GetData()), bitStream.GetNumberOfBytesUsed());

RakNet::BitStream message;
BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
message.Write(this->originator);
echo.Serialize(message);

Game::server->Send(message, UNASSIGNED_SYSTEM_ADDRESS, true);
}

this->syncEntries.erase(this->syncEntries.begin() + i);
}
}
Expand Down
10 changes: 9 additions & 1 deletion dGame/dBehaviors/DamageAbsorptionBehavior.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ void DamageAbsorptionBehavior::Handle(BehaviorContext* context, RakNet::BitStrea
destroyable->SetIsShielded(true);

context->RegisterTimerBehavior(this, branch, target->GetObjectID());

Game::entityManager->SerializeEntity(target);
}

void DamageAbsorptionBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
Expand All @@ -52,7 +54,13 @@ void DamageAbsorptionBehavior::Timer(BehaviorContext* context, BehaviorBranchCon

const auto toRemove = std::min(present, this->m_absorbAmount);

destroyable->SetDamageToAbsorb(present - toRemove);
const auto remaining = present - toRemove;

destroyable->SetDamageToAbsorb(remaining);

destroyable->SetIsShielded(remaining > 0);

Game::entityManager->SerializeEntity(target);
}

void DamageAbsorptionBehavior::Load() {
Expand Down
124 changes: 124 additions & 0 deletions dGame/dComponents/InventoryComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
#include "CDScriptComponentTable.h"
#include "CDObjectSkillsTable.h"
#include "CDSkillBehaviorTable.h"
#include "StringifiedEnum.h"

#include <ranges>

InventoryComponent::InventoryComponent(Entity* parent) : Component(parent) {
this->m_Dirty = true;
Expand Down Expand Up @@ -492,6 +495,11 @@ void InventoryComponent::LoadXml(const tinyxml2::XMLDocument& document) {
return;
}

auto* const groups = inventoryElement->FirstChildElement("grps");
if (groups) {
LoadGroupXml(*groups);
}

m_Consumable = inventoryElement->IntAttribute("csl", LOT_NULL);

auto* bag = bags->FirstChildElement();
Expand Down Expand Up @@ -630,6 +638,15 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) {
bags->LinkEndChild(bag);
}

auto* groups = inventoryElement->FirstChildElement("grps");
if (groups) {
groups->DeleteChildren();
} else {
groups = inventoryElement->InsertNewChildElement("grps");
}

UpdateGroupXml(*groups);

auto* items = inventoryElement->FirstChildElement("items");

if (items == nullptr) {
Expand Down Expand Up @@ -1603,3 +1620,110 @@ bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId) {
m_Skills.insert_or_assign(slot, skillId);
return true;
}

void InventoryComponent::UpdateGroup(const GroupUpdate& groupUpdate) {
if (groupUpdate.groupId.empty()) return;

if (groupUpdate.inventory != eInventoryType::BRICKS && groupUpdate.inventory != eInventoryType::MODELS) {
LOG("Invalid inventory type for grouping %s", StringifiedEnum::ToString(groupUpdate.inventory).data());
return;
}

auto& groups = m_Groups[groupUpdate.inventory];
auto groupItr = std::ranges::find_if(groups, [&groupUpdate](const Group& group) {
return group.groupId == groupUpdate.groupId;
});

if (groupUpdate.command != GroupUpdateCommand::ADD && groupItr == groups.end()) {
LOG("Group %s not found in inventory %s. Cannot process command.", groupUpdate.groupId.c_str(), StringifiedEnum::ToString(groupUpdate.inventory).data());
return;
}

if (groupUpdate.command == GroupUpdateCommand::ADD && groups.size() >= MaximumGroupCount) {
LOG("Cannot add group to inventory %s. Maximum group count reached.", StringifiedEnum::ToString(groupUpdate.inventory).data());
return;
}

switch (groupUpdate.command) {
case GroupUpdateCommand::ADD: {
auto& group = groups.emplace_back();
group.groupId = groupUpdate.groupId;
group.groupName = groupUpdate.groupName;
break;
}
case GroupUpdateCommand::ADD_LOT: {
groupItr->lots.insert(groupUpdate.lot);
break;
}
case GroupUpdateCommand::REMOVE: {
groups.erase(groupItr);
break;
}
case GroupUpdateCommand::REMOVE_LOT: {
groupItr->lots.erase(groupUpdate.lot);
break;
}
case GroupUpdateCommand::MODIFY: {
groupItr->groupName = groupUpdate.groupName;
break;
}
default: {
LOG("Invalid group update command %i", groupUpdate.command);
break;
}
}
}

void InventoryComponent::UpdateGroupXml(tinyxml2::XMLElement& groups) const {
for (const auto& [inventory, groupsData] : m_Groups) {
for (const auto& group : groupsData) {
auto* const groupElement = groups.InsertNewChildElement("grp");

groupElement->SetAttribute("id", group.groupId.c_str());
groupElement->SetAttribute("n", group.groupName.c_str());
groupElement->SetAttribute("t", static_cast<uint32_t>(inventory));
groupElement->SetAttribute("u", 0);
std::ostringstream lots;
bool first = true;
for (const auto lot : group.lots) {
if (!first) lots << ' ';
first = false;

lots << lot;
}
groupElement->SetAttribute("l", lots.str().c_str());
}
}
}

void InventoryComponent::LoadGroupXml(const tinyxml2::XMLElement& groups) {
auto* groupElement = groups.FirstChildElement("grp");

while (groupElement) {
const char* groupId = nullptr;
const char* groupName = nullptr;
const char* lots = nullptr;
uint32_t inventory = eInventoryType::INVALID;

groupElement->QueryStringAttribute("id", &groupId);
groupElement->QueryStringAttribute("n", &groupName);
groupElement->QueryStringAttribute("l", &lots);
groupElement->QueryAttribute("t", &inventory);

if (!groupId || !groupName || !lots) {
LOG("Failed to load group from xml id %i name %i lots %i",
groupId == nullptr, groupName == nullptr, lots == nullptr);
} else {
auto& group = m_Groups[static_cast<eInventoryType>(inventory)].emplace_back();
group.groupId = groupId;
group.groupName = groupName;

for (const auto& lotStr : GeneralUtils::SplitString(lots, ' ')) {
auto lot = GeneralUtils::TryParse<LOT>(lotStr);
if (lot) group.lots.insert(*lot);
}
}

groupElement = groupElement->NextSiblingElement("grp");
}
}
43 changes: 42 additions & 1 deletion dGame/dComponents/InventoryComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,35 @@ enum class eItemType : int32_t;
*/
class InventoryComponent final : public Component {
public:
struct Group {
// Generated ID for the group. The ID is sent by the client and has the format user_group + Math.random() * UINT_MAX.
std::string groupId;
// Custom name assigned by the user.
std::string groupName;
// All the lots the user has in the group.
std::set<LOT> lots;
};

enum class GroupUpdateCommand {
ADD,
ADD_LOT,
MODIFY,
REMOVE,
REMOVE_LOT,
};

// Based on the command, certain fields will be used or not used.
// for example, ADD_LOT wont use groupName, MODIFY wont use lots, etc.
struct GroupUpdate {
std::string groupId;
std::string groupName;
LOT lot;
eInventoryType inventory;
GroupUpdateCommand command;
};

static constexpr uint32_t MaximumGroupCount = 50;

static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
InventoryComponent(Entity* parent);

Expand Down Expand Up @@ -367,14 +396,23 @@ class InventoryComponent final : public Component {
*/
void UnequipScripts(Item* unequippedItem);

std::map<BehaviorSlot, uint32_t> GetSkills(){ return m_Skills; };
std::map<BehaviorSlot, uint32_t> GetSkills() { return m_Skills; };

bool SetSkill(int slot, uint32_t skillId);
bool SetSkill(BehaviorSlot slot, uint32_t skillId);

void UpdateGroup(const GroupUpdate& groupUpdate);
void RemoveGroup(const std::string& groupId);

~InventoryComponent() override;

private:
/**
* The key is the inventory the group belongs to, the value maps' key is the id for the group.
* This is only used for bricks and model inventories.
*/
std::map<eInventoryType, std::vector<Group>> m_Groups{ { eInventoryType::BRICKS, {} }, { eInventoryType::MODELS, {} } };

/**
* All the inventory this entity possesses
*/
Expand Down Expand Up @@ -477,6 +515,9 @@ class InventoryComponent final : public Component {
* @param document the xml doc to load from
*/
void UpdatePetXml(tinyxml2::XMLDocument& document);

void LoadGroupXml(const tinyxml2::XMLElement& groups);
void UpdateGroupXml(tinyxml2::XMLElement& groups) const;
};

#endif
34 changes: 15 additions & 19 deletions dGame/dComponents/RacingControlComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -398,25 +398,6 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu
GameMessages::SendNotifyRacingClient(
m_Parent->GetObjectID(), 2, 0, LWOOBJID_EMPTY, u"",
player->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);

auto* missionComponent = player->GetComponent<MissionComponent>();

if (missionComponent == nullptr) return;

missionComponent->Progress(eMissionTaskType::RACING, 0, static_cast<LWOOBJID>(eRacingTaskParam::COMPETED_IN_RACE)); // Progress task for competing in a race
missionComponent->Progress(eMissionTaskType::RACING, data->smashedTimes, static_cast<LWOOBJID>(eRacingTaskParam::SAFE_DRIVER)); // Finish a race without being smashed.

// If solo racing is enabled OR if there are 3 players in the race, progress placement tasks.
if (m_SoloRacing || m_LoadedPlayers > 2) {
missionComponent->Progress(eMissionTaskType::RACING, data->finished, static_cast<LWOOBJID>(eRacingTaskParam::FINISH_WITH_PLACEMENT)); // Finish in 1st place on a race
if (data->finished == 1) {
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::FIRST_PLACE_MULTIPLE_TRACKS)); // Finish in 1st place on multiple tracks.
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::WIN_RACE_IN_WORLD)); // Finished first place in specific world.
}
if (data->finished == m_LoadedPlayers) {
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::LAST_PLACE_FINISH)); // Finished first place in specific world.
}
}
} else if ((id == "ACT_RACE_EXIT_THE_RACE?" || id == "Exit") && button == m_ActivityExitConfirm) {
auto* vehicle = Game::entityManager->GetEntity(data->vehicleID);

Expand Down Expand Up @@ -870,6 +851,21 @@ void RacingControlComponent::Update(float deltaTime) {
// Entire race time
missionComponent->Progress(eMissionTaskType::RACING, (raceTime) * 1000, static_cast<LWOOBJID>(eRacingTaskParam::TOTAL_TRACK_TIME));

missionComponent->Progress(eMissionTaskType::RACING, 0, static_cast<LWOOBJID>(eRacingTaskParam::COMPETED_IN_RACE)); // Progress task for competing in a race
missionComponent->Progress(eMissionTaskType::RACING, player.smashedTimes, static_cast<LWOOBJID>(eRacingTaskParam::SAFE_DRIVER)); // Finish a race without being smashed.

// If solo racing is enabled OR if there are 3 players in the race, progress placement tasks.
if (m_SoloRacing || m_RacingPlayers.size() > 2) {
missionComponent->Progress(eMissionTaskType::RACING, player.finished, static_cast<LWOOBJID>(eRacingTaskParam::FINISH_WITH_PLACEMENT)); // Finish in 1st place on a race
if (player.finished == 1) {
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::FIRST_PLACE_MULTIPLE_TRACKS)); // Finish in 1st place on multiple tracks.
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::WIN_RACE_IN_WORLD)); // Finished first place in specific world.
}
if (player.finished == m_RacingPlayers.size()) {
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::LAST_PLACE_FINISH)); // Finished first place in specific world.
}
}

auto* characterComponent = playerEntity->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) {
characterComponent->TrackRaceCompleted(m_Finished == 1);
Expand Down
7 changes: 7 additions & 0 deletions dGame/dGameMessages/GameMessageHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,13 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System
case eGameMessageType::REQUEST_VENDOR_STATUS_UPDATE:
GameMessages::SendVendorStatusUpdate(entity, sysAddr, true);
break;
case eGameMessageType::UPDATE_INVENTORY_GROUP:
GameMessages::HandleUpdateInventoryGroup(inStream, entity, sysAddr);
break;
case eGameMessageType::UPDATE_INVENTORY_GROUP_CONTENTS:
GameMessages::HandleUpdateInventoryGroupContents(inStream, entity, sysAddr);
break;

default:
LOG_DEBUG("Received Unknown GM with ID: %4i, %s", messageID, StringifiedEnum::ToString(messageID).data());
break;
Expand Down
Loading

0 comments on commit 7f0faeb

Please sign in to comment.