DarkflameServer/dChatServer/PlayerContainer.cpp
David Markowitz 7f623d358c
refactor: Database abstraction and organization of files (#1274)
* Database: Convert to proper namespace

* Database: Use base class and getter

* Database: Move files around

* Database: Add property Management query

Database: Move over user queries

Tested at gm 0 that pre-approved names are pre-approved, unapproved need moderator approval
deleting characters deletes the selcted one
refreshing the character page shows the last character you logged in as
tested all my characters show up when i login
tested that you can delete all 4 characters and the correct character is selected each time
tested renaming, approving names as gm0

Database: Add ugc model getter

Hey it works, look I got around the mariadb issue.

Database: Add queries

Database: consolidate name query

Database: Add friends list query

Update name of approved names query

Documentation

Database: Add name check

Database: Add BFF Query

Database: Move BFF Setter

Database: Move new friend query

Database: Add remove friend queries

Database: Add activity log

Database: Add ugc & prop content removal

Database: Add model update

Database: Add migration queries

Database: Add character and xml queries

Database: Add user queries

Untested, but compiling code

Need to test that new character names are properly assigned in the following scenarios
gm 0 and pre-approved name
gm 0 and unapproved name
gm 9 and pre-approved name
gm 9 and unapproved name

Database: constify function arguments

Database: Add pet queries

* Database: Move property model queries

Untested.  Need to test
placing a new model
moving existing one
removing ugc model
placing ugc model
moving ugc model(?)
changing privacy option variously
change description and name
approve property
can properly travel to property

* Property: Move stale reference deletion

* Database: Move performance update query

* Database: Add bug report query

* Database: Add cheat detection query

* Database: Add mail send query

* Untested code

need to test mailing from slash command, from all users of SendMail, getting bbb of a property and sending messages to bffs

* Update CDComponentsRegistryTable.h

Database: Rename and add further comments

Datavbase: Add comments

Add some comments

Build: Fix PCH directories

Database: Fix time

thanks apple

Database: Fix compiler warnings

Overload destructor
Define specialty for time_t
Use string instead of string_view for temp empty string

Update CDTable.h

Property: Update queries to use mapId

Database: Reorganize

Reorganize into CDClient folder and GameDatabase folder for clearer meanings and file structure

Folders: Rename to GameDatabase

MySQL: Remove MySQL Specifier from table

Database: Move Tables to Interfaces

Database: Reorder functions in header

Database: Simplify property queries

Database: Remove unused queries

Remove extra query definitions as well

Database: Consolidate User getters

Database: Comment logs

Update MySQLDatabase.cpp

Database: Use generic code

Playkey: Fix bad optional access

Database: Move stuff around

WorldServer: Update queries

Ugc reduced by many scopes
use new queries
very fast
tested that ugc still loads

Database: Add auth queries

I tested that only the correct password can sign into an account.
Tested that disabled playkeys do not allow the user to play the game

Database: Add donation query

Database: add objectId queries

Database: Add master queries

Database: Fix mis-named function

Database: Add slash command queries

Mail: Fix itemId type

CharFilter: Use new query

ObjectID: Remove duplicate code

SlashCommand: Update query with function

Database: Add mail queries

Ugc: Fix issues with saving models

Resolve large scope blocks as well

* Database: Add debug try catch rethrow macro

* General fixes

* fix play key not working

* Further fixes

---------

Co-authored-by: Aaron Kimbre <aronwk.aaron@gmail.com>
2023-11-17 18:47:18 -06:00

387 lines
9.9 KiB
C++

#include "PlayerContainer.h"
#include "dNetCommon.h"
#include <iostream>
#include <algorithm>
#include "Game.h"
#include "Logger.h"
#include "ChatPacketHandler.h"
#include "GeneralUtils.h"
#include "BitStreamUtils.h"
#include "Database.h"
#include "eConnectionType.h"
#include "eChatInternalMessageType.h"
#include "ChatPackets.h"
#include "dConfig.h"
PlayerContainer::PlayerContainer() {
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_best_friends"), m_MaxNumberOfBestFriends);
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_friends"), m_MaxNumberOfFriends);
}
PlayerContainer::~PlayerContainer() {
m_Players.clear();
}
TeamData::TeamData() {
lootFlag = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1;
}
void PlayerContainer::InsertPlayer(Packet* packet) {
CINSTREAM_SKIP_HEADER;
PlayerData* data = new PlayerData();
inStream.Read(data->playerID);
uint32_t len;
inStream.Read<uint32_t>(len);
for (int i = 0; i < len; i++) {
char character; inStream.Read<char>(character);
data->playerName += character;
}
inStream.Read(data->zoneID);
inStream.Read(data->muteExpire);
data->sysAddr = packet->systemAddress;
m_Names[data->playerID] = GeneralUtils::UTF8ToUTF16(data->playerName);
m_Players.insert(std::make_pair(data->playerID, data));
LOG("Added user: %s (%llu), zone: %i", data->playerName.c_str(), data->playerID, data->zoneID.GetMapID());
Database::Get()->UpdateActivityLog(data->playerID, eActivityType::PlayerLoggedIn, data->zoneID.GetMapID());
}
void PlayerContainer::RemovePlayer(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID;
inStream.Read(playerID);
//Before they get kicked, we need to also send a message to their friends saying that they disconnected.
std::unique_ptr<PlayerData> player(this->GetPlayerData(playerID));
if (player == nullptr) {
return;
}
for (auto& fr : player->friends) {
auto fd = this->GetPlayerData(fr.friendID);
if (fd) ChatPacketHandler::SendFriendUpdate(fd, player.get(), 0, fr.isBestFriend);
}
auto* team = GetTeam(playerID);
if (team != nullptr) {
const auto memberName = GeneralUtils::UTF8ToUTF16(std::string(player->playerName.c_str()));
for (const auto memberId : team->memberIDs) {
auto* otherMember = GetPlayerData(memberId);
if (otherMember == nullptr) continue;
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 });
}
}
LOG("Removed user: %llu", playerID);
m_Players.erase(playerID);
Database::Get()->UpdateActivityLog(playerID, eActivityType::PlayerLoggedOut, player->zoneID.GetMapID());
}
void PlayerContainer::MuteUpdate(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID;
inStream.Read(playerID);
time_t expire = 0;
inStream.Read(expire);
auto* player = this->GetPlayerData(playerID);
if (player == nullptr) {
LOG("Failed to find user: %llu", playerID);
return;
}
player->muteExpire = expire;
BroadcastMuteUpdate(playerID, expire);
}
void PlayerContainer::CreateTeamServer(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID;
inStream.Read(playerID);
size_t membersSize = 0;
inStream.Read(membersSize);
std::vector<LWOOBJID> members;
members.reserve(membersSize);
for (size_t i = 0; i < membersSize; i++) {
LWOOBJID member;
inStream.Read(member);
members.push_back(member);
}
LWOZONEID zoneId;
inStream.Read(zoneId);
auto* team = CreateLocalTeam(members);
if (team != nullptr) {
team->zoneId = zoneId;
}
UpdateTeamsOnWorld(team, false);
}
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE);
bitStream.Write(player);
bitStream.Write(time);
Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
}
TeamData* PlayerContainer::CreateLocalTeam(std::vector<LWOOBJID> members) {
if (members.empty()) {
return nullptr;
}
TeamData* newTeam = nullptr;
for (const auto member : members) {
auto* team = GetTeam(member);
if (team != nullptr) {
RemoveMember(team, member, false, false, true);
}
if (newTeam == nullptr) {
newTeam = CreateTeam(member, true);
} else {
AddMember(newTeam, member);
}
}
newTeam->lootFlag = 1;
TeamStatusUpdate(newTeam);
return newTeam;
}
TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) {
auto* team = new TeamData();
team->teamID = ++m_TeamIDCounter;
team->leaderID = leader;
team->local = local;
mTeams.push_back(team);
AddMember(team, leader);
return team;
}
TeamData* PlayerContainer::GetTeam(LWOOBJID playerID) {
for (auto* team : mTeams) {
if (std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID) == team->memberIDs.end()) continue;
return team;
}
return nullptr;
}
void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) {
if (team->memberIDs.size() >= 4){
LOG("Tried to add player to team that already had 4 players");
auto* player = GetPlayerData(playerID);
if (!player) return;
ChatPackets::SendSystemMessage(player->sysAddr, u"The teams is full! You have not been added to a team!");
return;
}
const auto index = std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID);
if (index != team->memberIDs.end()) return;
team->memberIDs.push_back(playerID);
auto* leader = GetPlayerData(team->leaderID);
auto* member = GetPlayerData(playerID);
if (leader == nullptr || member == nullptr) return;
const auto leaderName = GeneralUtils::UTF8ToUTF16(leader->playerName);
const auto memberName = GeneralUtils::UTF8ToUTF16(member->playerName);
ChatPacketHandler::SendTeamInviteConfirm(member, false, leader->playerID, leader->zoneID, team->lootFlag, 0, 0, leaderName);
if (!team->local) {
ChatPacketHandler::SendTeamSetLeader(member, leader->playerID);
} else {
ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY);
}
UpdateTeamsOnWorld(team, false);
for (const auto memberId : team->memberIDs) {
auto* otherMember = GetPlayerData(memberId);
if (otherMember == member) continue;
const auto otherMemberName = GetName(memberId);
ChatPacketHandler::SendTeamAddPlayer(member, false, team->local, false, memberId, otherMemberName, otherMember != nullptr ? otherMember->zoneID : LWOZONEID(0, 0, 0));
if (otherMember != nullptr) {
ChatPacketHandler::SendTeamAddPlayer(otherMember, false, team->local, false, member->playerID, memberName, member->zoneID);
}
}
}
void PlayerContainer::RemoveMember(TeamData* team, LWOOBJID playerID, bool disband, bool kicked, bool leaving, bool silent) {
const auto index = std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID);
if (index == team->memberIDs.end()) return;
auto* member = GetPlayerData(playerID);
if (member != nullptr && !silent) {
ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY);
}
const auto memberName = GetName(playerID);
for (const auto memberId : team->memberIDs) {
if (silent && memberId == playerID) {
continue;
}
auto* otherMember = GetPlayerData(memberId);
if (otherMember == nullptr) continue;
ChatPacketHandler::SendTeamRemovePlayer(otherMember, disband, kicked, leaving, false, team->leaderID, playerID, memberName);
}
team->memberIDs.erase(index);
UpdateTeamsOnWorld(team, false);
if (team->memberIDs.size() <= 1) {
DisbandTeam(team);
} else {
if (playerID == team->leaderID) {
PromoteMember(team, team->memberIDs[0]);
}
}
}
void PlayerContainer::PromoteMember(TeamData* team, LWOOBJID newLeader) {
team->leaderID = newLeader;
for (const auto memberId : team->memberIDs) {
auto* otherMember = GetPlayerData(memberId);
if (otherMember == nullptr) continue;
ChatPacketHandler::SendTeamSetLeader(otherMember, newLeader);
}
}
void PlayerContainer::DisbandTeam(TeamData* team) {
const auto index = std::find(mTeams.begin(), mTeams.end(), team);
if (index == mTeams.end()) return;
for (const auto memberId : team->memberIDs) {
auto* otherMember = GetPlayerData(memberId);
if (otherMember == nullptr) continue;
const auto memberName = GeneralUtils::UTF8ToUTF16(otherMember->playerName);
ChatPacketHandler::SendTeamSetLeader(otherMember, LWOOBJID_EMPTY);
ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, otherMember->playerID, memberName);
}
UpdateTeamsOnWorld(team, true);
mTeams.erase(index);
delete team;
}
void PlayerContainer::TeamStatusUpdate(TeamData* team) {
const auto index = std::find(mTeams.begin(), mTeams.end(), team);
if (index == mTeams.end()) return;
auto* leader = GetPlayerData(team->leaderID);
if (leader == nullptr) return;
const auto leaderName = GeneralUtils::UTF8ToUTF16(leader->playerName);
for (const auto memberId : team->memberIDs) {
auto* otherMember = GetPlayerData(memberId);
if (otherMember == nullptr) continue;
if (!team->local) {
ChatPacketHandler::SendTeamStatus(otherMember, team->leaderID, leader->zoneID, team->lootFlag, 0, leaderName);
}
}
UpdateTeamsOnWorld(team, false);
}
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::TEAM_UPDATE);
bitStream.Write(team->teamID);
bitStream.Write(deleteTeam);
if (!deleteTeam) {
bitStream.Write(team->lootFlag);
bitStream.Write(static_cast<char>(team->memberIDs.size()));
for (const auto memberID : team->memberIDs) {
bitStream.Write(memberID);
}
}
Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
}
std::u16string PlayerContainer::GetName(LWOOBJID playerID) {
const auto& pair = m_Names.find(playerID);
if (pair == m_Names.end()) return u"";
return pair->second;
}
LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) {
for (const auto& pair : m_Names) {
if (pair.second == playerName) {
return pair.first;
}
}
return LWOOBJID_EMPTY;
}
bool PlayerContainer::GetIsMuted(PlayerData* data) {
return data->muteExpire == 1 || data->muteExpire > time(NULL);
}