mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2024-11-14 12:18:22 +00:00
24dbd3944d
* Add friends list migration * Change friends to use charID Update friends table to use charID and not LWOOBJID variant. * Fix remove friend Fix remove friend and make the query more readable at a glance. * Add and remove friends in the container Properly add and remove friends in the player container * add enums * Add best friends and basic GM support V1 * Add more features * not online / doesnt exist implementation Implements the not online and invalid character response codes * Address players not being removed Fix an issue where players would not be marked as offline in the friends list due to the message not being sent in all circumstances. Tested changes on 3 clients, switching characters, logging out from character select, switching characters, world transfer and my friends list looked as it was supposed to. * Implement proper friends system Remove debug logs Track count of best friends Add best friends list cap of 5 Add config option and best friend update Add a config option and implement the last missing best friend serialization Added comments and fixed remove best friend bug Added some comments and addressed an issue where removing best friends would not remove them from your internal count of friends. properties and logs fixes whoops, had an issue send reply if already BFFs Send the correct objectID I really need to rename these Fix white space goon * Replace queries with unique ptrs * remove user from player container on deletion Remove the user from the player container when they delete their character.
442 lines
10 KiB
C++
442 lines
10 KiB
C++
#include "PlayerContainer.h"
|
|
#include "dNetCommon.h"
|
|
#include <iostream>
|
|
#include <algorithm>
|
|
#include "Game.h"
|
|
#include "dLogger.h"
|
|
#include "ChatPacketHandler.h"
|
|
#include "GeneralUtils.h"
|
|
#include "dMessageIdentifiers.h"
|
|
#include "PacketUtils.h"
|
|
#include "Database.h"
|
|
|
|
PlayerContainer::PlayerContainer() {
|
|
}
|
|
|
|
PlayerContainer::~PlayerContainer() {
|
|
mPlayers.clear();
|
|
}
|
|
|
|
void PlayerContainer::InsertPlayer(Packet* packet) {
|
|
CINSTREAM;
|
|
PlayerData* data = new PlayerData();
|
|
inStream.Read(data->playerID);
|
|
inStream.Read(data->playerID);
|
|
inStream.Read(data->playerName);
|
|
inStream.Read(data->zoneID);
|
|
inStream.Read(data->muteExpire);
|
|
data->sysAddr = packet->systemAddress;
|
|
|
|
mNames[data->playerID] = GeneralUtils::ASCIIToUTF16(std::string(data->playerName.C_String()));
|
|
|
|
mPlayers.insert(std::make_pair(data->playerID, data));
|
|
Game::logger->Log("PlayerContainer", "Added user: %s (%llu), zone: %i\n", data->playerName.C_String(), data->playerID, data->zoneID.GetMapID());
|
|
|
|
auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
|
|
|
|
insertLog->setInt(1, data->playerID);
|
|
insertLog->setInt(2, 0);
|
|
insertLog->setUInt64(3, time(nullptr));
|
|
insertLog->setInt(4, data->zoneID.GetMapID());
|
|
|
|
insertLog->executeUpdate();
|
|
}
|
|
|
|
void PlayerContainer::RemovePlayer(Packet* packet) {
|
|
CINSTREAM;
|
|
LWOOBJID playerID;
|
|
inStream.Read(playerID); //skip header
|
|
inStream.Read(playerID);
|
|
|
|
//Before they get kicked, we need to also send a message to their friends saying that they disconnected.
|
|
auto player = this->GetPlayerData(playerID);
|
|
|
|
if (player == nullptr) {
|
|
return;
|
|
}
|
|
|
|
for (auto& fr : player->friends) {
|
|
//if (!fr.isOnline) continue;
|
|
|
|
auto fd = this->GetPlayerData(fr.friendID);
|
|
if (fd) ChatPacketHandler::SendFriendUpdate(fd, player, 0, fr.isBestFriend);
|
|
}
|
|
|
|
auto* team = GetTeam(playerID);
|
|
|
|
if (team != nullptr)
|
|
{
|
|
//TeamStatusUpdate(team);
|
|
|
|
const auto memberName = GeneralUtils::ASCIIToUTF16(std::string(player->playerName.C_String()));
|
|
|
|
for (const auto memberId : team->memberIDs)
|
|
{
|
|
auto* otherMember = GetPlayerData(memberId);
|
|
|
|
if (otherMember == nullptr) continue;
|
|
|
|
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, playerID, {0, 0, 0});
|
|
//ChatPacketHandler::SendTeamRemovePlayer(otherMember, false, false, true, false, team->leaderID, player->playerID, memberName);
|
|
}
|
|
}
|
|
|
|
Game::logger->Log("PlayerContainer", "Removed user: %llu\n", playerID);
|
|
mPlayers.erase(playerID);
|
|
|
|
auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
|
|
|
|
insertLog->setInt(1, playerID);
|
|
insertLog->setInt(2, 1);
|
|
insertLog->setUInt64(3, time(nullptr));
|
|
insertLog->setInt(4, player->zoneID.GetMapID());
|
|
|
|
insertLog->executeUpdate();
|
|
}
|
|
|
|
void PlayerContainer::MuteUpdate(Packet* packet)
|
|
{
|
|
CINSTREAM;
|
|
LWOOBJID playerID;
|
|
inStream.Read(playerID); //skip header
|
|
inStream.Read(playerID);
|
|
time_t expire = 0;
|
|
inStream.Read(expire);
|
|
|
|
auto* player = this->GetPlayerData(playerID);
|
|
|
|
if (player == nullptr)
|
|
{
|
|
Game::logger->Log("PlayerContainer", "Failed to find user: %llu\n", playerID);
|
|
|
|
return;
|
|
}
|
|
|
|
player->muteExpire = expire;
|
|
|
|
BroadcastMuteUpdate(playerID, expire);
|
|
}
|
|
|
|
void PlayerContainer::CreateTeamServer(Packet* packet)
|
|
{
|
|
CINSTREAM;
|
|
LWOOBJID playerID;
|
|
inStream.Read(playerID); //skip header
|
|
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;
|
|
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_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 = ++mTeamIDCounter;
|
|
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)
|
|
{
|
|
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::ASCIIToUTF16(std::string(leader->playerName.C_String()));
|
|
const auto memberName = GeneralUtils::ASCIIToUTF16(std::string(member->playerName.C_String()));
|
|
|
|
ChatPacketHandler::SendTeamInviteConfirm(member, false, leader->playerID, leader->zoneID, team->lootFlag, 0, 0, leaderName);
|
|
|
|
/*
|
|
ChatPacketHandler::SendTeamAddPlayer(member, false, false, false, leader->playerID, leaderName, leader->zoneID);
|
|
|
|
Game::logger->Log("PlayerContainer", "Team invite successfully accepted, leader: %s, member: %s\n", leader->playerName.C_String(), member->playerName.C_String());
|
|
*/
|
|
|
|
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::ASCIIToUTF16(std::string(otherMember->playerName.C_String()));
|
|
|
|
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::ASCIIToUTF16(std::string(leader->playerName.C_String()));
|
|
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
//ChatPacketHandler::SendTeamStatus(otherMember, LWOOBJID_EMPTY, LWOZONEID(0, 0, 0), 1, 0, u"");
|
|
}
|
|
}
|
|
|
|
UpdateTeamsOnWorld(team, false);
|
|
}
|
|
|
|
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam)
|
|
{
|
|
CBITSTREAM;
|
|
PacketUtils::WriteHeader(bitStream, CHAT_INTERNAL, MSG_CHAT_INTERNAL_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 = mNames.find(playerID);
|
|
|
|
if (pair == mNames.end()) return u"";
|
|
|
|
return pair->second;
|
|
}
|
|
|
|
LWOOBJID PlayerContainer::GetId(const std::u16string& playerName)
|
|
{
|
|
for (const auto& pair : mNames)
|
|
{
|
|
if (pair.second == playerName)
|
|
{
|
|
return pair.first;
|
|
}
|
|
}
|
|
|
|
return LWOOBJID_EMPTY;
|
|
}
|
|
|
|
bool PlayerContainer::GetIsMuted(PlayerData* data)
|
|
{
|
|
return data->muteExpire == 1 || data->muteExpire > time(NULL);
|
|
}
|