diff --git a/server/WorldStructs.xml b/server/WorldStructs.xml
index c80672b..cf9da76 100644
--- a/server/WorldStructs.xml
+++ b/server/WorldStructs.xml
@@ -1613,11 +1613,14 @@ to zero and treated like placeholders." />
-
-
+
-
+
+
+
+
+
@@ -1784,6 +1787,190 @@ to zero and treated like placeholders." />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1917,7 +2104,7 @@ to zero and treated like placeholders." />
-
+
@@ -2071,8 +2258,7 @@ to zero and treated like placeholders." />
-
-
+
@@ -2298,7 +2484,7 @@ to zero and treated like placeholders." />
-
+
@@ -2531,7 +2717,7 @@ to zero and treated like placeholders." />
-
+
@@ -3243,7 +3429,7 @@ to zero and treated like placeholders." />
-
+
@@ -7830,7 +8016,7 @@ to zero and treated like placeholders." />
-
+
@@ -7851,7 +8037,7 @@ to zero and treated like placeholders." />
-
+
@@ -7871,7 +8057,7 @@ to zero and treated like placeholders." />
-
+
@@ -7892,7 +8078,7 @@ to zero and treated like placeholders." />
-
+
@@ -11132,9 +11318,36 @@ to zero and treated like placeholders." />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/server/log_config.xml.example b/server/log_config.xml.example
index 690f0ef..db1a110 100644
--- a/server/log_config.xml.example
+++ b/server/log_config.xml.example
@@ -360,4 +360,12 @@
+
+
+
+
+
+
+
+
diff --git a/source/WorldServer/Bots/BotCommands.cpp b/source/WorldServer/Bots/BotCommands.cpp
index 494edc0..10934bb 100644
--- a/source/WorldServer/Bots/BotCommands.cpp
+++ b/source/WorldServer/Bots/BotCommands.cpp
@@ -583,6 +583,10 @@ void Commands::Command_Bot_Spawn(Client* client, Seperator* sep) {
}
client->GetPlayer()->SpawnedBots[bot_id] = bot->GetID();
+
+ if(bot->IsNPC()) {
+ ((NPC*)bot)->HaltMovement();
+ }
}
else {
client->Message(CHANNEL_ERROR, "Error spawning bot (%u)", bot_id);
diff --git a/source/WorldServer/Commands/Commands.cpp b/source/WorldServer/Commands/Commands.cpp
index f6b4b43..caeb25a 100644
--- a/source/WorldServer/Commands/Commands.cpp
+++ b/source/WorldServer/Commands/Commands.cpp
@@ -49,6 +49,7 @@ along with EQ2Emulator. If not, see .
#include "../classes.h"
#include "../Transmute.h"
#include "../Bots/Bot.h"
+#include "../Web/PeerManager.h"
extern WorldDatabase database;
extern MasterSpellList master_spell_list;
@@ -72,6 +73,7 @@ extern RuleManager rule_manager;
extern MasterAAList master_aa_list;
extern MasterRaceTypeList race_types_list;
extern Classes classes;
+extern PeerManager peer_manager;
//devn00b: Fix for linux builds since we dont use stricmp we use strcasecmp
#if defined(__GNUC__)
@@ -1921,6 +1923,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
world.SetReloadingSubsystem("Structs");
configReader.ReloadStructs();
world.RemoveReloadingSubSystem("Structs");
+ peer_manager.sendPeersMessage("/reloadcommand", command->handler);
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
break;
}
@@ -1931,6 +1934,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
client_list.ReloadQuests();
zone_list.ReloadClientQuests();
world.RemoveReloadingSubSystem("Quests");
+ peer_manager.sendPeersMessage("/reloadcommand", command->handler);
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
break;
}
@@ -1943,6 +1947,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading NPC Spells Lists (Note: Must Reload Spawns/Repop to reset npc spells)...");
world.PurgeNPCSpells();
database.LoadNPCSpells();
+ peer_manager.sendPeersMessage("/reloadcommand", command->handler, 1);
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
}
else {
@@ -1957,6 +1962,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
world.RemoveReloadingSubSystem("Spells");
world.PurgeNPCSpells();
database.LoadNPCSpells();
+ peer_manager.sendPeersMessage("/reloadcommand", command->handler);
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
}
break;
@@ -1979,6 +1985,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
if (lua_interface)
lua_interface->DestroyZoneScripts();
world.RemoveReloadingSubSystem("ZoneScripts");
+ peer_manager.sendPeersMessage("/reloadcommand", command->handler);
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
break;
}
@@ -1997,18 +2004,21 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
master_faction_list.Clear();
database.LoadFactionList();
world.RemoveReloadingSubSystem("Factions");
+ peer_manager.sendPeersMessage("/reloadcommand", command->handler);
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
break;
}
case COMMAND_RELOAD_MAIL: {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Mail...");
zone_list.ReloadMail();
+ peer_manager.sendPeersMessage("/reloadcommand", command->handler);
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
break;
}
case COMMAND_RELOAD_GUILDS: {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Guilds...");
world.ReloadGuilds();
+ peer_manager.sendPeersMessage("/reloadcommand", command->handler);
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
break;
}
@@ -2022,6 +2032,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
case COMMAND_RELOAD_RULES: {
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Rules...");
database.LoadRuleSets(true);
+ peer_manager.sendPeersMessage("/reloadcommand", command->handler);
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
break;
}
@@ -2035,6 +2046,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Starting Skills/Spells...");
world.PurgeStartingLists();
world.LoadStartingLists();
+ peer_manager.sendPeersMessage("/reloadcommand", command->handler);
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
break;
}
@@ -2042,6 +2054,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Voiceovers...");
world.PurgeVoiceOvers();
world.LoadVoiceOvers();
+ peer_manager.sendPeersMessage("/reloadcommand", command->handler);
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!");
break;
}
@@ -3178,8 +3191,10 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
}
case COMMAND_GROUPSAY:{
GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
- if(sep && sep->arg[0] && gmi)
+ if(sep && sep->arg[0] && gmi) {
world.GetGroupManager()->GroupChatMessage(gmi->group_id, client->GetPlayer(), client->GetPlayer()->GetCurrentLanguage(), sep->argplus[0]);
+ peer_manager.SendPeersChannelMessage(gmi->group_id, std::string(client->GetPlayer()->GetName()), std::string(sep->argplus[0]), CHANNEL_GROUP_SAY, client->GetPlayer()->GetCurrentLanguage());
+ }
break;
}
case COMMAND_GROUPINVITE: {
@@ -3211,18 +3226,11 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
}
int8 result = world.GetGroupManager()->Invite(client->GetPlayer(), target);
-
- if (result == 0) {
+
+ if (target && result == 0) {
client->Message(CHANNEL_COMMANDS, "You invite %s to group with you.", target->GetName());
if (target_client) {
- PacketStruct* packet = configReader.getStruct("WS_ReceiveOffer", target_client->GetVersion());
- if (packet) {
- packet->setDataByName("type", 1);
- packet->setDataByName("name", client->GetPlayer()->GetName());
- packet->setDataByName("unknown2", 1);
- target_client->QueuePacket(packet->serialize());
- safe_delete(packet);
- }
+ client->SendReceiveOffer(target_client, 1, std::string(client->GetPlayer()->GetName()), 1);
}
}
else if (result == 1)
@@ -3245,9 +3253,10 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
case COMMAND_GROUPDISBAND: {
GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
- if (gmi) { // TODO: Leader check
+ if (gmi && gmi->leader) { // TODO: Leader check..DONE! :X
// world.GetGroupManager()->SimpleGroupMessage(gmi->group_id, "Your group has been disbanded.");
world.GetGroupManager()->RemoveGroup(gmi->group_id);
+ peer_manager.sendPeersDisbandGroup(gmi->group_id);
}
break;
@@ -3419,24 +3428,68 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
break;
}
case COMMAND_GROUP_ACCEPT_INVITE: {
- if((sep && sep->arg[0] && strcmp(sep->arg[0], "group") == 0) || (!sep && client->GetVersion() <= 561)) {
- int8 result = world.GetGroupManager()->AcceptInvite(client->GetPlayer());
-
- if (result == 0)
- client->SimpleMessage(CHANNEL_GROUP_CHAT, "You have joined the group.");
- else if (result == 1)
- client->SimpleMessage(CHANNEL_GROUP_CHAT, "You do not have a pending invite.");
- else if (result == 2)
- client->SimpleMessage(CHANNEL_GROUP_CHAT, "Unable to join group - could not find leader.");
- else
- client->SimpleMessage(CHANNEL_GROUP_CHAT, "Unable to join group - unknown error.");
- }
+ int8 result = 3;
+ std::string leader = world.GetGroupManager()->HasPendingInvite(client->GetPlayer());
+ std::string playerName(client->GetPlayer()->GetName());
+ Client* leader_client = client->GetCurrentZone()->GetClientByName((char*)leader.c_str());
+ bool group_existed = false;
+ if(leader_client && leader_client->GetPlayer()->GetGroupMemberInfo()) {
+ group_existed = true;
+ }
+ if(client->GetPlayer()->GetGroupMemberInfo() && client->GetPlayer()->GetGroupMemberInfo()->leader) {
+ int8 raid_result = world.GetGroupManager()->AcceptRaidInvite(std::string(client->GetPlayer()->GetName()), client->GetPlayer()->GetGroupMemberInfo()->group_id);
+ if(raid_result == 1) {
+ GroupOptions options;
+ if(world.GetGroupManager()->GetDefaultGroupOptions(client->GetPlayer()->GetGroupMemberInfo()->group_id, &options)) {
+ std::vector raidGroups;
+ world.GetGroupManager()->GetRaidGroups(client->GetPlayer()->GetGroupMemberInfo()->group_id, &raidGroups);
+ peer_manager.sendPeersNewGroupRequest("", 0, client->GetPlayer()->GetGroupMemberInfo()->group_id, "", "", &options, "", &raidGroups, true);
+ }
+ world.GetGroupManager()->ClearGroupRaidLooterFlag(client->GetPlayer()->GetGroupMemberInfo()->group_id);
+ world.GetGroupManager()->SendGroupUpdate(client->GetPlayer()->GetGroupMemberInfo()->group_id);
+ break;
+ }
+ }
+ if(net.is_primary) {
+ int32 group_id = 0;
+ result = world.GetGroupManager()->AcceptInvite(client->GetPlayer(), &group_id, false);
+ client->HandleGroupAcceptResponse(result);
+ if(result == 0) {
+ GroupOptions options;
+ if(leader_client) {
+ if(!group_existed) {
+ leader_client->SetGroupOptionsReference(&options);
+ peer_manager.sendPeersNewGroupRequest("", 0, group_id, leader, playerName, &options);
+ }
+
+ world.GetGroupManager()->AddGroupMember(group_id, leader_client->GetPlayer(), true);
+ world.GetGroupManager()->GroupMessage(leader_client->GetPlayer()->GetGroupMemberInfo()->group_id, "%s has joined the group.", playerName.c_str());
+ world.GetGroupManager()->AddGroupMember(leader_client->GetPlayer()->GetGroupMemberInfo()->group_id, client->GetPlayer());
+ }
+ }
+ }
+ else {
+ if(leader.size() < 1) {
+ client->HandleGroupAcceptResponse(1);
+ }
+ else {
+ Client* leader_client = client->GetCurrentZone()->GetClientByName((char*)leader.c_str());
+ GroupOptions options;
+ if(leader_client) {
+ leader_client->SetGroupOptionsReference(&options);
+ world.GetGroupManager()->AddInvite(leader_client->GetPlayer(), client->GetPlayer());
+ peer_manager.sendPrimaryNewGroupRequest(leader, playerName, client->GetPlayer()->GetID(), &options);
+
+ }
+ else {
+ client->HandleGroupAcceptResponse(2);
+ }
+ }
+ }
break;
}
case COMMAND_GROUP_DECLINE_INVITE: {
- if(sep && sep->arg[0] && strcmp(sep->arg[0], "group") == 0) {
world.GetGroupManager()->DeclineInvite(client->GetPlayer()); // TODO: Add message to leader
- }
break;
}
case COMMAND_SUMMON:{
@@ -3519,8 +3572,9 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
int32 zone_id = 0;
bool listSearch = false;
bool isInstance = false;
+ bool notZoningCommand = false;
ZoneServer* zsZone = 0;
-
+ ZoneChangeDetails zone_details;
if(sep && sep->arg[0][0])
{
if(strncasecmp(sep->arg[0], "list", 4) == 0)
@@ -3529,6 +3583,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
else if(strncasecmp(sep->arg[0], "active", 6) == 0)
{
zone_list.SendZoneList(client);
+ notZoningCommand = true;
break;
}
@@ -3542,10 +3597,16 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
{
PrintSep(sep, "ZONE LOCK");
- if(sep->IsNumber(1))
- zsZone = zone_list.Get(atoul(sep->arg[1]), false, false, false);
- else
- zsZone = zone_list.Get(sep->arg[1], false, false, false);
+ if(sep->IsNumber(1)) {
+ if(zone_list.GetZone(&zone_details, atoul(sep->arg[1]), "", false, false, false, false)) {
+ zsZone = (ZoneServer*)zone_details.zonePtr;
+ }
+ }
+ else {
+ if(zone_list.GetZone(&zone_details, 0, std::string(sep->arg[1]), false, false, false, false)) {
+ zsZone = (ZoneServer*)zone_details.zonePtr;
+ }
+ }
if( zsZone )
{
@@ -3554,17 +3615,23 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
}
else
client->Message(CHANNEL_COLOR_RED, "Zone %s is not running and cannot be locked.", sep->arg[1]);
-
+ notZoningCommand = true;
break;
}
else if(strncasecmp(sep->arg[0], "unlock", 6) == 0)
{
PrintSep(sep, "ZONE UNLOCK");
- if(sep->IsNumber(1))
- zsZone = zone_list.Get(atoul(sep->arg[1]), false, false, false);
- else
- zsZone = zone_list.Get(sep->arg[1], false, false, false);
+ if(sep->IsNumber(1)) {
+ if(zone_list.GetZone(&zone_details, atoul(sep->arg[1]), "", false, false, false, false)) {
+ zsZone = (ZoneServer*)zone_details.zonePtr;
+ }
+ }
+ else {
+ if(zone_list.GetZone(&zone_details, 0, std::string(sep->arg[1]), false, false, false, false)) {
+ zsZone = (ZoneServer*)zone_details.zonePtr;
+ }
+ }
if( zsZone )
{
@@ -3573,6 +3640,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
}
else
client->Message(CHANNEL_COLOR_RED, "Zone %s is not running and cannot be unlocked.", sep->arg[1]);
+ notZoningCommand = true;
break;
}
else
@@ -3590,15 +3658,12 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
}
if(instanceID > 0)
{
- ZoneServer* zsInstance = zone_list.GetByInstanceID(instanceID);
-
- if(zsInstance != NULL)
- {
- instanceID = zsInstance->GetInstanceID();
- zone = zsInstance->GetZoneName();
- zone_id = zsInstance->GetZoneID();
- isInstance = true;
+ if(zone_list.GetZoneByInstance(&zone_details, instanceID, 0, true)) {
+ instanceID = zone_details.instanceId;
+ zone = zone_details.zoneName;
+ zone_id = zone_details.zoneId;
}
+ isInstance = true;
}
}
@@ -3616,7 +3681,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
{
client->Message(CHANNEL_COLOR_YELLOW,"Zoning to %s...", zonestr);
if(isInstance)
- client->Zone(instanceID,true,true,false);
+ client->Zone(&zone_details,(ZoneServer*)zone_details.zonePtr,true,false);
else
client->Zone(zonestr);
}
@@ -3765,7 +3830,9 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
if (!spawn || !client->HasOwnerOrEditAccess() || !spawn->GetPickupItemID())
break;
- client->SendMoveObjectMode(spawn, 0);
+ Item* item = master_item_list.GetItem(spawn->GetPickupItemID());
+
+ client->SendMoveObjectMode(spawn, (item && item->houseitem_info) ? item->houseitem_info->house_location : 0);
break;
}
case COMMAND_PICKUP:
@@ -5710,6 +5777,15 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
case COMMAND_ASSIST: { Command_Assist(client, sep); break; }
case COMMAND_TARGET: { Command_Target(client, sep); break; }
case COMMAND_TARGET_PET: { Command_Target_Pet(client, sep); break; }
+ case COMMAND_WHOGROUP: { Command_WhoGroup(client, sep); break; }
+ case COMMAND_WHORAID: { Command_WhoRaid(client, sep); break; }
+ case COMMAND_RAIDINVITE: { Command_RaidInvite(client, sep); break; }
+ case COMMAND_RAID_LOOTER: { Command_Raid_Looter(client, sep); break; }
+ case COMMAND_KICKFROMGROUP: { Command_KickFromGroup(client, sep); break; }
+ case COMMAND_KICKFROMRAID: { Command_KickFromRaid(client, sep); break; }
+ case COMMAND_LEAVERAID: { Command_LeaveRaid(client, sep); break; }
+ case COMMAND_SPLIT: { Command_Split(client, sep); break; }
+ case COMMAND_RAIDSAY: { Command_RaidSay(client, sep); break; }
default:
{
LogWrite(COMMAND__WARNING, 0, "Command", "Unhandled command: %s", command->command.data.c_str());
@@ -6263,12 +6339,20 @@ void Commands::Command_Guild(Client* client, Seperator* sep)
if (strncmp(command, "rank_name", length) == 0 && sep->arg[1] && sep->IsNumber(1) && sep->arg[2] && guild)
guild->SetRankName(atoi(sep->arg[1]), sep->argplus[2]);
- else if (strncmp(command, "rank_permission", length) == 0 && sep->arg[1] && sep->IsNumber(1) && sep->arg[2] && sep->IsNumber(2) && sep->arg[3] && guild)
+ else if (strncmp(command, "rank_permission", length) == 0 && sep->arg[1] && sep->IsNumber(1) && sep->arg[2] && sep->IsNumber(2) && sep->arg[3] && guild) {
guild->SetPermission(atoi(sep->arg[1]), atoi(sep->arg[2]), strncmp(sep->arg[3], "true", 4) == 0 ? 1 : 0);
- else if (strncmp(command, "filter_event", length) == 0 && sep->arg[1] && sep->IsNumber(1) && sep->arg[2] && sep->IsNumber(2) && sep->arg[3] && guild)
+ peer_manager.sendPeersGuildPermission(guild->GetID(), atoul(sep->arg[1]), atoul(sep->arg[2]), strncmp(sep->arg[3], "true", 4) == 0 ? 1 : 0);
+ }
+ else if (strncmp(command, "filter_event", length) == 0 && sep->arg[1] && sep->IsNumber(1) && sep->arg[2] && sep->IsNumber(2) && sep->arg[3] && guild) {
guild->SetEventFilter(atoi(sep->arg[1]), atoi(sep->arg[2]), strncmp(sep->arg[3], "true", 4) == 0 ? 1 : 0);
- else if (strncmp(command, "kick", length) == 0 && sep->arg[1] && guild)
- guild->KickGuildMember(client, sep->arg[1]);
+ peer_manager.sendPeersGuildEventFilter(guild->GetID(), atoul(sep->arg[1]), atoul(sep->arg[2]), strncmp(sep->arg[3], "true", 4) == 0 ? 1 : 0);
+ }
+ else if (strncmp(command, "kick", length) == 0 && sep->arg[1] && guild) {
+ int32 character_id = guild->KickGuildMember(client, sep->arg[1]);
+ if(character_id > 0) {
+ peer_manager.sendPeersRemoveGuildMember(character_id, guild->GetID(), std::string(client->GetPlayer()->GetName()));
+ }
+ }
else if (strncmp(command, "demote", length) == 0 && sep->arg[1] && guild)
guild->DemoteGuildMember(client, sep->arg[1]);
else if (strncmp(command, "promote", length) == 0 && sep->arg[1] && guild)
@@ -6381,9 +6465,19 @@ void Commands::Command_Guild(Client* client, Seperator* sep)
else if (strncmp(command, "create", length) == 0 && sep->arg[1])
{
const char* guild_name = sep->argplus[1];
-
- if (!guild_list.GetGuild(guild_name))
- world.CreateGuild(guild_name, client, client->GetPlayer()->GetGroupMemberInfo() ? client->GetPlayer()->GetGroupMemberInfo()->group_id : 0);
+ if(!guild_name || strlen(guild_name) < 4) {
+ client->SimpleMessage(CHANNEL_NARRATIVE, "Guild name is too short.");
+ }
+ else if (!guild_list.GetGuild(guild_name)) {
+ if(net.is_primary) {
+ int32 guildID = world.CreateGuild(guild_name, client, client->GetPlayer()->GetGroupMemberInfo() ? client->GetPlayer()->GetGroupMemberInfo()->group_id : 0);
+ if(guildID > 0)
+ peer_manager.sendPeersCreateGuild(guildID);
+ }
+ else {
+ peer_manager.sendPrimaryCreateGuildRequest(std::string(guild_name), std::string(client->GetPlayer()->GetName()));
+ }
+ }
else
client->SimpleMessage(CHANNEL_NARRATIVE, "A guild with that name already exists.");
}
@@ -6479,8 +6573,11 @@ void Commands::Command_GuildSay(Client* client, Seperator* sep)
if (guild)
{
- if (sep && sep->arg[0])
- guild->HandleGuildSay(client, sep->argplus[0]);
+ if (sep && sep->arg[0]) {
+ bool success = guild->HandleGuildSay(client, sep->argplus[0]);
+ if(success)
+ peer_manager.SendPeersGuildChannelMessage(guild->GetID(), std::string(client->GetPlayer()->GetName()), std::string(sep->argplus[0]), CHANNEL_GUILD_SAY, client->GetPlayer()->GetCurrentLanguage());
+ }
}
else
client->SimpleMessage(CHANNEL_NARRATIVE, "You are not a member of a guild");
@@ -6497,8 +6594,11 @@ void Commands::Command_OfficerSay(Client* client, Seperator* sep)
if (guild)
{
- if (sep && sep->arg[0])
- guild->HandleOfficerSay(client, sep->argplus[0]);
+ if (sep && sep->arg[0]) {
+ bool success = guild->HandleOfficerSay(client, sep->argplus[0]);
+ if(success)
+ peer_manager.SendPeersGuildChannelMessage(guild->GetID(), std::string(client->GetPlayer()->GetName()), std::string(sep->argplus[0]), CHANNEL_OFFICER_SAY, client->GetPlayer()->GetCurrentLanguage());
+ }
}
else
client->SimpleMessage(CHANNEL_NARRATIVE, "You are not a member of a guild");
@@ -6581,8 +6681,10 @@ void Commands::Command_GuildsCreate(Client* client, Seperator* sep)
if (sep && sep->arg[0])
{
const char* guild_name = sep->arg[0];
-
- if (!guild_list.GetGuild(guild_name))
+ if(!guild_name || strlen(guild_name) < 4) {
+ client->Message(CHANNEL_COLOR_YELLOW, "Guild name is too short.");
+ }
+ else if (!guild_list.GetGuild(guild_name))
{
bool ret = false;
@@ -6592,7 +6694,14 @@ void Commands::Command_GuildsCreate(Client* client, Seperator* sep)
if (to_client)
{
- world.CreateGuild(guild_name, to_client);
+ if(net.is_primary) {
+ int32 guildID = world.CreateGuild(guild_name, to_client, to_client->GetPlayer()->GetGroupMemberInfo() ? to_client->GetPlayer()->GetGroupMemberInfo()->group_id : 0);
+ if(guildID > 0)
+ peer_manager.sendPeersCreateGuild(guildID);
+ }
+ else {
+ peer_manager.sendPrimaryCreateGuildRequest(std::string(guild_name), std::string(to_client->GetPlayer()->GetName()));
+ }
ret = true;
}
else
@@ -6604,13 +6713,27 @@ void Commands::Command_GuildsCreate(Client* client, Seperator* sep)
if (to_client)
{
- world.CreateGuild(guild_name, to_client);
+ if(net.is_primary) {
+ int32 guildID = world.CreateGuild(guild_name, to_client, to_client->GetPlayer()->GetGroupMemberInfo() ? to_client->GetPlayer()->GetGroupMemberInfo()->group_id : 0);
+ if(guildID > 0)
+ peer_manager.sendPeersCreateGuild(guildID);
+ }
+ else {
+ peer_manager.sendPrimaryCreateGuildRequest(std::string(guild_name), std::string(to_client->GetPlayer()->GetName()));
+ }
ret = true;
}
}
else
{
- world.CreateGuild(guild_name);
+ if(net.is_primary) {
+ int32 guildID = world.CreateGuild(guild_name);
+ if(guildID > 0)
+ peer_manager.sendPeersCreateGuild(guildID);
+ }
+ else {
+ peer_manager.sendPrimaryCreateGuildRequest(std::string(guild_name), "");
+ }
ret = true;
}
@@ -6734,29 +6857,21 @@ void Commands::Command_GuildsRemove(Client* client, Seperator* sep)
if (found)
{
Client* to_client = 0;
-
- if (sep->arg[1] && strlen(sep->arg[1]) > 0)
- to_client = zone_list.GetClientByCharName(string(sep->arg[1]));
+ char* charName = nullptr;
+ if(sep->arg[1][0])
+ charName = sep->arg[1];
else if (client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsPlayer())
- to_client = ((Player*)client->GetPlayer()->GetTarget())->GetClient();
-
- if (to_client)
- {
- Player* to_player = to_client->GetPlayer();
- if (to_player->GetGuild())
- {
- if (to_player->GetGuild() == guild)
- {
- guild->KickGuildMember(client, to_player->GetName());
- }
- else
- client->Message(CHANNEL_COLOR_YELLOW, "%s is not in the guild '%s'.", to_player->GetName(), guild->GetName());
- }
- else
- client->Message(CHANNEL_COLOR_YELLOW, "%s is not in a guild.", to_player->GetName());
- }
+ charName = ((Player*)client->GetPlayer()->GetTarget())->GetName();
else
- client->Message(CHANNEL_COLOR_YELLOW, "Could not find player '%s' to invite to the guild.", sep->arg[1]);
+ {
+ client->Message(CHANNEL_COLOR_YELLOW, "Missing player name or not a valid target to remove from the guild.");
+ return;
+ }
+ int32 character_id = guild->KickGuildMember(client, charName);
+ if(character_id > 0)
+ peer_manager.sendPeersRemoveGuildMember(character_id, guild->GetID(), std::string(client->GetPlayer()->GetName()));
+ else
+ client->Message(CHANNEL_COLOR_YELLOW, "Could not find player '%s' to remove from the guild.", charName);
}
}
else
@@ -10775,6 +10890,31 @@ void Commands::Command_Test(Client* client, EQ2_16BitString* command_parms) {
client->QueuePacket(packet->serialize());
safe_delete(packet);
}
+ else if(atoi(sep->arg[0]) == 35) {
+ if(client->GetPlayer()->GetGroupMemberInfo() && client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsEntity()) {
+ Entity* target = (Entity*)client->GetPlayer()->GetTarget();
+ if(target->GetGroupMemberInfo()) {
+ PlayerGroup* group = world.GetGroupManager()->GetGroup(client->GetPlayer()->GetGroupMemberInfo()->group_id);
+ PlayerGroup* group2 = world.GetGroupManager()->GetGroup(target->GetGroupMemberInfo()->group_id);
+ if(group && group2) {
+ group->AddGroupToRaid(group2->GetID());
+ group2->AddGroupToRaid(group->GetID());
+ }
+ }
+ }
+ }
+ else if(atoi(sep->arg[0]) == 36) {
+ EQ2Packet* packet = client->GetPlayer()->GetRaidUpdatePacket(client->GetVersion());
+ if(packet) {
+ client->QueuePacket(packet);
+ }
+ }
+ else if(atoi(sep->arg[0]) == 37) {
+ Guild* guild = client->GetPlayer()->GetGuild();
+ if(guild)
+ guild->SendGuildMemberList();
+
+ }
}
else {
PacketStruct* packet2 = configReader.getStruct("WS_ExaminePartialSpellInfo", client->GetVersion());
@@ -11093,7 +11233,10 @@ void Commands::Command_ZoneSafeCoords(Client *client, Seperator *sep)
if (zone_id > 0)
{
- zone = zone_list.Get(zone_id, false, false, false);
+ ZoneChangeDetails zone_details;
+ if(zone_list.GetZone(&zone_details, zone_id, "", false, false, false)) {
+ zone = (ZoneServer*)zone_details.zonePtr;
+ }
if (zone)
{
zone->SetSafeX(client->GetPlayer()->GetX());
@@ -11162,18 +11305,21 @@ void Commands::Command_ZoneSet(Client* client, Seperator* sep)
{
ZoneServer* zone = 0;
int32 zone_id = 0;
-
+ ZoneChangeDetails zone_details;
if (sep->IsNumber(0) && atoi(sep->arg[0]) > 0)
{
- zone_id = atoi(sep->arg[0]);
- zone = zone_list.Get(atoi(sep->arg[0]), false, false, false);
+ zone_id = atoul(sep->arg[0]);
+ if(zone_list.GetZone(&zone_details, zone_id, "", false, false, false, false)) {
+ zone = (ZoneServer*)zone_details.zonePtr;
+ }
}
else
{
zone_id = database.GetZoneID(sep->arg[0]);
- if (zone_id > 0)
- zone = zone_list.Get(sep->arg[0], false, false, false);
+ if(zone_list.GetZone(&zone_details, zone_id, "", false, false, false, false)) {
+ zone = (ZoneServer*)zone_details.zonePtr;
+ }
}
if (zone_id > 0)
@@ -12379,3 +12525,245 @@ void Commands::Command_Target_Pet(Client* client, Seperator* sep) {
}
}
+
+/*
+ Function: Command_WhoGroup()
+ Purpose : Lists all members of current group
+ Example : /whogroup
+*/
+void Commands::Command_WhoGroup(Client* client, Seperator* sep) {
+ Entity* player = (Entity*)client->GetPlayer();
+ if(player->GetGroupMemberInfo()) {
+ world.GetGroupManager()->SendWhoGroupMembers(client, player->GetGroupMemberInfo()->group_id);
+ }
+ else {
+ client->SimpleMessage(CHANNEL_COLOR_RED, "You are not currently in a group.");
+ }
+}
+
+/*
+ Function: Command_WhoRaid()
+ Purpose : Lists all members of raid
+ Example : /whoraid
+*/
+void Commands::Command_WhoRaid(Client* client, Seperator* sep) {
+ Entity* player = (Entity*)client->GetPlayer();
+ if(player->GetGroupMemberInfo()) {
+ world.GetGroupManager()->SendWhoRaidMembers(client, player->GetGroupMemberInfo()->group_id);
+ }
+ else {
+ client->SimpleMessage(CHANNEL_COLOR_RED, "You are not currently in a group or raid.");
+ }
+}
+
+/*
+ Function: Command_RaidInvite()
+ Purpose : Invites a group to the raid
+ Example : /raidinvite
+*/
+void Commands::Command_RaidInvite(Client* client, Seperator* sep) {
+ Entity* target = nullptr;
+ if( sep && sep->arg[0] ) {
+ Client* target_client = zone_list.GetClientByCharName(sep->arg[0]);
+ if(target_client)
+ target = (Entity*)target_client->GetPlayer();
+ }
+ if(!target) {
+ if(client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsEntity())
+ target = (Entity*)client->GetPlayer()->GetTarget();
+ }
+ world.GetGroupManager()->SendRaidInvite(client, target);
+}
+
+/*
+ Function: Command_Raid_Looter()
+ Purpose : Adds a looter to the raid loot list
+ Example : /raid_looter
+*/
+void Commands::Command_Raid_Looter(Client* client, Seperator* sep) {
+ if(!client->GetPlayer()->GetGroupMemberInfo() || client->GetPlayer()->GetGroupMemberInfo()->leader)
+ return;
+ Entity* target = nullptr;
+ if( sep && sep->arg[0] ) {
+ Client* target_client = zone_list.GetClientByCharName(sep->arg[0]);
+ if(target_client)
+ target = (Entity*)target_client->GetPlayer();
+ }
+ if(!target) {
+ if(client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsEntity())
+ target = (Entity*)client->GetPlayer()->GetTarget();
+ }
+
+ bool isLeaderRaid = world.GetGroupManager()->IsInRaidGroup(client->GetPlayer()->GetGroupMemberInfo()->group_id, client->GetPlayer()->GetGroupMemberInfo()->group_id, true);
+ if(isLeaderRaid && target && target->IsEntity()) {
+ if(((Entity*)target)->GetGroupMemberInfo() && world.GetGroupManager()->IsInRaidGroup(client->GetPlayer()->GetGroupMemberInfo()->group_id, ((Entity*)target)->GetGroupMemberInfo()->group_id, false)) {
+ if(((Entity*)target)->GetGroupMemberInfo()->is_raid_looter) {
+ client->Message(CHANNEL_COLOR_YELLOW, "%s removed as a raid looter.", target->GetName());
+ ((Entity*)target)->GetGroupMemberInfo()->is_raid_looter = false;
+ }
+ else {
+ client->Message(CHANNEL_COLOR_YELLOW, "%s added as a raid looter.", target->GetName());
+ ((Entity*)target)->GetGroupMemberInfo()->is_raid_looter = true;
+ }
+ }
+ }
+}
+
+/*
+ Function: Command_KickFromGroup()
+ Purpose : Kick a player from a group
+ Example : /kickfromgroup
+*/
+void Commands::Command_KickFromGroup(Client* client, Seperator* sep) {
+ Entity* target = nullptr;
+ Client* target_client = nullptr;
+ if( sep && sep->arg[0] ) {
+ target_client = zone_list.GetClientByCharName(sep->arg[0]);
+ if(target_client) {
+ target = target_client->GetPlayer();
+ }
+ }
+ if(!target) {
+ if(client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsEntity())
+ target = (Entity*)client->GetPlayer()->GetTarget();
+
+ if(target && target->IsPlayer())
+ target_client = ((Player*)target)->GetClient();
+ }
+ GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
+
+ if (gmi && gmi->leader && target && target->GetGroupMemberInfo() && gmi->group_id == target->GetGroupMemberInfo()->group_id) {
+ int32 group_id = gmi->group_id;
+ world.GetGroupManager()->RemoveGroupMember(group_id, target);
+ if (!world.GetGroupManager()->IsGroupIDValid(group_id)) {
+ // leader->Message(CHANNEL_COLOR_GROUP, "%s has left the group.", client->GetPlayer()->GetName());
+ }
+ else {
+ world.GetGroupManager()->GroupMessage(group_id, "%s has been removed from the group.", target->GetName());
+ }
+
+ if(target_client)
+ target_client->SimpleMessage(CHANNEL_GROUP_CHAT, "You have been kicked from the group");
+ }
+}
+
+/*
+ Function: Command_KickFromRaid()
+ Purpose : Kick a group from a raid
+ Example : /kickfromraid
+*/
+void Commands::Command_KickFromRaid(Client* client, Seperator* sep) {
+ Entity* target = nullptr;
+ Client* target_client = nullptr;
+ if( sep && sep->arg[0] ) {
+ target_client = zone_list.GetClientByCharName(sep->arg[0]);
+ if(target_client) {
+ target = target_client->GetPlayer();
+ }
+ }
+ if(!target) {
+ if(client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsEntity())
+ target = (Entity*)client->GetPlayer()->GetTarget();
+
+ if(target && target->IsPlayer())
+ target_client = ((Player*)target)->GetClient();
+ }
+ GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
+ if(gmi && gmi->leader && target && target->GetGroupMemberInfo() && world.GetGroupManager()->IsInRaidGroup(gmi->group_id, target->GetGroupMemberInfo()->group_id, false) &&
+ world.GetGroupManager()->IsInRaidGroup(gmi->group_id, gmi->group_id, true)) {
+ GroupOptions goptions;
+ world.GetGroupManager()->GetDefaultGroupOptions(gmi->group_id, &goptions);
+ world.GetGroupManager()->RemoveGroupFromRaid(gmi->group_id, target->GetGroupMemberInfo()->group_id);
+ std::vector raidGroups;
+ world.GetGroupManager()->GetRaidGroups(gmi->group_id, &raidGroups);
+ peer_manager.sendPeersNewGroupRequest("", 0, gmi->group_id, "", "", &goptions, "", &raidGroups, true);
+ std::vector emptyRaid;
+ peer_manager.sendPeersNewGroupRequest("", 0, target->GetGroupMemberInfo()->group_id, "", "", &goptions, "", &emptyRaid, true);
+ }
+}
+
+/*
+ Function: Command_LeaveRaid()
+ Purpose : Leave a raid
+ Example : /leaveraid
+*/
+void Commands::Command_LeaveRaid(Client* client, Seperator* sep) {
+ GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
+ int32 orig_group_id = 0;
+ if(gmi && gmi->leader && world.GetGroupManager()->IsInRaidGroup(gmi->group_id, gmi->group_id, false)) {
+ orig_group_id = gmi->group_id;
+ GroupOptions goptions;
+ world.GetGroupManager()->GetDefaultGroupOptions(gmi->group_id, &goptions);
+ std::vector raidGroups;
+ world.GetGroupManager()->GetRaidGroups(gmi->group_id, &raidGroups);
+ std::vector::iterator cur_group_itr = std::find(raidGroups.begin(), raidGroups.end(), gmi->group_id);
+ if(cur_group_itr != raidGroups.end())
+ raidGroups.erase(cur_group_itr);
+
+ bool sendEmpty = false;
+ std::vector emptyRaid;
+ if(raidGroups.size() < 2) {
+ sendEmpty = true;
+ }
+
+ for(cur_group_itr = raidGroups.begin(); cur_group_itr != raidGroups.end(); cur_group_itr++) {
+ if(sendEmpty) {
+ world.GetGroupManager()->ClearGroupRaid((*cur_group_itr));
+ peer_manager.sendPeersNewGroupRequest("", 0, (*cur_group_itr), "", "", &goptions, "", &emptyRaid, true);
+ world.GetGroupManager()->SendGroupUpdate((*cur_group_itr), nullptr, true);
+ }
+ else {
+ world.GetGroupManager()->ReplaceRaidGroups((*cur_group_itr), &raidGroups);
+ }
+ }
+
+ if(!sendEmpty) {
+ peer_manager.sendPeersNewGroupRequest("", 0, orig_group_id, "", "", &goptions, "", &raidGroups, true);
+ }
+
+ world.GetGroupManager()->ClearGroupRaid(orig_group_id);
+ world.GetGroupManager()->SendGroupUpdate(orig_group_id, nullptr, true);
+
+ peer_manager.sendPeersNewGroupRequest("", 0, orig_group_id, "", "", &goptions, "", &emptyRaid, true);
+ }
+}
+
+/*
+ Function: Command_Split()
+ Purpose : split coin to group
+ Example : /split {plat} {gold} {silver} {copper}
+*/
+void Commands::Command_Split(Client* client, Seperator* sep) {
+ // int32 item_id = atoul(sep->arg[0,1,2,3]);
+ int32 plat = 0, gold = 0, silver = 0, copper = 0;
+ if(sep->IsNumber(0))
+ plat = atoul(sep->arg[0]);
+ if(sep->IsNumber(1))
+ gold = atoul(sep->arg[1]);
+ if(sep->IsNumber(2))
+ silver = atoul(sep->arg[2]);
+ if(sep->IsNumber(3))
+ copper = atoul(sep->arg[3]);
+
+ world.GetGroupManager()->SplitWithGroupOrRaid(client, plat, gold, silver, copper);
+}
+
+/*
+ Function: Command_RaidSay()
+ Purpose : Speak to raid members
+ Example : /raidsay {message}, /rsay {message}
+*/
+void Commands::Command_RaidSay(Client* client, Seperator* sep) {
+ GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
+ if(sep && sep->arg[0] && gmi) {
+ world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
+ int32 spawn_group_id = gmi->group_id;
+ PlayerGroup* spawn_group = world.GetGroupManager()->GetGroup(spawn_group_id);
+ bool israidgroup = (spawn_group && spawn_group->IsGroupRaid());
+ world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
+ if(israidgroup) {
+ world.GetGroupManager()->GroupChatMessage(gmi->group_id, client->GetPlayer(), client->GetPlayer()->GetCurrentLanguage(), sep->argplus[0], CHANNEL_RAID_SAY);
+ peer_manager.SendPeersChannelMessage(gmi->group_id, std::string(client->GetPlayer()->GetName()), std::string(sep->argplus[0]), CHANNEL_RAID_SAY, client->GetPlayer()->GetCurrentLanguage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/WorldServer/Commands/Commands.h b/source/WorldServer/Commands/Commands.h
index bea8f47..759fbec 100644
--- a/source/WorldServer/Commands/Commands.h
+++ b/source/WorldServer/Commands/Commands.h
@@ -454,6 +454,16 @@ public:
void Command_Assist(Client* client, Seperator* sep);
void Command_Target(Client* client, Seperator* sep);
void Command_Target_Pet(Client* client, Seperator* sep);
+
+ void Command_WhoGroup(Client* client, Seperator* sep);
+ void Command_WhoRaid(Client* client, Seperator* sep);
+ void Command_RaidInvite(Client* client, Seperator* sep);
+ void Command_Raid_Looter(Client* client, Seperator* sep);
+ void Command_KickFromGroup(Client* client, Seperator* sep);
+ void Command_KickFromRaid(Client* client, Seperator* sep);
+ void Command_LeaveRaid(Client* client, Seperator* sep);
+ void Command_Split(Client* client, Seperator* sep);
+ void Command_RaidSay(Client* client, Seperator* sep);
// AA Commands
void Get_AA_Xml(Client* client, Seperator* sep);
@@ -949,6 +959,16 @@ private:
#define COMMAND_SET_CONSUME_FOOD 538
+#define COMMAND_WHOGROUP 539 // /whogroup /whog
+#define COMMAND_WHORAID 540 // /whoraid /whor
+#define COMMAND_RAIDINVITE 541 // /raidinvite
+#define COMMAND_RAID_LOOTER 542 // /raid_looter
+#define COMMAND_KICKFROMGROUP 543 // /kickfromgroup
+#define COMMAND_KICKFROMRAID 544 // /kickfromraid
+#define COMMAND_LEAVERAID 545 // /leaveraid
+#define COMMAND_SPLIT 546 // /split
+#define COMMAND_RAIDSAY 547 // /raidsay /rsay
+
#define GET_AA_XML 750
#define ADD_AA 751
#define COMMIT_AA_PROFILE 752
diff --git a/source/WorldServer/Commands/ConsoleCommands.cpp b/source/WorldServer/Commands/ConsoleCommands.cpp
index 648ec93..e46ba9f 100644
--- a/source/WorldServer/Commands/ConsoleCommands.cpp
+++ b/source/WorldServer/Commands/ConsoleCommands.cpp
@@ -359,14 +359,14 @@ bool ConsoleZoneCommand(Seperator *sep)
{
if( sep->IsNumber(2) )
{
- zone = zone_list.Get(atoi(sep->arg[2]), false, false, false);
- if( zone )
+ ZoneChangeDetails zone_details;
+ if( zone_list.GetZone(&zone_details, atoi(sep->arg[2]), "", false, false, false) )
{
printf("> Zone status for zone ID %i...\n", atoi(sep->arg[2]));
printf("============================================================================================\n");
printf("| %30s | %10s | %42s |\n", "Zone", "Param", "Value");
printf("============================================================================================\n");
- printf("| %30s | %10s | %42s |\n", zone->GetZoneName(), "locked", zone->GetZoneLockState() ? "true" : "false");
+ printf("| %30s | %10s | %42s |\n", zone_details.zoneName, "locked", zone_details.lockState ? "true" : "false");
}
else
{
diff --git a/source/WorldServer/Guilds/Guild.cpp b/source/WorldServer/Guilds/Guild.cpp
index 7198d26..a45cebe 100644
--- a/source/WorldServer/Guilds/Guild.cpp
+++ b/source/WorldServer/Guilds/Guild.cpp
@@ -29,12 +29,14 @@
#include "../WorldDatabase.h"
#include "../../common/Log.h"
#include "../Rules/Rules.h"
+#include "../Web/PeerManager.h"
extern ConfigReader configReader;
extern ZoneList zone_list;
extern WorldDatabase database;
extern World world;
extern RuleManager rule_manager;
+extern PeerManager peer_manager;
/***************************************************************************************************************************************************
* GUILD
@@ -275,7 +277,7 @@ int8 Guild::GetRecruitingDescTag(int8 index) {
return ret;
}
-bool Guild::SetPermission(int8 rank, int8 permission, int8 value, bool send_packet) {
+bool Guild::SetPermission(int8 rank, int8 permission, int8 value, bool send_packet, bool save_needed) {
bool ret = false;
if (value == 0 || value == 1) {
@@ -287,7 +289,8 @@ bool Guild::SetPermission(int8 rank, int8 permission, int8 value, bool send_pack
if (ret && send_packet) {
LogWrite(GUILD__DEBUG, 1, "Guilds", "Set Guild Permissions - Rank: %i, Permission: %i, Value: %i", rank, permission, value);
SendGuildUpdate();
- ranks_save_needed = true;
+ if(save_needed)
+ ranks_save_needed = true;
}
return ret;
}
@@ -301,7 +304,7 @@ int8 Guild::GetPermission(int8 rank, int8 permission) {
return ret;
}
-bool Guild::SetEventFilter(int8 event_id, int8 category, int8 value, bool send_packet) {
+bool Guild::SetEventFilter(int8 event_id, int8 category, int8 value, bool send_packet, bool save_needed) {
bool ret = false;
if ((category == GUILD_EVENT_FILTER_CATEGORY_RETAIN_HISTORY || category == GUILD_EVENT_FILTER_CATEGORY_BROADCAST) && (value == 0 || value == 1)) {
@@ -313,7 +316,8 @@ bool Guild::SetEventFilter(int8 event_id, int8 category, int8 value, bool send_p
if (ret && send_packet) {
LogWrite(GUILD__DEBUG, 1, "Guilds", "Set Guild Event Filter - EventID: %i, Category: %i, Value: %i", event_id, category, value);
SendGuildUpdate();
- event_filters_save_needed = true;
+ if(save_needed)
+ event_filters_save_needed = true;
}
return ret;
}
@@ -712,6 +716,36 @@ bool Guild::AddNewGuildMember(Client *client, const char *invited_by, int8 rank)
}
member_save_needed = true;
+
+ peer_manager.sendPeersAddGuildMember(gm->character_id, GetID(), (invited_by != nullptr) ? std::string(invited_by) : "", gm->join_date, rank);
+ }
+
+ return true;
+}
+
+bool Guild::AddNewGuildMember(int32 characterID, const char *invited_by, int32 join_timestamp, int8 rank) {
+ GuildMember *gm;
+
+ if (members.count(characterID) == 0) {
+ gm = new GuildMember;
+ bool foundMember = peer_manager.GetClientGuildDetails(characterID, gm);
+ if(!foundMember) {
+ LogWrite(GUILD__ERROR, 0, "Guilds", "FAILED TO FIND MEMBER: %s invited %s to join guild: %s", invited_by, gm->name, GetName());
+ safe_delete(gm);
+ return false;
+ }
+ gm->rank = rank;
+ gm->join_date = join_timestamp;
+ gm->last_login_date = gm->join_date;
+ mMembers.writelock(__FUNCTION__, __LINE__);
+ members[characterID] = gm;
+ mMembers.releasewritelock(__FUNCTION__, __LINE__);
+
+ if (invited_by) {
+ AddNewGuildEvent(GUILD_EVENT_MEMBER_JOINS, "%s has accepted %s's invitation to join %s.", Timer::GetUnixTimeStamp(), true, gm->name, invited_by, GetName());
+ SendMessageToGuild(GUILD_EVENT_MEMBER_JOINS, "%s has accepted %s's invitation to join %s.", gm->name, invited_by, GetName());
+ LogWrite(GUILD__DEBUG, 0, "Guilds", "%s invited %s to join guild: %s", invited_by, gm->name, GetName());
+ }
}
return true;
@@ -848,20 +882,21 @@ bool Guild::PromoteGuildMember(Client *client, const char *name, bool send_packe
return ret;
}
-bool Guild::KickGuildMember(Client *client, const char *name, bool send_packet) {
+int32 Guild::KickGuildMember(Client *client, const char *name, bool send_packet) {
GuildMember *gm;
Client *kicked_client;
const char *kicker_name;
-
+ int32 character_id = 0;
assert(client);
assert(name);
if (!(gm = GetGuildMember(name)))
- return false;
+ return 0;
kicker_name = client->GetPlayer()->GetName();
-
+ character_id = gm->character_id;
+
if (!strncmp(kicker_name, gm->name, sizeof(gm->name))) {
AddNewGuildEvent(GUILD_EVENT_MEMBER_LEAVES, "%s left the guild.", Timer::GetUnixTimeStamp(), true, gm->name);
SendMessageToGuild(GUILD_EVENT_MEMBER_LEAVES, "%s left the guild.", gm->name);
@@ -897,7 +932,7 @@ bool Guild::KickGuildMember(Client *client, const char *name, bool send_packet)
safe_delete_array(gm->recruiter_picture_data);
safe_delete(gm);
- return true;
+ return character_id;
}
bool Guild::InvitePlayer(Client *client, const char *name, bool send_packet) {
@@ -1853,7 +1888,10 @@ void Guild::SendGuildMemberList(Client* client) {
}
mMembers.releasereadlock(__FUNCTION__, __LINE__);
//DumpPacket(packet->serialize());
- client->QueuePacket(packet->serialize());
+ //packet->PrintPacket();
+ EQ2Packet* pack = packet->serialize();
+ //DumpPacket(pack);
+ client->QueuePacket(pack);
safe_delete(packet);
}
}
@@ -2179,7 +2217,7 @@ void Guild::SendGuildRecruiterInfo(Client* client, Player* player) {
}
}
-void Guild::HandleGuildSay(Client* sender, const char* message) {
+bool Guild::HandleGuildSay(Client* sender, const char* message) {
map::iterator itr;
GuildMember *gm;
@@ -2189,11 +2227,11 @@ void Guild::HandleGuildSay(Client* sender, const char* message) {
assert(message);
if (!(gm = GetGuildMemberOnline(sender)))
- return;
+ return false;
if (!permissions.Get(gm->rank)->Get(GUILD_PERMISSIONS_SPEAK_IN_GUILD_CHAT)) {
sender->SimpleMessage(CHANNEL_NARRATIVE, "You do not have permission to speak in guild chat.");
- return;
+ return false;
}
mMembers.readlock(__FUNCTION__, __LINE__);
@@ -2206,9 +2244,30 @@ void Guild::HandleGuildSay(Client* sender, const char* message) {
}
mMembers.releasereadlock(__FUNCTION__, __LINE__);
LogWrite(GUILD__DEBUG, 0, "Guilds", "Guild Say");
+
+ return true;
}
-void Guild::HandleOfficerSay(Client* sender, const char* message) {
+void Guild::HandleGuildSay(std::string senderName, const char* message, int8 language) {
+
+ map::iterator itr;
+ GuildMember *gm;
+
+ assert(message);
+ Client* client = 0;
+ mMembers.readlock(__FUNCTION__, __LINE__);
+ for (itr = members.begin(); itr != members.end(); itr++) {
+ if (!(client = zone_list.GetClientByCharID(itr->second->character_id)))
+ continue;
+
+ if (permissions.Get(itr->second->rank)->Get(GUILD_PERMISSIONS_SEE_GUILD_CHAT))
+ client->GetCurrentZone()->HandleChatMessage(senderName, client->GetPlayer()->GetName(), CHANNEL_GUILD_SAY, message, 0, 0, language);
+ }
+ mMembers.releasereadlock(__FUNCTION__, __LINE__);
+ LogWrite(GUILD__DEBUG, 0, "Guilds", "Guild Say");
+}
+
+bool Guild::HandleOfficerSay(Client* sender, const char* message) {
map::iterator itr;
GuildMember *gm;
@@ -2218,11 +2277,11 @@ void Guild::HandleOfficerSay(Client* sender, const char* message) {
assert(message);
if (!(gm = GetGuildMemberOnline(sender)))
- return;
+ return false;
if (!permissions.Get(gm->rank)->Get(GUILD_PERMISSIONS_SPEAK_IN_OFFICER_CHAT)) {
sender->SimpleMessage(CHANNEL_NARRATIVE, "You do not have permission to speak in officer chat.");
- return;
+ return false;
}
mMembers.readlock(__FUNCTION__, __LINE__);
@@ -2235,6 +2294,25 @@ void Guild::HandleOfficerSay(Client* sender, const char* message) {
}
mMembers.releasereadlock(__FUNCTION__, __LINE__);
LogWrite(GUILD__DEBUG, 0, "Guilds", "Officer Say");
+ return true;
+}
+
+void Guild::HandleOfficerSay(std::string senderName, const char* message, int8 language) {
+
+ map::iterator itr;
+ Client *client;
+
+ mMembers.readlock(__FUNCTION__, __LINE__);
+ for (itr = members.begin(); itr != members.end(); itr++) {
+ if (!(client = zone_list.GetClientByCharID(itr->second->character_id)))
+ continue;
+
+ if (permissions.Get(itr->second->rank)->Get(GUILD_PERMISSIONS_SEE_OFFICER_CHAT))
+ client->GetCurrentZone()->HandleChatMessage(senderName, client->GetPlayer()->GetName(), CHANNEL_OFFICER_SAY, message, 0, 0, language);
+
+ }
+ mMembers.releasereadlock(__FUNCTION__, __LINE__);
+ LogWrite(GUILD__DEBUG, 0, "Guilds", "Officer Say");
}
void Guild::SendMessageToGuild(int8 event_type, const char* message, ...) {
@@ -2260,6 +2338,29 @@ void Guild::SendMessageToGuild(int8 event_type, const char* message, ...) {
LogWrite(GUILD__DEBUG, 0, "Guilds", "Sent message to entire guild.");
}
+void Guild::SendGuildChatMessage(const char* message, ...) {
+
+ map::iterator itr;
+ Client *client;
+ va_list argptr;
+ char buffer[4096];
+
+ va_start(argptr, message);
+ vsnprintf(buffer, sizeof(buffer), message, argptr);
+ va_end(argptr);
+
+ mMembers.readlock(__FUNCTION__, __LINE__);
+ for (itr = members.begin(); itr != members.end(); itr++) {
+ if (!(client = zone_list.GetClientByCharID(itr->second->character_id)))
+ continue;
+
+ if (event_filters.Get(itr->second->rank)->Get(GUILD_EVENT_FILTER_CATEGORY_BROADCAST))
+ client->SimpleMessage(CHANNEL_GUILD_CHAT, buffer);
+ }
+ mMembers.releasereadlock(__FUNCTION__, __LINE__);
+ LogWrite(GUILD__DEBUG, 0, "Guilds", "Sent message to entire guild.");
+}
+
string Guild::GetEpicMobDeathMessage(const char* player_name, const char* mob_name) {
char message[256];
diff --git a/source/WorldServer/Guilds/Guild.h b/source/WorldServer/Guilds/Guild.h
index 9ce4923..cad0f57 100644
--- a/source/WorldServer/Guilds/Guild.h
+++ b/source/WorldServer/Guilds/Guild.h
@@ -297,9 +297,9 @@ public:
int8 GetRecruitingPlayStyle() const {return recruiting_play_style;}
bool SetRecruitingDescTag(int8 index, int8 tag, bool send_packet = true);
int8 GetRecruitingDescTag(int8 index);
- bool SetPermission(int8 rank, int8 permission, int8 value, bool send_packet = true);
+ bool SetPermission(int8 rank, int8 permission, int8 value, bool send_packet = true, bool save_needed = true);
int8 GetPermission(int8 rank, int8 permission);
- bool SetEventFilter(int8 event_id, int8 category, int8 value, bool send_packet = true);
+ bool SetEventFilter(int8 event_id, int8 category, int8 value, bool send_packet = true, bool save_needed = true);
int8 GetEventFilter(int8 event_id, int8 category);
int32 GetNumUniqueAccounts();
int32 GetNumRecruiters();
@@ -321,12 +321,13 @@ public:
bool SetGuildMemberNote(const char* name, const char* note, bool send_packet = true);
bool SetGuildOfficerNote(const char* name, const char* note, bool send_packet = true);
bool AddNewGuildMember(Client* client, const char* invited_by = 0, int8 rank = GUILD_RANK_RECRUIT);
+ bool AddNewGuildMember(int32 characterID, const char *invited_by, int32 join_timestamp, int8 rank);
bool AddGuildMember(GuildMember* guild_member);
void RemoveGuildMember(int32 character_id, bool send_packet = true);
void RemoveAllGuildMembers();
bool DemoteGuildMember(Client* client, const char* name, bool send_packet = true);
bool PromoteGuildMember(Client* client, const char* name, bool send_packet = true);
- bool KickGuildMember(Client* client, const char* name, bool send_packet = true);
+ int32 KickGuildMember(Client* client, const char* name, bool send_packet = true);
bool InvitePlayer(Client* client, const char* name, bool send_packet = true);
bool AddPointsToAll(Client* client, float points, const char* comment = 0, bool send_packet = true);
bool AddPointsToAllOnline(Client* client, float points, const char* comment = 0, bool send_packet = true);
@@ -372,9 +373,12 @@ public:
void SendGuildRecruitingDetails(Client* client);
void SendGuildRecruitingImages(Client* client);
void SendGuildRecruiterInfo(Client* client, Player* player);
- void HandleGuildSay(Client* sender, const char* message);
- void HandleOfficerSay(Client* sender, const char* message);
+ bool HandleGuildSay(Client* sender, const char* message);
+ void HandleGuildSay(std::string senderName, const char* message, int8 language);
+ bool HandleOfficerSay(Client* sender, const char* message);
+ void HandleOfficerSay(std::string senderName, const char* message, int8 language);
void SendMessageToGuild(int8 event_type, const char* message, ...);
+ void SendGuildChatMessage(const char* message, ...);
void SetSaveNeeded(bool val) {save_needed = val;}
bool GetSaveNeeded() {return save_needed;}
void SetMemberSaveNeeded(bool val) {member_save_needed = val;}
diff --git a/source/WorldServer/Guilds/GuildDB.cpp b/source/WorldServer/Guilds/GuildDB.cpp
index 5c0ddae..eca4533 100644
--- a/source/WorldServer/Guilds/GuildDB.cpp
+++ b/source/WorldServer/Guilds/GuildDB.cpp
@@ -67,6 +67,39 @@ void WorldDatabase::LoadGuilds() {
LogWrite(GUILD__INFO, 0, "Guilds", "\tLoaded %u Guild(s)", num_guilds);
}
+void WorldDatabase::LoadGuild(int32 guild_id) {
+ Query query;
+ MYSQL_ROW row;
+ Guild* tmpGuild = guild_list.GetGuild(guild_id);
+ if(tmpGuild) // already loaded
+ return;
+
+ MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT `id`, `name`, `motd`, `level`, `xp`, `xp_needed`, `formed_on` FROM `guilds` where id=%u", guild_id);
+ if (result && (row = mysql_fetch_row(result))) {
+ LogWrite(GUILD__DEBUG, 1, "Guilds", "%u. %s", atoul(row[0]), row[1]);
+ Guild* guild = new Guild;
+ guild->SetID(atoul(row[0]));
+ guild->SetName(row[1]);
+ if (row[2])
+ guild->SetMOTD(row[2], false);
+ guild->SetLevel(atoi(row[3]), false);
+ guild->SetEXPCurrent(atoul(row[4]), false);
+ guild->SetEXPToNextLevel(atoul(row[5]), false);
+ guild->SetFormedDate(atoul(row[6]));
+
+ LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoaded %i guild members.", LoadGuildMembers(guild));
+ LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoading Guild Ranks...");
+ LoadGuildRanks(guild);
+ LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoading Guild Event Filters...");
+ LoadGuildEventFilters(guild);
+ LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoading Guild Events...");
+ LoadGuildEvents(guild);
+ LogWrite(GUILD__DEBUG, 3, "Guilds", "\tLoading Guild Recruiting...");
+ LoadGuildRecruiting(guild);
+ guild_list.AddGuild(guild);
+ }
+}
+
int32 WorldDatabase::LoadGuildMembers(Guild* guild) {
int32 num_members = 0;
Query query;
diff --git a/source/WorldServer/Items/Items.h b/source/WorldServer/Items/Items.h
index c098f50..9434128 100644
--- a/source/WorldServer/Items/Items.h
+++ b/source/WorldServer/Items/Items.h
@@ -888,6 +888,7 @@ public:
int32 status_rent_reduction;
float coin_rent_reduction;
int8 house_only;
+ int8 house_location; // 0 = floor, 1 = ceiling, 2 = wall
};
struct HouseContainer_Info{
int64 allowed_types;
diff --git a/source/WorldServer/Items/ItemsDB.cpp b/source/WorldServer/Items/ItemsDB.cpp
index 62888b8..98aab1b 100644
--- a/source/WorldServer/Items/ItemsDB.cpp
+++ b/source/WorldServer/Items/ItemsDB.cpp
@@ -458,7 +458,7 @@ int32 WorldDatabase::LoadHouseItem(int32 item_id)
MYSQL_ROW row;
std::string select_query_addition = std::string(" where item_id = ") + std::to_string(item_id);
- MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT item_id, rent_reduction, status_rent_reduction, coin_rent_reduction, house_only FROM item_details_house%s", (item_id == 0) ? "" : select_query_addition.c_str());
+ MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT item_id, rent_reduction, status_rent_reduction, coin_rent_reduction, house_only, house_location FROM item_details_house%s", (item_id == 0) ? "" : select_query_addition.c_str());
int32 total = 0;
int32 id = 0;
@@ -477,6 +477,7 @@ int32 WorldDatabase::LoadHouseItem(int32 item_id)
item->houseitem_info->status_rent_reduction = atoi(row[2]);
item->houseitem_info->coin_rent_reduction = atof(row[3]);
item->houseitem_info->house_only = atoi(row[4]);
+ item->houseitem_info->house_location = atoul(row[5]);
total++;
}
else
diff --git a/source/WorldServer/Items/LootDB.cpp b/source/WorldServer/Items/LootDB.cpp
index 374159a..b448fd1 100644
--- a/source/WorldServer/Items/LootDB.cpp
+++ b/source/WorldServer/Items/LootDB.cpp
@@ -152,7 +152,7 @@ void WorldDatabase::LoadGlobalLoot(ZoneServer* zone) {
count++;
}
- LogWrite(LOOT__DEBUG, 0, "Loot", "--Loaded %u Global loot list%s.", count, count == 1 ? "" : "s");
+ LogWrite(LOOT__DEBUG, 4, "Loot", "--Loaded %u Global loot list%s.", count, count == 1 ? "" : "s");
}
}
@@ -177,7 +177,7 @@ bool WorldDatabase::LoadSpawnLoot(ZoneServer* zone, Spawn* spawn)
LogWrite(LOOT__DEBUG, 5, "Loot", "---Adding loot table %u to spawn %u", table_id, spawn_id);
count++;
}
- LogWrite(LOOT__DEBUG, 0, "Loot", "--Loaded %u spawn loot list%s.", count, count == 1 ? "" : "s");
+ LogWrite(LOOT__DEBUG, 4, "Loot", "--Loaded %u spawn loot list%s.", count, count == 1 ? "" : "s");
return true;
}
return false;
diff --git a/source/WorldServer/LoginServer.cpp b/source/WorldServer/LoginServer.cpp
index 3f1426c..cd06b50 100644
--- a/source/WorldServer/LoginServer.cpp
+++ b/source/WorldServer/LoginServer.cpp
@@ -66,6 +66,8 @@ extern int errno;
#include "World.h"
#include "../common/ConfigReader.h"
#include "Rules/Rules.h"
+#include "Web/PeerManager.h"
+#include "Web/HTTPSClientPool.h"
extern sint32 numzones;
extern sint32 numclients;
@@ -80,6 +82,8 @@ extern volatile bool RunLoops;
volatile bool LoginLoopRunning = false;
extern ConfigReader configReader;
extern RuleManager rule_manager;
+extern PeerManager peer_manager;
+extern HTTPSClientPool peer_https_pool;
bool AttemptingConnect = false;
@@ -176,7 +180,7 @@ bool LoginServer::Process() {
LogWrite(WORLD__ERROR, 0, "World", "Login Server returned a fatal error: %s\n", pack->pBuffer);
tcpc->Disconnect();
ret = false;
- net.ReadLoginINI();
+ //net.ReadLoginINI(); // can't properly support with command line args now
break;
}
case ServerOP_CharTimeStamp:
@@ -367,10 +371,13 @@ bool LoginServer::Process() {
int32 access_key = 0;
- // if it is a accepted login, we add the zone auth request
- access_key = DetermineCharacterLoginRequest ( utwr );
- if ( access_key != 0 )
+ ZoneChangeDetails details;
+ std::string name = database.loadCharacterFromLogin(&details, utwr->char_id, utwr->lsaccountid);
+ // if it is a accepted login, we add the zone auth request
+ access_key = DetermineCharacterLoginRequest ( utwr, &details, name);
+
+ if ( access_key != 0 )
{
zone_auth.PurgeInactiveAuth();
char* characterName = database.GetCharacterName( utwr->char_id );
@@ -555,22 +562,11 @@ void LoginServer::SendFilterNameResponse ( int8 resp, int32 acct_id , int32 char
safe_delete(outpack);
}
-int32 LoginServer::DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* utwr ) {
+int32 LoginServer::DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* utwr, ZoneChangeDetails* details, std::string name) {
LogWrite(LOGIN__TRACE, 9, "Login", "Enter: %s", __FUNCTION__);
- ServerPacket* outpack = new ServerPacket;
- outpack->opcode = ServerOP_UsertoWorldResp;
- outpack->size = sizeof(UsertoWorldResponse_Struct);
- outpack->pBuffer = new uchar[outpack->size];
- memset(outpack->pBuffer, 0, outpack->size);
- UsertoWorldResponse_Struct* utwrs = (UsertoWorldResponse_Struct*) outpack->pBuffer;
- utwrs->lsaccountid = utwr->lsaccountid;
- utwrs->char_id = utwr->char_id;
- utwrs->ToID = utwr->FromID;
int32 timestamp = Timer::GetUnixTimeStamp();
- utwrs->access_key = timestamp;
-
- // set default response to 0
- utwrs->response = 0;
+ int32 key = static_cast(MakeRandomFloat(0.01,1.0) * UINT32_MAX);
+ int8 response = 0;
sint16 lowestStatus = database.GetLowestCharacterAdminStatus( utwr->lsaccountid );
@@ -587,19 +583,19 @@ int32 LoginServer::DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* u
LogWrite(WORLD__ERROR, 0, "World", "Login Rejected based on PLAY_ERROR (UserStatus) (MinStatus: %i), UserStatus: %i, CharID: %i",loginserver.minLockedStatus,status,utwr->char_id );
switch(status){
case -10:
- utwrs->response = PLAY_ERROR_CHAR_NOT_LOADED;
+ response = PLAY_ERROR_CHAR_NOT_LOADED;
break;
case -9:
- utwrs->response = 0;//PLAY_ERROR_ACCOUNT_IN_USE;
+ response = 0;//PLAY_ERROR_ACCOUNT_IN_USE;
break;
case -8:
- utwrs->response = PLAY_ERROR_LOADING_ERROR;
+ response = PLAY_ERROR_LOADING_ERROR;
break;
case -1:
- utwrs->response = PLAY_ERROR_ACCOUNT_BANNED;
+ response = PLAY_ERROR_ACCOUNT_BANNED;
break;
default:
- utwrs->response = PLAY_ERROR_PROBLEM;
+ response = PLAY_ERROR_PROBLEM;
}
}
else if(net.world_locked == true){
@@ -607,7 +603,7 @@ int32 LoginServer::DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* u
// has high enough status, allow it
if(status >= loginserver.minLockedStatus)
- utwrs->response = 1;
+ response = 1;
}
else if(loginserver.maxPlayers > -1 && ((sint16)client_list.Count()) >= loginserver.maxPlayers)
{
@@ -616,14 +612,66 @@ int32 LoginServer::DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* u
// has high enough status, allow it
if(status >= loginserver.minGameFullStatus)
{
- utwrs->response = 1;
+ response = 1;
}
else
- utwrs->response = -3; // server full response is -3
+ response = -3; // server full response is -3
}
else
- utwrs->response = 1;
+ response = 1;
+ bool attemptedPeer = false;
+ if(response == 1 && details->peerId.size() > 0 && details->peerId != "self" && name.size() > 0) {
+ boost::property_tree::ptree root;
+ root.put("account_id", utwr->lsaccountid);
+ root.put("character_name", std::string(name));
+ root.put("character_id", std::to_string(utwr->char_id));
+ root.put("zone_id", std::to_string(details->zoneId));
+ root.put("instance_id", std::to_string(details->instanceId));
+ root.put("login_key", std::to_string(key));
+ root.put("client_ip", std::string(utwr->ip_address));
+ root.put("world_id", std::to_string(utwr->worldid));
+ root.put("from_id", std::to_string(utwr->FromID));
+ root.put("first_login", true);
+ std::ostringstream jsonStream;
+ boost::property_tree::write_json(jsonStream, root);
+ std::string jsonPayload = jsonStream.str();
+ LogWrite(PEERING__INFO, 0, "Peering", "%s: Sending AddCharAuth for %s to peer %s:%u for existing zone %s", __FUNCTION__, name, details->peerWebAddress.c_str(), details->peerWebPort, details->zoneName.c_str());
+ attemptedPeer = true;
+ peer_https_pool.sendPostRequestToPeerAsync(details->peerId, details->peerWebAddress, std::to_string(details->peerWebPort), "/addcharauth", jsonPayload);
+ }
+ else if(response == 1 && details->peerId == "") {
+ std::shared_ptr peer = peer_manager.getHealthyPeerWithLeastClients();
+ if(peer != nullptr) {
+ boost::property_tree::ptree root;
+ char* characterName = database.GetCharacterName( utwr->char_id );
+ if(!characterName) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: AddCharAuth failed to identify character name for char id %u to peer %s:%u", __FUNCTION__, utwr->char_id, peer->webAddr.c_str(), peer->webPort);
+ }
+ else {
+ root.put("account_id", utwr->lsaccountid);
+ root.put("character_name", std::string(characterName));
+ root.put("character_id", std::to_string(utwr->char_id));
+ root.put("zone_id", std::to_string(details->zoneId));
+ root.put("instance_id", std::to_string(details->instanceId));
+ root.put("login_key", std::to_string(key));
+ root.put("client_ip", std::string(utwr->ip_address));
+ root.put("world_id", std::to_string(utwr->worldid));
+ root.put("from_id", std::to_string(utwr->FromID));
+ root.put("first_login", true);
+ std::ostringstream jsonStream;
+ boost::property_tree::write_json(jsonStream, root);
+ std::string jsonPayload = jsonStream.str();
+ LogWrite(PEERING__INFO, 0, "Peering", "%s: Sending AddCharAuth for %s to peer %s:%u for new zone %s", __FUNCTION__, characterName, peer->webAddr.c_str(), peer->webPort, details->zoneName.c_str());
+ attemptedPeer = true;
+ peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/addcharauth", jsonPayload);
+ }
+ }
+ else if(peer_manager.hasPeers()) {
+ LogWrite(PEERING__WARNING, 0, "Peering", "%s: AddCharAuth failed to find healthy peer for char id %u", __FUNCTION__, utwr->char_id);
+ }
+ }
+
/*sint32 x = database.CommandRequirement("$MAXCLIENTS");
if( (sint32)numplayers >= x && x != -1 && x != 255 && status < 80)
utwrs->response = -3;
@@ -634,34 +682,61 @@ int32 LoginServer::DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* u
utwrs->response = -2;
*/
//printf("Response is %i for %i\n",utwrs->response,id);struct sockaddr_in sa;
+
+ if(!attemptedPeer) {
+ SendCharApprovedLogin(response, "", "", std::string(utwr->ip_address), 0, utwr->lsaccountid, utwr->char_id, key, utwr->worldid, utwr->FromID);
+ }
+
+ LogWrite(LOGIN__TRACE, 9, "Login", "Exit: %s with timestamp=%u", __FUNCTION__, timestamp);
+ // depending on the response determined above, this could return 0 (for failure)
+ return (attemptedPeer) ? 0 : key;
+}
+
+void LoginServer::SendCharApprovedLogin(int8 response, std::string peerAddress, std::string peerInternalAddress, std::string clientIP, int16 peerPort, int32 account_id, int32 char_id, int32 key, int32 world_id, int32 from_id) {
+ ServerPacket* outpack = new ServerPacket;
+ outpack->opcode = ServerOP_UsertoWorldResp;
+ outpack->size = sizeof(UsertoWorldResponse_Struct);
+ outpack->pBuffer = new uchar[outpack->size];
+ memset(outpack->pBuffer, 0, outpack->size);
+ UsertoWorldResponse_Struct* utwrs = (UsertoWorldResponse_Struct*) outpack->pBuffer;
+ utwrs->response = response;
+ utwrs->lsaccountid = account_id;
+ utwrs->char_id = char_id;
+ utwrs->ToID = from_id;
+ utwrs->access_key = key;
+
int32 ipv4addr = 0;
int result = 0;
#ifdef WIN32
struct sockaddr_in myaddr;
ZeroMemory(&myaddr, sizeof(myaddr));
- result = InetPton(AF_INET, utwr->ip_address, &(myaddr.sin_addr));
+ result = InetPton(AF_INET, clientIP.c_str(), &(myaddr.sin_addr));
if(result)
ipv4addr = ntohl(myaddr.sin_addr.s_addr);
#else
- result = inet_pton(AF_INET, utwr->ip_address, &ipv4addr);
+ result = inet_pton(AF_INET, clientIP.c_str(), &ipv4addr);
if(result)
ipv4addr = ntohl(ipv4addr);
#endif
- if (((result > 0 && IsPrivateAddress(ipv4addr)) || (strcmp(net.GetWorldAddress(), utwr->ip_address) == 0)) && (strlen(net.GetInternalWorldAddress()) > 0))
- strcpy(utwrs->ip_address, net.GetInternalWorldAddress());
- else
- strcpy(utwrs->ip_address, net.GetWorldAddress());
- LogWrite(CCLIENT__INFO, 0, "World", "New client login attempt from %s, providing %s as the world server address.",utwr->ip_address, utwrs->ip_address );
+ std::string internalAddress = std::string(net.GetInternalWorldAddress());
+ std::string address = std::string(net.GetWorldAddress());
+ int16 worldport = net.GetWorldPort();
+ if(peerAddress.size() > 0 && peerPort > 0) {
+ internalAddress = peerInternalAddress;
+ address = peerAddress;
+ worldport = peerPort;
+ }
+ if (((result > 0 && IsPrivateAddress(ipv4addr)) || (strcmp(address.c_str(), clientIP.c_str()) == 0)) && (internalAddress.size() > 0))
+ strcpy(utwrs->ip_address, internalAddress.c_str());
+ else
+ strcpy(utwrs->ip_address, address.c_str());
+
+ LogWrite(CCLIENT__INFO, 0, "World", "New client login attempt from %s, providing %s:%u as the world server address.",clientIP.c_str(), utwrs->ip_address, worldport );
- utwrs->port = net.GetWorldPort();
- utwrs->worldid = utwr->worldid;
+ utwrs->port = worldport;
+ utwrs->worldid = world_id;
SendPacket(outpack);
delete outpack;
-
- LogWrite(LOGIN__TRACE, 9, "Login", "Exit: %s with timestamp=%u", __FUNCTION__, timestamp);
- // depending on the response determined above, this could return 0 (for failure)
- return timestamp;
}
-
diff --git a/source/WorldServer/LoginServer.h b/source/WorldServer/LoginServer.h
index d0b4839..e888c8b 100644
--- a/source/WorldServer/LoginServer.h
+++ b/source/WorldServer/LoginServer.h
@@ -58,8 +58,8 @@ public:
void SendDeleteCharacter ( CharacterTimeStamp_Struct* cts );
- int32 DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* utwr );
-
+ int32 DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* utwr, ZoneChangeDetails* details, std::string name);
+ void SendCharApprovedLogin(int8 response, std::string peerAddress, std::string peerInternalAddress, std::string clientIP, int16 peerPort, int32 account_id, int32 char_id, int32 key, int32 world_id, int32 from_id);
void InitLoginServerVariables();
sint16 minLockedStatus;
diff --git a/source/WorldServer/LuaFunctions.cpp b/source/WorldServer/LuaFunctions.cpp
index ef49870..da8bd1f 100644
--- a/source/WorldServer/LuaFunctions.cpp
+++ b/source/WorldServer/LuaFunctions.cpp
@@ -1700,19 +1700,21 @@ int EQ2Emu_lua_GetZone(lua_State* state) {
if (!lua_interface)
return 0;
int32 zone_id = lua_interface->GetInt32Value(state);
- ZoneServer* zone = 0;
- if (zone_id > 0)
- zone = zone_list.Get(zone_id, true, false, false);
+ ZoneChangeDetails zone_details;
+ std::string zone_name;
+ ZoneServer* zone = nullptr;
+
+ if(zone_id < 1) {
+ zone_name = lua_interface->GetStringValue(state);
+ }
+ bool zone_avail = zone_list.GetZone(&zone_details, zone_id, zone_name, true, false, false, false);
+ if (zone_avail) {
+ zone = (ZoneServer*)zone_details.zonePtr;
+ }
else {
- string zone_name = lua_interface->GetStringValue(state);
- if (zone_name.length() > 0) {
- zone = zone_list.Get(zone_name.c_str(), true, false, false);
- }
- else {
- Spawn* spawn = lua_interface->GetSpawn(state);
- if (spawn)
- zone = spawn->GetZone();
- }
+ Spawn* spawn = lua_interface->GetSpawn(state);
+ if (spawn)
+ zone = spawn->GetZone();
}
lua_interface->ResetFunctionStack(state);
if (zone) {
@@ -2597,7 +2599,7 @@ int EQ2Emu_lua_RemoveSpellBonus(lua_State* state) {
luaspell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__);
}
else {
- LogWrite(LUA__ERROR, 0, "LUA", "Error removing spell bonus buff %s called by %s, zone is not available.", luaspell->spell ? luaspell->spell->GetName() : "NotSet", spawn->GetName());
+ LogWrite(LUA__ERROR, 0, "LUA", "Error removing spell bonus buff %s called by %s, zone is not available.", luaspell->spell ? luaspell->spell->GetName() : "NotSet", spawn ? spawn->GetName() : "N/A");
}
}
else if (spawn && spawn->IsEntity()) {
@@ -6680,6 +6682,11 @@ int EQ2Emu_lua_RemoveWard(lua_State* state) {
}
ZoneServer* zone = spell->caster->GetZone();
+ if(!zone) {
+ lua_interface->LogError("%s: RemoveWard error: no valid zone for caster", lua_interface->GetScriptName(state));
+ return 0;
+ }
+
Spawn* target = 0;
spell->MSpellTargets.readlock(__FUNCTION__, __LINE__);
for (int32 i = 0; i < spell->targets.size(); i++) {
@@ -14270,3 +14277,34 @@ int EQ2Emu_lua_DespawnByLocationID(lua_State* state) {
return 1;
}
+int EQ2Emu_lua_AddRespawn(lua_State* state) {
+ ZoneServer* zone = lua_interface->GetZone(state);
+ int32 location_id = lua_interface->GetInt32Value(state, 2);
+ int32 respawn_time = lua_interface->GetInt32Value(state, 3);
+ lua_interface->ResetFunctionStack(state);
+ if (zone) {
+ zone->AddRespawn(location_id, respawn_time);
+ lua_interface->SetBooleanValue(state, true);
+ return 1;
+ }
+ lua_interface->SetBooleanValue(state, false);
+ return 1;
+}
+
+
+
+int EQ2Emu_lua_CreatePersistedRespawn(lua_State* state) {
+ int32 location_id = lua_interface->GetInt32Value(state);
+ int8 spawn_type = lua_interface->GetInt32Value(state, 2);
+ int32 respawn_time = lua_interface->GetInt32Value(state, 3);
+ int32 zone_id = lua_interface->GetInt32Value(state, 4);
+ lua_interface->ResetFunctionStack(state);
+ if (location_id && zone_id) {
+ database.CreatePersistedRespawn(location_id,spawn_type,respawn_time,zone_id);
+ lua_interface->SetBooleanValue(state, true);
+ return 1;
+ }
+ lua_interface->SetBooleanValue(state, false);
+ return 1;
+}
+
diff --git a/source/WorldServer/LuaFunctions.h b/source/WorldServer/LuaFunctions.h
index 877ec44..fb19357 100644
--- a/source/WorldServer/LuaFunctions.h
+++ b/source/WorldServer/LuaFunctions.h
@@ -660,4 +660,7 @@ int EQ2Emu_lua_ToggleCharacterFlag(lua_State* state);
int EQ2Emu_lua_GetSpellInitialTarget(lua_State* state);
int EQ2Emu_lua_DespawnByLocationID(lua_State* state);
+
+int EQ2Emu_lua_AddRespawn(lua_State* state);
+int EQ2Emu_lua_CreatePersistedRespawn(lua_State* state);
#endif
\ No newline at end of file
diff --git a/source/WorldServer/LuaInterface.cpp b/source/WorldServer/LuaInterface.cpp
index 69ab42f..9b85644 100644
--- a/source/WorldServer/LuaInterface.cpp
+++ b/source/WorldServer/LuaInterface.cpp
@@ -905,9 +905,9 @@ void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool
spell->caster->RemoveProc(0, spell);
spell->caster->RemoveMaintainedSpell(spell);
- int8 spell_type = spell->spell->GetSpellData()->spell_type;
- if(spell->caster->IsPlayer() && !removing_all_spells)
+ if(spell->spell && spell->spell->GetSpellData() && spell->caster->IsPlayer() && !removing_all_spells)
{
+ int8 spell_type = spell->spell->GetSpellData()->spell_type;
Player* player = (Player*)spell->caster;
switch(spell_type)
{
@@ -1562,6 +1562,9 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
lua_register(state, "GetSpellInitialTarget", EQ2Emu_lua_GetSpellInitialTarget);
lua_register(state,"DespawnByLocationID", EQ2Emu_lua_DespawnByLocationID);
+
+ lua_register(state,"AddRespawn", EQ2Emu_lua_AddRespawn);
+ lua_register(state,"CreatePersistedRespawn", EQ2Emu_lua_CreatePersistedRespawn);
}
void LuaInterface::LogError(const char* error, ...) {
diff --git a/source/WorldServer/NPC_AI.cpp b/source/WorldServer/NPC_AI.cpp
index 9934e88..07a9086 100644
--- a/source/WorldServer/NPC_AI.cpp
+++ b/source/WorldServer/NPC_AI.cpp
@@ -544,20 +544,30 @@ void Brain::AddToEncounter(Entity* entity) {
PlayerGroup* group = world.GetGroupManager()->GetGroup(group_id);
if (group)
{
- group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
- deque* members = group->GetMembers();
- for (itr = members->begin(); itr != members->end(); itr++) {
- if ((*itr)->member)
- {
- bool success = AddToEncounter((*itr)->member->GetID());
- if((*itr)->client && success) {
- m_encounter_playerlist.insert(make_pair((*itr)->client->GetPlayer()->GetCharacterID(), (*itr)->client->GetPlayer()->GetID()));
+ std::vector raidGroups;
+ group->GetRaidGroups(&raidGroups);
+ if(raidGroups.size() < 1)
+ raidGroups.push_back(group_id);
+
+ std::vector::iterator group_itr;
+ for(group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) {
+ group = world.GetGroupManager()->GetGroup((*group_itr));
+ if(group) {
+ group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
+ deque* members = group->GetMembers();
+ for (itr = members->begin(); itr != members->end(); itr++) {
+ if ((*itr)->member)
+ {
+ bool success = AddToEncounter((*itr)->member->GetID());
+ if((*itr)->client && success) {
+ m_encounter_playerlist.insert(make_pair((*itr)->client->GetPlayer()->GetCharacterID(), (*itr)->client->GetPlayer()->GetID()));
+ }
+ }
}
+ group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
}
}
- group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
}
-
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
}
else {
diff --git a/source/WorldServer/Player.cpp b/source/WorldServer/Player.cpp
index 13a9aea..32046a4 100644
--- a/source/WorldServer/Player.cpp
+++ b/source/WorldServer/Player.cpp
@@ -77,6 +77,8 @@ Player::Player(){
spell_count = 0;
spell_orig_packet = 0;
spell_xor_packet = 0;
+ raid_orig_packet = nullptr;
+ raid_xor_packet = nullptr;
resurrecting = false;
spawn_id = 1;
spawn_type = 4;
@@ -131,6 +133,8 @@ Player::Player(){
need_trait_update = true;
active_food_unique_id = 0;
active_drink_unique_id = 0;
+ raidsheet_changed = false;
+ hassent_raid = false;
}
Player::~Player(){
SetSaveSpellEffects(true);
@@ -181,6 +185,8 @@ Player::~Player(){
safe_delete_array(spawn_tmp_pos_xor_packet);
safe_delete_array(spell_xor_packet);
safe_delete_array(spell_orig_packet);
+ safe_delete_array(raid_orig_packet);
+ safe_delete_array(raid_xor_packet);
DestroyQuests();
WritePlayerStatistics();
RemovePlayerStatistics();
@@ -1067,7 +1073,7 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal
player->GetMaintainedMutex()->readlock(__FUNCTION__, __LINE__);
for (int i = 0; i < 45; i++) {
if (i < 30) {
- maintained_target = player->GetZone()->GetSpawnByID(info_struct->maintained_effects[i].target);
+ maintained_target = player->GetZone() ? player->GetZone()->GetSpawnByID(info_struct->maintained_effects[i].target) : nullptr;
packet->setSubstructDataByName("maintained_effects", "name", info_struct->maintained_effects[i].name, i, 0);
if (maintained_target)
packet->setSubstructDataByName("maintained_effects", "target", player->GetIDWithPlayerSpawn(maintained_target), i, 0);
@@ -3055,6 +3061,162 @@ EQ2Packet* Player::GetSpellBookUpdatePacket(int16 version) {
}
return ret;
}
+EQ2Packet* Player::GetRaidUpdatePacket(int16 version) {
+ std::unique_lock lock(raid_update_mutex);
+
+ std::vector raidGroups;
+ PacketStruct* packet = configReader.getStruct("WS_RaidUpdate", version);
+ EQ2Packet* ret = 0;
+ Entity* member = 0;
+ int8 det_count = 0;
+ int8 total_groups = 0;
+ if (packet) {
+ int16 ptr = 0;
+ // Get the packet size
+ PacketStruct* packet2 = configReader.getStruct("Substruct_RaidMember", version);
+ int32 total_bytes = packet2->GetTotalPacketSize();
+ safe_delete(packet2);
+ world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
+ if (GetGroupMemberInfo()) {
+ PlayerGroup* group = world.GetGroupManager()->GetGroup(GetGroupMemberInfo()->group_id);
+ if (group)
+ {
+ group->GetRaidGroups(&raidGroups);
+ std::vector::iterator raid_itr;
+ int32 group_pos = 0;
+ for(raid_itr = raidGroups.begin(); raid_itr != raidGroups.end(); raid_itr++) {
+ group = world.GetGroupManager()->GetGroup((*raid_itr));
+ if(!group)
+ continue;
+ total_groups++;
+ group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
+ deque* members = group->GetMembers();
+ deque::iterator itr;
+ GroupMemberInfo* info = 0;
+ int x = 1;
+ int lastpos = 1;
+ bool gotleader = false;
+ for (itr = members->begin(); itr != members->end(); itr++) {
+ info = *itr;
+
+ if(!info)
+ continue;
+
+ member = info->member;
+
+ std::string prop_name("group_member");
+ if(!gotleader && info->leader) {
+ lastpos = x;
+ x = 0;
+ gotleader = true;
+ }
+ else if(lastpos) {
+ x = lastpos;
+ lastpos = 0;
+ }
+ prop_name.append(std::to_string(x) + "_" + std::to_string(group_pos));
+ x++;
+ if (member && member->GetZone() == GetZone()) {
+ packet->setSubstructDataByName(prop_name.c_str(), "spawn_id", GetIDWithPlayerSpawn(member), 0);
+
+ if (member->HasPet()) {
+ if (member->GetPet())
+ packet->setSubstructDataByName(prop_name.c_str(), "pet_id", GetIDWithPlayerSpawn(member->GetPet()), 0);
+ else
+ packet->setSubstructDataByName(prop_name.c_str(), "pet_id", GetIDWithPlayerSpawn(member->GetCharmedPet()), 0);
+ }
+ else
+ packet->setSubstructDataByName(prop_name.c_str(), "pet_id", 0xFFFFFFFF, 0);
+
+ //Send detriment counts as 255 if all dets of that type are incurable
+ det_count = member->GetTraumaCount();
+ if (det_count > 0) {
+ if (!member->HasCurableDetrimentType(DET_TYPE_TRAUMA))
+ det_count = 255;
+ }
+ packet->setSubstructDataByName(prop_name.c_str(), "trauma_count", det_count, 0);
+
+ det_count = member->GetArcaneCount();
+ if (det_count > 0) {
+ if (!member->HasCurableDetrimentType(DET_TYPE_ARCANE))
+ det_count = 255;
+ }
+ packet->setSubstructDataByName(prop_name.c_str(), "arcane_count", det_count, 0);
+
+ det_count = member->GetNoxiousCount();
+ if (det_count > 0) {
+ if (!member->HasCurableDetrimentType(DET_TYPE_NOXIOUS))
+ det_count = 255;
+ }
+ packet->setSubstructDataByName(prop_name.c_str(), "noxious_count", det_count, 0);
+
+ det_count = member->GetElementalCount();
+ if (det_count > 0) {
+ if (!member->HasCurableDetrimentType(DET_TYPE_ELEMENTAL))
+ det_count = 255;
+ }
+ packet->setSubstructDataByName(prop_name.c_str(), "elemental_count", det_count, 0);
+
+ det_count = member->GetCurseCount();
+ if (det_count > 0) {
+ if (!member->HasCurableDetrimentType(DET_TYPE_CURSE))
+ det_count = 255;
+ }
+ packet->setSubstructDataByName(prop_name.c_str(), "curse_count", det_count, 0);
+
+ packet->setSubstructDataByName(prop_name.c_str(), "zone_status", 1, 0);
+ }
+ else {
+ packet->setSubstructDataByName(prop_name.c_str(), "pet_id", 0xFFFFFFFF, 0);
+ //packet->setSubstructDataByName(prop_name.c_str(), "unknown5", 1, 0, 1); // unknown5 > 1 = name is blue
+ packet->setSubstructDataByName(prop_name.c_str(), "zone_status", 2, 0);
+ }
+
+ packet->setSubstructDataByName(prop_name.c_str(), "name", info->name.c_str(), 0);
+ packet->setSubstructDataByName(prop_name.c_str(), "hp_current", info->hp_current, 0);
+ packet->setSubstructDataByName(prop_name.c_str(), "hp_max", info->hp_max, 0);
+ packet->setSubstructDataByName(prop_name.c_str(), "hp_current2", info->hp_current, 0);
+ packet->setSubstructDataByName(prop_name.c_str(), "power_current", info->power_current, 0);
+ packet->setSubstructDataByName(prop_name.c_str(), "power_max", info->power_max, 0);
+ packet->setSubstructDataByName(prop_name.c_str(), "level_current", info->level_current, 0);
+ packet->setSubstructDataByName(prop_name.c_str(), "level_max", info->level_max, 0);
+ packet->setSubstructDataByName(prop_name.c_str(), "zone", info->zone.c_str(), 0);
+ packet->setSubstructDataByName(prop_name.c_str(), "race_id", info->race_id, 0);
+ packet->setSubstructDataByName(prop_name.c_str(), "class_id", info->class_id, 0);
+ }
+
+ group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
+ group_pos += 1;
+ }
+ }
+ }
+ world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
+ //packet->PrintPacket();
+
+ hassent_raid = true;
+ string* data = packet->serializeString();
+ int32 size = data->length();
+
+ uchar* tmp = new uchar[size];
+ if(!raid_xor_packet){
+ raid_orig_packet = new uchar[size];
+ raid_xor_packet = new uchar[size];
+ memcpy(raid_orig_packet, (uchar*)data->c_str(), size);
+ size = Pack(tmp, (uchar*)data->c_str(), size, size, version);
+ }
+ else{
+ memcpy(raid_xor_packet, (uchar*)data->c_str(), size);
+ Encode(raid_xor_packet, raid_orig_packet, size);
+ size = Pack(tmp, raid_xor_packet, size, size, version);
+ }
+
+ ret = new EQ2Packet(OP_UpdateRaidMsg, tmp, size);
+ safe_delete_array(tmp);
+ safe_delete(packet);
+ //DumpPacket(ret);
+ }
+ return ret;
+}
PlayerInfo::~PlayerInfo(){
RemoveOldPackets();
@@ -3946,6 +4108,14 @@ bool Player::GetCharSheetChanged(){
return charsheet_changed;
}
+void Player::SetRaidSheetChanged(bool val){
+ raidsheet_changed = val;
+}
+
+bool Player::GetRaidSheetChanged(){
+ return raidsheet_changed;
+}
+
bool Player::AdventureXPEnabled(){
return (GetInfoStruct()->get_flags() & (1 << CF_COMBAT_EXPERIENCE_ENABLED));
}
@@ -5828,6 +5998,11 @@ void Player::SetReturningFromLD(bool val){
spell_orig_packet=0;
spell_xor_packet=0;
spell_count = 0;
+
+ safe_delete_array(raid_orig_packet);
+ safe_delete_array(raid_xor_packet);
+ raid_orig_packet=0;
+ raid_xor_packet=0;
reset_character_flag(CF_IS_SITTING);
if (GetActivityStatus() & ACTIVITY_STATUS_CAMPING)
@@ -5981,40 +6156,6 @@ void Player::DeleteMail(int32 mail_id, bool from_database) {
}
}
-ZoneServer* Player::GetGroupMemberInZone(int32 zone_id) {
- ZoneServer* ret = nullptr;
-
- GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
- // If the player has a group and destination zone id
- if (gmi && zone_id) {
- deque::iterator itr;
-
- world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
-
- PlayerGroup* group = world.GetGroupManager()->GetGroup(gmi->group_id);
- if (group)
- {
- group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
- deque* members = group->GetMembers();
- // Loop through the group members
- for (itr = members->begin(); itr != members->end(); itr++) {
- // If a group member matches a target
- if ((*itr)->member && (*itr)->member != this && (*itr)->member->GetZone() && (*itr)->member->GetZone()->GetInstanceID() > 0 &&
- (*itr)->member->GetZone()->GetZoneID() == zone_id) {
- // toggle the flag and break the loop
- ret = (*itr)->member->GetZone();
- break;
- }
- }
- group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
- }
-
- world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
- }
- return ret;
-}
-
-
/* CharacterInstances */
CharacterInstances::CharacterInstances() {
@@ -6154,7 +6295,8 @@ void CharacterInstances::ProcessInstanceTimers(Player* player) {
if (data->zone_instance_type == SOLO_PERSIST_INSTANCE || data->zone_instance_type == GROUP_PERSIST_INSTANCE || data->zone_instance_type == RAID_PERSIST_INSTANCE) {
// Check max duration (last success + success time)
- if (Timer::GetUnixTimeStamp() >= (data->last_success_timestamp + data->success_lockout_time)) {
+ // if the zone does not have a success lockout time, we should not apply this logic
+ if (data->success_lockout_time > 0 && (Timer::GetUnixTimeStamp() >= (data->last_success_timestamp + data->success_lockout_time))) {
// Max duration has passed, instance has expired lets remove the player from it,
// this will also delete the instace if all players have been removed from it
database.DeleteCharacterFromInstance(player->GetCharacterID(), data->instance_id);
@@ -7117,8 +7259,8 @@ void Player::SaveSpellEffects()
target_char_id = ((Player*)spawn)->GetCharacterID();
else if (spawn && spawn->IsPet() && ((Entity*)spawn)->GetOwner() == (Entity*)this)
target_char_id = 0xFFFFFFFF;
-
- int32 caster_char_id = (info->maintained_effects[i].spell->caster && info->maintained_effects[i].spell->caster->IsPlayer()) ? ((Player*)info->maintained_effects[i].spell->caster)->GetCharacterID() : 0;
+
+ int32 caster_char_id = info->maintained_effects[i].spell->initial_caster_char_id;
int32 timestamp = 0xFFFFFFFF;
if(info->maintained_effects[i].spell->spell->GetSpellData() && !info->maintained_effects[i].spell->spell->GetSpellData()->duration_until_cancel)
diff --git a/source/WorldServer/Player.h b/source/WorldServer/Player.h
index b1491ac..97beb80 100644
--- a/source/WorldServer/Player.h
+++ b/source/WorldServer/Player.h
@@ -437,6 +437,8 @@ public:
PlayerInfo* GetPlayerInfo();
void SetCharSheetChanged(bool val);
bool GetCharSheetChanged();
+ void SetRaidSheetChanged(bool val);
+ bool GetRaidSheetChanged();
void AddFriend(const char* name, bool save);
bool IsFriend(const char* name);
void RemoveFriend(const char* name);
@@ -552,8 +554,9 @@ public:
int8 GetSpellTier(int32 id);
void SetSpellStatus(Spell* spell, int8 status);
void RemoveSpellStatus(Spell* spell, int8 status);
- EQ2Packet* GetSpellBookUpdatePacket(int16 version);
EQ2Packet* GetSpellSlotMappingPacket(int16 version);
+ EQ2Packet* GetSpellBookUpdatePacket(int16 version);
+ EQ2Packet* GetRaidUpdatePacket(int16 version);
int32 GetCharacterID();
void SetCharacterID(int32 new_id);
EQ2Packet* GetQuickbarPacket(int16 version);
@@ -775,7 +778,6 @@ public:
void SetAwayMessage(string val) { away_message = val; }
void SetRangeAttack(bool val);
bool GetRangeAttack();
- ZoneServer* GetGroupMemberInZone(int32 zone_id);
bool AddMail(Mail* mail);
MutexMap* GetMail();
Mail* GetMail(int32 mail_id);
@@ -1112,6 +1114,7 @@ public:
map m_levelXPReq;
mutable std::shared_mutex spell_packet_update_mutex;
+ mutable std::shared_mutex raid_update_mutex;
private:
bool reset_mentorship;
bool range_attack;
@@ -1129,7 +1132,9 @@ private:
map current_quest_flagged;
PlayerFaction factions;
map completed_quests;
- bool charsheet_changed;
+ std::atomic charsheet_changed;
+ std::atomic raidsheet_changed;
+ std::atomic hassent_raid;
map spawn_vis_packet_list;
map spawn_info_packet_list;
map spawn_pos_packet_list;
@@ -1140,6 +1145,9 @@ private:
uchar* spell_orig_packet;
uchar* spell_xor_packet;
int16 spell_count;
+
+ uchar* raid_orig_packet;
+ uchar* raid_xor_packet;
//float speed;
int16 target_id;
Spawn* combat_target;
diff --git a/source/WorldServer/PlayerGroups.cpp b/source/WorldServer/PlayerGroups.cpp
index 483b007..698e9a3 100644
--- a/source/WorldServer/PlayerGroups.cpp
+++ b/source/WorldServer/PlayerGroups.cpp
@@ -26,10 +26,14 @@ along with EQ2Emulator. If not, see .
#include "Bots/Bot.h"
#include "SpellProcess.h"
#include "Rules/Rules.h"
+#include "Web/PeerManager.h"
+#include "WorldDatabase.h"
+extern ConfigReader configReader;
extern ZoneList zone_list;
extern RuleManager rule_manager;
-
+extern PeerManager peer_manager;
+extern WorldDatabase database;
/******************************************************** PlayerGroup ********************************************************/
PlayerGroup::PlayerGroup(int32 id) {
@@ -42,7 +46,7 @@ PlayerGroup::~PlayerGroup() {
Disband();
}
-bool PlayerGroup::AddMember(Entity* member) {
+bool PlayerGroup::AddMember(Entity* member, bool is_leader) {
// Check to make sure the entity we are adding is valid
if (!member) {
LogWrite(GROUP__ERROR, 0, "Group", "New member is null");
@@ -59,13 +63,27 @@ bool PlayerGroup::AddMember(Entity* member) {
GroupMemberInfo* gmi = new GroupMemberInfo;
gmi->group_id = m_id;
gmi->member = member;
- gmi->leader = false;
- if (member->IsPlayer())
+ gmi->leader = is_leader;
+ if (member->IsPlayer()) {
+ gmi->is_client = true;
gmi->client = ((Player*)member)->GetClient();
- else
+ }
+ else {
+ gmi->is_client = false;
gmi->client = 0;
+ }
gmi->mentor_target_char_id = 0;
+ if (member->GetZone()) {
+ gmi->zone_id = member->GetZone()->GetZoneID();
+ gmi->instance_id = member->GetZone()->GetInstanceID();
+ }
+
+ gmi->client_peer_address = std::string(net.GetWorldAddress());
+ gmi->client_peer_port = net.GetWorldPort();
+
+ gmi->is_raid_looter = false;
+
member->SetGroupMemberInfo(gmi);
member->group_id = gmi->group_id;
MGroupMembers.writelock();
@@ -77,6 +95,45 @@ bool PlayerGroup::AddMember(Entity* member) {
return true;
}
+bool PlayerGroup::AddMemberFromPeer(std::string name, bool isleader, bool isclient, int8 class_id, sint32 hp_cur, sint32 hp_max, int16 level_cur, int16 level_max,
+ sint32 power_cur, sint32 power_max, int8 race_id, std::string zonename, int32 mentor_target_char_id, int32 zone_id, int32 instance_id,
+ std::string peer_client_address, int16 peer_client_port, bool is_raid_looter) {
+ // Create a new GroupMemberInfo and assign it to the new member
+ GroupMemberInfo* gmi = new GroupMemberInfo;
+ gmi->group_id = m_id;
+ gmi->member = nullptr;
+ gmi->leader = isleader;
+ gmi->is_client = isclient;
+ gmi->client = 0;
+ gmi->mentor_target_char_id = 0;
+
+ gmi->class_id = class_id;
+ gmi->hp_max = hp_max;
+ gmi->hp_current = hp_cur;
+ gmi->level_max = level_max;
+ gmi->level_current = level_cur;
+ gmi->name = name;
+ gmi->power_current = power_cur;
+ gmi->power_max = power_max;
+ gmi->race_id = race_id;
+ gmi->zone = zonename;
+ gmi->zone_id = zone_id;
+ gmi->instance_id = instance_id;
+
+ gmi->mentor_target_char_id = mentor_target_char_id;
+
+ gmi->client_peer_address = peer_client_address;
+ gmi->client_peer_port = peer_client_port;
+
+ gmi->is_raid_looter = is_raid_looter;
+ MGroupMembers.writelock();
+ m_members.push_back(gmi);
+ MGroupMembers.releasewritelock();
+
+ SendGroupUpdate();
+ return true;
+}
+
bool PlayerGroup::RemoveMember(Entity* member) {
GroupMemberInfo* gmi = member->GetGroupMemberInfo();
if (!gmi) {
@@ -85,6 +142,8 @@ bool PlayerGroup::RemoveMember(Entity* member) {
bool ret = false;
+ bool selfInRaid = IsInRaidGroup(gmi->group_id, false);
+
MGroupMembers.writelock();
member->SetGroupMemberInfo(0);
@@ -93,15 +152,18 @@ bool PlayerGroup::RemoveMember(Entity* member) {
for (itr = m_members.begin(); itr != m_members.end(); itr++) {
if (gmi == *itr)
erase_itr = itr;
-
- if(member->IsPlayer() && (*itr)->mentor_target_char_id == ((Player*)member)->GetCharacterID() && (*itr)->client)
+
+ if (member->IsPlayer() && (*itr)->mentor_target_char_id == ((Player*)member)->GetCharacterID() && (*itr)->client)
{
(*itr)->mentor_target_char_id = 0;
(*itr)->client->GetPlayer()->EnableResetMentorship();
}
- if ((*itr)->client)
+ if ((*itr)->client) {
(*itr)->client->GetPlayer()->SetCharSheetChanged(true);
+ if (selfInRaid)
+ (*itr)->client->GetPlayer()->SetRaidSheetChanged(true);
+ }
}
if (erase_itr != m_members.end()) {
ret = true;
@@ -117,23 +179,65 @@ bool PlayerGroup::RemoveMember(Entity* member) {
return ret;
}
+bool PlayerGroup::RemoveMember(std::string name, bool is_client, int32 charID) {
+ bool ret = false;
+ GroupMemberInfo* gmi = nullptr;
+ bool selfInRaid = IsInRaidGroup(GetID(), false);
+ deque::iterator erase_itr = m_members.end();
+ deque::iterator itr;
+ for (itr = m_members.begin(); itr != m_members.end(); itr++) {
+ if ((*itr)->name == name && (*itr)->is_client == is_client) {
+ gmi = (*itr);
+ erase_itr = itr;
+ }
+
+ if (is_client && charID > 0 && (*itr)->mentor_target_char_id == charID && (*itr)->client)
+ {
+ (*itr)->mentor_target_char_id = 0;
+ (*itr)->client->GetPlayer()->EnableResetMentorship();
+ }
+
+ if ((*itr)->client) {
+ (*itr)->client->GetPlayer()->SetCharSheetChanged(true);
+ if (selfInRaid)
+ (*itr)->client->GetPlayer()->SetRaidSheetChanged(true);
+ }
+ }
+ if (erase_itr != m_members.end()) {
+ ret = true;
+ m_members.erase(erase_itr);
+ }
+ MGroupMembers.releasewritelock();
+
+ safe_delete(gmi);
+
+ return ret;
+}
+
void PlayerGroup::Disband() {
+ m_raidgroups.clear();
deque::iterator itr;
MGroupMembers.writelock();
+
+ bool selfInRaid = IsInRaidGroup(GetID(), false);
+
for (itr = m_members.begin(); itr != m_members.end(); itr++) {
if ((*itr)->member) {
(*itr)->member->SetGroupMemberInfo(0);
if ((*itr)->member->IsBot())
((Bot*)(*itr)->member)->Camp();
}
- if((*itr)->mentor_target_char_id && (*itr)->client)
+ if ((*itr)->mentor_target_char_id && (*itr)->client)
{
(*itr)->mentor_target_char_id = 0;
(*itr)->client->GetPlayer()->EnableResetMentorship();
}
- if ((*itr)->client)
+ if ((*itr)->client) {
(*itr)->client->GetPlayer()->SetCharSheetChanged(true);
+ if (selfInRaid)
+ (*itr)->client->GetPlayer()->SetRaidSheetChanged(true);
+ }
safe_delete(*itr);
}
@@ -142,13 +246,17 @@ void PlayerGroup::Disband() {
MGroupMembers.releasewritelock();
}
-void PlayerGroup::SendGroupUpdate(Client* exclude) {
+void PlayerGroup::SendGroupUpdate(Client* exclude, bool forceRaidUpdate) {
+ bool selfInRaid = IsInRaidGroup(GetID(), false);
deque::iterator itr;
MGroupMembers.readlock(__FUNCTION__, __LINE__);
for (itr = m_members.begin(); itr != m_members.end(); itr++) {
GroupMemberInfo* gmi = *itr;
- if (gmi->client && gmi->client != exclude && !gmi->client->IsZoning())
+ if (gmi->client && gmi->client != exclude && !gmi->client->IsZoning()) {
gmi->client->GetPlayer()->SetCharSheetChanged(true);
+ if (selfInRaid || forceRaidUpdate)
+ gmi->client->GetPlayer()->SetRaidSheetChanged(true);
+ }
}
MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
}
@@ -156,9 +264,9 @@ void PlayerGroup::SendGroupUpdate(Client* exclude) {
void PlayerGroup::SimpleGroupMessage(const char* message) {
deque::iterator itr;
MGroupMembers.readlock(__FUNCTION__, __LINE__);
- for(itr = m_members.begin(); itr != m_members.end(); itr++) {
+ for (itr = m_members.begin(); itr != m_members.end(); itr++) {
GroupMemberInfo* info = *itr;
- if(info->client)
+ if (info->client)
info->client->SimpleMessage(CHANNEL_GROUP_CHAT, message);
}
MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
@@ -182,58 +290,97 @@ void PlayerGroup::SendGroupMessage(int8 type, const char* message, ...) {
MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
}
-void PlayerGroup::GroupChatMessage(Spawn* from, int32 language, const char* message) {
+void PlayerGroup::GroupChatMessage(Spawn* from, int32 language, const char* message, int16 channel) {
deque::iterator itr;
MGroupMembers.readlock(__FUNCTION__, __LINE__);
- for(itr = m_members.begin(); itr != m_members.end(); itr++) {
+ for (itr = m_members.begin(); itr != m_members.end(); itr++) {
GroupMemberInfo* info = *itr;
- if(info && info->client && info->client->GetCurrentZone())
- info->client->GetCurrentZone()->HandleChatMessage(info->client, from, 0, CHANNEL_GROUP_SAY, message, 0, 0, true, language);
+ if (info && info->client && info->client->GetCurrentZone())
+ info->client->GetCurrentZone()->HandleChatMessage(info->client, from, 0, channel, message, 0, 0, true, language);
+ }
+ MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
+}
+
+void PlayerGroup::GroupChatMessage(std::string fromName, int32 language, const char* message, int16 channel) {
+ deque::iterator itr;
+ MGroupMembers.readlock(__FUNCTION__, __LINE__);
+ for (itr = m_members.begin(); itr != m_members.end(); itr++) {
+ GroupMemberInfo* info = *itr;
+ if (info && info->client && info->client->GetCurrentZone())
+ info->client->GetCurrentZone()->HandleChatMessage(info->client, fromName, "", channel, message, 0, 0, language);
}
MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
}
bool PlayerGroup::MakeLeader(Entity* new_leader) {
+ if (!new_leader || new_leader->GetGroupMemberInfo())
+ return false;
+
+ bool selfInRaid = IsInRaidGroup(GetID(), false);
+
+ deque::iterator itr;
+ MGroupMembers.readlock(__FUNCTION__, __LINE__);
+ new_leader->GetGroupMemberInfo()->leader = true;
+ for (itr = m_members.begin(); itr != m_members.end(); itr++) {
+ GroupMemberInfo* info = *itr;
+ if (info && info != new_leader->GetGroupMemberInfo() && info->leader) {
+ info->leader = false;
+ peer_manager.sendPeersGroupMember(GetID(), info, true);
+ break;
+ }
+ if ((*itr)->client) {
+ (*itr)->client->GetPlayer()->SetCharSheetChanged(true);
+ if (selfInRaid)
+ (*itr)->client->GetPlayer()->SetRaidSheetChanged(true);
+ }
+ }
+ peer_manager.sendPeersGroupMember(GetID(), new_leader->GetGroupMemberInfo(), true);
+ MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
+
+
+ SendGroupUpdate();
+
+ return true;
+}
+
+std::string PlayerGroup::GetLeaderName() {
+ std::string name("");
deque::iterator itr;
MGroupMembers.readlock(__FUNCTION__, __LINE__);
for (itr = m_members.begin(); itr != m_members.end(); itr++) {
GroupMemberInfo* info = *itr;
- if (info->leader) {
- info->leader = false;
+ if (info->leader && info->name.size() > 0) {
+ name = info->name;
break;
}
}
MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
-
- new_leader->GetGroupMemberInfo()->leader = true;
- SendGroupUpdate();
-
- return true;
+ return name;
}
bool PlayerGroup::ShareQuestWithGroup(Client* quest_sharer, Quest* quest) {
- if(!quest || !quest_sharer)
+ if (!quest || !quest_sharer)
return false;
-
+
bool canShare = quest->CanShareQuestCriteria(quest_sharer);
-
- if(!canShare) {
+
+ if (!canShare) {
return false;
}
-
+
deque::iterator itr;
MGroupMembers.readlock(__FUNCTION__, __LINE__);
- for(itr = m_members.begin(); itr != m_members.end(); itr++) {
+ for (itr = m_members.begin(); itr != m_members.end(); itr++) {
GroupMemberInfo* info = *itr;
- if(info && info->client && info->client->GetCurrentZone()) {
- if( quest_sharer != info->client && info->client->GetPlayer()->HasAnyQuest(quest->GetQuestID()) == 0 ) {
+ if (info && info->client && info->client->GetCurrentZone()) {
+ if (quest_sharer != info->client && info->client->GetPlayer()->HasAnyQuest(quest->GetQuestID()) == 0) {
info->client->AddPendingQuest(new Quest(quest));
info->client->Message(CHANNEL_COLOR_YELLOW, "%s has shared the quest %s with you.", quest_sharer->GetPlayer()->GetName(), quest->GetName());
}
}
}
MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
-
+
return true;
}
@@ -252,7 +399,7 @@ PlayerGroupManager::~PlayerGroupManager() {
m_pendingInvites.clear();
MPendingInvites.releasewritelock(__FUNCTION__, __LINE__);
- std::unique_lock lock(MGroups);
+ std::unique_lock lock(MGroups);
map::iterator itr;
for (itr = m_groups.begin(); itr != m_groups.end(); itr++)
safe_delete(itr->second);
@@ -260,37 +407,58 @@ PlayerGroupManager::~PlayerGroupManager() {
m_groups.clear();
}
-bool PlayerGroupManager::AddGroupMember(int32 group_id, Entity* member) {
- std::shared_lock lock(MGroups);
+bool PlayerGroupManager::AddGroupMember(int32 group_id, Entity* member, bool is_leader) {
+ std::shared_lock lock(MGroups);
bool ret = false;
if (m_groups.count(group_id) > 0) {
PlayerGroup* group = m_groups[group_id];
- ret = group->AddMember(member);
+ ret = group->AddMember(member, is_leader);
+ peer_manager.sendPeersGroupMember(group_id, member->GetGroupMemberInfo());
+ }
+
+ return ret;
+}
+
+bool PlayerGroupManager::AddGroupMemberFromPeer(int32 group_id, GroupMemberInfo* info) {
+ if (!info)
+ return false;
+
+ std::shared_lock lock(MGroups);
+ bool ret = false;
+
+ if (m_groups.count(group_id) > 0) {
+ PlayerGroup* group = m_groups[group_id];
+ ret = group->AddMemberFromPeer(info->name, info->leader, info->is_client, info->class_id, info->hp_current, info->hp_max, info->level_current, info->level_max,
+ info->power_current, info->power_max, info->race_id, info->zone, info->mentor_target_char_id, info->zone_id, info->instance_id,
+ info->client_peer_address, info->client_peer_port, info->is_raid_looter);
}
return ret;
}
bool PlayerGroupManager::RemoveGroupMember(int32 group_id, Entity* member) {
+ if (!member)
+ return false;
+
bool ret = false;
bool remove = false;
Client* client = 0;
- if(member->GetGroupMemberInfo()->mentor_target_char_id)
+ if (member->GetGroupMemberInfo() && member->GetGroupMemberInfo()->mentor_target_char_id)
{
- if(member->IsPlayer())
+ if (member->IsPlayer())
{
Player* tmpPlayer = (Player*)member;
member->GetGroupMemberInfo()->mentor_target_char_id = 0;
tmpPlayer->EnableResetMentorship();
}
}
-
+
GroupLock(__FUNCTION__, __LINE__);
if (m_groups.count(group_id) > 0) {
PlayerGroup* group = m_groups[group_id];
-
+
if (member->IsPlayer())
client = member->GetGroupMemberInfo()->client;
@@ -300,53 +468,87 @@ bool PlayerGroupManager::RemoveGroupMember(int32 group_id, Entity* member) {
if (group->Size() == 1)
remove = true;
}
-
+
ReleaseGroupLock(__FUNCTION__, __LINE__);
if (client)
RemoveGroupBuffs(group_id, client);
+ peer_manager.sendPeersRemoveGroupMember(group_id, std::string(member->GetName()), (member->IsPlayer() ? ((Player*)member)->GetCharacterID() : 0), member->IsPlayer());
+
// Call RemoveGroup outside the locks as it uses the same locks
- if (remove)
+ if (remove) {
RemoveGroup(group_id);
+ peer_manager.sendPeersDisbandGroup(group_id);
+ }
return ret;
}
+bool PlayerGroupManager::RemoveGroupMember(int32 group_id, std::string name, bool is_client, int32 charID) {
+ bool ret = false;
+ bool remove = false;
+ Client* client = 0;
-void PlayerGroupManager::NewGroup(Entity* leader) {
+ GroupLock(__FUNCTION__, __LINE__);
+
+ if (m_groups.count(group_id) > 0) {
+ PlayerGroup* group = m_groups[group_id];
+
+ ret = group->RemoveMember(name, is_client, charID);
+
+ // If only 1 person left in the group set a flag to remove the group
+ if (group->Size() == 1)
+ remove = true;
+ }
+
+ ReleaseGroupLock(__FUNCTION__, __LINE__);
+
+ //if (client)
+ // RemoveGroupBuffs(group_id, client);
+
+ // Call RemoveGroup outside the locks as it uses the same locks
+ if (remove)
+ RemoveGroup(group_id);
+ else {
+ GroupMessage(group_id, "%s has left the group.", name.c_str());
+ }
+ return ret;
+}
+
+int32 PlayerGroupManager::NewGroup(Entity* leader, GroupOptions* goptions, int32 override_group_id) {
std::unique_lock lock(MGroups);
+ int32 groupID = 0;
// Highly doubt this will ever be needed but putting it in any way, basically bump the id and ensure
// no active group is currently using this id, if we hit the max for an int32 then reset the id to 1
- while (m_groups.count(m_nextGroupID) > 0) {
+ if (!override_group_id) {
+ do {
+ groupID = peer_manager.getUniqueGroupId();
+ } while (m_groups.count(groupID) > 0);
+ }
+ else if (override_group_id) {
+ groupID = override_group_id;
+ if (m_groups.count(groupID))
+ return 0; // group already exists
+ }
+
+ // last resort if the unique group id is not working out
+ while (m_groups.count(groupID) > 0) {
// If m_nextGroupID is at its max then reset it to 1, else increment it
- if (m_nextGroupID == 4294967295)
- m_nextGroupID = 1;
+ if (groupID == 4294967295)
+ groupID = 1;
else
- m_nextGroupID++;
+ groupID++;
}
// Create a new group with the valid ID we got from above
- PlayerGroup* new_group = new PlayerGroup(m_nextGroupID);
-
- GroupOptions goptions;
- goptions.loot_method = leader->GetInfoStruct()->get_group_loot_method();
- goptions.loot_items_rarity = leader->GetInfoStruct()->get_group_loot_items_rarity();
- goptions.auto_split = leader->GetInfoStruct()->get_group_auto_split();
- goptions.default_yell = leader->GetInfoStruct()->get_group_default_yell();
- goptions.group_autolock = leader->GetInfoStruct()->get_group_autolock();
- goptions.group_lock_method = leader->GetInfoStruct()->get_group_lock_method();
- goptions.solo_autolock = leader->GetInfoStruct()->get_group_solo_autolock();
- goptions.auto_loot_method = leader->GetInfoStruct()->get_group_auto_loot_method();
- new_group->SetDefaultGroupOptions(&goptions);
+ PlayerGroup* new_group = new PlayerGroup(groupID);
+ new_group->SetDefaultGroupOptions(goptions);
// Add the new group to the list (need to do this first, AddMember needs ref to the PlayerGroup ptr -> UpdateGroupMemberInfo)
- m_groups[m_nextGroupID] = new_group;
+ m_groups[groupID] = new_group;
- // Add the leader to the group
- new_group->AddMember(leader);
-
- leader->GetGroupMemberInfo()->leader = true;
+ return groupID;
}
void PlayerGroupManager::RemoveGroup(int32 group_id) {
@@ -356,6 +558,18 @@ void PlayerGroupManager::RemoveGroup(int32 group_id) {
if (m_groups.count(group_id) > 0) {
// Get a pointer to the group
PlayerGroup* group = m_groups[group_id];
+
+ std::vector raidGroups;
+ m_groups[group_id]->GetRaidGroups(&raidGroups);
+ if (raidGroups.size() > 0) {
+ std::vector::iterator group_itr;
+ for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) {
+ if (m_groups.count((*group_itr))) {
+ m_groups[(*group_itr)]->ReplaceRaidGroups(&raidGroups);
+ }
+ }
+ }
+ m_groups[group_id]->ClearGroupRaid();
// Erase the group from the list
m_groups.erase(group_id);
// Delete the group
@@ -397,7 +611,7 @@ int8 PlayerGroupManager::Invite(Player* leader, Entity* member) {
}
}
// Inviter is not in a group
- else {
+ else {
// Check to see if the inviter has a pending invite himself
if (m_pendingInvites.count(leader->GetName()) > 0)
ret = 4; // inviter already has a pending group invite
@@ -411,16 +625,69 @@ int8 PlayerGroupManager::Invite(Player* leader, Entity* member) {
// Release the lock on pending invites
MPendingInvites.releasewritelock(__FUNCTION__, __LINE__);
+ bool group_existed = false;
+ if (leader && leader->GetGroupMemberInfo()) {
+ group_existed = true;
+ }
/* testing purposes only */
- if (ret == 0 && member->IsNPC())
- AcceptInvite(member);
+ if (ret == 0 && member->IsNPC()) {
+ if (net.is_primary) {
+ int32 group_id = 0;
+ int8 result = AcceptInvite(member, &group_id, false);
+ if (result == 0) {
+ if (leader && leader->GetClient()) {
+ if (group_existed) {
+ group_id = leader->GetGroupMemberInfo()->group_id;
+ GroupOptions options;
+ leader->GetClient()->SetGroupOptionsReference(&options);
+ peer_manager.sendPeersNewGroupRequest("", 0, group_id, std::string(leader->GetName()), std::string(member->GetName()), &options);
+ }
+ else {
+ AddGroupMember(group_id, leader, true);
+ }
+ GroupMessage(group_id, "%s has joined the group.", member->GetName());
+ AddGroupMember(group_id, member);
+ }
+ }
+ }
+ else {
+ GroupOptions options;
+ if (leader && leader->GetClient()) {
+ GroupOptions options;
+ leader->GetClient()->SetGroupOptionsReference(&options);
+ peer_manager.sendPrimaryNewGroupRequest(std::string(leader->GetName()), std::string(member->GetName()), member->GetID(), &options);
+ }
+ }
+ }
- return ret;
+ return ret;
}
-int8 PlayerGroupManager::AcceptInvite(Entity* member) {
- int8 ret = 3; // default to unknown error
+bool PlayerGroupManager::AddInvite(Player* leader, Entity* member) {
+ bool ret = true;
+ MPendingInvites.writelock(__FUNCTION__, __LINE__);
+ if (leader == member)
+ ret = false; // failure, can't invite yourself
+ else if (member->GetGroupMemberInfo())
+ ret = false;
+ // Check to see if the target of the invite already has a pending invite
+ else if (m_pendingInvites.count(member->GetName()) > 0)
+ ret = false; // Target already has an invite
+ if (leader->GetGroupMemberInfo()) {
+ m_pendingInvites[member->GetName()] = leader->GetName();
+ }
+ else {
+ m_pendingInvites[leader->GetName()] = leader->GetName();
+ m_pendingInvites[member->GetName()] = leader->GetName();
+ }
+ MPendingInvites.releasewritelock(__FUNCTION__, __LINE__);
+ return ret;
+}
+
+int8 PlayerGroupManager::AcceptInvite(Entity* member, int32* group_override_id, bool auto_add_group) {
+ int8 ret = 3; // default to unknown error
+ int32 groupResultID = 0;
MPendingInvites.writelock(__FUNCTION__, __LINE__);
if (m_pendingInvites.count(member->GetName()) > 0) {
@@ -429,20 +696,53 @@ int8 PlayerGroupManager::AcceptInvite(Entity* member) {
if (client_leader) {
if (m_pendingInvites.count(leader) > 0) {
- NewGroup(client_leader->GetPlayer());
+ if (!client_leader->GetPlayer()->GetGroupMemberInfo()) {
+ GroupOptions goptions;
+ goptions.loot_method = client_leader->GetPlayer()->GetInfoStruct()->get_group_loot_method();
+ goptions.loot_items_rarity = client_leader->GetPlayer()->GetInfoStruct()->get_group_loot_items_rarity();
+ goptions.auto_split = client_leader->GetPlayer()->GetInfoStruct()->get_group_auto_split();
+ goptions.default_yell = client_leader->GetPlayer()->GetInfoStruct()->get_group_default_yell();
+ goptions.group_autolock = client_leader->GetPlayer()->GetInfoStruct()->get_group_autolock();
+ goptions.group_lock_method = client_leader->GetPlayer()->GetInfoStruct()->get_group_lock_method();
+ goptions.solo_autolock = client_leader->GetPlayer()->GetInfoStruct()->get_group_solo_autolock();
+ goptions.auto_loot_method = client_leader->GetPlayer()->GetInfoStruct()->get_group_auto_loot_method();
+ groupResultID = NewGroup(client_leader->GetPlayer(), &goptions, (group_override_id != nullptr) ? (*group_override_id) : 0);
+ if (group_override_id != nullptr && *group_override_id == 0) {
+ *group_override_id = groupResultID;
+ }
+ }
+ else {
+ if (group_override_id != nullptr && *group_override_id == 0) {
+ if (client_leader->GetPlayer()->GetGroupMemberInfo())
+ *group_override_id = client_leader->GetPlayer()->GetGroupMemberInfo()->group_id;
+ }
+ }
m_pendingInvites.erase(leader);
}
// Remove from invite list and add to the group
- m_pendingInvites.erase(member->GetName());
-
- GroupMessage(client_leader->GetPlayer()->GetGroupMemberInfo()->group_id, "%s has joined the group.", member->GetName());
- AddGroupMember(client_leader->GetPlayer()->GetGroupMemberInfo()->group_id, member);
+ if (m_pendingInvites.count(member->GetName())) {
+ m_pendingInvites.erase(member->GetName());
+ }
+ int32 result_group_id = 0;
+ if (*group_override_id && *group_override_id > 0) {
+ result_group_id = *group_override_id;
+ }
+ else
+ result_group_id = groupResultID;
+
+ if (auto_add_group && result_group_id) {
+ AddGroupMember(result_group_id, client_leader->GetPlayer());
+ GroupMessage(result_group_id, "%s has joined the group.", member->GetName());
+ AddGroupMember(result_group_id, member);
+ }
ret = 0; // success
}
else {
// Was unable to find the leader, remove from the invite list
- m_pendingInvites.erase(member->GetName());
+ if (m_pendingInvites.count(member->GetName())) {
+ m_pendingInvites.erase(member->GetName());
+ }
ret = 2; // failure, can't find leader
}
}
@@ -459,11 +759,18 @@ void PlayerGroupManager::DeclineInvite(Entity* member) {
if (m_pendingInvites.count(member->GetName()) > 0) {
string leader = m_pendingInvites[member->GetName()];
+ // send decline to leader
m_pendingInvites.erase(member->GetName());
if (m_pendingInvites.count(leader) > 0)
m_pendingInvites.erase(leader);
}
+ if (m_raidPendingInvites.count(member->GetName()) > 0) {
+ string leader = m_raidPendingInvites[member->GetName()];
+ // send decline to leader
+ m_raidPendingInvites.erase(member->GetName());
+ }
+
MPendingInvites.releasewritelock(__FUNCTION__, __LINE__);
}
@@ -474,11 +781,20 @@ bool PlayerGroupManager::IsGroupIDValid(int32 group_id) {
return ret;
}
-void PlayerGroupManager::SendGroupUpdate(int32 group_id, Client* exclude) {
+void PlayerGroupManager::SendGroupUpdate(int32 group_id, Client* exclude, bool forceRaidUpdate) {
std::shared_lock lock(MGroups);
if (m_groups.count(group_id) > 0) {
- m_groups[group_id]->SendGroupUpdate(exclude);
+ std::vector raidGroups;
+ m_groups[group_id]->GetRaidGroups(&raidGroups);
+ if (raidGroups.size() < 1)
+ raidGroups.push_back(group_id);
+ std::vector::iterator group_itr;
+ for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) {
+ if (m_groups.count((*group_itr))) {
+ m_groups[(*group_itr)]->SendGroupUpdate(exclude, forceRaidUpdate);
+ }
+ }
}
}
@@ -498,6 +814,18 @@ void PlayerGroupManager::ClearPendingInvite(Entity* member) {
MPendingInvites.releasewritelock(__FUNCTION__, __LINE__);
}
+std::string PlayerGroupManager::HasPendingInvite(Entity* member) {
+ std::string leader("");
+ MPendingInvites.writelock(__FUNCTION__, __LINE__);
+
+ if (m_pendingInvites.count(member->GetName()) > 0) {
+ leader = m_pendingInvites[member->GetName()];
+ }
+
+ MPendingInvites.releasewritelock(__FUNCTION__, __LINE__);
+ return leader;
+}
+
void PlayerGroupManager::RemoveGroupBuffs(int32 group_id, Client* client) {
SpellEffects* se = 0;
Spell* spell = 0;
@@ -517,7 +845,7 @@ void PlayerGroupManager::RemoveGroupBuffs(int32 group_id, Client* client) {
player = client->GetPlayer();
bool recoup_lock = true;
for (int i = 0; i < NUM_SPELL_EFFECTS; i++) {
- if(recoup_lock) {
+ if (recoup_lock) {
player->GetSpellEffectMutex()->readlock(__FUNCTION__, __LINE__);
recoup_lock = false;
se = player->GetSpellEffects();
@@ -543,22 +871,22 @@ void PlayerGroupManager::RemoveGroupBuffs(int32 group_id, Client* client) {
//Also remove group buffs from pets
pet = 0;
charmed_pet = 0;
- if (player->HasPet()){
+ if (player->HasPet()) {
pet = player->GetPet();
pet = player->GetCharmedPet();
}
- if (pet){
+ if (pet) {
pet->RemoveSpellEffect(luaspell);
pet->RemoveSpellBonus(luaspell);
}
- if (charmed_pet){
+ if (charmed_pet) {
charmed_pet->RemoveSpellEffect(luaspell);
charmed_pet->RemoveSpellBonus(luaspell);
}
}
}
}
- if(!recoup_lock) { // we previously set a readlock that we now release
+ if (!recoup_lock) { // we previously set a readlock that we now release
player->GetSpellEffectMutex()->releasereadlock(__FUNCTION__, __LINE__);
}
packet = client->GetPlayer()->GetSkills()->GetSkillPacket(client->GetVersion());
@@ -609,7 +937,7 @@ bool PlayerGroupManager::HasGroupCompletedQuest(int32 group_id, int32 quest_id)
info = *itr;
if (info->client) {
bool isComplete = info->client->GetPlayer()->HasQuestBeenCompleted(quest_id);
- if(!isComplete)
+ if (!isComplete)
{
questComplete = isComplete;
break;
@@ -630,14 +958,14 @@ void PlayerGroupManager::SimpleGroupMessage(int32 group_id, const char* message)
void PlayerGroupManager::SendGroupMessage(int32 group_id, int8 type, const char* message, ...) {
std::shared_lock lock(MGroups);
-
+
va_list argptr;
char buffer[4096];
buffer[0] = 0;
va_start(argptr, message);
vsnprintf(buffer, sizeof(buffer), message, argptr);
va_end(argptr);
-
+
if (m_groups.count(group_id) > 0)
m_groups[group_id]->SendGroupMessage(type, buffer);
}
@@ -652,11 +980,74 @@ void PlayerGroupManager::GroupMessage(int32 group_id, const char* message, ...)
SimpleGroupMessage(group_id, buffer);
}
-void PlayerGroupManager::GroupChatMessage(int32 group_id, Spawn* from, int32 language, const char* message) {
+void PlayerGroupManager::GroupChatMessage(int32 group_id, Spawn* from, int32 language, const char* message, int16 channel) {
std::shared_lock lock(MGroups);
- if (m_groups.count(group_id) > 0)
- m_groups[group_id]->GroupChatMessage(from, language, message);
+ if (m_groups.count(group_id) > 0) {
+ std::vector raidGroups;
+
+ if (channel == CHANNEL_RAID_SAY)
+ m_groups[group_id]->GetRaidGroups(&raidGroups);
+
+ if (raidGroups.size() < 1)
+ raidGroups.push_back(group_id);
+
+ std::vector::iterator group_itr;
+ for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) {
+ if (m_groups.count((*group_itr))) {
+ m_groups[(*group_itr)]->GroupChatMessage(from, language, message, channel);
+ }
+ }
+ }
+}
+
+void PlayerGroupManager::GroupChatMessage(int32 group_id, std::string fromName, int32 language, const char* message, int16 channel) {
+ std::shared_lock lock(MGroups);
+
+ if (m_groups.count(group_id) > 0) {
+ std::vector raidGroups;
+
+ if (channel == CHANNEL_RAID_SAY)
+ m_groups[group_id]->GetRaidGroups(&raidGroups);
+
+ if (raidGroups.size() < 1)
+ raidGroups.push_back(group_id);
+
+ std::vector::iterator group_itr;
+ for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) {
+ if (m_groups.count((*group_itr))) {
+ m_groups[(*group_itr)]->GroupChatMessage(fromName, language, message, channel);
+ }
+ }
+ }
+}
+
+void PlayerGroupManager::SendGroupChatMessage(int32 group_id, int16 channel, const char* message, ...) {
+ std::shared_lock lock(MGroups);
+
+ va_list argptr;
+ char buffer[4096];
+ buffer[0] = 0;
+ va_start(argptr, message);
+ vsnprintf(buffer, sizeof(buffer), message, argptr);
+ va_end(argptr);
+
+ if (m_groups.count(group_id) > 0) {
+ std::vector raidGroups;
+
+ if (channel == CHANNEL_RAID_SAY || channel == CHANNEL_LOOT_ROLLS)
+ m_groups[group_id]->GetRaidGroups(&raidGroups);
+
+ if (raidGroups.size() < 1)
+ raidGroups.push_back(group_id);
+
+ std::vector::iterator group_itr;
+ for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) {
+ if (m_groups.count((*group_itr))) {
+ m_groups[(*group_itr)]->SendGroupMessage(channel, buffer);
+ }
+ }
+ }
}
bool PlayerGroupManager::MakeLeader(int32 group_id, Entity* new_leader) {
@@ -664,7 +1055,7 @@ bool PlayerGroupManager::MakeLeader(int32 group_id, Entity* new_leader) {
if (m_groups.count(group_id) > 0)
return m_groups[group_id]->MakeLeader(new_leader);
-
+
return false;
}
@@ -730,7 +1121,7 @@ void PlayerGroupManager::UpdateGroupBuffs() {
if (!luaspell)
continue;
-
+
if (!luaspell->caster)
{
LogWrite(PLAYER__ERROR, 0, "Player", "Bad luaspell, caster is NULL, spellid: %u", me[i].spell_id);
@@ -757,17 +1148,17 @@ void PlayerGroupManager::UpdateGroupBuffs() {
client = (*target_itr)->client;
has_effect = false;
-
+
if (group_member->GetSpellEffect(spell->GetSpellID(), caster)) {
has_effect = true;
}
- if(!has_effect && (std::find(luaspell->removed_targets.begin(),
+ if (!has_effect && (std::find(luaspell->removed_targets.begin(),
luaspell->removed_targets.end(), group_member->GetID()) != luaspell->removed_targets.end())) {
continue;
}
// Check if player is within range of the caster
- if (!rule_manager.GetGlobalRule(R_Spells, EnableCrossZoneGroupBuffs)->GetInt8() &&
- (group_member->GetZone() != caster->GetZone() || caster->GetDistance(group_member) > spell->GetSpellData()->radius)) {
+ if (!rule_manager.GetGlobalRule(R_Spells, EnableCrossZoneGroupBuffs)->GetInt8() &&
+ (group_member->GetZone() != caster->GetZone() || caster->GetDistance(group_member) > spell->GetSpellData()->radius)) {
if (has_effect) {
group_member->RemoveSpellEffect(luaspell);
group_member->RemoveSpellBonus(luaspell);
@@ -794,9 +1185,9 @@ void PlayerGroupManager::UpdateGroupBuffs() {
continue;
}
- if(group_member->GetZone() != caster->GetZone())
+ if (group_member->GetZone() != caster->GetZone())
{
- SpellProcess::AddSelfAndPetToCharTargets(luaspell, group_member);
+ SpellProcess::AddSelfAndPetToCharTargets(luaspell, group_member);
}
else
{
@@ -869,7 +1260,7 @@ void PlayerGroupManager::UpdateGroupBuffs() {
bool PlayerGroupManager::IsInGroup(int32 group_id, Entity* member) {
std::shared_lock lock(MGroups);
bool ret = false;
-
+
if (m_groups.count(group_id) > 0) {
m_groups[group_id]->MGroupMembers.readlock(__FUNCTION__, __LINE__);
deque* members = m_groups[group_id]->GetMembers();
@@ -887,7 +1278,7 @@ bool PlayerGroupManager::IsInGroup(int32 group_id, Entity* member) {
Entity* PlayerGroupManager::IsPlayerInGroup(int32 group_id, int32 character_id) {
std::shared_lock lock(MGroups);
-
+
Entity* ret = nullptr;
if (m_groups.count(group_id) > 0) {
@@ -901,10 +1292,36 @@ Entity* PlayerGroupManager::IsPlayerInGroup(int32 group_id, int32 character_id)
}
m_groups[group_id]->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
}
-
+
return ret;
}
+void PlayerGroupManager::SendPeerGroupData(std::string peerId) {
+ if (peerId.size() < 1) // must provide a peerId
+ return;
+
+ std::shared_lock lock(MGroups); // unique lock to avoid updates while we push to a new peer
+ map::iterator itr;
+ for (itr = m_groups.begin(); itr != m_groups.end(); itr++) {
+ PlayerGroup* group = itr->second;
+ if (group) {
+ int32 groupID = group->GetID();
+ std::vector raidGroups;
+ group->GetRaidGroups(&raidGroups);
+ peer_manager.sendPeersNewGroupRequest("", 0, groupID, "", "", group->GetGroupOptions(), peerId, &raidGroups);
+ m_groups[groupID]->MGroupMembers.readlock(__FUNCTION__, __LINE__);
+ deque* members = m_groups[groupID]->GetMembers();
+ for (int8 i = 0; i < members->size(); i++) {
+ GroupMemberInfo* info = members->at(i);
+ if (info)
+ peer_manager.sendPeersGroupMember(groupID, info, false, peerId);
+ }
+ m_groups[groupID]->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
+ }
+ }
+}
+
+
void PlayerGroup::RemoveClientReference(Client* remove) {
deque::iterator itr;
MGroupMembers.writelock();
@@ -926,7 +1343,7 @@ void PlayerGroup::UpdateGroupMemberInfo(Entity* ent, bool groupMembersLocked) {
if (!player || !player->GetGroupMemberInfo())
return;
- if(!groupMembersLocked)
+ if (!groupMembersLocked)
MGroupMembers.writelock();
GroupMemberInfo* group_member_info = player->GetGroupMemberInfo();
@@ -939,34 +1356,648 @@ void PlayerGroup::UpdateGroupMemberInfo(Entity* ent, bool groupMembersLocked) {
group_member_info->power_current = player->GetPower();
group_member_info->power_max = player->GetTotalPower();
group_member_info->race_id = player->GetRace();
- if (player->GetZone())
+ if (player->GetZone()) {
group_member_info->zone = player->GetZone()->GetZoneDescription();
- else
+ group_member_info->zone_id = player->GetZone()->GetZoneID();
+ group_member_info->instance_id = player->GetZone()->GetInstanceID();
+ }
+ else {
group_member_info->zone = "Unknown";
+ group_member_info->zone_id = 0;
+ group_member_info->instance_id = 0;
+ }
- if(!groupMembersLocked)
+ peer_manager.sendPeersGroupMember(group_member_info->group_id, group_member_info, true);
+
+ if (!groupMembersLocked)
MGroupMembers.releasewritelock();
}
+void PlayerGroup::GetRaidGroups(std::vector* groups) {
+ std::shared_lock lock(MRaidGroups);
+ if (groups)
+ groups->insert(groups->end(), m_raidgroups.begin(), m_raidgroups.end());
+}
+
+void PlayerGroup::ReplaceRaidGroups(std::vector* groups) {
+ std::unique_lock lock(MRaidGroups);
+ if (groups) {
+ m_raidgroups.clear();
+ m_raidgroups.insert(m_raidgroups.end(), groups->begin(), groups->end());
+ }
+}
+
+bool PlayerGroup::IsInRaidGroup(int32 groupID, bool isLeaderGroup) {
+ std::unique_lock lock(MRaidGroups);
+ if (isLeaderGroup) {
+ if (m_raidgroups.size() > 0) {
+ int32 group = m_raidgroups[0];
+ if (group == groupID)
+ return true;
+ }
+ }
+ else if (std::find(m_raidgroups.begin(), m_raidgroups.end(), groupID) != m_raidgroups.end()) {
+ return true;
+ }
+ return false;
+}
+void PlayerGroup::AddGroupToRaid(int32 groupID) {
+ std::unique_lock lock(MRaidGroups);
+ if (std::find(m_raidgroups.begin(), m_raidgroups.end(), groupID) == m_raidgroups.end()) {
+ m_raidgroups.push_back(groupID);
+ }
+}
+
+void PlayerGroup::RemoveGroupFromRaid(int32 groupID) {
+ std::unique_lock lock(MRaidGroups);
+ std::vector::iterator group_itr = std::find(m_raidgroups.begin(), m_raidgroups.end(), groupID);
+
+ if (group_itr != m_raidgroups.end()) {
+ m_raidgroups.erase(group_itr);
+ }
+}
+
+bool PlayerGroup::IsGroupRaid() {
+ std::unique_lock lock(MRaidGroups);
+ return (m_raidgroups.size() > 1);
+}
+
+void PlayerGroup::ClearGroupRaid() {
+ std::unique_lock lock(MRaidGroups);
+ m_raidgroups.clear();
+}
+
+void PlayerGroupManager::UpdateGroupMemberInfoFromPeer(int32 group_id, std::string name, bool is_client, GroupMemberInfo* updateinfo) {
+ std::shared_lock lock(MGroups);
+
+ if (m_groups.count(group_id) > 0) {
+ m_groups[group_id]->MGroupMembers.writelock(__FUNCTION__, __LINE__);
+ deque* members = m_groups[group_id]->GetMembers();
+ for (int8 i = 0; i < members->size(); i++) {
+ GroupMemberInfo* curinfo = members->at(i);
+ if (curinfo && curinfo->name == name && curinfo->is_client == is_client) {
+ curinfo->class_id = updateinfo->class_id;
+ curinfo->hp_max = updateinfo->hp_max;
+ curinfo->hp_current = updateinfo->hp_current;
+ curinfo->level_max = updateinfo->level_max;
+ curinfo->level_current = updateinfo->level_current;
+ curinfo->name = updateinfo->name;
+ curinfo->power_current = updateinfo->power_current;
+ curinfo->power_max = updateinfo->power_max;
+ curinfo->race_id = updateinfo->race_id;
+ curinfo->zone = updateinfo->zone;
+ curinfo->mentor_target_char_id = updateinfo->mentor_target_char_id;
+ curinfo->is_client = updateinfo->is_client;
+ curinfo->leader = updateinfo->leader;
+ curinfo->client_peer_address = updateinfo->client_peer_address;
+ curinfo->client_peer_port = updateinfo->client_peer_port;
+ curinfo->is_raid_looter = updateinfo->is_raid_looter;
+ break;
+ }
+ }
+ m_groups[group_id]->MGroupMembers.releasewritelock(__FUNCTION__, __LINE__);
+ }
+}
+
+void PlayerGroupManager::ClearGroupRaid(int32 groupID) {
+ std::shared_lock lock(MGroups);
+
+ // Check to see if the id is in the list
+ if (m_groups.count(groupID) > 0) {
+ m_groups[groupID]->ClearGroupRaid();
+ }
+ SendGroupUpdate(groupID);
+}
+
+void PlayerGroupManager::RemoveGroupFromRaid(int32 groupID, int32 targetGroupID) {
+ std::shared_lock lock(MGroups);
+
+ // Check to see if the id is in the list
+ if (m_groups.count(groupID) > 0) {
+ // Get a pointer to the group
+ PlayerGroup* group = m_groups[groupID];
+
+ std::vector raidGroups;
+ m_groups[groupID]->GetRaidGroups(&raidGroups);
+ if (raidGroups.size() > 0) {
+ std::vector::iterator group_itr;
+ for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) {
+ if (m_groups.count((*group_itr))) {
+ m_groups[(*group_itr)]->RemoveGroupFromRaid(targetGroupID);
+ }
+ }
+ }
+ }
+}
+
+bool PlayerGroupManager::IsInRaidGroup(int32 groupID, int32 targetGroupID, bool isLeaderGroup) {
+ std::shared_lock lock(MGroups);
+ if (m_groups.count(groupID))
+ return m_groups[groupID]->IsInRaidGroup(targetGroupID, isLeaderGroup);
+
+ return false;
+}
+
+bool PlayerGroupManager::GetDefaultGroupOptions(int32 groupID, GroupOptions* options) {
+ std::shared_lock lock(MGroups);
+ if (m_groups.count(groupID))
+ return m_groups[groupID]->GetDefaultGroupOptions(options);
+
+ return false;
+}
+
+void PlayerGroupManager::GetRaidGroups(int32 groupID, std::vector* newGroups) {
+ std::shared_lock lock(MGroups);
+ if (m_groups.count(groupID))
+ m_groups[groupID]->GetRaidGroups(newGroups);
+}
+
+void PlayerGroupManager::ReplaceRaidGroups(int32 groupID, std::vector* newGroups) {
+ std::shared_lock lock(MGroups);
+ if (m_groups.count(groupID))
+ m_groups[groupID]->ReplaceRaidGroups(newGroups);
+
+ SendGroupUpdate(groupID);
+}
+
+void PlayerGroupManager::SetGroupOptions(int32 groupID, GroupOptions* options) {
+ std::shared_lock lock(MGroups);
+ if (m_groups.count(groupID))
+ m_groups[groupID]->SetDefaultGroupOptions(options);
+}
+
+void PlayerGroupManager::SendWhoGroupMembers(Client* client, int32 groupID) {
+ std::shared_lock lock(MGroups);
+
+ if (m_groups.count(groupID) > 0) {
+ PacketStruct* packet = configReader.getStruct("WS_WhoQueryReply", client->GetVersion());
+ if (packet) {
+ packet->setDataByName("account_id", client->GetAccountID());
+ packet->setDataByName("unknown", 0xFFFFFFFF);
+ int8 num_characters = 0;
+
+ m_groups[groupID]->MGroupMembers.writelock(__FUNCTION__, __LINE__);
+ deque* members = m_groups[groupID]->GetMembers();
+ for (int8 i = 0; i < members->size(); i++) {
+ GroupMemberInfo* curinfo = members->at(i);
+ if (curinfo && curinfo->name.size() > 0) {
+ num_characters++;
+ }
+ }
+ packet->setDataByName("response", 2);
+ packet->setArrayLengthByName("num_characters", (int8)num_characters);
+ for (int8 i = 0; i < members->size(); i++) {
+ GroupMemberInfo* curinfo = members->at(i);
+ if (curinfo && curinfo->name.size() > 0) {
+ packet->setArrayDataByName("char_name", curinfo->name.c_str(), i);
+ packet->setArrayDataByName("level", curinfo->level_current, i);
+ packet->setArrayDataByName("class", curinfo->class_id, i);
+ packet->setArrayDataByName("unknown4", 0xFF, i); //probably tradeskill class
+ packet->setArrayDataByName("race", curinfo->race_id, i);
+ packet->setArrayDataByName("zone", curinfo->zone.c_str(), i);
+ }
+ }
+ client->QueuePacket(packet->serialize());
+ safe_delete(packet);
+ }
+ m_groups[groupID]->MGroupMembers.releasewritelock(__FUNCTION__, __LINE__);
+ }
+}
+
+void PlayerGroupManager::SendWhoRaidMembers(Client* client, int32 groupID) {
+ std::shared_lock lock(MGroups);
+
+ PlayerGroup* group = nullptr;
+ if (m_groups.count(groupID))
+ group = m_groups[groupID];
+
+ std::vector groups;
+ std::vector::iterator group_itr;
+ if (group)
+ group->GetRaidGroups(&groups);
+
+ if (groups.size() > 0) {
+ PacketStruct* packet = configReader.getStruct("WS_WhoQueryReply", client->GetVersion());
+ if (packet) {
+ packet->setDataByName("account_id", client->GetAccountID());
+ packet->setDataByName("unknown", 0xFFFFFFFF);
+ int8 num_characters = 0;
+ for (group_itr = groups.begin(); group_itr != groups.end(); group_itr++) {
+ if (m_groups.count((*group_itr))) {
+ // we will release this lock after we submit the data out below in the second group_itr
+ m_groups[(*group_itr)]->MGroupMembers.readlock(__FUNCTION__, __LINE__);
+ deque* members = m_groups[(*group_itr)]->GetMembers();
+ for (int8 i = 0; i < members->size(); i++) {
+ GroupMemberInfo* curinfo = members->at(i);
+ if (curinfo && curinfo->name.size() > 0) {
+ num_characters++;
+ }
+ }
+ }
+ }
+ packet->setDataByName("response", 2);
+ packet->setArrayLengthByName("num_characters", (int8)num_characters);
+ int8 pos = 0;
+ for (group_itr = groups.begin(); group_itr != groups.end(); group_itr++) {
+ if (m_groups.count((*group_itr))) {
+ deque* members = m_groups[(*group_itr)]->GetMembers();
+ for (int8 i = 0; i < members->size(); i++) {
+ GroupMemberInfo* curinfo = members->at(i);
+ if (curinfo && curinfo->name.size() > 0) {
+ packet->setArrayDataByName("char_name", curinfo->name.c_str(), pos);
+ packet->setArrayDataByName("level", curinfo->level_current, pos);
+ packet->setArrayDataByName("class", curinfo->class_id, pos);
+ packet->setArrayDataByName("unknown4", 0xFF, pos); //probably tradeskill class
+ packet->setArrayDataByName("race", curinfo->race_id, pos);
+ packet->setArrayDataByName("zone", curinfo->zone.c_str(), pos);
+ pos++;
+ }
+ }
+ // release previously established lock during the count
+ m_groups[(*group_itr)]->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
+ }
+ }
+ client->QueuePacket(packet->serialize());
+ safe_delete(packet);
+ }
+ }
+ else {
+ client->SimpleMessage(CHANNEL_COLOR_RED, "You are not currently in a raid.");
+ }
+}
+
+int8 PlayerGroupManager::AcceptRaidInvite(std::string acceptorName, int32 groupID) {
+ std::unique_lock lock(MGroups);
+
+ std::string raidLeaderName("");
+ MPendingInvites.writelock(__FUNCTION__, __LINE__);
+ bool inviteAccept = true;
+ if (m_raidPendingInvites.count(acceptorName.c_str()) > 0) {
+ raidLeaderName = m_raidPendingInvites[acceptorName.c_str()];
+ m_raidPendingInvites.erase(acceptorName.c_str());
+ }
+ MPendingInvites.releasewritelock(__FUNCTION__, __LINE__);
+ if (raidLeaderName.size() < 1)
+ return 0;
+
+ Client* client_leader = zone_list.GetClientByCharName(raidLeaderName.c_str());
+ if (client_leader) {
+ if (client_leader->GetPlayer()->GetGroupMemberInfo() && m_groups.count(groupID) && m_groups.count(client_leader->GetPlayer()->GetGroupMemberInfo()->group_id)) {
+ Player* player = client_leader->GetPlayer();
+ PlayerGroup* spawn_group = m_groups[groupID];
+ PlayerGroup* player_group = m_groups[player->GetGroupMemberInfo()->group_id];
+ if (spawn_group && player_group) {
+ std::vector groups;
+ spawn_group->GetRaidGroups(&groups);
+ if (groups.size() > 0) {
+ return 0;
+ }
+
+ player_group->GetRaidGroups(&groups);
+ if (groups.size() > 3) {
+ return 0;
+ }
+
+ spawn_group->SetDefaultGroupOptions(player_group->GetGroupOptions());
+ if (groups.size() < 1) {
+ player_group->AddGroupToRaid(player_group->GetID());
+ player_group->AddGroupToRaid(spawn_group->GetID());
+ player_group->GetRaidGroups(&groups);
+ spawn_group->ReplaceRaidGroups(&groups);
+ return 1;
+ }
+ else {
+ groups.clear();
+ player_group->AddGroupToRaid(spawn_group->GetID());
+ player_group->GetRaidGroups(&groups);
+ std::vector::iterator group_itr;
+ for (group_itr = groups.begin(); group_itr != groups.end(); group_itr++) {
+ int32 cur_group_id = (*group_itr);
+ if (cur_group_id == player_group->GetID() || m_groups.count(cur_group_id) < 1) // we already set the player_group above, skip
+ continue;
+ PlayerGroup* temp_group = m_groups[cur_group_id];
+ temp_group->ReplaceRaidGroups(&groups);
+ }
+ return 1;
+ }
+
+ }
+ }
+ }
+ else {
+ // must be somewhere else in the world
+ }
+ return 0;
+}
+
+bool PlayerGroupManager::SendRaidInvite(Client* sender, Entity* target) {
+ std::shared_lock lock(MGroups);
+ if (!sender || !target)
+ return false;
+
+ if (!target->IsPlayer() || !((Player*)target)->GetClient() || !target->GetGroupMemberInfo() || !sender->GetPlayer()->GetGroupMemberInfo())
+ return false;
+
+ Player* player = sender->GetPlayer();
+ int32 spawn_group_id = target->GetGroupMemberInfo()->group_id;
+
+ if (m_groups.count(spawn_group_id) < 1)
+ return false;
+
+ PlayerGroup* spawn_group = m_groups[spawn_group_id];
+
+ int32 player_group_id = player->GetGroupMemberInfo() ? player->GetGroupMemberInfo()->group_id : 0;
+ PlayerGroup* player_group = nullptr;
+ if (m_groups.count(player_group_id))
+ player_group = m_groups[player_group_id];
+ // check if already invited
+ if (spawn_group && !player->IsGroupMember((Player*)target) && !spawn_group->IsGroupRaid() && player_group && player->GetGroupMemberInfo()->leader
+ && (!player_group->IsInRaidGroup(spawn_group_id) || player_group->IsInRaidGroup(player_group_id, true))) {
+ std::string leader = spawn_group->GetLeaderName();
+ std::vector groups;
+ player_group->GetRaidGroups(&groups);
+ if (groups.size() > 3) {
+ sender->SimpleMessage(CHANNEL_COLOR_RED, "You are currently in a full raid.");
+ return false;
+ }
+ if (leader.size() < 1) {
+ sender->SimpleMessage(CHANNEL_COLOR_RED, "Your target has no leader.");
+ return false;
+ }
+ MPendingInvites.readlock(__FUNCTION__, __LINE__);
+ bool inviteAccept = true;
+ if (m_raidPendingInvites.count(leader.c_str()) > 0) {
+ inviteAccept = false;
+ sender->SimpleMessage(CHANNEL_COLOR_RED, "Leader of the other group has a pending raid invite.");
+ }
+ MPendingInvites.releasereadlock(__FUNCTION__, __LINE__);
+
+ if (!inviteAccept)
+ return false;
+
+ MPendingInvites.writelock(__FUNCTION__, __LINE__);
+ m_raidPendingInvites[leader] = player->GetGroupMemberInfo()->name.c_str();
+ MPendingInvites.releasewritelock(__FUNCTION__, __LINE__);
+
+ sender->SendReceiveOffer(((Player*)target)->GetClient(), 3, std::string(sender->GetPlayer()->GetName()), 1);
+ return true;
+ }
+ return false;
+}
+
+void PlayerGroupManager::SplitWithGroupOrRaid(Client* client, int32 coin_plat, int32 coin_gold, int32 coin_silver, int32 coin_copper) {
+ std::shared_lock lock(MGroups);
+ bool startWithLooter = true;
+ if (!client->GetPlayer()->GetGroupMemberInfo())
+ return;
+ if (!coin_plat && !coin_gold && !coin_silver && !coin_copper)
+ return;
+ if (client->GetPlayer()->GetInfoStruct()->get_coin_plat() < coin_plat &&
+ client->GetPlayer()->GetInfoStruct()->get_coin_gold() < coin_gold &&
+ client->GetPlayer()->GetInfoStruct()->get_coin_silver() < coin_silver &&
+ client->GetPlayer()->GetInfoStruct()->get_coin_copper() < coin_copper) {
+ // lacking coin
+ return;
+ }
+ int32 groupID = client->GetPlayer()->GetGroupMemberInfo()->group_id;
+
+ if (m_groups.count(groupID) < 1) {
+ return;
+ }
+
+ PlayerGroup* group = m_groups[groupID];
+ if (group)
+ {
+ bool isLeadGroup = group->IsInRaidGroup(group->GetID(), true);
+ bool isInRaid = group->IsInRaidGroup(group->GetID());
+ std::vector raidGroups;
+ group->GetRaidGroups(&raidGroups);
+
+ if (!isInRaid && raidGroups.size() < 1) {
+ raidGroups.push_back(group->GetID());
+ }
+ std::vector::iterator group_itr;
+
+ int32 split_coin_per_player = 0;
+ int32 actual_coins = coin_plat * 1000000 + coin_gold * 10000 + coin_silver * 100 + coin_copper;
+ int32 coins_remain_after_split = actual_coins;
+ int32 total_coins = actual_coins;
+
+ bool foundLooterResetRaidRun = false;
+ int8 members_in_zone = 0;
+ for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) {
+ group = m_groups[(*group_itr)];
+ if (!group)
+ continue;
+
+ group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
+ deque* members = group->GetMembers();
+ for (int8 i = 0; i < members->size(); i++) {
+ Entity* member = members->at(i)->member;
+ if (!member || !member->IsPlayer())
+ continue;
+ if (member->GetZone() != client->GetPlayer()->GetZone())
+ continue;
+
+ members_in_zone++;
+ }
+ group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
+ }
+ if (members_in_zone < 1) // this should not happen, but divide by zero checked
+ members_in_zone = 1;
+ split_coin_per_player = actual_coins / members_in_zone;
+ coins_remain_after_split = actual_coins - (split_coin_per_player * members_in_zone);
+ // try to individually take each tier of coin and not split the coin in inventory
+ client->GetPlayer()->GetInfoStruct()->set_coin_plat(client->GetPlayer()->GetInfoStruct()->get_coin_plat() - coin_plat);
+ client->GetPlayer()->GetInfoStruct()->set_coin_gold(client->GetPlayer()->GetInfoStruct()->get_coin_gold() - coin_gold);
+ client->GetPlayer()->GetInfoStruct()->set_coin_silver(client->GetPlayer()->GetInfoStruct()->get_coin_silver() - coin_silver);
+ client->GetPlayer()->GetInfoStruct()->set_coin_copper(client->GetPlayer()->GetInfoStruct()->get_coin_copper() - coin_copper);
+ int32 lootGroup = 0;
+ for (group_itr = raidGroups.begin(); group_itr != raidGroups.end();) {
+ group = m_groups[(*group_itr)];
+ if (!group)
+ continue;
+
+ isLeadGroup = group->IsInRaidGroup((*group_itr), true);
+
+ group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
+ deque* members = group->GetMembers();
+
+ LogWrite(LOOT__INFO, 0, "Loot", "%s: Group SplitWithGroupOrRaid, split coin per player %u, remaining coin after split %u", client->GetPlayer()->GetName(), split_coin_per_player, coins_remain_after_split);
+ for (int8 i = 0; i < members->size(); i++) {
+ Entity* member = members->at(i)->member;
+ if (!member || !member->IsPlayer())
+ continue;
+
+ if (member->GetZone() != client->GetPlayer()->GetZone())
+ continue;
+
+ // this will make sure we properly send the loot window to the initial requester if there is no item rarity matches
+ if (startWithLooter && member != client->GetPlayer())
+ continue;
+ else if (!startWithLooter && member == client->GetPlayer())
+ continue;
+
+ int32 coin_recv = 0;
+ if (member == client->GetPlayer() && (split_coin_per_player + coins_remain_after_split) > 0) {
+ coin_recv = split_coin_per_player + coins_remain_after_split;
+ ((Player*)member)->AddCoins(split_coin_per_player + coins_remain_after_split);
+ if (coins_remain_after_split > 0) // overflow of coin division went to the first player
+ coins_remain_after_split = 0;
+ }
+ else if (split_coin_per_player > 0) {
+ coin_recv = split_coin_per_player;
+ ((Player*)member)->AddCoins(split_coin_per_player);
+ }
+ if (coin_recv && ((Player*)member)->GetClient()) {
+ ((Player*)member)->GetClient()->Message(CHANNEL_MONEY_SPLIT, "Your share of the %s split from %s is %s.", client->GetCoinMessage(total_coins).c_str(), client->GetPlayer()->GetName(), client->GetCoinMessage(coin_recv).c_str());
+ }
+ if (startWithLooter) {
+ startWithLooter = false;
+ foundLooterResetRaidRun = true; // we got it, shouldn't hit this again
+ break;
+ }
+ }
+ group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
+ if (foundLooterResetRaidRun) {
+ group_itr = raidGroups.begin();
+ foundLooterResetRaidRun = false; // disable running it again
+ }
+ else
+ group_itr++;
+ } // end raid groups
+ } // end group
+}
+
+bool PlayerGroupManager::IdentifyMemberInGroupOrRaid(ZoneChangeDetails* details, Client* client, int32 zoneID, int32 instanceID) {
+ std::shared_lock lock(MGroups);
+ ZoneServer* ret = nullptr;
+ PlayerGroup* group = nullptr;
+ bool succeed = false;
+ if (client->GetPlayer()->GetGroupMemberInfo() && m_groups.count(client->GetPlayer()->GetGroupMemberInfo()->group_id))
+ group = m_groups[client->GetPlayer()->GetGroupMemberInfo()->group_id];
+ else
+ return false;
+
+ std::vector raidGroups;
+ std::vector::iterator group_itr;
+ if (group)
+ group->GetRaidGroups(&raidGroups);
+
+ group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
+ deque* members = group->GetMembers();
+ deque::iterator itr;
+ for (itr = members->begin(); itr != members->end(); itr++) {
+ // If a group member matches a target
+ if ((*itr)->is_client && (*itr)->member && (*itr)->member->GetZone() && (*itr)->zone_id == zoneID && (*itr)->instance_id == instanceID) {
+ // toggle the flag and break the loop
+ ret = (*itr)->member->GetZone();
+ break;
+ }
+ else if ((*itr)->is_client && (*itr)->zone_id == zoneID && (*itr)->instance_id == instanceID) {
+ // toggle the flag and break the loop
+ std::string id = peer_manager.isPeer((*itr)->client_peer_address, (*itr)->client_peer_port);
+ std::shared_ptr peer = peer_manager.getPeerById(id);
+ if (peer) {
+ ZoneServer* tmp = new ZoneServer((*itr)->zone.c_str());
+ database.LoadZoneInfo(tmp);
+ peer_manager.setZonePeerData(details, peer->id, peer->worldAddr, peer->internalWorldAddr, peer->worldPort, peer->webAddr, peer->webPort, std::string(tmp->GetZoneFile()), std::string(tmp->GetZoneName()), tmp->GetZoneID(),
+ tmp->GetInstanceID(), tmp->GetSafeX(), tmp->GetSafeY(), tmp->GetSafeZ(), tmp->GetSafeHeading(),
+ tmp->GetZoneLockState(), tmp->GetMinimumStatus(), tmp->GetMinimumLevel(), tmp->GetMaximumLevel(),
+ tmp->GetMinimumVersion(), tmp->GetDefaultLockoutTime(), tmp->GetDefaultReenterTime(),
+ tmp->GetInstanceType(), tmp->NumPlayers());
+ safe_delete(tmp);
+ succeed = true;
+ break;
+ }
+ }
+ }
+ group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
+ if (succeed) {
+ return true;
+ }
+ else if (ret) {
+ peer_manager.setZonePeerDataSelf(details, std::string(ret->GetZoneFile()), std::string(ret->GetZoneName()),
+ ret->GetZoneID(), ret->GetInstanceID(), ret->GetSafeX(), ret->GetSafeY(),
+ ret->GetSafeZ(), ret->GetSafeHeading(), ret->GetZoneLockState(),
+ ret->GetMinimumStatus(), ret->GetMinimumLevel(), ret->GetMaximumLevel(),
+ ret->GetMinimumVersion(), ret->GetDefaultLockoutTime(), ret->GetDefaultReenterTime(),
+ ret->GetInstanceType(), ret->NumPlayers(), ret);
+ return true;
+ }
+
+
+ for (group_itr = raidGroups.begin(); group_itr != raidGroups.end(); group_itr++) {
+ if (m_groups.count((*group_itr))) {
+ group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
+ deque* members = group->GetMembers();
+ deque::iterator itr;
+ for (itr = members->begin(); itr != members->end(); itr++) {
+ // If a group member matches a target
+ if ((*itr)->is_client && (*itr)->zone_id == zoneID && (*itr)->instance_id == instanceID) {
+ // toggle the flag and break the loop
+
+ std::string id = peer_manager.isPeer((*itr)->client_peer_address, (*itr)->client_peer_port);
+ std::shared_ptr peer = peer_manager.getPeerById(id);
+ if (peer) {
+ ZoneServer* tmp = new ZoneServer((*itr)->zone.c_str());
+ database.LoadZoneInfo(tmp);
+ peer_manager.setZonePeerData(details, peer->id, peer->worldAddr, peer->internalWorldAddr, peer->worldPort, peer->webAddr, peer->webPort, std::string(tmp->GetZoneFile()), std::string(tmp->GetZoneName()), tmp->GetZoneID(),
+ tmp->GetInstanceID(), tmp->GetSafeX(), tmp->GetSafeY(), tmp->GetSafeZ(), tmp->GetSafeHeading(),
+ tmp->GetZoneLockState(), tmp->GetMinimumStatus(), tmp->GetMinimumLevel(), tmp->GetMaximumLevel(),
+ tmp->GetMinimumVersion(), tmp->GetDefaultLockoutTime(), tmp->GetDefaultReenterTime(),
+ tmp->GetInstanceType(), tmp->NumPlayers());
+ safe_delete(tmp);
+ succeed = true;
+ break;
+ }
+ }
+ }
+ group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
+ if (succeed)
+ break;
+ }
+ }
+ return succeed;
+}
+
+void PlayerGroupManager::ClearGroupRaidLooterFlag(int32 groupID) {
+ std::shared_lock lock(MGroups);
+
+ if (m_groups.count(groupID) > 0) {
+ m_groups[groupID]->MGroupMembers.readlock(__FUNCTION__, __LINE__);
+ deque* members = m_groups[groupID]->GetMembers();
+ for (int8 i = 0; i < members->size(); i++) {
+ GroupMemberInfo* curinfo = members->at(i);
+ if (curinfo) {
+ curinfo->is_raid_looter = false;
+ }
+ }
+ m_groups[groupID]->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
+ }
+}
+
+
+
Entity* PlayerGroup::GetGroupMemberByPosition(Entity* seeker, int32 mapped_position) {
Entity* ret = nullptr;
deque::iterator itr;
-
+
MGroupMembers.readlock();
-
+
int32 count = 1;
for (itr = m_members.begin(); itr != m_members.end(); itr++) {
if ((*itr)->member == seeker) {
continue;
}
count++;
- if(count >= mapped_position) {
+ if (count >= mapped_position) {
ret = (Entity*)(*itr)->member;
break;
}
}
-
+
MGroupMembers.releasereadlock();
return ret;
@@ -998,3 +2029,21 @@ void PlayerGroup::SetDefaultGroupOptions(GroupOptions* options) {
MGroupMembers.releasewritelock();
}
+
+bool PlayerGroup::GetDefaultGroupOptions(GroupOptions* options) {
+ bool setOptions = false;
+ MGroupMembers.readlock();
+ if (options != nullptr) {
+ options->loot_method = group_options.loot_method;
+ options->loot_items_rarity = group_options.loot_items_rarity;
+ options->auto_split = group_options.auto_split;
+ options->default_yell = group_options.default_yell;
+ options->group_lock_method = group_options.group_lock_method;
+ options->group_autolock = group_options.group_autolock;
+ options->solo_autolock = group_options.solo_autolock;
+ options->auto_loot_method = group_options.auto_loot_method;
+ setOptions = true;
+ }
+ MGroupMembers.releasereadlock();
+ return setOptions;
+}
diff --git a/source/WorldServer/PlayerGroups.h b/source/WorldServer/PlayerGroups.h
index e7b9b27..1a24d6b 100644
--- a/source/WorldServer/PlayerGroups.h
+++ b/source/WorldServer/PlayerGroups.h
@@ -33,7 +33,7 @@ along with EQ2Emulator. If not, see .
using namespace std;
// GroupOptions isn't used yet
-struct GroupOptions{
+struct GroupOptions {
int8 loot_method;
int8 loot_items_rarity;
int8 auto_split;
@@ -59,9 +59,15 @@ struct GroupMemberInfo {
int8 race_id;
int8 class_id;
bool leader;
- Client* client;
- Entity* member;
+ Client* client;
+ Entity* member;
int32 mentor_target_char_id;
+ bool is_client;
+ int32 zone_id;
+ int32 instance_id;
+ string client_peer_address;
+ int16 client_peer_port;
+ bool is_raid_looter;
};
/// Represents a players group in game
@@ -73,19 +79,22 @@ public:
/// Adds a new member to the players group
/// Entity to add to the group, can be a Player or NPC
/// True if the member was added
- bool AddMember(Entity* member);
-
+ bool AddMember(Entity* member, bool is_leader);
+ bool AddMemberFromPeer(std::string name, bool isleader, bool isclient, int8 class_id, sint32 hp_cur, sint32 hp_max, int16 level_cur, int16 level_max,
+ sint32 power_cur, sint32 power_max, int8 race_id, std::string zonename, int32 mentor_target_char_id, int32 zone_id, int32 instance_id,
+ std::string peer_client_address, int16 peer_client_port, bool is_raid_looter);
/// Removes a member from the players group
/// Entity to remove from the player group
/// True if the member was removed
bool RemoveMember(Entity* member);
+ bool RemoveMember(std::string name, bool is_client, int32 charID);
/// Removes all members from the group and destroys the group
void Disband();
/// Sends updates to all the clients in the group
/// Client to exclude from the update
- void SendGroupUpdate(Client* exclude = 0);
+ void SendGroupUpdate(Client* exclude = 0, bool forceRaidUpdate = false);
/// Gets the total number of members in the group
/// int32, number of members in the group
@@ -98,26 +107,40 @@ public:
void SimpleGroupMessage(const char* message);
void SendGroupMessage(int8 type, const char* message, ...);
- void GroupChatMessage(Spawn* from, int32 language, const char* message);
+ void GroupChatMessage(Spawn* from, int32 language, const char* message, int16 channel = CHANNEL_GROUP_SAY);
+ void GroupChatMessage(std::string fromName, int32 language, const char* message, int16 channel = CHANNEL_GROUP_SAY);
bool MakeLeader(Entity* new_leader);
-
+ std::string GetLeaderName();
+
bool ShareQuestWithGroup(Client* quest_sharer, Quest* quest);
-
+
void RemoveClientReference(Client* remove);
- void UpdateGroupMemberInfo(Entity* ent, bool groupMembersLocked=false);
+ void UpdateGroupMemberInfo(Entity* ent, bool groupMembersLocked = false);
Entity* GetGroupMemberByPosition(Entity* seeker, int32 mapped_position);
-
- void SetDefaultGroupOptions(GroupOptions* options=nullptr);
-
+
+ void SetDefaultGroupOptions(GroupOptions* options = nullptr);
+ bool GetDefaultGroupOptions(GroupOptions* options);
+
GroupOptions* GetGroupOptions() { return &group_options; }
int8 GetLastLooterIndex() { return group_options.last_looted_index; }
void SetNextLooterIndex(int8 new_index) { group_options.last_looted_index = new_index; }
+
+ int32 GetID() { return m_id; }
+
+ void GetRaidGroups(std::vector* groups);
+ void ReplaceRaidGroups(std::vector* groups);
+ bool IsInRaidGroup(int32 groupID, bool isLeaderGroup = false);
+ void AddGroupToRaid(int32 groupID);
+ void RemoveGroupFromRaid(int32 groupID);
+ bool IsGroupRaid();
+ void ClearGroupRaid();
Mutex MGroupMembers; // Mutex for the group members
private:
GroupOptions group_options;
int32 m_id; // ID of this group
deque m_members; // List of members in this group
-
+ std::vector m_raidgroups;
+ mutable std::shared_mutex MRaidGroups; // mutex for std vector
};
/// Responsible for managing all the player groups in the world
@@ -130,17 +153,19 @@ public:
/// ID of the group to add a member to
/// Entity* to add to the group
/// True if the member was added to the group
- bool AddGroupMember(int32 group_id, Entity* member);
+ bool AddGroupMember(int32 group_id, Entity* member, bool is_leader = false);
+ bool AddGroupMemberFromPeer(int32 group_id, GroupMemberInfo* info);
/// Removes a member from a group
/// ID of the group to remove a member from
/// Entity* to remove from the group
/// True if the member was removed from the group
bool RemoveGroupMember(int32 group_id, Entity* member);
+ bool RemoveGroupMember(int32 group_id, std::string name, bool is_client, int32 charID);
/// Creates a new group with the provided Entity* as the leader
/// The Entity* that will be the leader of the group
- void NewGroup(Entity* leader);
+ int32 NewGroup(Entity* leader, GroupOptions* goptions, int32 override_group_id = 0);
/// Removes the group from the group manager
/// ID of the group to remove
@@ -151,11 +176,12 @@ public:
/// Player or NPC that is the target of the invite
/// Error code if invite was unsuccessful, 0 if successful
int8 Invite(Player* leader, Entity* member);
+ bool AddInvite(Player* leader, Entity* member);
/// Handles accepting of a group invite
/// Entity* that is accepting the invite
/// Error code if accepting the invite failed, 0 if successful
- int8 AcceptInvite(Entity* member);
+ int8 AcceptInvite(Entity* member, int32* group_override_id = nullptr, bool auto_add_group = true);
/// Handles declining of a group invite
/// Entity* that is declining the invite
@@ -169,7 +195,7 @@ public:
/// Send updates to all the clients in the group
/// ID of the group to send updates to
/// Client* to exclude from the update, usually the one that triggers the update
- void SendGroupUpdate(int32 group_id, Client* exclude = 0);
+ void SendGroupUpdate(int32 group_id, Client* exclude = 0, bool forceRaidUpdate = false);
PlayerGroup* GetGroup(int32 group_id);
@@ -188,6 +214,8 @@ public:
void ClearPendingInvite(Entity* member);
+ std::string HasPendingInvite(Entity* member);
+
void RemoveGroupBuffs(int32 group_id, Client* client);
int32 GetGroupSize(int32 group_id);
@@ -198,7 +226,9 @@ public:
void SimpleGroupMessage(int32 group_id, const char* message);
void SendGroupMessage(int32 group_id, int8 type, const char* message, ...);
void GroupMessage(int32 group_id, const char* message, ...);
- void GroupChatMessage(int32 group_id, Spawn* from, int32 language, const char* message);
+ void GroupChatMessage(int32 group_id, Spawn* from, int32 language, const char* message, int16 channel = CHANNEL_GROUP_SAY);
+ void GroupChatMessage(int32 group_id, std::string fromName, int32 language, const char* message, int16 channel = CHANNEL_GROUP_SAY);
+ void SendGroupChatMessage(int32 group_id, int16 channel, const char* message, ...);
bool MakeLeader(int32 group_id, Entity* new_leader);
void UpdateGroupBuffs();
@@ -208,11 +238,29 @@ public:
bool IsSpawnInGroup(int32 group_id, string name); // used in follow
Player* GetGroupLeader(int32 group_id);
+ void UpdateGroupMemberInfoFromPeer(int32 group_id, std::string name, bool is_client, GroupMemberInfo* updateinfo);
+ void SendPeerGroupData(std::string peerId);
+
+ void ClearGroupRaid(int32 groupID);
+ void RemoveGroupFromRaid(int32 groupID, int32 targetGroupID);
+ bool IsInRaidGroup(int32 groupID, int32 targetGroupID, bool isLeaderGroup = false);
+ bool GetDefaultGroupOptions(int32 group_id, GroupOptions* options);
+ void GetRaidGroups(int32 group_id, std::vector* groups);
+ void ReplaceRaidGroups(int32 groupID, std::vector* newGroups);
+ void SetGroupOptions(int32 groupID, GroupOptions* options);
+ void SendWhoGroupMembers(Client* client, int32 groupID);
+ void SendWhoRaidMembers(Client* client, int32 groupID);
+ int8 AcceptRaidInvite(std::string acceptorName, int32 groupID);
+ bool SendRaidInvite(Client* sender, Entity* target);
+ void SplitWithGroupOrRaid(Client* client, int32 coin_plat, int32 coin_gold, int32 coin_silver, int32 coin_copper);
+ bool IdentifyMemberInGroupOrRaid(ZoneChangeDetails* details, Client* client, int32 zoneID, int32 instanceID = 0);
+ void ClearGroupRaidLooterFlag(int32 groupID);
private:
int32 m_nextGroupID; // Used to generate a new unique id for new groups
map m_groups; // int32 is the group id, PlayerGroup* is a pointer to the actual group
map m_pendingInvites; // First string is the person invited to the group, second string is the leader of the group
+ map m_raidPendingInvites; // First string is the other group leader invited to the group, second string is the leader of the raid
mutable std::shared_mutex MGroups; // Mutex for the group map (m_groups)
Mutex MPendingInvites; // Mutex for the pending invites map (m_pendingInvites)
diff --git a/source/WorldServer/Spawn.cpp b/source/WorldServer/Spawn.cpp
index 72eb580..67f1ae0 100644
--- a/source/WorldServer/Spawn.cpp
+++ b/source/WorldServer/Spawn.cpp
@@ -1320,6 +1320,9 @@ EQ2Packet* Spawn::serialize(Player* player, int16 version){
Spawn* Spawn::GetTarget(){
Spawn* ret = 0;
+ if(!GetZone())
+ return 0;
+
// only attempt to get a spawn if we had a target stored
if (target != 0)
{
@@ -4933,14 +4936,7 @@ void Spawn::AddIgnoredWidget(int32 id) {
void Spawn::SendGroupUpdate() {
if (IsEntity() && ((Entity*)this)->GetGroupMemberInfo()) {
((Entity*)this)->UpdateGroupMemberInfo();
- if (IsPlayer()) {
- Client* client = ((Player*)this)->GetClient();
- if(client) {
- world.GetGroupManager()->SendGroupUpdate(((Entity*)this)->GetGroupMemberInfo()->group_id, client);
- }
- }
- else
- world.GetGroupManager()->SendGroupUpdate(((Entity*)this)->GetGroupMemberInfo()->group_id);
+ world.GetGroupManager()->SendGroupUpdate(((Entity*)this)->GetGroupMemberInfo()->group_id);
}
}
@@ -4996,7 +4992,7 @@ bool Spawn::HasSpawnLootWindowCompleted(int32 spawn_id) {
bool Spawn::HasSpawnNeedGreedEntry(int32 item_id, int32 spawn_id) {
for (auto [itr, rangeEnd] = need_greed_items.equal_range(item_id); itr != rangeEnd; itr++) {
- LogWrite(LOOT__DEBUG, 8, "Loot", "%s: HasSpawnNeedGreedEntry Item ID: %u, Spawn ID: %u", GetName(), itr->first, itr->second.first);
+ LogWrite(LOOT__DEBUG, 1, "Loot", "%s: HasSpawnNeedGreedEntry Item ID: %u, Spawn ID: %u", GetName(), itr->first, itr->second.first);
if (spawn_id == itr->second.first) {
return true;
}
@@ -5006,7 +5002,7 @@ bool Spawn::HasSpawnNeedGreedEntry(int32 item_id, int32 spawn_id) {
bool Spawn::HasSpawnLottoEntry(int32 item_id, int32 spawn_id) {
for (auto [itr, rangeEnd] = lotto_items.equal_range(item_id); itr != rangeEnd; itr++) {
- LogWrite(LOOT__DEBUG, 8, "Loot", "%s: HasSpawnLottoEntry Item ID: %u, Spawn ID: %u", GetName(), itr->first, itr->second);
+ LogWrite(LOOT__DEBUG, 1, "Loot", "%s: HasSpawnLottoEntry Item ID: %u, Spawn ID: %u", GetName(), itr->first, itr->second);
if (spawn_id == itr->second) {
return true;
}
diff --git a/source/WorldServer/SpellProcess.cpp b/source/WorldServer/SpellProcess.cpp
index 18e9492..87f24ad 100644
--- a/source/WorldServer/SpellProcess.cpp
+++ b/source/WorldServer/SpellProcess.cpp
@@ -51,6 +51,8 @@ void SpellProcess::RemoveCaster(Spawn* caster){
if(spell->caster == caster) {
spell->caster = nullptr;
}
+ if(spell->initial_target == caster->GetID())
+ spell->initial_target = 0;
spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__);
}
}
@@ -2051,14 +2053,16 @@ void SpellProcess::RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all, boo
bool foundMatch = false;
- spell->MSpellTargets.readlock(__FUNCTION__, __LINE__);
- for (i = 0; i < spell->targets.size(); i++){
- if (spawn->GetID() == spell->targets.at(i)){
- foundMatch = true;
- break;
+ if(spawn != spell->caster) {
+ spell->MSpellTargets.readlock(__FUNCTION__, __LINE__);
+ for (i = 0; i < spell->targets.size(); i++){
+ if (spawn->GetID() == spell->targets.at(i)){
+ foundMatch = true;
+ break;
+ }
}
- }
spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__);
+ }
if(foundMatch) {
if (spawn->IsEntity())
((Entity*)spawn)->RemoveSpellEffect(spell);
diff --git a/source/WorldServer/Web/HTTPSClient.cpp b/source/WorldServer/Web/HTTPSClient.cpp
new file mode 100644
index 0000000..6627447
--- /dev/null
+++ b/source/WorldServer/Web/HTTPSClient.cpp
@@ -0,0 +1,450 @@
+/*
+EQ2Emu: Everquest II Server Emulator
+Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com)
+
+This file is part of EQ2Emu.
+
+EQ2Emu is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+EQ2Emu is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with EQ2Emu. If not, see .
+*/
+
+#include "HTTPSClient.h"
+#include "PeerManager.h"
+
+#include "../net.h"
+#include "../../common/Log.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace boost_net = boost::asio; // From
+extern NetConnection net;
+extern PeerManager peer_manager;
+static const std::string base64_chars =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+"abcdefghijklmnopqrstuvwxyz"
+"0123456789+/";
+
+std::string base64_encode(const std::string& input) {
+ std::string encoded_string;
+ unsigned char const* bytes_to_encode = reinterpret_cast(input.c_str());
+ size_t in_len = input.size();
+ int i = 0;
+ int j = 0;
+ unsigned char char_array_3[3];
+ unsigned char char_array_4[4];
+
+ while (in_len--) {
+ char_array_3[i++] = *(bytes_to_encode++);
+ if (i == 3) {
+ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+ char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+ char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+ char_array_4[3] = char_array_3[2] & 0x3f;
+
+ for (i = 0; (i < 4); i++)
+ encoded_string += base64_chars[char_array_4[i]];
+ i = 0;
+ }
+ }
+
+ if (i) {
+ for (j = i; j < 3; j++)
+ char_array_3[j] = '\0';
+
+ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+ char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+ char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+ char_array_4[3] = char_array_3[2] & 0x3f;
+
+ for (j = 0; (j < i + 1); j++)
+ encoded_string += base64_chars[char_array_4[j]];
+
+ while ((i++ < 3))
+ encoded_string += '=';
+ }
+
+ return encoded_string;
+}
+
+HTTPSClient::HTTPSClient(const std::string& certFile, const std::string& keyFile)
+ : certFile(certFile), keyFile(keyFile) {}
+
+std::shared_ptr HTTPSClient::createSSLContext() {
+ auto sslCtx = std::make_shared(boost::asio::ssl::context::tlsv13_client);
+ sslCtx->set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use);
+ sslCtx->use_certificate_file(certFile, boost::asio::ssl::context::pem);
+ sslCtx->use_private_key_file(keyFile, boost::asio::ssl::context::pem);
+ sslCtx->set_verify_mode(ssl::verify_peer);
+ sslCtx->set_default_verify_paths();
+ return sslCtx;
+}
+
+void HTTPSClient::parseAndStoreCookies(const http::response& res) {
+ if (res.count(http::field::set_cookie)) {
+ std::istringstream stream(res[http::field::set_cookie].to_string());
+ std::string token;
+
+ // Parse "Set-Cookie" field for name-value pairs
+ while (std::getline(stream, token, ';')) {
+ auto pos = token.find('=');
+ if (pos != std::string::npos) {
+ std::string name = token.substr(0, pos);
+ std::string value = token.substr(pos + 1);
+ cookies[name] = value; // Store each cookie
+ }
+ }
+ }
+}
+
+std::string HTTPSClient::buildCookieHeader() const {
+ std::string cookieHeader;
+ for (const auto& [name, value] : cookies) {
+ cookieHeader += name + "=" + value;
+ }
+ return cookieHeader;
+}
+
+std::string HTTPSClient::sendRequest(const std::string& server, const std::string& port, const std::string& target) {
+ try {
+ boost::asio::io_context ioContext;
+
+ // SSL and TCP setup
+ auto sslCtx = createSSLContext();
+ auto stream = std::make_shared>(ioContext, *sslCtx);
+ auto resolver = std::make_shared(ioContext);
+ auto results = resolver->resolve(server, port);
+
+ // Persistent objects to manage response, request, and buffer
+ auto res = std::make_shared>();
+ auto buffer = std::make_shared();
+ auto req = std::make_shared>(http::verb::get, target, 11);
+
+ // SNI hostname (required for many hosts)
+ if (!SSL_set_tlsext_host_name(stream->native_handle(), server.c_str())) {
+ throw boost::beast::system_error(
+ boost::beast::error_code(static_cast(::ERR_get_error()), boost::asio::error::get_ssl_category()));
+ }
+
+ // Prepare request headers
+ req->set(http::field::host, server);
+ req->set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
+ req->set(boost::beast::http::field::connection, "close");
+ req->set(http::field::content_type, "application/json");
+ if (!cookies.empty()) {
+ req->set(http::field::cookie, buildCookieHeader());
+ }
+ else {
+ std::string credentials = net.GetCmdUser() + ":" + net.GetCmdPassword();
+ std::string encodedCredentials = base64_encode(credentials);
+ req->set(http::field::authorization, "Basic " + encodedCredentials);
+ }
+
+ // Step 1: Asynchronous connect with timeout
+ auto connect_timer = std::make_shared(ioContext);
+ connect_timer->expires_after(std::chrono::seconds(2));
+
+ connect_timer->async_wait([stream, server, port, target](boost::system::error_code ec) {
+ if (!ec) {
+ stream->lowest_layer().cancel(); // Cancel operation on timeout
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Connect Timeout for %s:%s/%s", __FUNCTION__, server.c_str(), port.c_str(), target.c_str());
+ peer_manager.SetPeerErrorState(server, port);
+ }
+ });
+
+ auto timer = std::make_shared(ioContext, std::chrono::seconds(2));
+ boost::asio::async_connect(stream->lowest_layer(), results,
+ [stream, connect_timer, req, buffer, res, timer, server, port, target](boost::system::error_code ec, const auto&) {
+ connect_timer->cancel();
+ if (ec) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Connect Error %s for %s:%s/%s", __FUNCTION__, ec.message().c_str(), server.c_str(), port.c_str(), target.c_str());
+ peer_manager.SetPeerErrorState(server, port);
+ return;
+ }
+
+ // Step 2: Asynchronous handshake with timeout
+ timer->expires_after(std::chrono::seconds(2));
+
+ timer->async_wait([stream, server, port, target](boost::system::error_code ec) {
+ if (!ec) {
+ stream->lowest_layer().cancel();
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Handshake Timeout for %s:%s/%s", __FUNCTION__, server.c_str(), port.c_str(), target.c_str());
+ peer_manager.SetPeerErrorState(server, port);
+ }
+ });
+
+ stream->async_handshake(boost::asio::ssl::stream_base::client,
+ [stream, timer, req, buffer, res, server, port, target](boost::system::error_code ec) {
+ timer->cancel();
+ if (ec) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Handshake Error %s for %s:%s/%s", __FUNCTION__, ec.message().c_str(), server.c_str(), port.c_str(), target.c_str());
+ peer_manager.SetPeerErrorState(server, port);
+ return;
+ }
+
+ // Step 3: Asynchronous write request
+ timer->expires_after(std::chrono::seconds(2));
+
+ timer->async_wait([stream, server, port, target](boost::system::error_code ec) {
+ if (!ec) {
+ stream->lowest_layer().cancel();
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Write Timeout for %s:%s/%s", __FUNCTION__, server.c_str(), port.c_str(), target.c_str());
+ peer_manager.SetPeerErrorState(server, port);
+ }
+ });
+
+ http::async_write(*stream, *req,
+ [stream, buffer, res, timer, server, port, target](boost::system::error_code ec, std::size_t) {
+ timer->cancel();
+ if (ec) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Write Error %s for %s:%s/%s", __FUNCTION__, ec.message().c_str(), server.c_str(), port.c_str(), target.c_str());
+ peer_manager.SetPeerErrorState(server, port);
+ return;
+ }
+
+ // Step 4: Asynchronous read response
+ timer->expires_after(std::chrono::seconds(2));
+
+ timer->async_wait([stream, server, port, target](boost::system::error_code ec) {
+ if (!ec) {
+ stream->lowest_layer().cancel();
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Read Timeout for %s:%s/%s", __FUNCTION__, server.c_str(), port.c_str(), target.c_str());
+ peer_manager.SetPeerErrorState(server, port);
+ }
+ });
+
+ http::async_read(*stream, *buffer, *res,
+ [stream, timer, res, server, port, target](boost::system::error_code ec, std::size_t) {
+ timer->cancel();
+ if (ec) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Read Error %s for %s:%s/%s", __FUNCTION__, ec.message().c_str(), server.c_str(), port.c_str(), target.c_str());
+ peer_manager.SetPeerErrorState(server, port);
+ return;
+ }
+
+ // Step 5: Shutdown the stream
+ stream->async_shutdown([stream, server, port](boost::system::error_code ec) {
+ if (ec && ec != boost::asio::error::eof) {
+ // ignore these
+ //std::cerr << "Shutdown error: " << ec.message() << std::endl;
+ }
+ });
+ });
+ });
+ });
+ });
+
+ ioContext.run();
+
+ // Store cookies from the response
+ if (res->base().count(http::field::set_cookie) > 0) {
+ auto set_cookie_value = res->base()[http::field::set_cookie].to_string();
+ std::istringstream stream(set_cookie_value);
+ std::string token;
+
+ // Parse "Set-Cookie" field for name-value pairs
+ while (std::getline(stream, token, ';')) {
+ auto pos = token.find('=');
+ if (pos != std::string::npos) {
+ std::string name = token.substr(0, pos);
+ std::string value = token.substr(pos + 1);
+ cookies[name] = value; // Store each cookie
+ }
+ }
+ }
+
+ if (res->body() == "Unauthorized") {
+ cookies.clear();
+ }
+
+ // Return the response body, if available
+ return res->body();
+ }
+ catch (const std::exception& e) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Request Error %s for %s:%s/%s", __FUNCTION__, e.what() ? e.what() : "??", server.c_str(), port.c_str(), target.c_str());
+ return {};
+ }
+}
+
+std::string HTTPSClient::sendPostRequest(const std::string& server, const std::string& port, const std::string& target, const std::string& jsonPayload) {
+ try {
+ boost::asio::io_context ioContext;
+
+ // SSL and TCP setup
+ auto sslCtx = createSSLContext();
+ auto stream = std::make_shared>(ioContext, *sslCtx);
+ auto resolver = std::make_shared(ioContext);
+ auto results = resolver->resolve(server, port);
+
+ // Persistent objects to manage response, request, and buffer
+ auto res = std::make_shared>();
+ auto buffer = std::make_shared();
+ auto req = std::make_shared>(http::verb::post, target, 11);
+
+ // SNI hostname (required for many hosts)
+ if (!SSL_set_tlsext_host_name(stream->native_handle(), server.c_str())) {
+ throw boost::beast::system_error(
+ boost::beast::error_code(static_cast(::ERR_get_error()), boost::asio::error::get_ssl_category()));
+ }
+
+ // Prepare HTTP POST request with JSON payload
+ req->set(http::field::host, server);
+ req->set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
+ req->set(boost::beast::http::field::connection, "close");
+ req->set(http::field::content_type, "application/json");
+ if (!cookies.empty()) {
+ req->set(http::field::cookie, buildCookieHeader());
+ }
+ else {
+ std::string credentials = net.GetCmdUser() + ":" + net.GetCmdPassword();
+ std::string encodedCredentials = base64_encode(credentials);
+ req->set(http::field::authorization, "Basic " + encodedCredentials);
+ }
+
+ req->body() = jsonPayload;
+ req->prepare_payload();
+
+ // Step 1: Asynchronous connect with timeout
+ auto connect_timer = std::make_shared(ioContext);
+ connect_timer->expires_after(std::chrono::seconds(2));
+
+ connect_timer->async_wait([stream, server, port, target](boost::system::error_code ec) {
+ if (!ec) {
+ stream->lowest_layer().cancel(); // Cancel operation on timeout
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Connect Timeout for %s:%s/%s", __FUNCTION__, server.c_str(), port.c_str(), target.c_str());
+ peer_manager.SetPeerErrorState(server, port);
+ }
+ });
+
+ auto timer = std::make_shared(ioContext, std::chrono::seconds(2));
+ boost::asio::async_connect(stream->lowest_layer(), results,
+ [stream, connect_timer, req, buffer, res, timer, server, port, target](boost::system::error_code ec, const auto&) {
+ connect_timer->cancel();
+ if (ec) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Connect Error %s for %s:%s/%s", __FUNCTION__, ec.message().c_str(), server.c_str(), port.c_str(), target.c_str());
+ peer_manager.SetPeerErrorState(server, port);
+ return;
+ }
+
+ // Step 2: Asynchronous handshake with timeout
+ timer->expires_after(std::chrono::seconds(2));
+
+ timer->async_wait([stream, server, port, target](boost::system::error_code ec) {
+ if (!ec) {
+ stream->lowest_layer().cancel();
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Handshake Timeout for %s:%s/%s", __FUNCTION__, server.c_str(), port.c_str(), target.c_str());
+ peer_manager.SetPeerErrorState(server, port);
+ }
+ });
+
+ stream->async_handshake(boost::asio::ssl::stream_base::client,
+ [stream, timer, req, buffer, res, server, port, target](boost::system::error_code ec) {
+ timer->cancel();
+ if (ec) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Handshake Error %s for %s:%s/%s", __FUNCTION__, ec.message().c_str(), server.c_str(), port.c_str(), target.c_str());
+ peer_manager.SetPeerErrorState(server, port);
+ return;
+ }
+
+ // Step 3: Asynchronous write request
+ timer->expires_after(std::chrono::seconds(2));
+
+ timer->async_wait([stream, server, port, target](boost::system::error_code ec) {
+ if (!ec) {
+ stream->lowest_layer().cancel();
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Write Timeout for %s:%s/%s", __FUNCTION__, server.c_str(), port.c_str(), target.c_str());
+ peer_manager.SetPeerErrorState(server, port);
+ }
+ });
+
+ http::async_write(*stream, *req,
+ [stream, buffer, res, timer, server, port, target](boost::system::error_code ec, std::size_t) {
+ timer->cancel();
+ if (ec) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Write Error %s for %s:%s/%s", __FUNCTION__, ec.message().c_str(), server.c_str(), port.c_str(), target.c_str());
+ peer_manager.SetPeerErrorState(server, port);
+ return;
+ }
+
+ // Step 4: Asynchronous read response
+ timer->expires_after(std::chrono::seconds(2));
+
+ timer->async_wait([stream, server, port, target](boost::system::error_code ec) {
+ if (!ec) {
+ stream->lowest_layer().cancel();
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Read Timeout for %s:%s/%s", __FUNCTION__, server.c_str(), port.c_str(), target.c_str());
+ peer_manager.SetPeerErrorState(server, port);
+ }
+ });
+
+ http::async_read(*stream, *buffer, *res,
+ [stream, timer, res, server, port, target](boost::system::error_code ec, std::size_t) {
+ timer->cancel();
+ if (ec) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Read Error %s for %s:%s/%s", __FUNCTION__, ec.message().c_str(), server.c_str(), port.c_str(), target.c_str());
+ peer_manager.SetPeerErrorState(server, port);
+ return;
+ }
+
+ // Step 5: Shutdown the stream
+ stream->async_shutdown([stream, server, port](boost::system::error_code ec) {
+ if (ec && ec != boost::asio::error::eof) {
+ // ignore these
+ //std::cerr << "Shutdown error: " << ec.message() << std::endl;
+ }
+ });
+ });
+ });
+ });
+ });
+
+ ioContext.run();
+
+ // Store cookies from the response
+ if (res->base().count(http::field::set_cookie) > 0) {
+ auto set_cookie_value = res->base()[http::field::set_cookie].to_string();
+ std::istringstream stream(set_cookie_value);
+ std::string token;
+
+ // Parse "Set-Cookie" field for name-value pairs
+ while (std::getline(stream, token, ';')) {
+ auto pos = token.find('=');
+ if (pos != std::string::npos) {
+ std::string name = token.substr(0, pos);
+ std::string value = token.substr(pos + 1);
+ cookies[name] = value; // Store each cookie
+ }
+ }
+ }
+
+ if (res->body() == "Unauthorized") {
+ cookies.clear();
+ }
+
+ // Return the response body, if available
+ return res->body();
+ }
+ catch (const std::exception& e) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Request Error %s for %s:%s/%s", __FUNCTION__, e.what() ? e.what() : "??", server.c_str(), port.c_str(), target.c_str());
+ return {};
+ }
+}
\ No newline at end of file
diff --git a/source/WorldServer/Web/HTTPSClient.h b/source/WorldServer/Web/HTTPSClient.h
new file mode 100644
index 0000000..75690f1
--- /dev/null
+++ b/source/WorldServer/Web/HTTPSClient.h
@@ -0,0 +1,61 @@
+/*
+EQ2Emu: Everquest II Server Emulator
+Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com)
+
+This file is part of EQ2Emu.
+
+EQ2Emu is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+EQ2Emu is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with EQ2Emu. If not, see .
+*/
+
+#ifndef HTTPSCLIENT_H
+#define HTTPSCLIENT_H
+
+#include
+#include
+#include
+#include
+#include
+
+namespace ssl = boost::asio::ssl;
+namespace asio = boost::asio;
+namespace beast = boost::beast;
+namespace http = beast::http;
+
+class HTTPSClient {
+public:
+ HTTPSClient(const std::string& certFile, const std::string& keyFile);
+
+ // Send a request with stored cookies and return response as string
+ std::string sendRequest(const std::string& server, const std::string& port, const std::string& target);
+
+ // Send a POST request with a JSON payload and return response as string
+ std::string sendPostRequest(const std::string& server, const std::string& port, const std::string& target, const std::string& jsonPayload);
+
+ std::string getServer() const { return server; }
+ std::string getPort() const { return port; }
+
+private:
+ std::unordered_map cookies;
+
+ std::string certFile;
+ std::string keyFile;
+ std::string server;
+ std::string port;
+
+ void parseAndStoreCookies(const http::response& res);
+ std::shared_ptr createSSLContext(); // New helper function
+ std::string buildCookieHeader() const;
+};
+
+#endif // HTTPSCLIENT_H
diff --git a/source/WorldServer/Web/HTTPSClientPool.cpp b/source/WorldServer/Web/HTTPSClientPool.cpp
new file mode 100644
index 0000000..a69c542
--- /dev/null
+++ b/source/WorldServer/Web/HTTPSClientPool.cpp
@@ -0,0 +1,773 @@
+/*
+EQ2Emu: Everquest II Server Emulator
+Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com)
+
+This file is part of EQ2Emu.
+
+EQ2Emu is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+EQ2Emu is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with EQ2Emu. If not, see .
+*/
+
+#include "HTTPSClientPool.h"
+#include "PeerManager.h"
+#include "../net.h"
+#include "../World.h"
+#include "../LoginServer.h"
+#include "../WorldDatabase.h"
+#include "../Guilds/Guild.h"
+#include "../../common/Log.h"
+#include
+#include
+#include
+#include
+#include
+
+extern NetConnection net;
+extern PeerManager peer_manager;
+extern World world;
+extern LoginServer loginserver;
+extern ZoneList zone_list;
+extern WorldDatabase database;
+extern GuildList guild_list;
+extern HTTPSClientPool peer_https_pool;
+
+// Handler functions for each endpoint
+void AddCharAuth(boost::property_tree::ptree tree) {
+ int32 success = 0;
+ std::string charName(""), zoneName("");
+ int32 acct_id = 0;
+ int32 zoneId = 0, instanceId = 0;
+ bool firstLogin = false;
+ std::string worldAddr(""), internalWorldAddr(""), clientIP("");
+ int16 worldPort = 0;
+ int32 charID = 0;
+ int32 worldID = 0, fromID = 0;
+ int32 loginKey = 0;
+ if (auto char_name = tree.get_optional("character_name")) {
+ charName = char_name.get();
+ }
+ if (auto successful = tree.get_optional("success")) {
+ success = successful.get();
+ }
+ if (auto account_id = tree.get_optional("account_id")) {
+ acct_id = account_id.get();
+ }
+ if (auto zone_name = tree.get_optional("zone_name")) {
+ zoneName = zone_name.get();
+ }
+ if (auto zone_id = tree.get_optional("zone_id")) {
+ zoneId = zone_id.get();
+ }
+ if (auto instance_id = tree.get_optional("instance_id")) {
+ instanceId = instance_id.get();
+ }
+ if (auto first_login = tree.get_optional("first_login")) {
+ firstLogin = first_login.get();
+ }
+
+ if (auto peerclientaddr = tree.get_optional("peer_client_address")) {
+ worldAddr = peerclientaddr.get();
+ }
+ if (auto peerclient_internaladdr = tree.get_optional("peer_client_internal_address")) {
+ internalWorldAddr = peerclient_internaladdr.get();
+ }
+ if (auto peerclientport = tree.get_optional("peer_client_port")) {
+ worldPort = peerclientport.get();
+ }
+ if (auto clientip = tree.get_optional("client_ip")) {
+ clientIP = clientip.get();
+ }
+ if (auto character_id = tree.get_optional("character_id")) {
+ charID = character_id.get();
+ }
+ if (auto login_key = tree.get_optional("login_key")) {
+ loginKey = login_key.get();
+ }
+ if (auto world_id = tree.get_optional("world_id")) {
+ worldID = world_id.get();
+ }
+ if (auto from_id = tree.get_optional("from_id")) {
+ fromID = from_id.get();
+ }
+
+ LogWrite(PEERING__INFO, 0, "Peering", "%s: Handling AddCharAuth %s (%u) for zone %u instance %u", __FUNCTION__, charName.c_str(), charID, zoneId, instanceId);
+
+ if (firstLogin) {
+ loginserver.SendCharApprovedLogin(success, worldAddr, internalWorldAddr, clientIP, worldPort, acct_id, charID, loginKey, worldID, fromID);
+ }
+ else if (acct_id > 0 && charName.length() > 0) {
+ world.ClientAuthApproval(success, charName, acct_id, zoneName, zoneId, instanceId, firstLogin);
+ }
+}
+
+void StartZone(boost::property_tree::ptree tree) {
+ std::string peerWebAddress("");
+ int16 peerWebPort = 0;
+ if (auto addr = tree.get_optional("peer_web_address")) {
+ peerWebAddress = addr.get();
+ }
+ if (auto port = tree.get_optional("peer_web_port")) {
+ peerWebPort = port.get();
+ }
+
+ std::string id = peer_manager.isPeer(peerWebAddress, peerWebPort);
+
+ if (id.size() > 0) {
+ std::string strPort = std::to_string(peerWebPort);
+ auto client = peer_https_pool.getOrCreateClient(id, peerWebAddress, strPort);
+ if (client) {
+ auto responseZones = client->sendRequest(peerWebAddress, strPort, "/zones"); // Assumes HTTPSClient has a get method
+ // Load the JSON data into the property tree
+ std::istringstream json_stream(responseZones);
+ if (json_stream.str().empty()) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: JSON Stream Empty for %s:%s/zones", __FUNCTION__, peerWebAddress.c_str(), strPort.c_str());
+ }
+ else if (json_stream.fail()) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: JSON Failed State for %s:%s/zones", __FUNCTION__, peerWebAddress.c_str(), strPort.c_str());
+ }
+ else if (json_stream.bad()) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: JSON Stream Bad for %s:%s/zones", __FUNCTION__, peerWebAddress.c_str(), strPort.c_str());
+ }
+ else if (json_stream.eof()) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: JSON Stream EOF for %s:%s/zones", __FUNCTION__, peerWebAddress.c_str(), strPort.c_str());
+ }
+ else {
+ boost::property_tree::ptree pt;
+ boost::property_tree::read_json(json_stream, pt);
+ peer_manager.updateZoneTree(id, pt);
+ LogWrite(PEERING__DEBUG, 5, "Peering", "%s: StartZone Update for %s:%s/zones complete, zone tree updated.", __FUNCTION__, peerWebAddress.c_str(), strPort.c_str());
+ }
+ }
+ }
+}
+
+void SendGlobalMessage(boost::property_tree::ptree tree) {
+ int32 success = 0;
+ int8 language = 0;
+ int16 in_channel = 0;
+ std::string toName(""), fromName(""), msg(""), awaymsg("");
+ int8 away_language = 0;
+ int32 group_id = 0;
+ if (auto successful = tree.get_optional("success")) {
+ success = successful.get();
+ }
+ if (auto name = tree.get_optional("to_name")) {
+ toName = name.get();
+ }
+ if (auto name = tree.get_optional("from_name")) {
+ fromName = name.get();
+ }
+ if (auto message = tree.get_optional("message")) {
+ msg = message.get();
+ }
+ if (auto from_language = tree.get_optional("from_language")) {
+ language = from_language.get();
+ }
+ if (auto channel = tree.get_optional("channel")) {
+ in_channel = channel.get();
+ }
+
+ if (auto msg = tree.get_optional("away_message")) {
+ awaymsg = msg.get();
+ }
+ if (auto away_lang = tree.get_optional("away_language")) {
+ away_language = away_lang.get();
+ }
+ if (auto group = tree.get_optional("group_id")) {
+ group_id = group.get();
+ }
+
+ switch (in_channel) {
+ case CHANNEL_PRIVATE_TELL: {
+ Client* from_client = zone_list.GetClientByCharName(fromName.c_str());
+ if (from_client) {
+ if (success) {
+ from_client->HandleTellMessage(from_client->GetPlayer()->GetName(), msg.c_str(), toName.c_str(), language);
+ if (awaymsg.size() > 0) {
+ from_client->HandleTellMessage(toName.c_str(), awaymsg.c_str(), from_client->GetPlayer()->GetName(), away_language);
+ }
+ }
+ else {
+ from_client->SimpleMessage(CHANNEL_COLOR_CHAT_RELATIONSHIP, "That character does not exist.");
+ }
+ }
+ break;
+ }
+ }
+}
+
+void HandleNewGroup(boost::property_tree::ptree tree) {
+
+ int32 success = 0;
+ if (auto successful = tree.get_optional("success")) {
+ success = successful.get();
+ }
+ if (!net.is_primary) {
+ // we are a peer we ask primary for a new group and get the response
+ GroupOptions options;
+ std::string leader(""), member("");
+ int32 group_id = 0;
+ int32 member_entity_id = 0;
+ if (auto leader_name = tree.get_optional("leader_name")) {
+ leader = leader_name.get();
+ }
+ if (auto member_name = tree.get_optional("member_name")) {
+ member = member_name.get();
+ }
+ if (auto lootmethod = tree.get_optional("loot_method")) {
+ options.loot_method = lootmethod.get();
+ }
+ if (auto itemrarity = tree.get_optional("loot_item_rarity")) {
+ options.loot_items_rarity = itemrarity.get();
+ }
+ if (auto autosplit = tree.get_optional("auto_split")) {
+ options.auto_split = autosplit.get();
+ }
+ if (auto defaultyell = tree.get_optional("default_yell")) {
+ options.default_yell = defaultyell.get();
+ }
+ if (auto grouplockmethod = tree.get_optional("group_lock_method")) {
+ options.group_lock_method = grouplockmethod.get();
+ }
+ if (auto groupautolock = tree.get_optional("group_auto_lock")) {
+ options.group_autolock = groupautolock.get();
+ }
+ if (auto soloautolock = tree.get_optional("solo_auto_lock")) {
+ options.solo_autolock = soloautolock.get();
+ }
+ if (auto autoloot = tree.get_optional("auto_loot_method")) {
+ options.auto_loot_method = autoloot.get();
+ }
+ if (auto lastlootindex = tree.get_optional("last_looted_index")) {
+ options.last_looted_index = lastlootindex.get();
+ }
+ if (auto groupid = tree.get_optional("group_id")) {
+ group_id = groupid.get();
+ }
+
+ if (auto entityid = tree.get_optional("member_entity_id")) {
+ member_entity_id = entityid.get();
+ }
+
+ LogWrite(PEERING__INFO, 0, "Peering", "%s: Add New Group %u with member %s (%u) leader %s", __FUNCTION__, group_id, member.c_str(), member_entity_id, leader.c_str());
+ Client* member_client = zone_list.GetClientByCharName(member.c_str());
+ Client* client_leader = zone_list.GetClientByCharName(leader.c_str());
+ if (success) {
+ if (!member_client) {
+ if (client_leader && client_leader->GetPlayer()->GetZone()) {
+ Spawn* spawn = client_leader->GetPlayer()->GetZone()->GetSpawnByID(member_entity_id);
+ if (spawn && spawn->IsEntity()) {
+ world.GetGroupManager()->AcceptInvite((Entity*)spawn, &group_id);
+ }
+ else {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Failed Adding New Group %u, entity %s (%u) did not exist.", __FUNCTION__, group_id, member.c_str(), member_entity_id);
+ }
+ }
+ else {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Failed Adding New Group %u, member %s (%u) did not exist.", __FUNCTION__, group_id, member.c_str(), member_entity_id);
+ }
+ }
+ else if (!client_leader) {
+ if (member_client)
+ member_client->HandleGroupAcceptResponse(2);
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Failed Adding New Group %u, leader %s did not exist.", __FUNCTION__, group_id, leader.c_str());
+ }
+ else {
+ world.GetGroupManager()->AcceptInvite(member_client->GetPlayer(), &group_id);
+ }
+ }
+ }
+}
+
+
+void HandleCreateGuild(boost::property_tree::ptree tree) {
+
+ bool success = false;
+ int32 guildID = 0;
+ std::string leaderName("");
+ if (auto successful = tree.get_optional("success")) {
+ success = successful.get();
+ }
+
+ if (auto guild_id = tree.get_optional("guild_id")) {
+ guildID = guild_id.get();
+ }
+
+ if (auto name = tree.get_optional("leader_name")) {
+ leaderName = name.get();
+ }
+
+ if (net.is_primary) {
+ // we send out to peers
+ }
+ else if (guildID) {
+ database.LoadGuild(guildID);
+ Guild* guild = guild_list.GetGuild(guildID);
+ Client* leader = zone_list.GetClientByCharName(leaderName.c_str());
+ if (leader && guild && !leader->GetPlayer()->GetGuild()) {
+ guild->AddNewGuildMember(leader, 0, GUILD_RANK_LEADER);
+ database.SaveGuildMembers(guild);
+ if (leader && leader->GetPlayer()->GetGroupMemberInfo()) {
+ world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
+
+ PlayerGroup* group = world.GetGroupManager()->GetGroup(leader->GetPlayer()->GetGroupMemberInfo()->group_id);
+ if (group)
+ {
+ GroupMemberInfo* gmi = nullptr;
+ deque::iterator itr;
+ group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
+ deque* members = group->GetMembers();
+ for (itr = members->begin(); itr != members->end(); itr++) {
+ gmi = *itr;
+ if (gmi->client && gmi->client != leader && !gmi->client->GetPlayer()->GetGuild())
+ guild->InvitePlayer(gmi->client, leader->GetPlayer()->GetName());
+ }
+ group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
+ }
+
+ world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
+ }
+ }
+ }
+}
+
+// Define a type for handler functions
+using HandlerFunction = std::function;
+
+// Set up the endpoint-to-function map
+std::unordered_map endpointHandlers = {
+ {"/addcharauth", AddCharAuth},
+ {"/startzone", StartZone},
+ {"/sendglobalmessage", SendGlobalMessage},
+ {"/newgroup", HandleNewGroup},
+ {"/createguild", HandleCreateGuild}
+};
+
+HTTPSClientPool::HTTPSClientPool() {}
+
+void HTTPSClientPool::init(const std::string& cert, const std::string& key) {
+ certFile = cert;
+ keyFile = key;
+ pollingInterval = 1000;
+
+ int32 numThreads = 2;
+ // Start worker threads
+ for (int32 i = 0; i < numThreads; ++i) {
+ workers.emplace_back(&HTTPSClientPool::workerFunction, this);
+ }
+}
+
+HTTPSClientPool::~HTTPSClientPool() {
+ {
+ std::unique_lock lock(queueMutex);
+ stop = true;
+ }
+ condition.notify_all();
+ for (std::thread& worker : workers) {
+ if (worker.joinable()) {
+ worker.join();
+ }
+ }
+}
+
+void HTTPSClientPool::addPeerClient(const std::string& peerId, const std::string& server, const std::string& port, const std::string& authEndpoint) {
+ bool newClient = false;
+ // Check if client already exists for the specified (server, port) pair
+ auto it = clients.find(std::make_pair(server, port));
+ if (it == clients.end()) {
+ newClient = true;
+ }
+
+ if (newClient) {
+ auto client = getOrCreateClient(peerId, server, port);
+ }
+}
+
+boost::property_tree::ptree HTTPSClientPool::sendRequestToPeer(const std::string& peerId, const std::string& target) {
+ auto client = getClient(peerId);
+ boost::property_tree::ptree pt;
+
+ if (!client) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Client for peer %s could not be found.", __FUNCTION__, peerId.c_str());
+ return pt;
+ }
+
+ // Retrieve the response from the client
+ std::string responseBody = client->sendRequest(client->getServer(), client->getPort(), target);
+
+ if (responseBody.size() < 1) {
+
+ int16 peer_port = 0;
+ try {
+ peer_port = std::stoul(client->getPort());
+ }
+ catch (const std::exception& e) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Error for convering peer port for %s: %s.", __FUNCTION__, peerId.c_str(), e.what() ? e.what() : "??");
+ }
+ HealthStatus curStatus = peer_manager.getPeerStatus(client->getServer(), peer_port);
+ switch (curStatus) {
+ case HealthStatus::WARN: {
+ peer_manager.updateHealth(peerId, HealthStatus::ERROR);
+ break;
+ }
+ case HealthStatus::ERROR: {
+ peer_manager.updateHealth(peerId, HealthStatus::SHUTDOWN);
+ break;
+ }
+ default: {
+ peer_manager.updateHealth(peerId, HealthStatus::WARN);
+ break;
+ }
+ }
+ }
+ else {
+ // Parse the response as JSON
+ try {
+ std::istringstream responseStream(responseBody);
+ boost::property_tree::read_json(responseStream, pt);
+ }
+ catch (const boost::property_tree::json_parser_error& e) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: JSON Parsing error for %s: %s.", __FUNCTION__, peerId.c_str(), e.what() ? e.what() : "??");
+ }
+ }
+
+ return pt;
+}
+
+boost::property_tree::ptree HTTPSClientPool::sendPostRequestToPeer(const std::string& peerId, const std::string& target, const std::string& jsonPayload) {
+ auto client = getClient(peerId);
+ boost::property_tree::ptree pt;
+
+ if (!client) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Client for peer %s could not be found.", __FUNCTION__, peerId.c_str());
+ return pt;
+ }
+
+ // Retrieve the response from the client
+ std::string responseBody = client->sendPostRequest(client->getServer(), client->getPort(), target, jsonPayload);
+
+ // Parse the response as JSON
+ try {
+ std::istringstream responseStream(responseBody);
+ boost::property_tree::read_json(responseStream, pt);
+ }
+ catch (const boost::property_tree::json_parser_error& e) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Client for peer %s could not be found.", __FUNCTION__, peerId.c_str());
+ }
+
+ return pt;
+}
+
+std::shared_ptr HTTPSClientPool::getClient(const std::string& peerId) {
+ std::unique_lock lock(queueMutex);
+ // Lookup the client by ID
+ auto idIt = clientsById.find(peerId);
+ if (idIt != clientsById.end()) {
+ return idIt->second;
+ }
+ return nullptr; // Return nullptr if no client exists with the given ID
+}
+
+std::shared_ptr HTTPSClientPool::getOrCreateClient(const std::string& id, const std::string& server, const std::string& port) {
+ std::unique_lock lock(queueMutex);
+ // Create a key based on (server, port)
+ auto clientKey = std::make_pair(server, port);
+
+ // Check if client already exists for the specified (server, port) pair
+ auto it = clients.find(clientKey);
+ if (it != clients.end()) {
+ // Client already exists, return the existing client
+ return it->second;
+ }
+
+ // No existing client for this (server, port), create and store a new HTTPSClient
+ auto client = std::make_shared(certFile, keyFile);
+ clients[clientKey] = client;
+ clientsById[id] = client;
+
+ return client;
+}
+
+void HTTPSClientPool::pollPeerHealth(const std::string& server, const std::string& port) {
+ int16 web_worldport = 0;
+ try {
+ web_worldport = std::stoul(port);
+ }
+ catch (const std::exception& e) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Getting port for peer %s:%s could not be complete.", __FUNCTION__, server.c_str(), port.c_str());
+ }
+ std::string id = peer_manager.isPeer(server, web_worldport);
+ if (id.size() < 1) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Error finding peer %s:%s.", __FUNCTION__, server.c_str(), port.c_str());
+ }
+ else {
+ auto client = getOrCreateClient(id, port, server + ":" + port);
+ int16 interval = pollingInterval;
+ while (running.load()) {
+ interval++;
+ if (interval > pollingInterval) {
+ HealthStatus curStatus = peer_manager.getPeerStatus(server, web_worldport);
+ id = peer_manager.isPeer(server, web_worldport);
+ try {
+ auto response = client->sendRequest(server, port, "/status"); // Assumes HTTPSClient has a get method
+ //std::cout << "Health check response from " << server << ":" << port << " - " << response << std::endl;
+
+ boost::property_tree::ptree json_tree;
+ std::istringstream json_stream(response);
+ boost::property_tree::read_json(json_stream, json_tree);
+ std::string online_status;
+ int16 peer_priority = 65535;
+ bool peer_primary = false;
+ if (auto status = json_tree.get_optional("world_status")) {
+ online_status = status.get();
+ }
+ if (auto priority = json_tree.get_optional("peer_priority")) {
+ peer_priority = priority.get();
+ }
+ if (auto isprimary = json_tree.get_optional("peer_primary")) {
+ peer_primary = isprimary.get();
+ }
+ peer_manager.updatePriority(id, peer_priority);
+
+ if (peer_primary && net.is_primary) {
+ peer_manager.handlePrimaryConflict(id);
+ std::shared_ptr hasPrimary = peer_manager.getHealthyPrimaryPeerPtr();
+ if (hasPrimary) { // demote self
+ LogWrite(PEERING__INFO, 0, "Peering", "%s: Peer %s at %s:%s - HAS PRIMARY status, demoting self.", __FUNCTION__, id.c_str(), server.c_str(), port.c_str());
+ net.SetPrimary(false);
+ }
+ }
+
+ switch (curStatus) {
+ case HealthStatus::STARTUP: {
+ pollPeerHealthData(client, id, server, port);
+ if (online_status == "offline") {
+ std::shared_ptr peer = peer_manager.getPeerById(id);
+ if (peer) {
+ peer->wasOffline = true;
+ if (peer->sentInitialPeerData) {
+ peer->sentInitialPeerData = false;
+ }
+ }
+ }
+ if (online_status == "online") {
+ peer_manager.updateHealth(id, HealthStatus::OK);
+ std::shared_ptr peer = peer_manager.getPeerById(id);
+ if (net.is_primary) {
+ if (peer) {
+ if (peer->wasOffline && !peer->sentInitialPeerData) {
+ world.GetGroupManager()->SendPeerGroupData(id);
+ }
+ peer->sentInitialPeerData = true;
+ }
+ }
+ else if (peer) { // set as if we already sent the data since if we take over we don't want the peer trying to resubmit all groups
+ peer->wasOffline = false;
+ peer->sentInitialPeerData = true;
+ }
+ LogWrite(PEERING__INFO, 0, "Peering", "%s: Peer %s at %s:%s - HAS OK/UP state.", __FUNCTION__, id.c_str(), server.c_str(), port.c_str());
+ }
+ if (!net.is_primary && !peer_manager.hasPrimary() && peer_priority < net.GetPeerPriority()) {
+ LogWrite(PEERING__INFO, 0, "Peering", "%s: Peer %s at %s:%s - HAS PRIMARY.", __FUNCTION__, id.c_str(), server.c_str(), port.c_str());
+ peer_manager.setPrimary(id);
+ net.SetPrimary(false);
+ }
+ else if (!peer_manager.hasPrimary() && !net.is_primary && net.GetPeerPriority() <= peer_priority) {
+ LogWrite(PEERING__INFO, 0, "Peering", "%s: I AM PRIMARY!", __FUNCTION__);
+ net.SetPrimary();
+ }
+ break;
+ }
+ case HealthStatus::OK: {
+ pollPeerHealthData(client, id, server, port);
+ if (!net.is_primary && !peer_manager.hasPrimary() && peer_priority < net.GetPeerPriority()) {
+ LogWrite(PEERING__INFO, 0, "Peering", "%s: Peer %s at %s:%s - HAS PRIMARY.", __FUNCTION__, id.c_str(), server.c_str(), port.c_str());
+ peer_manager.setPrimary(id);
+ net.SetPrimary(false);
+ }
+ else if (!peer_manager.hasPrimary() && !net.is_primary && net.GetPeerPriority() <= peer_priority) {
+ LogWrite(PEERING__INFO, 0, "Peering", "%s: I AM PRIMARY!!", __FUNCTION__);
+ net.SetPrimary();
+ }
+ break;
+ }
+ case HealthStatus::WARN:
+ case HealthStatus::ERROR:
+ case HealthStatus::SHUTDOWN: {
+ peer_manager.updateHealth(id, HealthStatus::STARTUP);
+ LogWrite(PEERING__INFO, 0, "Peering", "%s: Peer %s at %s:%s - HAS ENTERED STARTUP state.", __FUNCTION__, id.c_str(), server.c_str(), port.c_str());
+
+ if (net.is_primary) {
+ std::shared_ptr peer = peer_manager.getPeerById(id);
+ if (peer && peer->sentInitialPeerData == true) {
+ peer->sentInitialPeerData = false;
+ }
+ }
+ break;
+ }
+ }
+ }
+ catch (const std::exception& e) {
+ HealthStatus curStatus = peer_manager.getPeerStatus(server, web_worldport);
+ switch (curStatus) {
+ case HealthStatus::WARN: {
+ peer_manager.updateHealth(id, HealthStatus::ERROR);
+ break;
+ }
+ case HealthStatus::ERROR: {
+
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Peer %s at %s:%s - HAS ERROR->SHUTDOWN state.", __FUNCTION__, id.c_str(), server.c_str(), port.c_str());
+ peer_manager.updateHealth(id, HealthStatus::SHUTDOWN);
+ if (peer_manager.getHealthyPeer() == std::nullopt) {
+ if (!net.is_primary && world.world_loaded) {
+ LogWrite(PEERING__INFO, 0, "Peering", "%s: TAKING OVER AS PRIMARY, NO PEERS AVAILABLE TO CHECK", __FUNCTION__);
+ net.SetPrimary();
+ }
+ }
+ else if (!peer_manager.hasPrimary()) {
+ std::string newPrimary = peer_manager.getPriorityPeer();
+ if (newPrimary.size() > 0) {
+ LogWrite(PEERING__INFO, 0, "Peering", "%s: NEW PRIMARY %s", __FUNCTION__, newPrimary);
+ peer_manager.setPrimary(newPrimary);
+ net.SetPrimary(false);
+ }
+ else {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: NEW PRIMARY CANNOT BE ESTABLISHED!", __FUNCTION__);
+ }
+ }
+ break;
+ }
+ default: {
+ peer_manager.updateHealth(id, HealthStatus::WARN);
+ break;
+ }
+ }
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: ERROR POLLING %s:%s reason: %s", __FUNCTION__, server.c_str(), port.c_str(), e.what() ? e.what() : "??");
+ }
+ interval = 0;
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+ }
+}
+
+void HTTPSClientPool::pollPeerHealthData(auto client, const std::string& id, const std::string& server, const std::string& port) {
+ if (client == nullptr) {
+
+ }
+ auto responseZones = client->sendRequest(server, port, "/zones"); // Assumes HTTPSClient has a get method
+ // Load the JSON data into the property tree
+ std::istringstream json_stream(responseZones);
+ if (json_stream.str().empty()) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Polling JSON Stream Empty for %s:%s/zones", __FUNCTION__, server.c_str(), port.c_str());
+ }
+ else if (json_stream.fail()) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Polling JSON Failed State for %s:%s/zones", __FUNCTION__, server.c_str(), port.c_str());
+ }
+ else if (json_stream.bad()) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Polling JSON Stream Bad for %s:%s/zones", __FUNCTION__, server.c_str(), port.c_str());
+ }
+ else if (json_stream.eof()) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Polling JSON Stream EOF for %s:%s/zones", __FUNCTION__, server.c_str(), port.c_str());
+ }
+ else {
+ boost::property_tree::ptree pt;
+ boost::property_tree::read_json(json_stream, pt);
+ peer_manager.updateZoneTree(id, pt);
+ LogWrite(PEERING__DEBUG, 5, "Peering", "%s: Polling for %s:%s/zones complete, zone tree updated.", __FUNCTION__, server.c_str(), port.c_str());
+ }
+ auto responseClients = client->sendRequest(server, port, "/clients"); // Assumes HTTPSClient has a get method
+
+ // Load the JSON data into the property tree
+ std::istringstream json_stream2(responseClients);
+ if (json_stream2.str().empty()) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Polling JSON Stream Empty for %s:%s/clients", __FUNCTION__, server.c_str(), port.c_str());
+ }
+ else if (json_stream2.fail()) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Polling JSON Failed State for %s:%s/clients", __FUNCTION__, server.c_str(), port.c_str());
+ }
+ else if (json_stream2.bad()) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Polling JSON Stream Bad for %s:%s/clients", __FUNCTION__, server.c_str(), port.c_str());
+ }
+ else if (json_stream2.eof()) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Polling JSON Stream EOF for %s:%s/clients", __FUNCTION__, server.c_str(), port.c_str());
+ }
+ else {
+ boost::property_tree::ptree pt2;
+ boost::property_tree::read_json(json_stream2, pt2);
+ peer_manager.updateClientTree(id, pt2);
+ }
+}
+
+void HTTPSClientPool::startPolling() {
+ running.store(true);
+
+ for (const auto& clientPair : clients) {
+ auto server = clientPair.first.first;
+ auto port = clientPair.first.second;
+
+ std::async(std::launch::async, &HTTPSClientPool::pollPeerHealth, this, server, port);
+ }
+}
+
+void HTTPSClientPool::stopPolling() {
+ running.store(false);
+}
+
+void HTTPSClientPool::sendPostRequestToPeerAsync(const std::string& peerId, const std::string& server, const std::string& port, const std::string& target, const std::string& payload) {
+ {
+ std::unique_lock lock(queueMutex);
+ taskQueue.emplace([this, peerId, server, port, target, payload]() {
+ std::shared_ptr client = getClient(peerId);
+ if (client) {
+ std::string response = client->sendPostRequest(server, port, target, payload);
+
+ boost::property_tree::ptree pt;
+
+ try {
+ std::istringstream responseStream(response);
+ boost::property_tree::read_json(responseStream, pt);
+ }
+ catch (const boost::property_tree::json_parser_error& e) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: JSON Parsing error for %s (%s:%s): %s.", __FUNCTION__, peerId.c_str(), server.c_str(), port.c_str(), e.what() ? e.what() : "??");
+ }
+ if (endpointHandlers.find(target) != endpointHandlers.end()) {
+ endpointHandlers[target](pt); // Call the corresponding handler
+ }
+ else {
+ //std::cout << "No handler for endpoint: " << endpoint << std::endl;
+ }
+ }
+ else {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Client not found for %s (%s:%s).", __FUNCTION__, peerId.c_str(), server.c_str(), port.c_str());
+ }
+ });
+ }
+ condition.notify_one();
+}
+
+void HTTPSClientPool::workerFunction() {
+ while (true) {
+ std::function task;
+ {
+ std::unique_lock lock(queueMutex);
+ condition.wait(lock, [this] { return stop || !taskQueue.empty(); });
+ if (stop && taskQueue.empty()) {
+ return;
+ }
+ task = std::move(taskQueue.front());
+ taskQueue.pop();
+ }
+ task();
+ }
+}
\ No newline at end of file
diff --git a/source/WorldServer/Web/HTTPSClientPool.h b/source/WorldServer/Web/HTTPSClientPool.h
new file mode 100644
index 0000000..d980cfd
--- /dev/null
+++ b/source/WorldServer/Web/HTTPSClientPool.h
@@ -0,0 +1,92 @@
+/*
+EQ2Emu: Everquest II Server Emulator
+Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com)
+
+This file is part of EQ2Emu.
+
+EQ2Emu is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+EQ2Emu is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with EQ2Emu. If not, see .
+*/
+
+#ifndef HTTPSCLIENTPOOL_H
+#define HTTPSCLIENTPOOL_H
+
+#include "HTTPSClient.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include // Include Boost property tree
+
+struct pair_hash {
+ template
+ std::size_t operator()(const std::pair& pair) const {
+ return std::hash()(pair.first) ^ std::hash()(pair.second);
+ }
+};
+
+class HTTPSClientPool {
+public:
+ HTTPSClientPool();
+ ~HTTPSClientPool();
+
+ // init cert and key file
+ void init(const std::string& cert, const std::string& key);
+
+ // Pre-authenticate and add a client to the pool
+ void addPeerClient(const std::string& peerId, const std::string& server, const std::string& port, const std::string& authEndpoint);
+
+ std::shared_ptr getOrCreateClient(const std::string& id, const std::string& server, const std::string& port);
+
+ // Send a request to a peer by ID and parse response as a ptree
+ boost::property_tree::ptree sendRequestToPeer(const std::string& peerId, const std::string& target);
+ boost::property_tree::ptree sendPostRequestToPeer(const std::string& peerId, const std::string& target, const std::string& jsonPayload);
+ void pollPeerHealthData(auto client, const std::string& id, const std::string& server, const std::string& port);
+
+ void startPolling(); // Starts asynchronous polling of peers
+ void stopPolling(); // Stops the polling process
+
+ // Sends a POST request asynchronously by adding it to the task queue
+ void sendPostRequestToPeerAsync(const std::string& peerId, const std::string& server, const std::string& port, const std::string& target, const std::string& payload);
+
+ // Worker thread function
+ void workerFunction();
+
+ bool isPolling() { return running; }
+private:
+ std::shared_ptr getClient(const std::string& peerId);
+
+ std::unordered_map, std::shared_ptr, pair_hash> clients;
+ std::unordered_map> clientsById; // New map for ID-based lookup
+ std::string certFile;
+ std::string keyFile;
+ int pollingInterval; // Polling interval in milliseconds
+
+ std::queue> taskQueue; // Queue of tasks to execute
+ std::mutex queueMutex; // Mutex to protect access to the task queue
+ std::condition_variable condition; // Condition variable to signal worker threads
+ std::vector workers; // Worker threads
+ bool stop = false; // Flag to stop workers
+
+ std::atomic running; // Flag to control polling loop
+ void pollPeerHealth(const std::string& server, const std::string& port); // Polls individual peer
+};
+
+#endif // HTTPSCLIENTPOOL_H
diff --git a/source/WorldServer/Web/PeerManager.cpp b/source/WorldServer/Web/PeerManager.cpp
new file mode 100644
index 0000000..74d219d
--- /dev/null
+++ b/source/WorldServer/Web/PeerManager.cpp
@@ -0,0 +1,772 @@
+/*
+EQ2Emu: Everquest II Server Emulator
+Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com)
+
+This file is part of EQ2Emu.
+
+EQ2Emu is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+EQ2Emu is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with EQ2Emu. If not, see .
+*/
+
+#include "PeerManager.h"
+#include "../../common/Log.h"
+#include "../net.h"
+#include "../PlayerGroups.h"
+#include "HTTPSClientPool.h"
+
+extern NetConnection net;
+extern HTTPSClientPool peer_https_pool;
+
+// HealthCheck method definitions
+void HealthCheck::updateStatus(HealthStatus newStatus) {
+ status = newStatus;
+ lastReceived = std::chrono::system_clock::now();
+}
+
+std::chrono::duration HealthCheck::timeSinceLastCheck() const {
+ return std::chrono::system_clock::now() - lastReceived;
+}
+
+ZoneChangeDetails::ZoneChangeDetails(std::string peer_id, std::string peer_world_address, std::string peer_internal_world_address, int16 peer_world_port,
+ std::string peer_web_address, int16 peer_web_port, std::string zone_file_name, std::string zone_name, int32 zone_id,
+ int32 instance_id, float safe_x, float safe_y, float safe_z, float safe_heading, bool lock_state, sint16 min_status,
+ int16 min_level, int16 max_level, int16 min_version, int32 default_lockout_time, int32 default_reenter_time, int8 instance_type, int32 num_players)
+ : peerId(std::move(peer_id)), peerWorldAddress(peer_world_address), peerInternalWorldAddress(peer_internal_world_address), peerWorldPort(peer_world_port),
+ peerWebAddress(peer_web_address), peerWebPort(peer_web_port), zoneFileName(zone_file_name), zoneName(zone_name), zoneId(zone_id), instanceId(instance_id),
+ safeX(safe_x), safeY(safe_y), safeZ(safe_z), safeHeading(safe_heading), lockState(lock_state), minStatus(min_status), minLevel(min_level),
+ maxLevel(max_level), minVersion(min_version), defaultLockoutTime(default_lockout_time), defaultReenterTime(default_reenter_time), instanceType(instance_type), numPlayers(num_players) {
+ zonePtr = nullptr;
+}
+
+ZoneChangeDetails::ZoneChangeDetails(ZoneChangeDetails* copy_details) : peerId(copy_details->peerId), peerWorldAddress(copy_details->peerWorldAddress), peerWorldPort(copy_details->peerWorldPort),
+peerWebAddress(copy_details->peerWebAddress), peerWebPort(copy_details->peerWebPort), zoneFileName(copy_details->zoneFileName), zoneName(copy_details->zoneName),
+zoneId(copy_details->zoneId), instanceId(copy_details->instanceId), safeX(copy_details->safeX), safeY(copy_details->safeY), safeZ(copy_details->safeZ),
+safeHeading(copy_details->safeHeading), lockState(copy_details->lockState), minStatus(copy_details->minStatus), minLevel(copy_details->minLevel),
+maxLevel(copy_details->maxLevel), minVersion(copy_details->minVersion), defaultLockoutTime(copy_details->defaultLockoutTime),
+defaultReenterTime(copy_details->defaultReenterTime), instanceType(copy_details->instanceType), numPlayers(copy_details->numPlayers),
+peerAuthorized(copy_details->peerAuthorized), zoneKey(copy_details->zoneKey), authDispatchedTime(copy_details->authDispatchedTime),
+zoningPastAuth(copy_details->zoningPastAuth), zonePtr(copy_details->zonePtr) {
+
+}
+
+// PeerManager method definitions
+void PeerManager::addPeer(std::string id, PeeringStatus status, std::string client_address, std::string client_internal_address, int16 client_port, std::string web_address, int16 web_port) {
+ std::shared_ptr peer = std::make_shared(id, PeeringStatus::SECONDARY, client_address, client_internal_address, client_port, web_address, web_port);
+ peers.emplace(id, peer);
+}
+
+void PeerManager::updateHealth(const std::string& id, HealthStatus newStatus) {
+ if (peers.find(id) != peers.end()) {
+ peers[id]->healthCheck.updateStatus(newStatus);
+ }
+}
+
+void PeerManager::updatePriority(const std::string& id, int16 priority) {
+ if (peers.find(id) != peers.end()) {
+ peers[id]->peerPriority = priority;
+ }
+}
+
+void PeerManager::updateZoneTree(const std::string& id, const boost::property_tree::ptree& newTree) {
+ auto it = peers.find(id);
+ if (it != peers.end()) {
+ std::lock_guard lock(it->second->dataMutex);
+ *(it->second->zone_tree) = newTree;
+ }
+}
+
+void PeerManager::updateClientTree(const std::string& id, const boost::property_tree::ptree& newTree) {
+ auto it = peers.find(id);
+ if (it != peers.end()) {
+ std::lock_guard lock(it->second->dataMutex);
+ *(it->second->client_tree) = newTree;
+ }
+}
+
+void PeerManager::setZonePeerData(ZoneChangeDetails* opt_details, std::string peerId, std::string peerWorldAddress, std::string peerInternalWorldAddress, int16 peerWorldPort,
+ std::string peerWebAddress, int16 peerWebPort, std::string zoneFileName,
+ std::string zoneName, int32 zoneId, int32 instanceId, float safeX, float safeY, float safeZ, float safeHeading, bool lockState, sint16 minStatus,
+ int16 minLevel, int16 maxLevel, int16 minVersion, int32 defaultLockoutTime, int32 defaultReenterTime, int8 instanceType, int32 numPlayers) {
+ if (opt_details) {
+ opt_details->peerId = peerId;
+ opt_details->peerWorldAddress = peerWorldAddress;
+ opt_details->peerInternalWorldAddress = peerInternalWorldAddress;
+ opt_details->peerWorldPort = peerWorldPort;
+ opt_details->peerWebAddress = peerWebAddress;
+ opt_details->peerWebPort = peerWebPort;
+ opt_details->zoneFileName = zoneFileName;
+ opt_details->zoneName = zoneName;
+ opt_details->zoneId = zoneId;
+ opt_details->instanceId = instanceId;
+ opt_details->safeX = safeX;
+ opt_details->safeY = safeY;
+ opt_details->safeZ = safeZ;
+ opt_details->safeHeading = safeHeading;
+ opt_details->lockState = lockState;
+ opt_details->minStatus = minStatus;
+ opt_details->minLevel = minLevel;
+ opt_details->maxLevel = maxLevel;
+ opt_details->minVersion = minVersion;
+ opt_details->minVersion = minVersion;
+ opt_details->defaultLockoutTime = defaultLockoutTime;
+ opt_details->defaultReenterTime = defaultReenterTime;
+ opt_details->instanceType = instanceType;
+ opt_details->numPlayers = numPlayers;
+ opt_details->zonePtr = nullptr;
+ opt_details->peerAuthorized = false;
+ opt_details->zoneKey = 0;
+ opt_details->authDispatchedTime = 0;
+ opt_details->zoningPastAuth = false;
+ }
+}
+
+void PeerManager::setZonePeerDataSelf(ZoneChangeDetails* opt_details, std::string zoneFileName, std::string zoneName, int32 zoneId,
+ int32 instanceId, float safeX, float safeY, float safeZ, float safeHeading, bool lockState, sint16 minStatus,
+ int16 minLevel, int16 maxLevel, int16 minVersion, int32 defaultLockoutTime, int32 defaultReenterTime, int8 instanceType,
+ int32 numPlayers, void* zonePtr) {
+ if (opt_details) {
+ opt_details->peerId = "self";
+ opt_details->peerWorldAddress = net.GetWorldAddress();
+ opt_details->peerInternalWorldAddress = net.GetInternalWorldAddress();
+ opt_details->peerWorldPort = net.GetWorldPort();
+ opt_details->peerWebAddress = net.GetWebWorldAddress();
+ opt_details->peerWebPort = net.GetWebWorldPort();
+ opt_details->zoneFileName = zoneFileName;
+ opt_details->zoneName = zoneName;
+ opt_details->zoneId = zoneId;
+ opt_details->instanceId = instanceId;
+ opt_details->safeX = safeX;
+ opt_details->safeY = safeY;
+ opt_details->safeZ = safeZ;
+ opt_details->safeHeading = safeHeading;
+ opt_details->lockState = lockState;
+ opt_details->minStatus = minStatus;
+ opt_details->minLevel = minLevel;
+ opt_details->maxLevel = maxLevel;
+ opt_details->minVersion = minVersion;
+ opt_details->defaultLockoutTime = defaultLockoutTime;
+ opt_details->defaultReenterTime = defaultReenterTime;
+ opt_details->instanceType = instanceType;
+ opt_details->numPlayers = numPlayers;
+ opt_details->zonePtr = zonePtr;
+ opt_details->peerAuthorized = true;
+ opt_details->zoneKey = 0;
+ opt_details->authDispatchedTime = 0;
+ opt_details->zoningPastAuth = true;
+ }
+}
+
+std::string PeerManager::getZonePeerId(const std::string& inc_zone_name, int32 inc_zone_id, int32 inc_instance_id, ZoneChangeDetails* opt_details, bool only_always_loaded) {
+ for (auto& [peerId, peer] : peers) {
+ if (peer->healthCheck.status != HealthStatus::OK)
+ continue;
+ try {
+ std::lock_guard lock(peer->dataMutex);
+ for (const auto& zone : peer->zone_tree->get_child("Zones")) {
+ // Access each field within the current zone
+ std::string zone_name = zone.second.get("zone_name");
+ std::string zone_file_name = zone.second.get("zone_file_name");
+ int32 zone_id = zone.second.get("zone_id");
+ int32 instance_id = zone.second.get("instance_id");
+ bool shutting_down = zone.second.get("shutting_down") == "true";
+ bool instance_zone = zone.second.get("instance_zone") == "true";
+ int32 num_players = zone.second.get("num_players");
+ bool city_zone = zone.second.get("city_zone") == "true";
+ float safe_x = zone.second.get