
- Raid support and cross peer support for Isle of Refuge, DoF, KoS and AoM clients. - Zone Persistence added to non-instanced zones. - Commands: /whogroup, /whoraid, /raidinvite, /raid_looter, /kickfromgroup, /kickfromraid, /leaveraid, /split, /raidsay (rsay) added. - Cross peer zone and instance support - Cross tell support (along with ignore) - Cross ooc support - Cross group support (can chat, leave group, disband cross peers, update group options) - Cross who all support - houses/instances fixed no more cross objects/spawns/etc from other houses - houses now display characters name with the house zone description - 1000's of house items now properly work with wall/ceiling - debug messages removed from housing placement - Encounters locked to raid instead of group - group options restricted to raid leader - reload rules for following are peer wide: COMMAND_RELOADSTRUCTS COMMAND_RELOAD_QUESTS COMMAND_RELOAD_SPELLS COMMAND_RELOAD_ZONESCRIPTS COMMAND_RELOAD_FACTIONS COMMAND_RELOAD_MAIL COMMAND_RELOAD_GUILDS COMMAND_RELOAD_RULES COMMAND_RELOAD_STARTABILITIES COMMAND_RELOAD_VOICEOVERS COMMAND_RELOADSPAWNSCRIPTS COMMAND_RELOADREGIONSCRIPTS COMMAND_RELOADLUASYSTEM - special/static zones (always_loaded) are now defined by a peer_priority unsigned short (smallint(5)) in zones table. peer_priority = server_config world.peerpriority will spawn on that exe instance, if it is not available it is distributed to all peers. Using the value of 0 (assuming no peer has priority of 0) or 65535 will result in peer distribution of zones. server_config.json "WorldServer" block updated with the following (web peer port information), priority must be unique for EACH peer: "peeraddress": "10.1.1.2", "peerport": "9102", "peerpriority": "1", New Command Line Run Arguments for World Exe to override server_config.json values Allowed options: --worldaddress arg World address --internalworldaddress arg Internal world address --worldport arg (=0) Web world port --webworldaddress arg Web world address --webworldport arg (=0) Web world port --peerpriority arg (=0) Peer priority - fixed Isle of Refuge client group struct (raids added also) - new log category Peering - new LUA Functions AddRespawn(Zone, LocationID, RespawnTime) and CreatePersistedRespawn(LocationID, SpawnType, RespawnTime, ZoneID)
228 lines
9.5 KiB
C++
228 lines
9.5 KiB
C++
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifndef PEERMANAGER_H
|
|
#define PEERMANAGER_H
|
|
|
|
#include <iostream>
|
|
#include <chrono>
|
|
#include <map>
|
|
#include <string>
|
|
#include <optional>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <boost/property_tree/ptree.hpp>
|
|
#include <boost/property_tree/json_parser.hpp>
|
|
#include "../../common/types.h"
|
|
|
|
class Client;
|
|
|
|
enum HealthStatus {
|
|
UNKNOWN = 0,
|
|
WARN = 1,
|
|
ERROR = 2,
|
|
STARTUP = 3,
|
|
OK = 4,
|
|
SHUTDOWN = 5
|
|
};
|
|
|
|
enum class PeeringStatus {
|
|
PRIMARY,
|
|
SECONDARY
|
|
};
|
|
|
|
struct HealthCheck {
|
|
HealthStatus status;
|
|
std::chrono::system_clock::time_point lastReceived;
|
|
|
|
void updateStatus(HealthStatus newStatus);
|
|
std::chrono::duration<double> timeSinceLastCheck() const;
|
|
};
|
|
|
|
struct GroupOptions;
|
|
struct GroupMemberInfo;
|
|
struct WhoAllPeerPlayer;
|
|
struct GuildMember;
|
|
|
|
struct ZoneChangeDetails {
|
|
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;
|
|
void* zonePtr;
|
|
bool peerAuthorized;
|
|
int32 zoneKey;
|
|
int32 authDispatchedTime;
|
|
bool zoningPastAuth;
|
|
ZoneChangeDetails() = default;
|
|
ZoneChangeDetails(ZoneChangeDetails* copy_details);
|
|
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);
|
|
};
|
|
|
|
struct Peer {
|
|
std::string id;
|
|
PeeringStatus peeringStatus;
|
|
HealthCheck healthCheck;
|
|
std::string worldAddr;
|
|
std::string internalWorldAddr;
|
|
int16 worldPort;
|
|
int16 peerPriority;
|
|
std::string webAddr;
|
|
int16 webPort;
|
|
std::shared_ptr<boost::property_tree::ptree> zone_tree;
|
|
std::shared_ptr<boost::property_tree::ptree> client_tree;
|
|
std::atomic<bool> sentInitialPeerData;
|
|
std::atomic<bool> wasOffline;
|
|
mutable std::mutex dataMutex; // Mutex to protect access to ptree
|
|
// Default constructor
|
|
Peer()
|
|
: zone_tree(std::make_shared<boost::property_tree::ptree>()),
|
|
client_tree(std::make_shared<boost::property_tree::ptree>()) {
|
|
healthCheck.status = HealthStatus::UNKNOWN;
|
|
peerPriority = 65535;
|
|
sentInitialPeerData = false;
|
|
wasOffline = false;
|
|
}
|
|
|
|
Peer(std::string peerId, PeeringStatus status, std::string client_address,
|
|
std::string client_internal_address, int16 client_port,
|
|
std::string web_address, int16 web_port)
|
|
: id(std::move(peerId)), peeringStatus(status), worldAddr(std::move(client_address)),
|
|
internalWorldAddr(std::move(client_internal_address)), worldPort(client_port),
|
|
webAddr(std::move(web_address)), webPort(web_port),
|
|
zone_tree(std::make_shared<boost::property_tree::ptree>()),
|
|
client_tree(std::make_shared<boost::property_tree::ptree>()) {
|
|
healthCheck.status = HealthStatus::STARTUP;
|
|
peerPriority = 65535;
|
|
sentInitialPeerData = false;
|
|
wasOffline = false;
|
|
}
|
|
|
|
// Example function to output data as JSON string (for debug or logging)
|
|
std::string getZoneDataAsJson() const {
|
|
std::lock_guard<std::mutex> lock(dataMutex);
|
|
std::ostringstream oss;
|
|
boost::property_tree::write_json(oss, *zone_tree); // Dereference data
|
|
return oss.str();
|
|
}
|
|
|
|
// Example function to output data as JSON string (for debug or logging)
|
|
std::string getClientDataAsJson() const {
|
|
std::lock_guard<std::mutex> lock(dataMutex);
|
|
std::ostringstream oss;
|
|
boost::property_tree::write_json(oss, *client_tree); // Dereference data
|
|
return oss.str();
|
|
}
|
|
};
|
|
|
|
class PeerManager {
|
|
private:
|
|
std::map<std::string, std::shared_ptr<Peer>> peers;
|
|
std::atomic<int32> uniqueGroupID{ 1 }; // Shared counter for unique IDs
|
|
std::mutex idMutex;
|
|
|
|
public:
|
|
void 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);
|
|
void updateHealth(const std::string& id, HealthStatus newStatus);
|
|
void updatePriority(const std::string& id, int16 priority);
|
|
void updateZoneTree(const std::string& id, const boost::property_tree::ptree& newTree);
|
|
void updateClientTree(const std::string& id, const boost::property_tree::ptree& newTree);
|
|
void 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);
|
|
void 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 = nullptr);
|
|
bool IsClientConnectedPeer(int32 account_id);
|
|
std::string GetCharacterPeerId(std::string charName);
|
|
void SendPeersChannelMessage(int32 group_id, std::string fromName, std::string message, int16 channel, int32 language_id = 0);
|
|
void SendPeersGuildChannelMessage(int32 guild_id, std::string fromName, std::string message, int16 channel, int32 language_id = 0);
|
|
void sendZonePeerList(Client* client);
|
|
std::string getZonePeerId(const std::string& inc_zone_name, int32 inc_zone_id, int32 inc_instance_id, ZoneChangeDetails* opt_details = nullptr, bool only_always_loaded = false);
|
|
void setZonePeerData(ZoneChangeDetails* opt_details);
|
|
void setPrimary(const std::string& id);
|
|
bool hasPrimary();
|
|
bool hasPriorityPeer(int16 priority);
|
|
std::string getPriorityPeer();
|
|
void updatePeer(const std::string& web_address, int16 web_port, const std::string& client_address, const std::string& client_internal_address, int16 client_port, bool is_primary = false);
|
|
std::string isPeer(const std::string& web_address, int16 web_port);
|
|
HealthStatus getPeerStatus(const std::string& web_address, int16 web_port);
|
|
bool hasPeers();
|
|
std::string assignUniqueNameForSecondary(const std::string& baseName, std::string client_address, std::string client_internal_address, int16 client_port, std::string web_address, int16 web_port);
|
|
std::optional<std::string> getHealthyPeer() const;
|
|
std::shared_ptr<Peer> getHealthyPeerPtr() const;
|
|
std::shared_ptr<Peer> getHealthyPrimaryPeerPtr() const;
|
|
std::shared_ptr<Peer> getHealthyPeerWithLeastClients() const;
|
|
std::shared_ptr<Peer> getPeerById(const std::string& id) const;
|
|
int32 getUniqueGroupId();
|
|
bool sendPrimaryNewGroupRequest(std::string leader, std::string member, int32 entity_id, GroupOptions* options);
|
|
void sendPeersGroupMember(int32 group_id, GroupMemberInfo* info, bool is_update = false, std::string peerId = "");
|
|
void sendPeersRemoveGroupMember(int32 group_id, std::string name, int32 char_id, bool is_client);
|
|
void populateGroupOptions(boost::property_tree::ptree& root, GroupOptions* options);
|
|
void sendPeersNewGroupRequest(std::string peer_creation_address, int16 peer_creation_port,
|
|
int32 group_id, std::string leader, std::string member, GroupOptions* options,
|
|
std::string peerId = "", std::vector<int32>* raidGroups = nullptr, bool is_update = false);
|
|
|
|
void sendPeersDisbandGroup(int32 group_id);
|
|
bool sendPrimaryCreateGuildRequest(std::string guild_name, std::string leader_name);
|
|
void sendPeersAddGuildMember(int32 character_id, int32 guild_id, std::string invited_by, int32 join_timestamp, int8 rank);
|
|
void sendPeersRemoveGuildMember(int32 character_id, int32 guild_id, std::string removed_by);
|
|
void sendPeersCreateGuild(int32 guild_id);
|
|
void sendPeersGuildPermission(int32 guild_id, int8 rank, int8 permission, int8 value_);
|
|
void sendPeersGuildEventFilter(int32 guild_id, int8 event_id, int8 category, int8 value_);
|
|
|
|
void SetPeerErrorState(std::string address, std::string port);
|
|
void handlePrimaryConflict(const std::string& reconnectingPeerId);
|
|
std::shared_ptr<Peer> getCurrentPrimary();
|
|
|
|
void sendPeersMessage(const std::string& endpoint, int32 command, int32 sub_command = 0);
|
|
|
|
void sendZonePlayerList(std::vector<string>* queries, std::vector<WhoAllPeerPlayer>* peer_list, bool isGM);
|
|
|
|
bool GetClientGuildDetails(int32 matchCharID, GuildMember* member_details);
|
|
|
|
};
|
|
|
|
#endif // PEERMANAGER_H
|