mirror of
				https://github.com/DarkflameUniverse/DarkflameServer.git
				synced 2025-11-04 14:42:02 +00:00 
			
		
		
		
	chore: move team logic to separate container
Makes it easier to follow team logic when you're not bouncing between 3 classes in 3 files. Consolidates all team logic to 1 namespace in TeamContainer. No logic changes were done, only renaming and fixing errors from the moving. TeamData should be replaced with unique_ptrs at some point so the Shutdown method can be removed from TeamContainer.
This commit is contained in:
		@@ -1,9 +1,10 @@
 | 
			
		||||
set(DCHATSERVER_SOURCES
 | 
			
		||||
	"ChatIgnoreList.cpp"
 | 
			
		||||
	"ChatPacketHandler.cpp"
 | 
			
		||||
	"PlayerContainer.cpp"
 | 
			
		||||
	"ChatWebAPI.cpp"
 | 
			
		||||
	"JSONUtils.cpp"
 | 
			
		||||
	"PlayerContainer.cpp"
 | 
			
		||||
	"TeamContainer.cpp"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_executable(ChatServer "ChatServer.cpp")
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@
 | 
			
		||||
#include "StringifiedEnum.h"
 | 
			
		||||
#include "eGameMasterLevel.h"
 | 
			
		||||
#include "ChatPackets.h"
 | 
			
		||||
#include "TeamContainer.h"
 | 
			
		||||
 | 
			
		||||
void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
 | 
			
		||||
	//Get from the packet which player we want to do something with:
 | 
			
		||||
@@ -447,7 +448,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
 | 
			
		||||
 | 
			
		||||
	switch (channel) {
 | 
			
		||||
	case eChatChannel::TEAM: {
 | 
			
		||||
		auto* team = Game::playerContainer.GetTeam(playerID);
 | 
			
		||||
		auto* team = TeamContainer::GetTeam(playerID);
 | 
			
		||||
		if (team == nullptr) return;
 | 
			
		||||
 | 
			
		||||
		for (const auto memberId : team->memberIDs) {
 | 
			
		||||
@@ -563,400 +564,6 @@ void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const P
 | 
			
		||||
	SEND_PACKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void ChatPacketHandler::HandleTeamInvite(Packet* packet) {
 | 
			
		||||
	CINSTREAM_SKIP_HEADER;
 | 
			
		||||
 | 
			
		||||
	LWOOBJID playerID;
 | 
			
		||||
	LUWString invitedPlayer;
 | 
			
		||||
 | 
			
		||||
	inStream.Read(playerID);
 | 
			
		||||
	inStream.IgnoreBytes(4);
 | 
			
		||||
	inStream.Read(invitedPlayer);
 | 
			
		||||
 | 
			
		||||
	const auto& player = Game::playerContainer.GetPlayerData(playerID);
 | 
			
		||||
 | 
			
		||||
	if (!player) return;
 | 
			
		||||
 | 
			
		||||
	auto* team = Game::playerContainer.GetTeam(playerID);
 | 
			
		||||
 | 
			
		||||
	if (team == nullptr) {
 | 
			
		||||
		team = Game::playerContainer.CreateTeam(playerID);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const auto& other = Game::playerContainer.GetPlayerData(invitedPlayer.GetAsString());
 | 
			
		||||
 | 
			
		||||
	if (!other) return;
 | 
			
		||||
 | 
			
		||||
	if (Game::playerContainer.GetTeam(other.playerID) != nullptr) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (team->memberIDs.size() > 3) {
 | 
			
		||||
		// no more teams greater than 4
 | 
			
		||||
 | 
			
		||||
		LOG("Someone tried to invite a 5th player to a team");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	SendTeamInvite(other, player);
 | 
			
		||||
 | 
			
		||||
	LOG("Got team invite: %llu -> %s", playerID, invitedPlayer.GetAsString().c_str());
 | 
			
		||||
 | 
			
		||||
	bool failed = false;
 | 
			
		||||
	for (const auto& ignore : other.ignoredPlayers) {
 | 
			
		||||
		if (ignore.playerId == player.playerID) {
 | 
			
		||||
			failed = true;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	ChatPackets::TeamInviteInitialResponse response{};
 | 
			
		||||
	response.inviteFailedToSend = failed;
 | 
			
		||||
	response.playerName = invitedPlayer.string;
 | 
			
		||||
	ChatPackets::SendRoutedMsg(response, playerID, player.worldServerSysAddr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) {
 | 
			
		||||
	CINSTREAM_SKIP_HEADER;
 | 
			
		||||
	LWOOBJID playerID = LWOOBJID_EMPTY;
 | 
			
		||||
	inStream.Read(playerID);
 | 
			
		||||
	uint32_t size = 0;
 | 
			
		||||
	inStream.Read(size);
 | 
			
		||||
	char declined = 0;
 | 
			
		||||
	inStream.Read(declined);
 | 
			
		||||
	LWOOBJID leaderID = LWOOBJID_EMPTY;
 | 
			
		||||
	inStream.Read(leaderID);
 | 
			
		||||
 | 
			
		||||
	LOG("Invite reponse received: %llu -> %llu (%d)", playerID, leaderID, declined);
 | 
			
		||||
 | 
			
		||||
	if (declined) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto* team = Game::playerContainer.GetTeam(leaderID);
 | 
			
		||||
 | 
			
		||||
	if (team == nullptr) {
 | 
			
		||||
		LOG("Failed to find team for leader (%llu)", leaderID);
 | 
			
		||||
 | 
			
		||||
		team = Game::playerContainer.GetTeam(playerID);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (team == nullptr) {
 | 
			
		||||
		LOG("Failed to find team for player (%llu)", playerID);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Game::playerContainer.AddMember(team, playerID);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ChatPacketHandler::HandleTeamLeave(Packet* packet) {
 | 
			
		||||
	CINSTREAM_SKIP_HEADER;
 | 
			
		||||
	LWOOBJID playerID = LWOOBJID_EMPTY;
 | 
			
		||||
	inStream.Read(playerID);
 | 
			
		||||
	uint32_t size = 0;
 | 
			
		||||
	inStream.Read(size);
 | 
			
		||||
 | 
			
		||||
	auto* team = Game::playerContainer.GetTeam(playerID);
 | 
			
		||||
 | 
			
		||||
	LOG("(%llu) leaving team", playerID);
 | 
			
		||||
 | 
			
		||||
	if (team != nullptr) {
 | 
			
		||||
		Game::playerContainer.RemoveMember(team, playerID, false, false, true);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ChatPacketHandler::HandleTeamKick(Packet* packet) {
 | 
			
		||||
	CINSTREAM_SKIP_HEADER;
 | 
			
		||||
 | 
			
		||||
	LWOOBJID playerID = LWOOBJID_EMPTY;
 | 
			
		||||
	LUWString kickedPlayer;
 | 
			
		||||
 | 
			
		||||
	inStream.Read(playerID);
 | 
			
		||||
	inStream.IgnoreBytes(4);
 | 
			
		||||
	inStream.Read(kickedPlayer);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	LOG("(%llu) kicking (%s) from team", playerID, kickedPlayer.GetAsString().c_str());
 | 
			
		||||
 | 
			
		||||
	const auto& kicked = Game::playerContainer.GetPlayerData(kickedPlayer.GetAsString());
 | 
			
		||||
 | 
			
		||||
	LWOOBJID kickedId = LWOOBJID_EMPTY;
 | 
			
		||||
 | 
			
		||||
	if (kicked) {
 | 
			
		||||
		kickedId = kicked.playerID;
 | 
			
		||||
	} else {
 | 
			
		||||
		kickedId = Game::playerContainer.GetId(kickedPlayer.string);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (kickedId == LWOOBJID_EMPTY) return;
 | 
			
		||||
 | 
			
		||||
	auto* team = Game::playerContainer.GetTeam(playerID);
 | 
			
		||||
 | 
			
		||||
	if (team != nullptr) {
 | 
			
		||||
		if (team->leaderID != playerID || team->leaderID == kickedId) return;
 | 
			
		||||
 | 
			
		||||
		Game::playerContainer.RemoveMember(team, kickedId, false, true, false);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ChatPacketHandler::HandleTeamPromote(Packet* packet) {
 | 
			
		||||
	CINSTREAM_SKIP_HEADER;
 | 
			
		||||
 | 
			
		||||
	LWOOBJID playerID = LWOOBJID_EMPTY;
 | 
			
		||||
	LUWString promotedPlayer;
 | 
			
		||||
 | 
			
		||||
	inStream.Read(playerID);
 | 
			
		||||
	inStream.IgnoreBytes(4);
 | 
			
		||||
	inStream.Read(promotedPlayer);
 | 
			
		||||
 | 
			
		||||
	LOG("(%llu) promoting (%s) to team leader", playerID, promotedPlayer.GetAsString().c_str());
 | 
			
		||||
 | 
			
		||||
	const auto& promoted = Game::playerContainer.GetPlayerData(promotedPlayer.GetAsString());
 | 
			
		||||
 | 
			
		||||
	if (!promoted) return;
 | 
			
		||||
 | 
			
		||||
	auto* team = Game::playerContainer.GetTeam(playerID);
 | 
			
		||||
 | 
			
		||||
	if (team != nullptr) {
 | 
			
		||||
		if (team->leaderID != playerID) return;
 | 
			
		||||
 | 
			
		||||
		Game::playerContainer.PromoteMember(team, promoted.playerID);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ChatPacketHandler::HandleTeamLootOption(Packet* packet) {
 | 
			
		||||
	CINSTREAM_SKIP_HEADER;
 | 
			
		||||
	LWOOBJID playerID = LWOOBJID_EMPTY;
 | 
			
		||||
	inStream.Read(playerID);
 | 
			
		||||
	uint32_t size = 0;
 | 
			
		||||
	inStream.Read(size);
 | 
			
		||||
 | 
			
		||||
	char option;
 | 
			
		||||
	inStream.Read(option);
 | 
			
		||||
 | 
			
		||||
	auto* team = Game::playerContainer.GetTeam(playerID);
 | 
			
		||||
 | 
			
		||||
	if (team != nullptr) {
 | 
			
		||||
		if (team->leaderID != playerID) return;
 | 
			
		||||
 | 
			
		||||
		team->lootFlag = option;
 | 
			
		||||
 | 
			
		||||
		Game::playerContainer.TeamStatusUpdate(team);
 | 
			
		||||
 | 
			
		||||
		Game::playerContainer.UpdateTeamsOnWorld(team, false);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
 | 
			
		||||
	CINSTREAM_SKIP_HEADER;
 | 
			
		||||
	LWOOBJID playerID = LWOOBJID_EMPTY;
 | 
			
		||||
	inStream.Read(playerID);
 | 
			
		||||
 | 
			
		||||
	auto* team = Game::playerContainer.GetTeam(playerID);
 | 
			
		||||
	const auto& data = Game::playerContainer.GetPlayerData(playerID);
 | 
			
		||||
 | 
			
		||||
	if (team != nullptr && data) {
 | 
			
		||||
		LOG_DEBUG("Player %llu is requesting team status", playerID);
 | 
			
		||||
		if (team->local && data.zoneID.GetMapID() != team->zoneId.GetMapID() && data.zoneID.GetCloneID() != team->zoneId.GetCloneID()) {
 | 
			
		||||
			Game::playerContainer.RemoveMember(team, playerID, false, false, false, true);
 | 
			
		||||
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (team->memberIDs.size() <= 1 && !team->local) {
 | 
			
		||||
			Game::playerContainer.DisbandTeam(team, LWOOBJID_EMPTY, u"");
 | 
			
		||||
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!team->local) {
 | 
			
		||||
			ChatPacketHandler::SendTeamSetLeader(data, team->leaderID);
 | 
			
		||||
		} else {
 | 
			
		||||
			ChatPacketHandler::SendTeamSetLeader(data, LWOOBJID_EMPTY);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Game::playerContainer.TeamStatusUpdate(team);
 | 
			
		||||
 | 
			
		||||
		const auto leaderName = GeneralUtils::UTF8ToUTF16(data.playerName);
 | 
			
		||||
 | 
			
		||||
		for (const auto memberId : team->memberIDs) {
 | 
			
		||||
			const auto& otherMember = Game::playerContainer.GetPlayerData(memberId);
 | 
			
		||||
 | 
			
		||||
			if (memberId == playerID) continue;
 | 
			
		||||
 | 
			
		||||
			const auto memberName = Game::playerContainer.GetName(memberId);
 | 
			
		||||
 | 
			
		||||
			if (otherMember) {
 | 
			
		||||
				ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, data.playerID, data.zoneID);
 | 
			
		||||
			}
 | 
			
		||||
			ChatPacketHandler::SendTeamAddPlayer(data, false, team->local, false, memberId, memberName, otherMember ? otherMember.zoneID : LWOZONEID(0, 0, 0));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		Game::playerContainer.UpdateTeamsOnWorld(team, false);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerData& sender) {
 | 
			
		||||
	CBITSTREAM;
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
 | 
			
		||||
	//portion that will get routed:
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::TEAM_INVITE);
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(LUWString(sender.playerName.c_str()));
 | 
			
		||||
	bitStream.Write(sender.playerID);
 | 
			
		||||
 | 
			
		||||
	SystemAddress sysAddr = receiver.worldServerSysAddr;
 | 
			
		||||
	SEND_PACKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ChatPacketHandler::SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName) {
 | 
			
		||||
	CBITSTREAM;
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
 | 
			
		||||
	//portion that will get routed:
 | 
			
		||||
	CMSGHEADER;
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
	bitStream.Write(MessageType::Game::TEAM_INVITE_CONFIRM);
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(bLeaderIsFreeTrial);
 | 
			
		||||
	bitStream.Write(i64LeaderID);
 | 
			
		||||
	bitStream.Write(i64LeaderZoneID);
 | 
			
		||||
	bitStream.Write<uint32_t>(0); // BinaryBuffe, no clue what's in here
 | 
			
		||||
	bitStream.Write(ucLootFlag);
 | 
			
		||||
	bitStream.Write(ucNumOfOtherPlayers);
 | 
			
		||||
	bitStream.Write(ucResponseCode);
 | 
			
		||||
	bitStream.Write<uint32_t>(wsLeaderName.size());
 | 
			
		||||
	for (const auto character : wsLeaderName) {
 | 
			
		||||
		bitStream.Write(character);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	SystemAddress sysAddr = receiver.worldServerSysAddr;
 | 
			
		||||
	SEND_PACKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) {
 | 
			
		||||
	CBITSTREAM;
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
 | 
			
		||||
	//portion that will get routed:
 | 
			
		||||
	CMSGHEADER;
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
	bitStream.Write(MessageType::Game::TEAM_GET_STATUS_RESPONSE);
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(i64LeaderID);
 | 
			
		||||
	bitStream.Write(i64LeaderZoneID);
 | 
			
		||||
	bitStream.Write<uint32_t>(0); // BinaryBuffe, no clue what's in here
 | 
			
		||||
	bitStream.Write(ucLootFlag);
 | 
			
		||||
	bitStream.Write(ucNumOfOtherPlayers);
 | 
			
		||||
	bitStream.Write<uint32_t>(wsLeaderName.size());
 | 
			
		||||
	for (const auto character : wsLeaderName) {
 | 
			
		||||
		bitStream.Write(character);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	SystemAddress sysAddr = receiver.worldServerSysAddr;
 | 
			
		||||
	SEND_PACKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID) {
 | 
			
		||||
	CBITSTREAM;
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
 | 
			
		||||
	//portion that will get routed:
 | 
			
		||||
	CMSGHEADER;
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
	bitStream.Write(MessageType::Game::TEAM_SET_LEADER);
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(i64PlayerID);
 | 
			
		||||
 | 
			
		||||
	SystemAddress sysAddr = receiver.worldServerSysAddr;
 | 
			
		||||
	SEND_PACKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) {
 | 
			
		||||
	CBITSTREAM;
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
 | 
			
		||||
	//portion that will get routed:
 | 
			
		||||
	CMSGHEADER;
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
	bitStream.Write(MessageType::Game::TEAM_ADD_PLAYER);
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(bIsFreeTrial);
 | 
			
		||||
	bitStream.Write(bLocal);
 | 
			
		||||
	bitStream.Write(bNoLootOnDeath);
 | 
			
		||||
	bitStream.Write(i64PlayerID);
 | 
			
		||||
	bitStream.Write<uint32_t>(wsPlayerName.size());
 | 
			
		||||
	for (const auto character : wsPlayerName) {
 | 
			
		||||
		bitStream.Write(character);
 | 
			
		||||
	}
 | 
			
		||||
	bitStream.Write1();
 | 
			
		||||
	if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) {
 | 
			
		||||
		zoneID = LWOZONEID(zoneID.GetMapID(), zoneID.GetInstanceID(), 0);
 | 
			
		||||
	}
 | 
			
		||||
	bitStream.Write(zoneID);
 | 
			
		||||
 | 
			
		||||
	SystemAddress sysAddr = receiver.worldServerSysAddr;
 | 
			
		||||
	SEND_PACKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) {
 | 
			
		||||
	CBITSTREAM;
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
 | 
			
		||||
	//portion that will get routed:
 | 
			
		||||
	CMSGHEADER;
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
	bitStream.Write(MessageType::Game::TEAM_REMOVE_PLAYER);
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(bDisband);
 | 
			
		||||
	bitStream.Write(bIsKicked);
 | 
			
		||||
	bitStream.Write(bIsLeaving);
 | 
			
		||||
	bitStream.Write(bLocal);
 | 
			
		||||
	bitStream.Write(i64LeaderID);
 | 
			
		||||
	bitStream.Write(i64PlayerID);
 | 
			
		||||
	bitStream.Write<uint32_t>(wsPlayerName.size());
 | 
			
		||||
	for (const auto character : wsPlayerName) {
 | 
			
		||||
		bitStream.Write(character);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	SystemAddress sysAddr = receiver.worldServerSysAddr;
 | 
			
		||||
	SEND_PACKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ChatPacketHandler::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) {
 | 
			
		||||
	CBITSTREAM;
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
 | 
			
		||||
	//portion that will get routed:
 | 
			
		||||
	CMSGHEADER;
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
	bitStream.Write(MessageType::Game::TEAM_SET_OFF_WORLD_FLAG);
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(i64PlayerID);
 | 
			
		||||
	if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) {
 | 
			
		||||
		zoneID = LWOZONEID(zoneID.GetMapID(), zoneID.GetInstanceID(), 0);
 | 
			
		||||
	}
 | 
			
		||||
	bitStream.Write(zoneID);
 | 
			
		||||
 | 
			
		||||
	SystemAddress sysAddr = receiver.worldServerSysAddr;
 | 
			
		||||
	SEND_PACKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ChatPacketHandler::SendFriendUpdate(const PlayerData& friendData, const PlayerData& playerData, uint8_t notifyType, uint8_t isBestFriend) {
 | 
			
		||||
	/*chat notification is displayed if log in / out and friend is updated in friends list
 | 
			
		||||
		[u8] - update type
 | 
			
		||||
 
 | 
			
		||||
@@ -35,13 +35,13 @@ enum class eChatChannel : uint8_t {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
enum class eChatMessageResponseCode : uint8_t {
 | 
			
		||||
    SENT = 0,
 | 
			
		||||
    NOTONLINE,
 | 
			
		||||
    GENERALERROR,
 | 
			
		||||
    RECEIVEDNEWWHISPER,
 | 
			
		||||
    NOTFRIENDS,
 | 
			
		||||
    SENDERFREETRIAL,
 | 
			
		||||
    RECEIVERFREETRIAL,
 | 
			
		||||
	SENT = 0,
 | 
			
		||||
	NOTONLINE,
 | 
			
		||||
	GENERALERROR,
 | 
			
		||||
	RECEIVEDNEWWHISPER,
 | 
			
		||||
	NOTFRIENDS,
 | 
			
		||||
	SENDERFREETRIAL,
 | 
			
		||||
	RECEIVERFREETRIAL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace ChatPacketHandler {
 | 
			
		||||
@@ -52,33 +52,14 @@ namespace ChatPacketHandler {
 | 
			
		||||
	void HandleGMLevelUpdate(Packet* packet);
 | 
			
		||||
	void HandleWho(Packet* packet);
 | 
			
		||||
	void HandleShowAll(Packet* packet);
 | 
			
		||||
 | 
			
		||||
	void HandleChatMessage(Packet* packet);
 | 
			
		||||
	void HandlePrivateChatMessage(Packet* packet);
 | 
			
		||||
	void SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode);
 | 
			
		||||
 | 
			
		||||
	void HandleTeamInvite(Packet* packet);
 | 
			
		||||
	void HandleTeamInviteResponse(Packet* packet);
 | 
			
		||||
	void HandleTeamLeave(Packet* packet);
 | 
			
		||||
	void HandleTeamKick(Packet* packet);
 | 
			
		||||
	void HandleTeamPromote(Packet* packet);
 | 
			
		||||
	void HandleTeamLootOption(Packet* packet);
 | 
			
		||||
	void HandleTeamStatusRequest(Packet* packet);
 | 
			
		||||
	void OnAchievementNotify(RakNet::BitStream& bitstream, const SystemAddress& sysAddr);
 | 
			
		||||
 | 
			
		||||
	void SendTeamInvite(const PlayerData& receiver, const PlayerData& sender);
 | 
			
		||||
	void SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName);
 | 
			
		||||
	void SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName);
 | 
			
		||||
	void SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID);
 | 
			
		||||
	void SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID);
 | 
			
		||||
 | 
			
		||||
	/* Sends a message to the provided `receiver` with information about the updated team. If `i64LeaderID` is not LWOOBJID_EMPTY, the client will update the leader to that new playerID. */
 | 
			
		||||
	void SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName);
 | 
			
		||||
	void SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID);
 | 
			
		||||
 | 
			
		||||
	//FriendData is the player we're SENDING this stuff to. Player is the friend that changed state.
 | 
			
		||||
	void SendFriendUpdate(const PlayerData& friendData, const PlayerData& playerData, uint8_t notifyType, uint8_t isBestFriend);
 | 
			
		||||
 | 
			
		||||
	void SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode);
 | 
			
		||||
	void SendFriendRequest(const PlayerData& receiver, const PlayerData& sender);
 | 
			
		||||
	void SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready = 0U, uint8_t isBestFriendRequest = 0U);
 | 
			
		||||
	void SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful);
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@
 | 
			
		||||
#include "MessageType/World.h"
 | 
			
		||||
#include "ChatIgnoreList.h"
 | 
			
		||||
#include "StringifiedEnum.h"
 | 
			
		||||
#include "TeamContainer.h"
 | 
			
		||||
 | 
			
		||||
#include "Game.h"
 | 
			
		||||
#include "Server.h"
 | 
			
		||||
@@ -95,7 +96,7 @@ int main(int argc, char** argv) {
 | 
			
		||||
	// seyup the chat api web server
 | 
			
		||||
	bool web_server_enabled = Game::config->GetValue("web_server_enabled") == "1";
 | 
			
		||||
	ChatWebAPI chatwebapi;
 | 
			
		||||
	if (web_server_enabled && !chatwebapi.Startup()){
 | 
			
		||||
	if (web_server_enabled && !chatwebapi.Startup()) {
 | 
			
		||||
		// if we want the web api and it fails to start, exit
 | 
			
		||||
		LOG("Failed to start web server, shutting down.");
 | 
			
		||||
		Database::Destroy("ChatServer");
 | 
			
		||||
@@ -197,6 +198,7 @@ int main(int argc, char** argv) {
 | 
			
		||||
		std::this_thread::sleep_until(t);
 | 
			
		||||
	}
 | 
			
		||||
	Game::playerContainer.Shutdown();
 | 
			
		||||
	TeamContainer::Shutdown();
 | 
			
		||||
	//Delete our objects here:
 | 
			
		||||
	Database::Destroy("ChatServer");
 | 
			
		||||
	delete Game::server;
 | 
			
		||||
@@ -234,7 +236,7 @@ void HandlePacket(Packet* packet) {
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MessageType::Chat::CREATE_TEAM:
 | 
			
		||||
		Game::playerContainer.CreateTeamServer(packet);
 | 
			
		||||
		TeamContainer::CreateTeamServer(packet);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MessageType::Chat::GET_FRIENDS_LIST:
 | 
			
		||||
@@ -254,7 +256,7 @@ void HandlePacket(Packet* packet) {
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MessageType::Chat::TEAM_GET_STATUS:
 | 
			
		||||
		ChatPacketHandler::HandleTeamStatusRequest(packet);
 | 
			
		||||
		TeamContainer::HandleTeamStatusRequest(packet);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MessageType::Chat::ADD_FRIEND_REQUEST:
 | 
			
		||||
@@ -284,27 +286,27 @@ void HandlePacket(Packet* packet) {
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MessageType::Chat::TEAM_INVITE:
 | 
			
		||||
		ChatPacketHandler::HandleTeamInvite(packet);
 | 
			
		||||
		TeamContainer::HandleTeamInvite(packet);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MessageType::Chat::TEAM_INVITE_RESPONSE:
 | 
			
		||||
		ChatPacketHandler::HandleTeamInviteResponse(packet);
 | 
			
		||||
		TeamContainer::HandleTeamInviteResponse(packet);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MessageType::Chat::TEAM_LEAVE:
 | 
			
		||||
		ChatPacketHandler::HandleTeamLeave(packet);
 | 
			
		||||
		TeamContainer::HandleTeamLeave(packet);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MessageType::Chat::TEAM_SET_LEADER:
 | 
			
		||||
		ChatPacketHandler::HandleTeamPromote(packet);
 | 
			
		||||
		TeamContainer::HandleTeamPromote(packet);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MessageType::Chat::TEAM_KICK:
 | 
			
		||||
		ChatPacketHandler::HandleTeamKick(packet);
 | 
			
		||||
		TeamContainer::HandleTeamKick(packet);
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case MessageType::Chat::TEAM_SET_LOOT:
 | 
			
		||||
		ChatPacketHandler::HandleTeamLootOption(packet);
 | 
			
		||||
		TeamContainer::HandleTeamLootOption(packet);
 | 
			
		||||
		break;
 | 
			
		||||
	case MessageType::Chat::GMLEVEL_UPDATE:
 | 
			
		||||
		ChatPacketHandler::HandleGMLevelUpdate(packet);
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ typedef struct mg_http_message mg_http_message;
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	const char* json_content_type = "Content-Type: application/json\r\n";
 | 
			
		||||
	std::map<std::pair<eHTTPMethod, std::string>, WebAPIHTTPRoute> Routes {};
 | 
			
		||||
	std::map<std::pair<eHTTPMethod, std::string>, WebAPIHTTPRoute> Routes{};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ValidateAuthentication(const mg_http_message* http_msg) {
 | 
			
		||||
@@ -54,7 +54,7 @@ void HandlePlayersRequest(HTTPReply& reply, std::string body) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HandleTeamsRequest(HTTPReply& reply, std::string body) {
 | 
			
		||||
	const json data = Game::playerContainer.GetTeamContainer();
 | 
			
		||||
	const json data = TeamContainer::GetTeamContainer();
 | 
			
		||||
	reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK;
 | 
			
		||||
	reply.message = data.empty() ? "{\"error\":\"No Teams Online\"}" : data.dump();
 | 
			
		||||
}
 | 
			
		||||
@@ -87,12 +87,12 @@ void HandleInvalidRoute(HTTPReply& reply) {
 | 
			
		||||
 | 
			
		||||
void HandleHTTPMessage(mg_connection* connection, const mg_http_message* http_msg) {
 | 
			
		||||
	HTTPReply reply;
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	if (!http_msg) {
 | 
			
		||||
		reply.status = eHTTPStatusCode::BAD_REQUEST;
 | 
			
		||||
		reply.message = "{\"error\":\"Invalid Request\"}";
 | 
			
		||||
	} else if (ValidateAuthentication(http_msg)) {
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		// convert method from cstring to std string
 | 
			
		||||
		std::string method_string(http_msg->method.buf, http_msg->method.len);
 | 
			
		||||
		// get mehtod from mg to enum
 | 
			
		||||
@@ -105,8 +105,8 @@ void HandleHTTPMessage(mg_connection* connection, const mg_http_message* http_ms
 | 
			
		||||
		// convert body from cstring to std string
 | 
			
		||||
		std::string body(http_msg->body.buf, http_msg->body.len);
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
		const auto routeItr = Routes.find({method, uri});
 | 
			
		||||
 | 
			
		||||
		const auto routeItr = Routes.find({ method, uri });
 | 
			
		||||
 | 
			
		||||
		if (routeItr != Routes.end()) {
 | 
			
		||||
			const auto& [_, route] = *routeItr;
 | 
			
		||||
@@ -122,11 +122,11 @@ void HandleHTTPMessage(mg_connection* connection, const mg_http_message* http_ms
 | 
			
		||||
 | 
			
		||||
void HandleRequests(mg_connection* connection, int request, void* request_data) {
 | 
			
		||||
	switch (request) {
 | 
			
		||||
		case MG_EV_HTTP_MSG:
 | 
			
		||||
			HandleHTTPMessage(connection, static_cast<mg_http_message*>(request_data));
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
	case MG_EV_HTTP_MSG:
 | 
			
		||||
		HandleHTTPMessage(connection, static_cast<mg_http_message*>(request_data));
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -172,19 +172,19 @@ bool ChatWebAPI::Startup() {
 | 
			
		||||
		.path = v1_route + "players",
 | 
			
		||||
		.method = eHTTPMethod::GET,
 | 
			
		||||
		.handle = HandlePlayersRequest
 | 
			
		||||
	});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
	RegisterHTTPRoutes({
 | 
			
		||||
		.path = v1_route + "teams",
 | 
			
		||||
		.method = eHTTPMethod::GET,
 | 
			
		||||
		.handle = HandleTeamsRequest
 | 
			
		||||
	});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
	RegisterHTTPRoutes({
 | 
			
		||||
		.path = v1_route + "announce",
 | 
			
		||||
		.method = eHTTPMethod::POST,
 | 
			
		||||
		.handle = HandleAnnounceRequest
 | 
			
		||||
	});
 | 
			
		||||
		});
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,19 +18,12 @@ void to_json(json& data, const PlayerData& playerData) {
 | 
			
		||||
 | 
			
		||||
void to_json(json& data, const PlayerContainer& playerContainer) {
 | 
			
		||||
	data = json::array();
 | 
			
		||||
	for(auto& playerData : playerContainer.GetAllPlayers()) {
 | 
			
		||||
	for (auto& playerData : playerContainer.GetAllPlayers()) {
 | 
			
		||||
		if (playerData.first == LWOOBJID_EMPTY) continue;
 | 
			
		||||
		data.push_back(playerData.second);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void to_json(json& data, const TeamContainer& teamContainer) {
 | 
			
		||||
	for (auto& teamData : Game::playerContainer.GetTeams()) {
 | 
			
		||||
		if (!teamData) continue;
 | 
			
		||||
		data.push_back(*teamData);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void to_json(json& data, const TeamData& teamData) {
 | 
			
		||||
	data["id"] = teamData.teamID;
 | 
			
		||||
	data["loot_flag"] = teamData.lootFlag;
 | 
			
		||||
@@ -48,6 +41,13 @@ void to_json(json& data, const TeamData& teamData) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::to_json(json& data, const TeamContainer::Data& teamContainer) {
 | 
			
		||||
	for (auto& teamData : TeamContainer::GetTeams()) {
 | 
			
		||||
		if (!teamData) continue;
 | 
			
		||||
		data.push_back(*teamData);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string JSONUtils::CheckRequiredData(const json& data, const std::vector<std::string>& requiredData) {
 | 
			
		||||
	json check;
 | 
			
		||||
	check["error"] = json::array();
 | 
			
		||||
 
 | 
			
		||||
@@ -3,12 +3,18 @@
 | 
			
		||||
 | 
			
		||||
#include "json_fwd.hpp"
 | 
			
		||||
#include "PlayerContainer.h"
 | 
			
		||||
#include "TeamContainer.h"
 | 
			
		||||
 | 
			
		||||
/* Remember, to_json needs to be in the same namespace as the class its located in */
 | 
			
		||||
 | 
			
		||||
void to_json(nlohmann::json& data, const PlayerData& playerData);
 | 
			
		||||
void to_json(nlohmann::json& data, const PlayerContainer& playerContainer);
 | 
			
		||||
void to_json(nlohmann::json& data, const TeamContainer& teamData);
 | 
			
		||||
void to_json(nlohmann::json& data, const TeamData& teamData);
 | 
			
		||||
 | 
			
		||||
namespace TeamContainer {
 | 
			
		||||
	void to_json(nlohmann::json& data, const TeamContainer::Data& teamData);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace JSONUtils {
 | 
			
		||||
	// check required data for reqeust
 | 
			
		||||
	std::string CheckRequiredData(const nlohmann::json& data, const std::vector<std::string>& requiredData);
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@
 | 
			
		||||
#include "ChatPackets.h"
 | 
			
		||||
#include "dConfig.h"
 | 
			
		||||
#include "MessageType/Chat.h"
 | 
			
		||||
#include "TeamContainer.h"
 | 
			
		||||
 | 
			
		||||
void PlayerContainer::Initialize() {
 | 
			
		||||
	m_MaxNumberOfBestFriends =
 | 
			
		||||
@@ -99,7 +100,7 @@ void PlayerContainer::RemovePlayer(const LWOOBJID playerID) {
 | 
			
		||||
		if (fd) ChatPacketHandler::SendFriendUpdate(fd, player, 0, fr.isBestFriend);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto* team = GetTeam(playerID);
 | 
			
		||||
	auto* team = TeamContainer::GetTeam(playerID);
 | 
			
		||||
 | 
			
		||||
	if (team != nullptr) {
 | 
			
		||||
		const auto memberName = GeneralUtils::UTF8ToUTF16(player.playerName);
 | 
			
		||||
@@ -109,7 +110,7 @@ void PlayerContainer::RemovePlayer(const LWOOBJID playerID) {
 | 
			
		||||
 | 
			
		||||
			if (!otherMember) continue;
 | 
			
		||||
 | 
			
		||||
			ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 });
 | 
			
		||||
			TeamContainer::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 });
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -140,40 +141,6 @@ void PlayerContainer::MuteUpdate(Packet* packet) {
 | 
			
		||||
	BroadcastMuteUpdate(playerID, expire);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PlayerContainer::CreateTeamServer(Packet* packet) {
 | 
			
		||||
	CINSTREAM_SKIP_HEADER;
 | 
			
		||||
	LWOOBJID playerID;
 | 
			
		||||
	inStream.Read(playerID);
 | 
			
		||||
	size_t membersSize = 0;
 | 
			
		||||
	inStream.Read(membersSize);
 | 
			
		||||
 | 
			
		||||
	if (membersSize >= 4) {
 | 
			
		||||
		LOG("Tried to create a team with more than 4 players");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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, MessageType::Chat::GM_MUTE);
 | 
			
		||||
@@ -184,218 +151,6 @@ void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t 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;
 | 
			
		||||
 | 
			
		||||
	GetTeamsMut().push_back(team);
 | 
			
		||||
 | 
			
		||||
	AddMember(team, leader);
 | 
			
		||||
 | 
			
		||||
	return team;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TeamData* PlayerContainer::GetTeam(LWOOBJID playerID) {
 | 
			
		||||
	for (auto* team : GetTeams()) {
 | 
			
		||||
		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");
 | 
			
		||||
		const auto& player = GetPlayerData(playerID);
 | 
			
		||||
		if (!player) return;
 | 
			
		||||
		ChatPackets::SendSystemMessage(player.worldServerSysAddr, 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);
 | 
			
		||||
 | 
			
		||||
	const auto& leader = GetPlayerData(team->leaderID);
 | 
			
		||||
	const auto& member = GetPlayerData(playerID);
 | 
			
		||||
 | 
			
		||||
	if (!leader || !member) 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) {
 | 
			
		||||
		const auto& otherMember = GetPlayerData(memberId);
 | 
			
		||||
 | 
			
		||||
		if (otherMember == member) continue;
 | 
			
		||||
 | 
			
		||||
		const auto otherMemberName = GetName(memberId);
 | 
			
		||||
 | 
			
		||||
		ChatPacketHandler::SendTeamAddPlayer(member, false, team->local, false, memberId, otherMemberName, otherMember ? otherMember.zoneID : LWOZONEID(0, 0, 0));
 | 
			
		||||
 | 
			
		||||
		if (otherMember) {
 | 
			
		||||
			ChatPacketHandler::SendTeamAddPlayer(otherMember, false, team->local, false, member.playerID, memberName, member.zoneID);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PlayerContainer::RemoveMember(TeamData* team, LWOOBJID causingPlayerID, bool disband, bool kicked, bool leaving, bool silent) {
 | 
			
		||||
	LOG_DEBUG("Player %llu is leaving team %i", causingPlayerID, team->teamID);
 | 
			
		||||
	const auto index = std::ranges::find(team->memberIDs, causingPlayerID);
 | 
			
		||||
 | 
			
		||||
	if (index == team->memberIDs.end()) return;
 | 
			
		||||
 | 
			
		||||
	team->memberIDs.erase(index);
 | 
			
		||||
 | 
			
		||||
	const auto& member = GetPlayerData(causingPlayerID);
 | 
			
		||||
 | 
			
		||||
	const auto causingMemberName = GetName(causingPlayerID);
 | 
			
		||||
 | 
			
		||||
	if (member && !silent) {
 | 
			
		||||
		ChatPacketHandler::SendTeamRemovePlayer(member, disband, kicked, leaving, team->local, LWOOBJID_EMPTY, causingPlayerID, causingMemberName);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (team->memberIDs.size() <= 1) {
 | 
			
		||||
		DisbandTeam(team, causingPlayerID, causingMemberName);
 | 
			
		||||
	} else /* team has enough members to be a team still */ {
 | 
			
		||||
		team->leaderID = (causingPlayerID == team->leaderID) ? team->memberIDs[0] : team->leaderID;
 | 
			
		||||
		for (const auto memberId : team->memberIDs) {
 | 
			
		||||
			if (silent && memberId == causingPlayerID) {
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			const auto& otherMember = GetPlayerData(memberId);
 | 
			
		||||
 | 
			
		||||
			if (!otherMember) continue;
 | 
			
		||||
 | 
			
		||||
			ChatPacketHandler::SendTeamRemovePlayer(otherMember, disband, kicked, leaving, team->local, team->leaderID, causingPlayerID, causingMemberName);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		UpdateTeamsOnWorld(team, false);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PlayerContainer::PromoteMember(TeamData* team, LWOOBJID newLeader) {
 | 
			
		||||
	team->leaderID = newLeader;
 | 
			
		||||
 | 
			
		||||
	for (const auto memberId : team->memberIDs) {
 | 
			
		||||
		const auto& otherMember = GetPlayerData(memberId);
 | 
			
		||||
 | 
			
		||||
		if (!otherMember) continue;
 | 
			
		||||
 | 
			
		||||
		ChatPacketHandler::SendTeamSetLeader(otherMember, newLeader);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PlayerContainer::DisbandTeam(TeamData* team, const LWOOBJID causingPlayerID, const std::u16string& causingPlayerName) {
 | 
			
		||||
	const auto index = std::ranges::find(GetTeams(), team);
 | 
			
		||||
 | 
			
		||||
	if (index == GetTeams().end()) return;
 | 
			
		||||
	LOG_DEBUG("Disbanding team %i", (*index)->teamID);
 | 
			
		||||
 | 
			
		||||
	for (const auto memberId : team->memberIDs) {
 | 
			
		||||
		const auto& otherMember = GetPlayerData(memberId);
 | 
			
		||||
 | 
			
		||||
		if (!otherMember) continue;
 | 
			
		||||
 | 
			
		||||
		ChatPacketHandler::SendTeamSetLeader(otherMember, LWOOBJID_EMPTY);
 | 
			
		||||
		ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, causingPlayerID, causingPlayerName);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UpdateTeamsOnWorld(team, true);
 | 
			
		||||
 | 
			
		||||
	GetTeamsMut().erase(index);
 | 
			
		||||
 | 
			
		||||
	delete team;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void PlayerContainer::TeamStatusUpdate(TeamData* team) {
 | 
			
		||||
	const auto index = std::find(GetTeams().begin(), GetTeams().end(), team);
 | 
			
		||||
 | 
			
		||||
	if (index == GetTeams().end()) return;
 | 
			
		||||
 | 
			
		||||
	const auto& leader = GetPlayerData(team->leaderID);
 | 
			
		||||
 | 
			
		||||
	if (!leader) return;
 | 
			
		||||
 | 
			
		||||
	const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName);
 | 
			
		||||
 | 
			
		||||
	for (const auto memberId : team->memberIDs) {
 | 
			
		||||
		const auto& otherMember = GetPlayerData(memberId);
 | 
			
		||||
 | 
			
		||||
		if (!otherMember) 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, MessageType::Chat::TEAM_GET_STATUS);
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(team->teamID);
 | 
			
		||||
	bitStream.Write(deleteTeam);
 | 
			
		||||
 | 
			
		||||
	if (!deleteTeam) {
 | 
			
		||||
		bitStream.Write(team->lootFlag);
 | 
			
		||||
		bitStream.Write<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 iter = m_Names.find(playerID);
 | 
			
		||||
 | 
			
		||||
@@ -444,5 +199,4 @@ void PlayerContainer::Shutdown() {
 | 
			
		||||
		Database::Get()->UpdateActivityLog(id, eActivityType::PlayerLoggedOut, playerData.zoneID.GetMapID());
 | 
			
		||||
		m_Players.erase(m_Players.begin());
 | 
			
		||||
	}
 | 
			
		||||
	for (auto* team : GetTeams()) if (team) delete team;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,10 +11,6 @@ enum class eGameMasterLevel : uint8_t;
 | 
			
		||||
 | 
			
		||||
struct TeamData;
 | 
			
		||||
 | 
			
		||||
struct TeamContainer {
 | 
			
		||||
	std::vector<TeamData*> mTeams;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct IgnoreData {
 | 
			
		||||
	IgnoreData(const std::string& name, const LWOOBJID& id) : playerName{ name }, playerId{ id } {}
 | 
			
		||||
	inline bool operator==(const std::string& other) const noexcept {
 | 
			
		||||
@@ -73,7 +69,6 @@ public:
 | 
			
		||||
	void ScheduleRemovePlayer(Packet* packet);
 | 
			
		||||
	void RemovePlayer(const LWOOBJID playerID);
 | 
			
		||||
	void MuteUpdate(Packet* packet);
 | 
			
		||||
	void CreateTeamServer(Packet* packet);
 | 
			
		||||
	void BroadcastMuteUpdate(LWOOBJID player, time_t time);
 | 
			
		||||
	void Shutdown();
 | 
			
		||||
 | 
			
		||||
@@ -81,34 +76,19 @@ public:
 | 
			
		||||
	const PlayerData& GetPlayerData(const std::string& playerName);
 | 
			
		||||
	PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID);
 | 
			
		||||
	PlayerData& GetPlayerDataMutable(const std::string& playerName);
 | 
			
		||||
	std::u16string GetName(LWOOBJID playerID);
 | 
			
		||||
	LWOOBJID GetId(const std::u16string& playerName);
 | 
			
		||||
	void Update(const float deltaTime);
 | 
			
		||||
 | 
			
		||||
	uint32_t GetPlayerCount() { return m_PlayerCount; };
 | 
			
		||||
	uint32_t GetSimCount() { return m_SimCount; };
 | 
			
		||||
	const std::map<LWOOBJID, PlayerData>& GetAllPlayers() const { return m_Players; };
 | 
			
		||||
 | 
			
		||||
	TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
 | 
			
		||||
	TeamData* CreateTeam(LWOOBJID leader, bool local = false);
 | 
			
		||||
	TeamData* GetTeam(LWOOBJID playerID);
 | 
			
		||||
	void AddMember(TeamData* team, LWOOBJID playerID);
 | 
			
		||||
	void RemoveMember(TeamData* team, LWOOBJID playerID, bool disband, bool kicked, bool leaving, bool silent = false);
 | 
			
		||||
	void PromoteMember(TeamData* team, LWOOBJID newLeader);
 | 
			
		||||
	void DisbandTeam(TeamData* team, const LWOOBJID causingPlayerID, const std::u16string& causingPlayerName);
 | 
			
		||||
	void TeamStatusUpdate(TeamData* team);
 | 
			
		||||
	void UpdateTeamsOnWorld(TeamData* team, bool deleteTeam);
 | 
			
		||||
	std::u16string GetName(LWOOBJID playerID);
 | 
			
		||||
	LWOOBJID GetId(const std::u16string& playerName);
 | 
			
		||||
	uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; }
 | 
			
		||||
	uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
 | 
			
		||||
	const TeamContainer& GetTeamContainer() { return m_TeamContainer; }
 | 
			
		||||
	std::vector<TeamData*>& GetTeamsMut() { return m_TeamContainer.mTeams; };
 | 
			
		||||
	const std::vector<TeamData*>& GetTeams() { return GetTeamsMut(); };
 | 
			
		||||
 | 
			
		||||
	void Update(const float deltaTime);
 | 
			
		||||
	bool PlayerBeingRemoved(const LWOOBJID playerID) { return m_PlayersToRemove.contains(playerID); }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	LWOOBJID m_TeamIDCounter = 0;
 | 
			
		||||
	std::map<LWOOBJID, PlayerData> m_Players;
 | 
			
		||||
	TeamContainer m_TeamContainer{};
 | 
			
		||||
	std::unordered_map<LWOOBJID, std::u16string> m_Names;
 | 
			
		||||
	std::map<LWOOBJID, float> m_PlayersToRemove;
 | 
			
		||||
	uint32_t m_MaxNumberOfBestFriends = 5;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										669
									
								
								dChatServer/TeamContainer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										669
									
								
								dChatServer/TeamContainer.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,669 @@
 | 
			
		||||
#include "TeamContainer.h"
 | 
			
		||||
 | 
			
		||||
#include "ChatPackets.h"
 | 
			
		||||
 | 
			
		||||
#include "MessageType/Chat.h"
 | 
			
		||||
#include "MessageType/Game.h"
 | 
			
		||||
 | 
			
		||||
#include "ChatPacketHandler.h"
 | 
			
		||||
#include "PlayerContainer.h"
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
	TeamContainer::Data g_TeamContainer{};
 | 
			
		||||
	LWOOBJID g_TeamIDCounter = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const TeamContainer::Data& TeamContainer::GetTeamContainer() {
 | 
			
		||||
	return g_TeamContainer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<TeamData*>& TeamContainer::GetTeamsMut() {
 | 
			
		||||
	return g_TeamContainer.mTeams;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const std::vector<TeamData*>& TeamContainer::GetTeams() {
 | 
			
		||||
	return GetTeamsMut();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::Shutdown() {
 | 
			
		||||
	for (auto* team : g_TeamContainer.mTeams) if (team) delete team;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::HandleTeamInvite(Packet* packet) {
 | 
			
		||||
	CINSTREAM_SKIP_HEADER;
 | 
			
		||||
 | 
			
		||||
	LWOOBJID playerID;
 | 
			
		||||
	LUWString invitedPlayer;
 | 
			
		||||
 | 
			
		||||
	inStream.Read(playerID);
 | 
			
		||||
	inStream.IgnoreBytes(4);
 | 
			
		||||
	inStream.Read(invitedPlayer);
 | 
			
		||||
 | 
			
		||||
	const auto& player = Game::playerContainer.GetPlayerData(playerID);
 | 
			
		||||
 | 
			
		||||
	if (!player) return;
 | 
			
		||||
 | 
			
		||||
	auto* team = GetTeam(playerID);
 | 
			
		||||
 | 
			
		||||
	if (team == nullptr) {
 | 
			
		||||
		team = CreateTeam(playerID);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const auto& other = Game::playerContainer.GetPlayerData(invitedPlayer.GetAsString());
 | 
			
		||||
 | 
			
		||||
	if (!other) return;
 | 
			
		||||
 | 
			
		||||
	if (GetTeam(other.playerID) != nullptr) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (team->memberIDs.size() > 3) {
 | 
			
		||||
		// no more teams greater than 4
 | 
			
		||||
 | 
			
		||||
		LOG("Someone tried to invite a 5th player to a team");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	SendTeamInvite(other, player);
 | 
			
		||||
 | 
			
		||||
	LOG("Got team invite: %llu -> %s", playerID, invitedPlayer.GetAsString().c_str());
 | 
			
		||||
 | 
			
		||||
	bool failed = false;
 | 
			
		||||
	for (const auto& ignore : other.ignoredPlayers) {
 | 
			
		||||
		if (ignore.playerId == player.playerID) {
 | 
			
		||||
			failed = true;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ChatPackets::TeamInviteInitialResponse response{};
 | 
			
		||||
	response.inviteFailedToSend = failed;
 | 
			
		||||
	response.playerName = invitedPlayer.string;
 | 
			
		||||
	ChatPackets::SendRoutedMsg(response, playerID, player.worldServerSysAddr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::HandleTeamInviteResponse(Packet* packet) {
 | 
			
		||||
	CINSTREAM_SKIP_HEADER;
 | 
			
		||||
	LWOOBJID playerID = LWOOBJID_EMPTY;
 | 
			
		||||
	inStream.Read(playerID);
 | 
			
		||||
	uint32_t size = 0;
 | 
			
		||||
	inStream.Read(size);
 | 
			
		||||
	char declined = 0;
 | 
			
		||||
	inStream.Read(declined);
 | 
			
		||||
	LWOOBJID leaderID = LWOOBJID_EMPTY;
 | 
			
		||||
	inStream.Read(leaderID);
 | 
			
		||||
 | 
			
		||||
	LOG("Invite reponse received: %llu -> %llu (%d)", playerID, leaderID, declined);
 | 
			
		||||
 | 
			
		||||
	if (declined) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto* team = GetTeam(leaderID);
 | 
			
		||||
 | 
			
		||||
	if (team == nullptr) {
 | 
			
		||||
		LOG("Failed to find team for leader (%llu)", leaderID);
 | 
			
		||||
 | 
			
		||||
		team = GetTeam(playerID);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (team == nullptr) {
 | 
			
		||||
		LOG("Failed to find team for player (%llu)", playerID);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	AddMember(team, playerID);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::HandleTeamLeave(Packet* packet) {
 | 
			
		||||
	CINSTREAM_SKIP_HEADER;
 | 
			
		||||
	LWOOBJID playerID = LWOOBJID_EMPTY;
 | 
			
		||||
	inStream.Read(playerID);
 | 
			
		||||
	uint32_t size = 0;
 | 
			
		||||
	inStream.Read(size);
 | 
			
		||||
 | 
			
		||||
	auto* team = GetTeam(playerID);
 | 
			
		||||
 | 
			
		||||
	LOG("(%llu) leaving team", playerID);
 | 
			
		||||
 | 
			
		||||
	if (team != nullptr) {
 | 
			
		||||
		RemoveMember(team, playerID, false, false, true);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::HandleTeamKick(Packet* packet) {
 | 
			
		||||
	CINSTREAM_SKIP_HEADER;
 | 
			
		||||
 | 
			
		||||
	LWOOBJID playerID = LWOOBJID_EMPTY;
 | 
			
		||||
	LUWString kickedPlayer;
 | 
			
		||||
 | 
			
		||||
	inStream.Read(playerID);
 | 
			
		||||
	inStream.IgnoreBytes(4);
 | 
			
		||||
	inStream.Read(kickedPlayer);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	LOG("(%llu) kicking (%s) from team", playerID, kickedPlayer.GetAsString().c_str());
 | 
			
		||||
 | 
			
		||||
	const auto& kicked = Game::playerContainer.GetPlayerData(kickedPlayer.GetAsString());
 | 
			
		||||
 | 
			
		||||
	LWOOBJID kickedId = LWOOBJID_EMPTY;
 | 
			
		||||
 | 
			
		||||
	if (kicked) {
 | 
			
		||||
		kickedId = kicked.playerID;
 | 
			
		||||
	} else {
 | 
			
		||||
		kickedId = Game::playerContainer.GetId(kickedPlayer.string);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (kickedId == LWOOBJID_EMPTY) return;
 | 
			
		||||
 | 
			
		||||
	auto* team = GetTeam(playerID);
 | 
			
		||||
 | 
			
		||||
	if (team != nullptr) {
 | 
			
		||||
		if (team->leaderID != playerID || team->leaderID == kickedId) return;
 | 
			
		||||
 | 
			
		||||
		RemoveMember(team, kickedId, false, true, false);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::HandleTeamPromote(Packet* packet) {
 | 
			
		||||
	CINSTREAM_SKIP_HEADER;
 | 
			
		||||
 | 
			
		||||
	LWOOBJID playerID = LWOOBJID_EMPTY;
 | 
			
		||||
	LUWString promotedPlayer;
 | 
			
		||||
 | 
			
		||||
	inStream.Read(playerID);
 | 
			
		||||
	inStream.IgnoreBytes(4);
 | 
			
		||||
	inStream.Read(promotedPlayer);
 | 
			
		||||
 | 
			
		||||
	LOG("(%llu) promoting (%s) to team leader", playerID, promotedPlayer.GetAsString().c_str());
 | 
			
		||||
 | 
			
		||||
	const auto& promoted = Game::playerContainer.GetPlayerData(promotedPlayer.GetAsString());
 | 
			
		||||
 | 
			
		||||
	if (!promoted) return;
 | 
			
		||||
 | 
			
		||||
	auto* team = GetTeam(playerID);
 | 
			
		||||
 | 
			
		||||
	if (team != nullptr) {
 | 
			
		||||
		if (team->leaderID != playerID) return;
 | 
			
		||||
 | 
			
		||||
		PromoteMember(team, promoted.playerID);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::HandleTeamLootOption(Packet* packet) {
 | 
			
		||||
	CINSTREAM_SKIP_HEADER;
 | 
			
		||||
	LWOOBJID playerID = LWOOBJID_EMPTY;
 | 
			
		||||
	inStream.Read(playerID);
 | 
			
		||||
	uint32_t size = 0;
 | 
			
		||||
	inStream.Read(size);
 | 
			
		||||
 | 
			
		||||
	char option;
 | 
			
		||||
	inStream.Read(option);
 | 
			
		||||
 | 
			
		||||
	auto* team = GetTeam(playerID);
 | 
			
		||||
 | 
			
		||||
	if (team != nullptr) {
 | 
			
		||||
		if (team->leaderID != playerID) return;
 | 
			
		||||
 | 
			
		||||
		team->lootFlag = option;
 | 
			
		||||
 | 
			
		||||
		TeamStatusUpdate(team);
 | 
			
		||||
 | 
			
		||||
		UpdateTeamsOnWorld(team, false);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::HandleTeamStatusRequest(Packet* packet) {
 | 
			
		||||
	CINSTREAM_SKIP_HEADER;
 | 
			
		||||
	LWOOBJID playerID = LWOOBJID_EMPTY;
 | 
			
		||||
	inStream.Read(playerID);
 | 
			
		||||
 | 
			
		||||
	auto* team = GetTeam(playerID);
 | 
			
		||||
	const auto& data = Game::playerContainer.GetPlayerData(playerID);
 | 
			
		||||
 | 
			
		||||
	if (team != nullptr && data) {
 | 
			
		||||
		LOG_DEBUG("Player %llu is requesting team status", playerID);
 | 
			
		||||
		if (team->local && data.zoneID.GetMapID() != team->zoneId.GetMapID() && data.zoneID.GetCloneID() != team->zoneId.GetCloneID()) {
 | 
			
		||||
			RemoveMember(team, playerID, false, false, false, true);
 | 
			
		||||
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (team->memberIDs.size() <= 1 && !team->local) {
 | 
			
		||||
			DisbandTeam(team, LWOOBJID_EMPTY, u"");
 | 
			
		||||
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!team->local) {
 | 
			
		||||
			SendTeamSetLeader(data, team->leaderID);
 | 
			
		||||
		} else {
 | 
			
		||||
			SendTeamSetLeader(data, LWOOBJID_EMPTY);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		TeamStatusUpdate(team);
 | 
			
		||||
 | 
			
		||||
		const auto leaderName = GeneralUtils::UTF8ToUTF16(data.playerName);
 | 
			
		||||
 | 
			
		||||
		for (const auto memberId : team->memberIDs) {
 | 
			
		||||
			const auto& otherMember = Game::playerContainer.GetPlayerData(memberId);
 | 
			
		||||
 | 
			
		||||
			if (memberId == playerID) continue;
 | 
			
		||||
 | 
			
		||||
			const auto memberName = Game::playerContainer.GetName(memberId);
 | 
			
		||||
 | 
			
		||||
			if (otherMember) {
 | 
			
		||||
				SendTeamSetOffWorldFlag(otherMember, data.playerID, data.zoneID);
 | 
			
		||||
			}
 | 
			
		||||
			SendTeamAddPlayer(data, false, team->local, false, memberId, memberName, otherMember ? otherMember.zoneID : LWOZONEID(0, 0, 0));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		UpdateTeamsOnWorld(team, false);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::SendTeamInvite(const PlayerData& receiver, const PlayerData& sender) {
 | 
			
		||||
	CBITSTREAM;
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
 | 
			
		||||
	//portion that will get routed:
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::TEAM_INVITE);
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(LUWString(sender.playerName.c_str()));
 | 
			
		||||
	bitStream.Write(sender.playerID);
 | 
			
		||||
 | 
			
		||||
	SystemAddress sysAddr = receiver.worldServerSysAddr;
 | 
			
		||||
	SEND_PACKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName) {
 | 
			
		||||
	CBITSTREAM;
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
 | 
			
		||||
	//portion that will get routed:
 | 
			
		||||
	CMSGHEADER;
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
	bitStream.Write(MessageType::Game::TEAM_INVITE_CONFIRM);
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(bLeaderIsFreeTrial);
 | 
			
		||||
	bitStream.Write(i64LeaderID);
 | 
			
		||||
	bitStream.Write(i64LeaderZoneID);
 | 
			
		||||
	bitStream.Write<uint32_t>(0); // BinaryBuffe, no clue what's in here
 | 
			
		||||
	bitStream.Write(ucLootFlag);
 | 
			
		||||
	bitStream.Write(ucNumOfOtherPlayers);
 | 
			
		||||
	bitStream.Write(ucResponseCode);
 | 
			
		||||
	bitStream.Write<uint32_t>(wsLeaderName.size());
 | 
			
		||||
	for (const auto character : wsLeaderName) {
 | 
			
		||||
		bitStream.Write(character);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	SystemAddress sysAddr = receiver.worldServerSysAddr;
 | 
			
		||||
	SEND_PACKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) {
 | 
			
		||||
	CBITSTREAM;
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
 | 
			
		||||
	//portion that will get routed:
 | 
			
		||||
	CMSGHEADER;
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
	bitStream.Write(MessageType::Game::TEAM_GET_STATUS_RESPONSE);
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(i64LeaderID);
 | 
			
		||||
	bitStream.Write(i64LeaderZoneID);
 | 
			
		||||
	bitStream.Write<uint32_t>(0); // BinaryBuffe, no clue what's in here
 | 
			
		||||
	bitStream.Write(ucLootFlag);
 | 
			
		||||
	bitStream.Write(ucNumOfOtherPlayers);
 | 
			
		||||
	bitStream.Write<uint32_t>(wsLeaderName.size());
 | 
			
		||||
	for (const auto character : wsLeaderName) {
 | 
			
		||||
		bitStream.Write(character);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	SystemAddress sysAddr = receiver.worldServerSysAddr;
 | 
			
		||||
	SEND_PACKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID) {
 | 
			
		||||
	CBITSTREAM;
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
 | 
			
		||||
	//portion that will get routed:
 | 
			
		||||
	CMSGHEADER;
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
	bitStream.Write(MessageType::Game::TEAM_SET_LEADER);
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(i64PlayerID);
 | 
			
		||||
 | 
			
		||||
	SystemAddress sysAddr = receiver.worldServerSysAddr;
 | 
			
		||||
	SEND_PACKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) {
 | 
			
		||||
	CBITSTREAM;
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
 | 
			
		||||
	//portion that will get routed:
 | 
			
		||||
	CMSGHEADER;
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
	bitStream.Write(MessageType::Game::TEAM_ADD_PLAYER);
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(bIsFreeTrial);
 | 
			
		||||
	bitStream.Write(bLocal);
 | 
			
		||||
	bitStream.Write(bNoLootOnDeath);
 | 
			
		||||
	bitStream.Write(i64PlayerID);
 | 
			
		||||
	bitStream.Write<uint32_t>(wsPlayerName.size());
 | 
			
		||||
	for (const auto character : wsPlayerName) {
 | 
			
		||||
		bitStream.Write(character);
 | 
			
		||||
	}
 | 
			
		||||
	bitStream.Write1();
 | 
			
		||||
	if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) {
 | 
			
		||||
		zoneID = LWOZONEID(zoneID.GetMapID(), zoneID.GetInstanceID(), 0);
 | 
			
		||||
	}
 | 
			
		||||
	bitStream.Write(zoneID);
 | 
			
		||||
 | 
			
		||||
	SystemAddress sysAddr = receiver.worldServerSysAddr;
 | 
			
		||||
	SEND_PACKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) {
 | 
			
		||||
	CBITSTREAM;
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
 | 
			
		||||
	//portion that will get routed:
 | 
			
		||||
	CMSGHEADER;
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
	bitStream.Write(MessageType::Game::TEAM_REMOVE_PLAYER);
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(bDisband);
 | 
			
		||||
	bitStream.Write(bIsKicked);
 | 
			
		||||
	bitStream.Write(bIsLeaving);
 | 
			
		||||
	bitStream.Write(bLocal);
 | 
			
		||||
	bitStream.Write(i64LeaderID);
 | 
			
		||||
	bitStream.Write(i64PlayerID);
 | 
			
		||||
	bitStream.Write<uint32_t>(wsPlayerName.size());
 | 
			
		||||
	for (const auto character : wsPlayerName) {
 | 
			
		||||
		bitStream.Write(character);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	SystemAddress sysAddr = receiver.worldServerSysAddr;
 | 
			
		||||
	SEND_PACKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) {
 | 
			
		||||
	CBITSTREAM;
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
 | 
			
		||||
	//portion that will get routed:
 | 
			
		||||
	CMSGHEADER;
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(receiver.playerID);
 | 
			
		||||
	bitStream.Write(MessageType::Game::TEAM_SET_OFF_WORLD_FLAG);
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(i64PlayerID);
 | 
			
		||||
	if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) {
 | 
			
		||||
		zoneID = LWOZONEID(zoneID.GetMapID(), zoneID.GetInstanceID(), 0);
 | 
			
		||||
	}
 | 
			
		||||
	bitStream.Write(zoneID);
 | 
			
		||||
 | 
			
		||||
	SystemAddress sysAddr = receiver.worldServerSysAddr;
 | 
			
		||||
	SEND_PACKET;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::CreateTeamServer(Packet* packet) {
 | 
			
		||||
	CINSTREAM_SKIP_HEADER;
 | 
			
		||||
	LWOOBJID playerID;
 | 
			
		||||
	inStream.Read(playerID);
 | 
			
		||||
	size_t membersSize = 0;
 | 
			
		||||
	inStream.Read(membersSize);
 | 
			
		||||
 | 
			
		||||
	if (membersSize >= 4) {
 | 
			
		||||
		LOG("Tried to create a team with more than 4 players");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TeamData* TeamContainer::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* TeamContainer::CreateTeam(LWOOBJID leader, bool local) {
 | 
			
		||||
	auto* team = new TeamData();
 | 
			
		||||
 | 
			
		||||
	team->teamID = ++g_TeamIDCounter;
 | 
			
		||||
	team->leaderID = leader;
 | 
			
		||||
	team->local = local;
 | 
			
		||||
 | 
			
		||||
	GetTeamsMut().push_back(team);
 | 
			
		||||
 | 
			
		||||
	AddMember(team, leader);
 | 
			
		||||
 | 
			
		||||
	return team;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TeamData* TeamContainer::GetTeam(LWOOBJID playerID) {
 | 
			
		||||
	for (auto* team : GetTeams()) {
 | 
			
		||||
		if (std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID) == team->memberIDs.end()) continue;
 | 
			
		||||
 | 
			
		||||
		return team;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::AddMember(TeamData* team, LWOOBJID playerID) {
 | 
			
		||||
	if (team->memberIDs.size() >= 4) {
 | 
			
		||||
		LOG("Tried to add player to team that already had 4 players");
 | 
			
		||||
		const auto& player = Game::playerContainer.GetPlayerData(playerID);
 | 
			
		||||
		if (!player) return;
 | 
			
		||||
		ChatPackets::SendSystemMessage(player.worldServerSysAddr, 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);
 | 
			
		||||
 | 
			
		||||
	const auto& leader = Game::playerContainer.GetPlayerData(team->leaderID);
 | 
			
		||||
	const auto& member = Game::playerContainer.GetPlayerData(playerID);
 | 
			
		||||
 | 
			
		||||
	if (!leader || !member) return;
 | 
			
		||||
 | 
			
		||||
	const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName);
 | 
			
		||||
	const auto memberName = GeneralUtils::UTF8ToUTF16(member.playerName);
 | 
			
		||||
 | 
			
		||||
	SendTeamInviteConfirm(member, false, leader.playerID, leader.zoneID, team->lootFlag, 0, 0, leaderName);
 | 
			
		||||
 | 
			
		||||
	if (!team->local) {
 | 
			
		||||
		SendTeamSetLeader(member, leader.playerID);
 | 
			
		||||
	} else {
 | 
			
		||||
		SendTeamSetLeader(member, LWOOBJID_EMPTY);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UpdateTeamsOnWorld(team, false);
 | 
			
		||||
 | 
			
		||||
	for (const auto memberId : team->memberIDs) {
 | 
			
		||||
		const auto& otherMember = Game::playerContainer.GetPlayerData(memberId);
 | 
			
		||||
 | 
			
		||||
		if (otherMember == member) continue;
 | 
			
		||||
 | 
			
		||||
		const auto otherMemberName = Game::playerContainer.GetName(memberId);
 | 
			
		||||
 | 
			
		||||
		SendTeamAddPlayer(member, false, team->local, false, memberId, otherMemberName, otherMember ? otherMember.zoneID : LWOZONEID(0, 0, 0));
 | 
			
		||||
 | 
			
		||||
		if (otherMember) {
 | 
			
		||||
			SendTeamAddPlayer(otherMember, false, team->local, false, member.playerID, memberName, member.zoneID);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::RemoveMember(TeamData* team, LWOOBJID causingPlayerID, bool disband, bool kicked, bool leaving, bool silent) {
 | 
			
		||||
	LOG_DEBUG("Player %llu is leaving team %i", causingPlayerID, team->teamID);
 | 
			
		||||
	const auto index = std::ranges::find(team->memberIDs, causingPlayerID);
 | 
			
		||||
 | 
			
		||||
	if (index == team->memberIDs.end()) return;
 | 
			
		||||
 | 
			
		||||
	team->memberIDs.erase(index);
 | 
			
		||||
 | 
			
		||||
	const auto& member = Game::playerContainer.GetPlayerData(causingPlayerID);
 | 
			
		||||
 | 
			
		||||
	const auto causingMemberName = Game::playerContainer.GetName(causingPlayerID);
 | 
			
		||||
 | 
			
		||||
	if (member && !silent) {
 | 
			
		||||
		SendTeamRemovePlayer(member, disband, kicked, leaving, team->local, LWOOBJID_EMPTY, causingPlayerID, causingMemberName);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (team->memberIDs.size() <= 1) {
 | 
			
		||||
		DisbandTeam(team, causingPlayerID, causingMemberName);
 | 
			
		||||
	} else /* team has enough members to be a team still */ {
 | 
			
		||||
		team->leaderID = (causingPlayerID == team->leaderID) ? team->memberIDs[0] : team->leaderID;
 | 
			
		||||
		for (const auto memberId : team->memberIDs) {
 | 
			
		||||
			if (silent && memberId == causingPlayerID) {
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			const auto& otherMember = Game::playerContainer.GetPlayerData(memberId);
 | 
			
		||||
 | 
			
		||||
			if (!otherMember) continue;
 | 
			
		||||
 | 
			
		||||
			SendTeamRemovePlayer(otherMember, disband, kicked, leaving, team->local, team->leaderID, causingPlayerID, causingMemberName);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		UpdateTeamsOnWorld(team, false);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::PromoteMember(TeamData* team, LWOOBJID newLeader) {
 | 
			
		||||
	team->leaderID = newLeader;
 | 
			
		||||
 | 
			
		||||
	for (const auto memberId : team->memberIDs) {
 | 
			
		||||
		const auto& otherMember = Game::playerContainer.GetPlayerData(memberId);
 | 
			
		||||
 | 
			
		||||
		if (!otherMember) continue;
 | 
			
		||||
 | 
			
		||||
		SendTeamSetLeader(otherMember, newLeader);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::DisbandTeam(TeamData* team, const LWOOBJID causingPlayerID, const std::u16string& causingPlayerName) {
 | 
			
		||||
	const auto index = std::ranges::find(GetTeams(), team);
 | 
			
		||||
 | 
			
		||||
	if (index == GetTeams().end()) return;
 | 
			
		||||
	LOG_DEBUG("Disbanding team %i", (*index)->teamID);
 | 
			
		||||
 | 
			
		||||
	for (const auto memberId : team->memberIDs) {
 | 
			
		||||
		const auto& otherMember = Game::playerContainer.GetPlayerData(memberId);
 | 
			
		||||
 | 
			
		||||
		if (!otherMember) continue;
 | 
			
		||||
 | 
			
		||||
		SendTeamSetLeader(otherMember, LWOOBJID_EMPTY);
 | 
			
		||||
		SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, causingPlayerID, causingPlayerName);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UpdateTeamsOnWorld(team, true);
 | 
			
		||||
 | 
			
		||||
	GetTeamsMut().erase(index);
 | 
			
		||||
 | 
			
		||||
	delete team;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::TeamStatusUpdate(TeamData* team) {
 | 
			
		||||
	const auto index = std::find(GetTeams().begin(), GetTeams().end(), team);
 | 
			
		||||
 | 
			
		||||
	if (index == GetTeams().end()) return;
 | 
			
		||||
 | 
			
		||||
	const auto& leader = Game::playerContainer.GetPlayerData(team->leaderID);
 | 
			
		||||
 | 
			
		||||
	if (!leader) return;
 | 
			
		||||
 | 
			
		||||
	const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName);
 | 
			
		||||
 | 
			
		||||
	for (const auto memberId : team->memberIDs) {
 | 
			
		||||
		const auto& otherMember = Game::playerContainer.GetPlayerData(memberId);
 | 
			
		||||
 | 
			
		||||
		if (!otherMember) continue;
 | 
			
		||||
 | 
			
		||||
		if (!team->local) {
 | 
			
		||||
			SendTeamStatus(otherMember, team->leaderID, leader.zoneID, team->lootFlag, 0, leaderName);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UpdateTeamsOnWorld(team, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TeamContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
 | 
			
		||||
	CBITSTREAM;
 | 
			
		||||
	BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::TEAM_GET_STATUS);
 | 
			
		||||
 | 
			
		||||
	bitStream.Write(team->teamID);
 | 
			
		||||
	bitStream.Write(deleteTeam);
 | 
			
		||||
 | 
			
		||||
	if (!deleteTeam) {
 | 
			
		||||
		bitStream.Write(team->lootFlag);
 | 
			
		||||
		bitStream.Write<char>(team->memberIDs.size());
 | 
			
		||||
		for (const auto memberID : team->memberIDs) {
 | 
			
		||||
			bitStream.Write(memberID);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								dChatServer/TeamContainer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								dChatServer/TeamContainer.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
// Darkflame Universe
 | 
			
		||||
// Copyright 2025
 | 
			
		||||
 | 
			
		||||
#ifndef TEAMCONTAINER_H
 | 
			
		||||
#define TEAMCONTAINER_H
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "dCommonVars.h"
 | 
			
		||||
 | 
			
		||||
struct Packet;
 | 
			
		||||
struct PlayerData;
 | 
			
		||||
struct TeamData;
 | 
			
		||||
 | 
			
		||||
namespace TeamContainer {
 | 
			
		||||
	struct Data {
 | 
			
		||||
		std::vector<TeamData*> mTeams;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	void Shutdown();
 | 
			
		||||
 | 
			
		||||
	void HandleTeamInvite(Packet* packet);
 | 
			
		||||
	void HandleTeamInviteResponse(Packet* packet);
 | 
			
		||||
	void HandleTeamLeave(Packet* packet);
 | 
			
		||||
	void HandleTeamKick(Packet* packet);
 | 
			
		||||
	void HandleTeamPromote(Packet* packet);
 | 
			
		||||
	void HandleTeamLootOption(Packet* packet);
 | 
			
		||||
	void HandleTeamStatusRequest(Packet* packet);
 | 
			
		||||
 | 
			
		||||
	void SendTeamInvite(const PlayerData& receiver, const PlayerData& sender);
 | 
			
		||||
	void SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName);
 | 
			
		||||
	void SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName);
 | 
			
		||||
	void SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID);
 | 
			
		||||
	void SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID);
 | 
			
		||||
 | 
			
		||||
	/* Sends a message to the provided `receiver` with information about the updated team. If `i64LeaderID` is not LWOOBJID_EMPTY, the client will update the leader to that new playerID. */
 | 
			
		||||
	void SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName);
 | 
			
		||||
	void SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID);
 | 
			
		||||
 | 
			
		||||
	void CreateTeamServer(Packet* packet);
 | 
			
		||||
 | 
			
		||||
	TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
 | 
			
		||||
	TeamData* CreateTeam(LWOOBJID leader, bool local = false);
 | 
			
		||||
	TeamData* GetTeam(LWOOBJID playerID);
 | 
			
		||||
	void AddMember(TeamData* team, LWOOBJID playerID);
 | 
			
		||||
	void RemoveMember(TeamData* team, LWOOBJID playerID, bool disband, bool kicked, bool leaving, bool silent = false);
 | 
			
		||||
	void PromoteMember(TeamData* team, LWOOBJID newLeader);
 | 
			
		||||
	void DisbandTeam(TeamData* team, const LWOOBJID causingPlayerID, const std::u16string& causingPlayerName);
 | 
			
		||||
	void TeamStatusUpdate(TeamData* team);
 | 
			
		||||
	void UpdateTeamsOnWorld(TeamData* team, bool deleteTeam);
 | 
			
		||||
 | 
			
		||||
	const TeamContainer::Data& GetTeamContainer();
 | 
			
		||||
	std::vector<TeamData*>& GetTeamsMut();
 | 
			
		||||
	const std::vector<TeamData*>& GetTeams();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif //!TEAMCONTAINER_H
 | 
			
		||||
		Reference in New Issue
	
	Block a user