diff --git a/internal/udp/udp_test.go b/internal/udp/udp_test.go index 5477f12..e225e7c 100644 --- a/internal/udp/udp_test.go +++ b/internal/udp/udp_test.go @@ -391,7 +391,7 @@ func BenchmarkCRC(b *testing.B) { } for b.Loop() { - CalculateCRC32(data) + //CalculateCRC32(data) } } diff --git a/old/LoginServer/Character.cpp b/old/LoginServer/Character.cpp new file mode 100644 index 0000000..67423d8 --- /dev/null +++ b/old/LoginServer/Character.cpp @@ -0,0 +1,20 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include "Character.h" \ No newline at end of file diff --git a/old/LoginServer/Character.h b/old/LoginServer/Character.h new file mode 100644 index 0000000..a0e89a8 --- /dev/null +++ b/old/LoginServer/Character.h @@ -0,0 +1,25 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef _EQ2_CHARACTER_ +#define _EQ2_CHARACTER_ +class Character{ + +}; +#endif diff --git a/old/LoginServer/lworld.hpp b/old/LoginServer/LWorld.cpp similarity index 52% rename from old/LoginServer/lworld.hpp rename to old/LoginServer/LWorld.cpp index 10fd06e..6a79dbe 100644 --- a/old/LoginServer/lworld.hpp +++ b/old/LoginServer/LWorld.cpp @@ -1,339 +1,80 @@ -// Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) - EQ2Emulator License +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) -#pragma once + This file is part of EQ2Emulator. +*/ +#include "../common/debug.h" +#include +#include +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#else #include #include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "../common/unix.h" -#include "net.hpp" -#include "client.hpp" -#include "login_opcodes.hpp" -#include "login_structs.hpp" -#include "login_database.hpp" -#include "packet_headers.hpp" -#include "../common/log.hpp" -#include "../common/tcp.hpp" -#include "../common/unix.hpp" -#include "../common/timer.hpp" -#include "../common/types.hpp" -#include "../common/queue.hpp" -#include "../common/debug.hpp" -#include "../common/servertalk.hpp" -#include "../common/linked_list.hpp" -#include "../common/config_reader.hpp" -#include "../common/packet/packet_dump.hpp" -#include "../WorldServer/MutexList.h" -#include "../WorldServer/MutexMap.h" - -namespace beast = boost::beast; -namespace http = beast::http; - -using namespace std; - -#define ERROR_BADPASSWORD "Bad password" -#define INVALID_ACCOUNT "Invalid Server Account." -#define ERROR_BADVERSION "Incorrect version" -#define ERROR_UNNAMED "Unnamed servers not allowed to connect to full login servers" -#define ERROR_NOTMASTER "Not a master server" -#define ERROR_NOTMESH "Not a mesh server" -#define ERROR_GHOST "Ghost kick" -#define ERROR_UnknownServerType "Unknown Server Type" -#define ERROR_BADNAME_SERVER "Bad server name, name may not contain the word \"Server\" (case sensitive)" -#define ERROR_BADNAME "Bad server name. Unknown reason." -#define WORLD_NAME_SUFFIX " Server" -#define MAX_UPDATE_COUNT 20 -#define MAX_LOGIN_APPEARANCE_COUNT 100 -#define SOCKET_ERROR -1 -#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#define INVALID_SOCKET -1 extern int errno; +#endif -enum ConType { UnknownW, World, Chat, Login }; +#include "../common/servertalk.h" +#include "LWorld.h" +#include "net.h" +#include "client.h" +#include "../common/packet_dump.h" +#include "login_opcodes.h" +#include "login_structs.h" +#include "LoginDatabase.h" +#include "PacketHeaders.h" +#include "../common/ConfigReader.h" -// Forward declarations -class LWorld; -class LWorldList; -class ClientList; -class NetConnection; -class LoginDatabase; -class ConfigReader; +#ifdef WIN32 +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif extern ClientList client_list; extern NetConnection net; -extern LWorldList world_list; +extern LWorldList world_list; extern LoginDatabase database; extern ConfigReader configReader; extern volatile bool RunLoops; -void* ServerUpdateLoop(void* tmp); - -// Represents a login world server connection and manages communication -class LWorld -{ -public: - // Constructor for incoming TCP connections from world servers - LWorld(TCPConnection* in_con, bool OutgoingLoginUplink = false, int32 iIP = 0, int16 iPort = 0, bool iNeverKick = false); - - // Constructor for placeholder world entries from database - LWorld(int32 in_accountid, char* in_accountname, char* in_worldname, int32 in_admin_id); - - // Constructor for remote world server connections via mesh networking - LWorld(TCPConnection* in_RemoteLink, int32 in_ip, int32 in_RemoteID, int32 in_accountid, char* in_accountname, char* in_worldname, char* in_address, sint32 in_status, int32 in_adminid, bool in_showdown, int8 in_authlevel, bool in_placeholder, int32 iLinkWorldID); - - ~LWorld(); - - // Validates server name format and character restrictions - static bool CheckServerName(const char* name); - - // Main processing loop - handles incoming packets and connection state - bool Process(); - - // Sends a server packet to this world server - void SendPacket(ServerPacket* pack); - - // Sends a formatted message to a specific player on this world server - void Message(const char* to, const char* message, ...); - - // Authenticates and configures the world server connection - bool SetupWorld(char* in_worldname, char* in_worldaddress, char* in_account, char* in_password, char* in_version); - - // Updates the world server's status information and triggers world list refresh if changed - void UpdateStatus(sint32 in_status, sint32 in_players, sint32 in_zones, int8 in_level) - { - if (status != in_status || num_players != in_players || num_zones != in_zones || world_max_level != in_level) { - status = in_status; - num_players = in_players; - num_zones = in_zones; - world_max_level = in_level; - UpdateWorldList(); - } - } - - // Triggers a world list update notification - void UpdateWorldList(LWorld* to = 0); - - // Sets remote server information for mesh networking - void SetRemoteInfo(int32 in_ip, int32 in_accountid, char* in_account, char* in_name, char* in_address, int32 in_status, int32 in_adminid, sint32 in_players, sint32 in_zones); - - // Accessor methods - inline bool IsPlaceholder() { return pPlaceholder; } - inline int32 GetAccountID() { return accountid; } - inline char* GetAccount() { return account; } - inline char* GetAddress() { return address; } - inline int16 GetClientPort() { return pClientPort; } - inline bool IsAddressIP() { return isaddressip; } - inline char* GetName() { return worldname; } - inline sint32 GetStatus() { return status; } - inline bool IsLocked() { return status == -2; } - inline int32 GetIP() { return ip; } - inline int16 GetPort() { return port; } - inline int32 GetID() { return ID; } - inline int32 GetAdmin() { return admin_id; } - inline bool ShowDown() { return pshowdown; } - inline bool ShowDownActive() { return show_down_active; } - inline bool Connected() { return pConnected; } - inline bool IsKicked() { return kicked; } - inline bool IsNeverKick() { return pNeverKick; } - inline ConType GetType() { return ptype; } - inline bool IsOutgoingUplink() { return OutgoingUplink; } - inline TCPConnection* GetLink() { return Link; } - inline int32 GetRemoteID() { return RemoteID; } - inline int32 GetLinkWorldID() { return LinkWorldID; } - inline sint32 GetZoneNum() { return num_zones; } - inline sint32 GetPlayerNum() { return num_players; } - inline int8 GetMaxWorldLevel() { return world_max_level; } - inline bool IsDevelServer() { return devel_server; } - - // Mutator methods - inline void ShowDownActive(bool show) { show_down_active = show; } - inline void ShowDown(bool show) { pshowdown = show; } - inline void SetID(int32 new_id) { ID = new_id; } - - // Returns world status for client display (0=up, 1=dev, 2=locked) - int8 GetWorldStatus(); - - // Converts active world to offline placeholder - void ChangeToPlaceholder(); - - // Disconnects the world server with optional error message - void Kick(const char* message = ERROR_GHOST, bool iSetKickedFlag = true); - - // Sends character deletion notification to world server - void SendDeleteCharacter(int32 char_id, int32 account_id); - - bool IsInit; // Public flag indicating if world is fully initialized - -protected: - friend class LWorldList; - TCPConnection* Link; // TCP connection to world server - Timer* pReconnectTimer; // Timer for reconnection attempts - Timer* pStatsTimer; // Timer for stats updates - -private: - int32 ID; // Unique world server ID - int32 ip; // IP address of world server - char IPAddr[64]; // String representation of IP - int16 port; // Port number of world server - bool kicked; // Flag indicating if server was kicked - bool pNeverKick; // Flag preventing automatic kicks - bool pPlaceholder; // Flag indicating placeholder entry - bool devel_server; // Flag indicating development server - int32 accountid; // Database account ID - char account[30]; // Account name - char address[250]; // Server address string - bool isAuthenticated; // Authentication status - int16 pClientPort; // Client connection port - bool isaddressip; // Flag indicating if address is IP - char worldname[200]; // Display name of world server - sint32 status; // Current server status - int32 admin_id; // Administrator account ID - bool pshowdown; // Show server as down flag - bool show_down_active; // Active showdown state - ConType ptype; // Connection type - bool OutgoingUplink; // Outgoing connection flag - bool pConnected; // Connection state flag - sint32 num_players; // Current player count - sint32 num_zones; // Current zone count - int32 RemoteID; // Remote server ID for mesh - int32 LinkWorldID; // Linked world ID for mesh - int8 world_max_level; // Maximum character level -}; - -// Manages list of world servers and handles server discovery/communication -class LWorldList -{ -public: - LWorldList(); - ~LWorldList(); - - // Search methods for finding world servers - LWorld* FindByID(int32 WorldID); - LWorld* FindByIP(int32 ip); - LWorld* FindByAddress(char* address); - LWorld* FindByLink(TCPConnection* in_link, int32 in_id); - LWorld* FindByAccount(int32 in_accountid, ConType in_type = World); - - // World server management - void Add(LWorld* worldserver); - void AddInitiateWorld(LWorld* world); - void Process(); - void ReceiveData(); - - // Packet distribution methods - void SendPacket(ServerPacket* pack, LWorld* butnotme = 0); - void SendPacketLocal(ServerPacket* pack, LWorld* butnotme = 0); - void SendPacketLogin(ServerPacket* pack, LWorld* butnotme = 0); - - // World list maintenance - void SendWorldChanged(int32 server_id, bool sendtoallclients = false, Client* sendto = 0); - vector* GetServerListUpdate(int16 version); - EQ2Packet* MakeServerListPacket(int8 lsadmin, int16 version); - void UpdateWorldList(LWorld* to = 0); - void UpdateWorldStats(); - - // Connection management - void KickGhost(ConType in_type, int32 in_accountid = 0, LWorld* ButNotMe = 0); - void KickGhostIP(int32 ip, LWorld* NotMe = 0, int16 iClientPort = 0); - void RemoveByLink(TCPConnection* in_link, int32 in_id = 0, LWorld* ButNotMe = 0); - void RemoveByID(int32 in_id); - - // Administrative functions - void SendWorldStatus(LWorld* chat, char* adminname); - void ConnectUplink(); - bool Init(); - void InitWorlds(); - void Shutdown(); - bool WriteXML(); - - // Statistics and information - int32 GetCount(ConType type); - void PopulateWorldList(http::response& res); - void ListWorldsToConsole(); - - // Zone and equipment update handling - void AddServerEquipmentUpdates(LWorld* world, map updates); - void ProcessLSEquipUpdates(); - void RequestServerEquipUpdates(LWorld* world); - void ProcessServerUpdates(); - void RequestServerUpdates(LWorld* world); - void AddServerZoneUpdates(LWorld* world, map updates); - - // Thread control - void SetUpdateServerList(bool var) { UpdateServerList = var; } - bool ContinueServerUpdates() { return server_update_thread; } - void ResetServerUpdates() { server_update_thread = true; } - -protected: - friend class LWorld; - int32 GetNextID() { return NextID++; } - -private: - Mutex MWorldMap; // Mutex for thread-safe world map access - map> zone_updates_already_used; // DOS prevention for zone updates - MutexMap zone_update_timeouts; // Zone update timeout tracking - MutexMap awaiting_zone_update; // Pending zone update tracking - MutexMap last_updated; // Last update time per world - MutexMap> server_zone_updates; // Zone update data - bool server_update_thread; // Thread control flag - int32 NextID; // Next available world ID - LinkedList list; // Initialization world list - map worldmap; // Active world server map - TCPServer* tcplistener; // TCP server for connections - TCPConnection* OutLink; // Outgoing connection for slave mode - map> equip_updates_already_used; // Equipment update DOS prevention - MutexMap equip_update_timeouts; // Equipment update timeouts - MutexMap awaiting_equip_update; // Pending equipment updates - MutexMap last_equip_updated; // Last equipment update time - MutexMap> server_equip_updates; // Equipment update data - map ServerListData; // Cached server list packets - bool UpdateServerList; // Server list update flag -}; - -// Implementation - -// Constructor for incoming TCP connections -LWorld::LWorld(TCPConnection* in_con, bool in_OutgoingLoginUplink, int32 iIP, int16 iPort, bool iNeverKick) -{ +#include "../common/Log.h" +using namespace std; +LWorld::LWorld(TCPConnection* in_con, bool in_OutgoingLoginUplink, int32 iIP, int16 iPort, bool iNeverKick) { Link = in_con; RemoteID = 0; LinkWorldID = 0; - if (iIP) ip = iIP; else ip = in_con->GetrIP(); - struct in_addr in; + struct in_addr in; in.s_addr = in_con->GetrIP(); char* ipadd = inet_ntoa(in); - if (ipadd) - strncpy(IPAddr, ipadd, 64); + if(ipadd) + strncpy(IPAddr,ipadd,64); if (iPort) port = iPort; else port = in_con->GetrPort(); - ID = 0; pClientPort = 0; memset(account, 0, sizeof(account)); @@ -360,7 +101,8 @@ LWorld::LWorld(TCPConnection* in_con, bool in_OutgoingLoginUplink, int32 iIP, in pReconnectTimer = new Timer(INTERSERVER_TIMER); pReconnectTimer->Trigger(); } - } else { + } + else { ptype = UnknownW; OutgoingUplink = false; } @@ -368,19 +110,18 @@ LWorld::LWorld(TCPConnection* in_con, bool in_OutgoingLoginUplink, int32 iIP, in in.s_addr = GetIP(); strcpy(address, inet_ntoa(in)); isaddressip = true; + num_players = 0; num_zones = 0; } -// Constructor for placeholder entries -LWorld::LWorld(int32 in_accountid, char* in_accountname, char* in_worldname, int32 in_admin_id) -{ +LWorld::LWorld(int32 in_accountid, char* in_accountname, char* in_worldname, int32 in_admin_id) { pPlaceholder = true; Link = 0; ip = 0; port = 0; ID = 0; - strcpy(IPAddr, ""); + strcpy(IPAddr,""); pClientPort = 0; memset(account, 0, sizeof(account)); memset(address, 0, sizeof(address)); @@ -397,33 +138,35 @@ LWorld::LWorld(int32 in_accountid, char* in_accountname, char* in_worldname, int OutgoingUplink = false; pReconnectTimer = 0; pConnected = false; + pStatsTimer = NULL; + ptype = World; - strcpy(account, in_accountname); strcpy(worldname, in_worldname); + strcpy(address, "none"); + isaddressip = true; + num_players = 0; num_zones = 0; } -// Constructor for remote connections -LWorld::LWorld(TCPConnection* in_RemoteLink, int32 in_ip, int32 in_RemoteID, int32 in_accountid, char* in_accountname, char* in_worldname, char* in_address, sint32 in_status, int32 in_adminid, bool in_showdown, int8 in_authlevel, bool in_placeholder, int32 iLinkWorldID) -{ +LWorld::LWorld(TCPConnection* in_RemoteLink, int32 in_ip, int32 in_RemoteID, int32 in_accountid, char* in_accountname, char* in_worldname, char* in_address, sint32 in_status, int32 in_adminid, bool in_showdown, int8 in_authlevel, bool in_placeholder, int32 iLinkWorldID) { Link = in_RemoteLink; RemoteID = in_RemoteID; LinkWorldID = iLinkWorldID; ip = in_ip; - struct in_addr in; - if (in_RemoteLink) + struct in_addr in; + if(in_RemoteLink) in.s_addr = in_RemoteLink->GetrIP(); else if (in_ip) in.s_addr = in_ip; char* ipadd = inet_ntoa(in); - if (ipadd) - strncpy(IPAddr, ipadd, 64); + if(ipadd) + strncpy(IPAddr,ipadd,64); port = 0; ID = 0; @@ -443,20 +186,22 @@ LWorld::LWorld(TCPConnection* in_RemoteLink, int32 in_ip, int32 in_RemoteID, int pReconnectTimer = 0; pConnected = true; pStatsTimer = NULL; + ptype = World; - strcpy(account, in_accountname); strcpy(worldname, in_worldname); + strcpy(address, in_address); + isaddressip = false; + num_players = 0; num_zones = 0; } -// Destructor - cleanup resources and notify other servers -LWorld::~LWorld() -{ - safe_delete(pStatsTimer); +LWorld::~LWorld() { + + safe_delete ( pStatsTimer ); num_zones = 0; num_players = 0; database.UpdateWorldServerStats(this, -4); @@ -479,230 +224,290 @@ LWorld::~LWorld() } Link = 0; safe_delete(pReconnectTimer); - world_list.RemoveByID(this->GetID()); + + world_list.RemoveByID ( this->GetID ( ) ); } -// Main packet processing loop -bool LWorld::Process() -{ +bool LWorld::Process() { bool ret = true; if (Link == 0) return true; - if (Link->Connected()) { - if (!pConnected) + if (!pConnected) { pConnected = true; - } else { + } + } + else { pConnected = false; if (pReconnectTimer) { - if (pReconnectTimer->Check() && Link->ConnectReady()) - pReconnectTimer->Start(pReconnectTimer->GetTimerTime() + (rand() % 15000), false); + if (pReconnectTimer->Check() && Link->ConnectReady()) { + pReconnectTimer->Start(pReconnectTimer->GetTimerTime() + (rand()%15000), false); + } return true; } return false; } - if (RemoteID != 0) return true; - // Check for authentication and ban status periodically - if (pStatsTimer && pStatsTimer->Check()) { - if (isAuthenticated && (database.IsServerAccountDisabled(account) || database.IsIPBanned(IPAddr))) { + if(pStatsTimer && pStatsTimer->Check()) + { + if(isAuthenticated && (database.IsServerAccountDisabled(account) || database.IsIPBanned(IPAddr))) + { this->Kick(ERROR_BADPASSWORD); return false; } + database.UpdateWorldServerStats(this, GetStatus()); } ServerPacket* pack = 0; while (ret && (pack = Link->PopPacket())) { - // Require authentication before processing most packets - if (!isAuthenticated && pack->opcode != ServerOP_LSInfo) { + + // this stops connections from sending invalid packets without first authenticating + // with the login server to show it is a legit server + if(!isAuthenticated && pack->opcode != ServerOP_LSInfo) + { Kick("This connection has not authenticated."); break; } - switch (pack->opcode) { + switch(pack->opcode) { case 0: break; - - case ServerOP_KeepAlive: - // Ignore keepalive packets + case ServerOP_KeepAlive: { + // ignore this break; - - case ServerOP_LSFatalError: + } + case ServerOP_LSFatalError: { net.Uplink_WrongVersion = true; ret = false; kicked = true; break; - + } case ServerOP_CharacterCreate: { WorldCharNameFilterResponse_Struct* wcnfr = (WorldCharNameFilterResponse_Struct*) pack->pBuffer; + Client* client = client_list.FindByLSID(wcnfr->account_id); - if (!client) { - if (wcnfr->account_id == 0) + if(!client){ + if(wcnfr->account_id == 0){ client_list.FindByCreateRequest(); + } break; } - if (wcnfr->response == 1) - client->CharacterApproved(GetID(), wcnfr->char_id); + if(wcnfr->response == 1) + { + client->CharacterApproved(GetID(),wcnfr->char_id); + } else + { client->CharacterRejected(wcnfr->response); + } break; - } - + } case ServerOP_UsertoWorldReq: { UsertoWorldRequest_Struct* ustwr = (UsertoWorldRequest_Struct*) pack->pBuffer; if (ustwr->ToID) { LWorld* world = world_list.FindByID(ustwr->ToID); - if (!world || this->GetType() != Login) + if (!world) { break; + } + if (this->GetType() != Login) { + break; + } ustwr->FromID = this->GetID(); world->SendPacket(pack); } break; - } - + } case ServerOP_UsertoWorldResp: { if (pack->size != sizeof(UsertoWorldResponse_Struct)) break; + UsertoWorldResponse_Struct* seps = (UsertoWorldResponse_Struct*) pack->pBuffer; if (seps->ToID) { LWorld* world = world_list.FindByID(seps->ToID); - if (this->GetType() != Login) + + if (this->GetType() != Login) { break; + } if (world) { seps->ToID = world->GetRemoteID(); world->SendPacket(pack); } - } else { - Client* client = client_list.FindByLSID(seps->lsaccountid); - if (client == 0 || (this->GetID() != seps->worldid && this->GetType() != Login)) + } + else { + Client* client = 0; + client = client_list.FindByLSID(seps->lsaccountid); + if(client == 0) break; - client->WorldResponse(GetID(), seps->response, seps->ip_address, seps->port, seps->access_key); + if(this->GetID() != seps->worldid && this->GetType() != Login) + break; + + client->WorldResponse(GetID(),seps->response, seps->ip_address, seps->port, seps->access_key); } break; - } - - case ServerOP_CharTimeStamp: { - if (pack->size != sizeof(CharacterTimeStamp_Struct)) + } + case ServerOP_CharTimeStamp: { // This is being sent to synch a new timestamp on the login server + if(pack->size != sizeof(CharacterTimeStamp_Struct)) break; + CharacterTimeStamp_Struct* cts = (CharacterTimeStamp_Struct*) pack->pBuffer; - if (!database.UpdateCharacterTimeStamp(cts->account_id, cts->char_id, cts->unix_timestamp, GetAccountID())) - printf("TimeStamp update error with character id %i of account id %i on server %i\n", cts->char_id, cts->account_id, GetAccountID()); + + if(!database.UpdateCharacterTimeStamp(cts->account_id,cts->char_id,cts->unix_timestamp,GetAccountID())) + printf("TimeStamp update error with character id %i of account id %i on server %i\n",cts->char_id,cts->account_id,GetAccountID()); + + //Todo: Synch with the other login servers + break; - } - - case ServerOP_GetTableData: - case ServerOP_GetTableQuery: - case ServerOP_GetLatestTables: + } + case ServerOP_GetTableData:{ Kick("This is not an update server."); break; - - case ServerOP_ZoneUpdate: { - if (pack->size > CHARZONESTRUCT_MAXSIZE) + } + case ServerOP_GetTableQuery:{ + Kick("This is not an update server."); + break; + } + case ServerOP_GetLatestTables:{ + Kick("This is not an update server."); + break; + } + case ServerOP_ZoneUpdate:{ + if(pack->size > CHARZONESTRUCT_MAXSIZE) break; CharZoneUpdate_Struct* czu = (CharZoneUpdate_Struct*)pack->pBuffer; database.UpdateCharacterZone(czu->account_id, czu->char_id, czu->zone_id, GetAccountID()); break; } - case ServerOP_RaceUpdate: { - if (pack->size != sizeof(RaceUpdate_Struct)) + + if(pack->size != sizeof(RaceUpdate_Struct)) break; + RaceUpdate_Struct* ru = (RaceUpdate_Struct*) pack->pBuffer; - database.UpdateCharacterRace(ru->account_id, ru->char_id, ru->model_type, ru->race, this->GetAccountID()); + database.UpdateCharacterRace(ru->account_id , ru->char_id , ru->model_type , ru->race , this->GetAccountID ( )); break; - } - + } case ServerOP_BasicCharUpdate: { - if (pack->size != sizeof(CharDataUpdate_Struct)) + if(pack->size != sizeof(CharDataUpdate_Struct)) break; + CharDataUpdate_Struct* cdu = (CharDataUpdate_Struct*) pack->pBuffer; - switch (cdu->update_field) { + + switch(cdu->update_field) + { case LEVEL_UPDATE_FLAG: - database.UpdateCharacterLevel(cdu->account_id, cdu->char_id, cdu->update_data, this->GetAccountID()); - break; + { + database.UpdateCharacterLevel(cdu->account_id,cdu->char_id,cdu->update_data,this->GetAccountID()); + break; + } case CLASS_UPDATE_FLAG: - database.UpdateCharacterClass(cdu->account_id, cdu->char_id, cdu->update_data, this->GetAccountID()); - break; + { + database.UpdateCharacterClass(cdu->account_id,cdu->char_id,cdu->update_data,this->GetAccountID()); + break; + } case GENDER_UPDATE_FLAG: - database.UpdateCharacterGender(cdu->account_id, cdu->char_id, cdu->update_data, this->GetAccountID()); - break; + { + database.UpdateCharacterGender(cdu->account_id,cdu->char_id,cdu->update_data,this->GetAccountID()); + break; + } case DELETE_UPDATE_FLAG: - if (cdu->update_field == 1) - database.DeleteCharacter(cdu->account_id, cdu->char_id, this->GetAccountID()); + { + if(cdu->update_field == 1) + database.DeleteCharacter(cdu->account_id,cdu->char_id,this->GetAccountID()); + break; + } + } + break; + } + case ServerOP_NameCharUpdate: { + CharNameUpdate_Struct* cnu = (CharNameUpdate_Struct*) pack->pBuffer; + if (cnu->name_length > 0 && cnu->name_length < 64) { + char name_buffer[64]; + + // Copy up to name_length characters from cnu->new_name + strncpy(name_buffer, cnu->new_name, cnu->name_length); + + // Null-terminate the string just in case + name_buffer[cnu->name_length] = '\0'; + + database.UpdateCharacterName(cnu->account_id,cnu->char_id,name_buffer,this->GetAccountID()); + } break; } - break; - } - - case ServerOP_NameCharUpdate: { - CharNameUpdate_Struct* cnu = (CharNameUpdate_Struct*) pack->pBuffer; - if (cnu->name_length > 0 && cnu->name_length < 64) { - char name_buffer[64]; - strncpy(name_buffer, cnu->new_name, cnu->name_length); - name_buffer[cnu->name_length] = '\0'; - database.UpdateCharacterName(cnu->account_id, cnu->char_id, name_buffer, this->GetAccountID()); - } - break; - } - case ServerOP_LSInfo: { if (pack->size != sizeof(ServerLSInfo_Struct)) { this->Kick(ERROR_BADVERSION); ret = false; - } else { + //struct in_addr in; + //in.s_addr = GetIP(); + } + else { ServerLSInfo_Struct* lsi = (ServerLSInfo_Struct*) pack->pBuffer; if (strcmp(lsi->protocolversion, EQEMU_PROTOCOL_VERSION) != 0 || !database.CheckVersion(lsi->serverversion)) { cout << "ERROR - KICK BAD VERSION: Got versions: protocol: '" << lsi->protocolversion << "', database version: " << lsi->serverversion << endl; cout << "To allow all world server versions to login, run query on your login database (alternatively replacing * with the database version if preferred): insert into login_versions set version = '*';" << endl; this->Kick(ERROR_BADVERSION); ret = false; - } else if (!SetupWorld(lsi->name, lsi->address, lsi->account, lsi->password, lsi->serverversion)) { + //struct in_addr in; + //in.s_addr = GetIP(); + } + else if (!SetupWorld(lsi->name, lsi->address, lsi->account, lsi->password, lsi->serverversion)) { this->Kick(ERROR_BADPASSWORD); ret = false; - } else { + //struct in_addr in; + //in.s_addr = GetIP(); + } + else{ isAuthenticated = true; devel_server = (lsi->servertype == 4); } } break; - } - + } case ServerOP_LSStatus: { ServerLSStatus_Struct* lss = (ServerLSStatus_Struct*) pack->pBuffer; - if (lss->num_players > 5000 || lss->num_zones > 500) { + + if(lss->num_players > 5000 || lss->num_zones > 500) { this->Kick("Your server has exceeded a number of players and/or zone limit."); ret = false; break; } UpdateStatus(lss->status, lss->num_players, lss->num_zones, lss->world_max_level); break; - } - + } case ServerOP_SystemwideMessage: { if (this->GetType() == Login) { - // Prevent message loops - } else if (this->GetType() == Chat) { + // no looping plz =p + //world_list.SendPacket(pack, this); + } + else if (this->GetType() == Chat) { world_list.SendPacket(pack); } + else { + } break; - } - + } case ServerOP_ListWorlds: { - if (pack->size <= 1 || pack->pBuffer[pack->size - 1] != 0) + if (pack->size <= 1 || pack->pBuffer[pack->size - 1] != 0) { break; + } world_list.SendWorldStatus(this, (char*) pack->pBuffer); break; - } - - case ServerOP_WorldListUpdate: + } + case ServerOP_WorldListUpdate: { break; - + } case ServerOP_WorldListRemove: { - if (this->GetType() != Login || pack->size != sizeof(int32)) + if (this->GetType() != Login) { + // cout << "Error: ServerOP_WorldListRemove from a non-login connection? WTF!" << endl; break; + } + if (pack->size != sizeof(int32)) { + // cout << "Wrong size on ServerOP_WorldListRemove. Got: " << pack->size << ", Expected: " << sizeof(int32) << endl; + break; + } cout << "Got world remove for remote #" << *((int32*) pack->pBuffer) << endl; if ((*((int32*) pack->pBuffer)) > 0) { LWorld* world = world_list.FindByLink(this->GetLink(), *((int32*) pack->pBuffer)); @@ -713,116 +518,122 @@ bool LWorld::Process() world_list.RemoveByID(*((int32*) pack->pBuffer)); } } + else { + // cout << "Error: ServerOP_WorldListRemove: ID = 0? ops!" << endl; + } break; - } - + } case ServerOP_TriggerWorldListRefresh: { world_list.UpdateWorldList(); if (net.GetLoginMode() != Mesh) world_list.SendPacketLogin(pack, this); break; } - - case ServerOP_ZoneUpdates: { + case ServerOP_ZoneUpdates:{ pack->Inflate(); ZoneUpdateList_Struct* updates = 0; - if (pack->size >= sizeof(ZoneUpdateList_Struct) && ((ZoneUpdateList_Struct*)pack->pBuffer)->total_updates <= MAX_UPDATE_COUNT) { + if(pack->size >= sizeof(ZoneUpdateList_Struct) && ((ZoneUpdateList_Struct*)pack->pBuffer)->total_updates <= MAX_UPDATE_COUNT){ updates = (ZoneUpdateList_Struct*)pack->pBuffer; ZoneUpdate_Struct* zone = 0; int32 pos = sizeof(ZoneUpdateList_Struct); sint16 num_updates = 0; map zone_updates; LoginZoneUpdate update; - - while (pos < pack->size && num_updates < updates->total_updates) { - zone = (ZoneUpdate_Struct*)(pack->pBuffer + pos); - if ((pos + zone->zone_name_length + zone->zone_desc_length + sizeof(ZoneUpdate_Struct)) <= pack->size) { + while(pos < pack->size && num_updates < updates->total_updates){ + zone = (ZoneUpdate_Struct*)(pack->pBuffer+pos); + if((pos + zone->zone_name_length + zone->zone_desc_length + sizeof(ZoneUpdate_Struct)) <= pack->size){ update.name = string(zone->data, zone->zone_name_length); update.description = string(zone->data + zone->zone_name_length, zone->zone_desc_length); pos += sizeof(ZoneUpdate_Struct) + zone->zone_name_length + zone->zone_desc_length; num_updates++; zone_updates[zone->zone_id] = update; - } else { - break; } + else + break; } - - if (zone_updates.size() == updates->total_updates) + if(zone_updates.size() == updates->total_updates) world_list.AddServerZoneUpdates(this, zone_updates); else cout << "Error processing zone updates for server: " << GetAccount() << endl; - } else { - Kick("Possible Hacking Attempt"); } + else + Kick("Possible Hacking Attempt"); break; } case ServerOP_LoginEquipment: { LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode %04X (%i): ServerOP_LoginEquipment", pack->opcode, pack->opcode); + pack->Inflate(); EquipmentUpdateList_Struct* updates = 0; - - if (pack->size >= sizeof(EquipmentUpdateList_Struct) && ((EquipmentUpdateList_Struct*)pack->pBuffer)->total_updates <= MAX_LOGIN_APPEARANCE_COUNT) { + if(pack->size >= sizeof(EquipmentUpdateList_Struct) && ((EquipmentUpdateList_Struct*)pack->pBuffer)->total_updates <= MAX_LOGIN_APPEARANCE_COUNT){ updates = (EquipmentUpdateList_Struct*)pack->pBuffer; EquipmentUpdate_Struct* equip = 0; int32 pos = sizeof(EquipmentUpdateList_Struct); sint16 num_updates = 0; map equip_updates; LoginEquipmentUpdate update; - - while (pos < pack->size && num_updates < updates->total_updates) { - equip = (EquipmentUpdate_Struct*)(pack->pBuffer + pos); - update.world_char_id = equip->world_char_id; - update.equip_type = equip->equip_type; - update.red = equip->red; - update.green = equip->green; - update.blue = equip->blue; - update.highlight_red = equip->highlight_red; - update.highlight_green = equip->highlight_green; - update.highlight_blue = equip->highlight_blue; - update.slot = equip->slot; + while(pos < pack->size && num_updates < updates->total_updates){ + equip = (EquipmentUpdate_Struct*)(pack->pBuffer+pos); + update.world_char_id = equip->world_char_id; + update.equip_type = equip->equip_type; + update.red = equip->red; + update.green = equip->green; + update.blue = equip->blue; + update.highlight_red = equip->highlight_red; + update.highlight_green = equip->highlight_green; + update.highlight_blue = equip->highlight_blue; + update.slot = equip->slot; pos += sizeof(EquipmentUpdate_Struct); num_updates++; - equip_updates[equip->id] = update; + equip_updates[equip->id] = update; // JohnAdams: I think I need item_appearances.id from World here? } LogWrite(LOGIN__DEBUG, 1, "Login", "Processing %i Login Appearance Updates...", num_updates); - if (equip_updates.size() == updates->total_updates) { + if(equip_updates.size() == updates->total_updates) + { world_list.AddServerEquipmentUpdates(this, equip_updates); - } else { + } + else + { LogWrite(LOGIN__ERROR, 0, "Login", "Error processing login appearance updates for server: %s\n\t%s, function %s, line %i", GetAccount(), __FILE__, __FUNCTION__, __LINE__); } - } else { + } + else + { LogWrite(LOGIN__ERROR, 0, "Login", "World ID '%i', Possible Hacking Attempt (func: %s, line: %i", GetAccountID(), __FUNCTION__, __LINE__); Kick("Possible Hacking Attempt"); } break; - } - - case ServerOP_BugReport: { - if (pack->size == sizeof(BugReport)) { + } + case ServerOP_BugReport:{ + if(pack->size == sizeof(BugReport)){ BugReport* report = (BugReport*)pack->pBuffer; database.SaveBugReport(GetAccountID(), report->category, report->subcategory, report->causes_crash, report->reproducible, report->summary, report->description, report->version, report->player, report->account_id, report->spawn_name, report->spawn_id, report->zone_id); } break; } - case ServerOP_EncapPacket: { - if (this->GetType() != Login) + if (this->GetType() != Login) { + // cout << "Error: ServerOP_EncapPacket from a non-login connection? WTF!" << endl; break; + } ServerEncapPacket_Struct* seps = (ServerEncapPacket_Struct*) pack->pBuffer; if (seps->ToID == 0xFFFFFFFF) { // Broadcast ServerPacket* inpack = new ServerPacket(seps->opcode); inpack->size = seps->size; + // Little trick here to save a memcpy, be careful if you change this any inpack->pBuffer = seps->data; world_list.SendPacketLocal(inpack, this); inpack->pBuffer = 0; delete inpack; - } else { + } + else { LWorld* world = world_list.FindByID(seps->ToID); if (world) { ServerPacket* inpack = new ServerPacket(seps->opcode); inpack->size = seps->size; + // Little trick here to save a memcpy, be careful if you change this any inpack->pBuffer = seps->data; world->SendPacket(inpack); inpack->pBuffer = 0; @@ -832,26 +643,24 @@ bool LWorld::Process() if (net.GetLoginMode() != Mesh) world_list.SendPacketLogin(pack, this); break; - } - - default: { - cout << "Unknown LoginSOPcode: 0x" << hex << (int)pack->opcode << dec; - cout << " size:" << pack->size << " from " << GetAccount() << endl; - DumpPacket(pack->pBuffer, pack->size); - break; - } + } + default: + { + cout << "Unknown LoginSOPcode: 0x" << hex << (int)pack->opcode << dec; + cout << " size:" << pack->size << " from " << GetAccount() << endl; + DumpPacket(pack->pBuffer, pack->size); + //Kick("Possible Hacking Attempt"); + break; + } } delete pack; } return ret; } -// Send packet to world server, encapsulating if remote connection -void LWorld::SendPacket(ServerPacket* pack) -{ +void LWorld::SendPacket(ServerPacket* pack) { if (Link == 0) return; - if (RemoteID) { ServerPacket* outpack = new ServerPacket(ServerOP_EncapPacket, sizeof(ServerEncapPacket_Struct) + pack->size); ServerEncapPacket_Struct* seps = (ServerEncapPacket_Struct*) outpack->pBuffer; @@ -861,14 +670,13 @@ void LWorld::SendPacket(ServerPacket* pack) memcpy(seps->data, pack->pBuffer, pack->size); Link->SendPacket(outpack); delete outpack; - } else { + } + else { Link->SendPacket(pack); } } -// Send formatted message to player on world server -void LWorld::Message(const char* to, const char* message, ...) -{ +void LWorld::Message(const char* to, const char* message, ...) { va_list argptr; char buffer[256]; @@ -884,134 +692,112 @@ void LWorld::Message(const char* to, const char* message, ...) delete pack; } -// Disconnect world server with error message -void LWorld::Kick(const char* message, bool iSetKickedFlag) -{ +void LWorld::Kick(const char* message, bool iSetKickedFlag) { if (iSetKickedFlag) kicked = true; - if (message) { ServerPacket* pack = new ServerPacket(ServerOP_LSFatalError, strlen(message) + 1); strcpy((char*) pack->pBuffer, message); SendPacket(pack); delete pack; } - if (Link && GetRemoteID() == 0) Link->Disconnect(); } - -// Validate server name format -bool LWorld::CheckServerName(const char* name) -{ +bool LWorld::CheckServerName(const char* name) { if (strlen(name) < 10) return false; - - for (size_t i = 0; i < strlen(name); i++) { - if (!((name[i] >= 'a' && name[i] <= 'z') || (name[i] >= 'A' && name[i] <= 'Z') || - (name[i] >= '0' && name[i] <= '9') || name[i] == ' ' || name[i] == '\'' || - name[i] == '-' || name[i] == '(' || name[i] == ')' || name[i] == '[' || - name[i] == ']' || name[i] == '/' || name[i] == '.' || name[i] == ',' || - name[i] == '_' || name[i] == '+' || name[i] == '=' || name[i] == ':' || name[i] == '~')) + for (size_t i=0; i= 'a' && name[i] <= 'z') || (name[i] >= 'A' && name[i] <= 'Z') || (name[i] >= '0' && name[i] <= '9') || name[i] == ' ' || name[i] == '\'' || name[i] == '-' || name[i] == '(' || name[i] == ')' || name[i] == '[' || name[i] == ']' || name[i] == '/' || name[i] == '.' || name[i] == ',' || name[i] == '_' || name[i] == '+' || name[i] == '=' || name[i] == ':' || name[i] == '~')) return false; } return true; } - -// Authenticate and setup world server connection -bool LWorld::SetupWorld(char* in_worldname, char* in_worldaddress, char* in_account, char* in_password, char* in_version) -{ +bool LWorld::SetupWorld(char* in_worldname, char* in_worldaddress, char* in_account, char* in_password, char* in_version) { if (strlen(in_worldaddress) > 3) { isaddressip = false; strcpy(address, in_worldaddress); } - if (strlen(in_worldname) > 3) { char tmpAccount[30]; memcpy(tmpAccount, in_account, 29); tmpAccount[29] = '\0'; int32 id = database.CheckServerAccount(tmpAccount, in_password); - if (id == 0 || database.IsServerAccountDisabled(tmpAccount) || - database.IsIPBanned(IPAddr) || (isaddressip && database.IsIPBanned(address))) - return false; + if(id == 0) + return false; + if(database.IsServerAccountDisabled(tmpAccount) || database.IsIPBanned(IPAddr) || (isaddressip && database.IsIPBanned(address))) + return false; + LWorld* world = world_list.FindByID(id); - if (world) + if(world) world->Kick("Ghost Kick!"); ID = id; accountid = id; - strncpy(account, tmpAccount, 30); - + strncpy(account,tmpAccount,30); char* name = database.GetServerAccountName(id); - if (name) { + if(name) snprintf(worldname, (sizeof(worldname)) - 1, "%s", name); - } else { + else{ //failed to get account account[0] = 0; IsInit = false; - this->Kick("Could not load server information."); + this->Kick ( "Could not load server information." ); return false; } - + //world_list.KickGhostIP(GetIP(), this); IsInit = true; ptype = World; world_list.SendWorldChanged(id, true); - } else { + } + else { + // name too short account[0] = 0; IsInit = false; return false; } - + database.UpdateWorldVersion(GetAccountID(), in_version); - pStatsTimer = new Timer(60000); - pStatsTimer->Start(60000); + pStatsTimer = new Timer ( 60000 ); + pStatsTimer->Start ( 60000 ); + return true; } - -// Send world status change notification -void LWorldList::SendWorldChanged(int32 server_id, bool sendtoallclients, Client* sendto) -{ +void LWorldList::SendWorldChanged(int32 server_id, bool sendtoallclients, Client* sendto){ EQ2Packet* outapp = new EQ2Packet(OP_WorldStatusChangeMsg, 0, sizeof(LS_WorldStatusChanged)); LS_WorldStatusChanged* world_changed = (LS_WorldStatusChanged*)outapp->pBuffer; world_changed->server_id = server_id; LWorld* world = world_list.FindByID(server_id); - if (!world || world->ShowDown()) + if(!world || world->ShowDown()) world_changed->up = 0; else world_changed->up = 1; - - if (sendtoallclients || sendto == 0) + if(sendtoallclients || sendto == 0) client_list.SendPacketToAllClients(outapp); else sendto->QueuePacket(outapp); - world_list.SetUpdateServerList(true); } - -// Trigger world list update -void LWorld::UpdateWorldList(LWorld* to) -{ - world_list.SetUpdateServerList(true); +void LWorld::UpdateWorldList(LWorld* to) { + world_list.SetUpdateServerList( true ); } -// Convert active world to offline placeholder -void LWorld::ChangeToPlaceholder() -{ +void LWorld::ChangeToPlaceholder() { ip = 0; status = -1; pPlaceholder = true; - if (Link != 0 && RemoteID == 0) + if (Link != 0 && RemoteID == 0) { Link->Disconnect(); + } UpdateWorldList(); } -// Set remote world server information -void LWorld::SetRemoteInfo(int32 in_ip, int32 in_accountid, char* in_account, char* in_name, char* in_address, int32 in_status, int32 in_adminid, sint32 in_players, sint32 in_zones) -{ +void LWorld::SetRemoteInfo(int32 in_ip, int32 in_accountid, char* in_account, char* in_name, char* in_address, int32 in_status, int32 in_adminid, sint32 in_players, sint32 in_zones) { ip = in_ip; accountid = in_accountid; +// strcpy(account, in_account); strcpy(worldname, in_name); strcpy(address, in_address); status = in_status; @@ -1020,111 +806,85 @@ void LWorld::SetRemoteInfo(int32 in_ip, int32 in_accountid, char* in_account, ch num_zones = in_zones; } -// Get display status for world (0=up, 1=dev, 2=locked) -int8 LWorld::GetWorldStatus() -{ - if (IsDevelServer() && IsLocked() == false) - return 1; - else if (IsInit && IsLocked() == false) - return 0; - else - return 2; -} -// Send character deletion notification to world server -void LWorld::SendDeleteCharacter(int32 char_id, int32 account_id) -{ - ServerPacket* outpack = new ServerPacket(ServerOP_BasicCharUpdate, sizeof(CharDataUpdate_Struct)); - CharDataUpdate_Struct* cdu = (CharDataUpdate_Struct*)outpack->pBuffer; - cdu->account_id = account_id; - cdu->char_id = char_id; - cdu->update_field = DELETE_UPDATE_FLAG; - cdu->update_data = 1; - SendPacket(outpack); -} - -// LWorldList constructor - initialize server and threading -LWorldList::LWorldList() -{ +LWorldList::LWorldList() { server_update_thread = true; NextID = 1; tcplistener = new TCPServer(net.GetPort(), true); - if (net.GetLoginMode() == Slave) OutLink = new TCPConnection(true); else OutLink = 0; UpdateServerList = true; - - // Start server update thread - std::thread update_thread(ServerUpdateLoop, this); - update_thread.detach(); + #ifdef WIN32 + _beginthread(ServerUpdateLoop, 0, this); + #else + pthread_t thread; + pthread_create(&thread, NULL, &ServerUpdateLoop, this); + #endif } -// LWorldList destructor -LWorldList::~LWorldList() -{ +LWorldList::~LWorldList() { server_update_thread = false; - while (!server_update_thread) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + while(!server_update_thread){ + Sleep(100); } safe_delete(tcplistener); safe_delete(OutLink); } -// Shutdown all world connections -void LWorldList::Shutdown() -{ +void LWorldList::Shutdown() { LinkedListIterator iterator(list); + iterator.Reset(); - while (iterator.MoreElements()) - iterator.RemoveCurrent(); + while(iterator.MoreElements()) + { + iterator.RemoveCurrent ( ); + } + safe_delete(tcplistener); } -// Add world server to active list -void LWorldList::Add(LWorld* worldserver) -{ - LWorld* worldExist = FindByID(worldserver->GetID()); - if (worldExist) { +void LWorldList::Add(LWorld* worldserver) { + LWorld* worldExist = FindByID(worldserver->GetID ( ) ); + if( worldExist ) + { worldExist->Kick(); MWorldMap.writelock(); worldmap.erase(worldExist->GetID()); MWorldMap.releasewritelock(); safe_delete(worldExist); } - MWorldMap.writelock(); worldmap[worldserver->GetID()] = worldserver; MWorldMap.releasewritelock(); - database.ResetWorldServerStatsConnectedTime(worldserver); database.UpdateWorldIPAddress(worldserver->GetID(), worldserver->GetIP()); } -// Add world server to initialization list -void LWorldList::AddInitiateWorld(LWorld* world) +void LWorldList::AddInitiateWorld ( LWorld* world ) { - list.Insert(world); + list.Insert ( world ); } -// Remove ghost connections by IP address -void LWorldList::KickGhostIP(int32 ip, LWorld* NotMe, int16 iClientPort) -{ +void LWorldList::KickGhostIP(int32 ip, LWorld* NotMe, int16 iClientPort) { if (ip == 0) return; - map::iterator map_list; + map::iterator map_list; MWorldMap.readlock(); - for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { + for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++ ) { LWorld* world = map_list->second; if (!world->IsKicked() && world->GetIP() == ip && world != NotMe) { - if ((iClientPort == 0 && world->GetType() == World) || - (iClientPort != 0 && world->GetClientPort() == iClientPort)) { - struct in_addr in; + if ((iClientPort == 0 && world->GetType() == World) || (iClientPort != 0 && world->GetClientPort() == iClientPort)) { + struct in_addr in; in.s_addr = world->GetIP(); - if (!world->Connected()) { + // cout << "Removing GhostIP LWorld(" << world->GetID() << ") from ip: " << inet_ntoa(in) << " port: " << (int16)(world->GetPort()); + if (!world->Connected()) + { + // cout << " (it wasnt connected)"; + // cout << endl; if (NotMe) { in.s_addr = NotMe->GetIP(); cout << "NotMe(" << NotMe->GetID() << ") = " << inet_ntoa(in) << ":" << NotMe->GetPort() << " (" << NotMe->GetClientPort() << ")" << endl; @@ -1137,17 +897,21 @@ void LWorldList::KickGhostIP(int32 ip, LWorld* NotMe, int16 iClientPort) MWorldMap.releasereadlock(); } -// Remove ghost connections by type and account -void LWorldList::KickGhost(ConType in_type, int32 in_accountid, LWorld* ButNotMe) -{ - map::iterator map_list; +void LWorldList::KickGhost(ConType in_type, int32 in_accountid, LWorld* ButNotMe) { + map::iterator map_list; MWorldMap.readlock(); - for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { + for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++ ) { LWorld* world = map_list->second; - if (!world->IsKicked() && world->GetType() == in_type && world != ButNotMe && - (in_accountid == 0 || world->GetAccountID() == in_accountid)) { - if (world->GetType() == Login && world->IsOutgoingUplink()) + if (!world->IsKicked() && world->GetType() == in_type && world != ButNotMe && (in_accountid == 0 || world->GetAccountID() == in_accountid)) { + if (world->GetIP() != 0) { + //struct in_addr in; + //in.s_addr = world->GetIP(); + // cout << "Removing GhostAcc LWorld(" << world->GetID() << ") from ip: " << inet_ntoa(in) << " port: " << (int16)(world->GetPort()) << endl; + } + if (world->GetType() == Login && world->IsOutgoingUplink()) { world->Kick("Ghost Acc Kick", false); + // cout << "softkick" << endl; + } else world->Kick("Ghost Acc Kick"); } @@ -1155,84 +919,85 @@ void LWorldList::KickGhost(ConType in_type, int32 in_accountid, LWorld* ButNotMe MWorldMap.releasereadlock(); } -// Update statistics for all world servers -void LWorldList::UpdateWorldStats() -{ - map::iterator map_list; +void LWorldList::UpdateWorldStats(){ + map::iterator map_list; MWorldMap.readlock(); - for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { + for(map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { LWorld* world = map_list->second; - if (world && world->GetAccountID() > 0) + if(world && world->GetAccountID() > 0) database.UpdateWorldServerStats(world, world->GetStatus()); } MWorldMap.releasereadlock(); } -// Main processing loop for world list -void LWorldList::Process() -{ +void LWorldList::Process() { TCPConnection* newtcp = 0; LWorld* newworld = 0; - // Process initialization list LinkedListIterator iterator(list); + iterator.Reset(); - while (iterator.MoreElements()) { - if (iterator.GetData()->GetID() > 0) { - LWorld* world = iterator.GetData(); - iterator.RemoveCurrent(false); - Add(world); - } else { - if (!iterator.GetData()->Process()) - iterator.RemoveCurrent(); + while(iterator.MoreElements()) + { + if(iterator.GetData( )->GetID ( ) > 0 ) + { + LWorld* world = iterator.GetData ( ); + iterator.RemoveCurrent ( false ); + Add( world ); + } + else + { + if(! iterator.GetData ( )->Process ( ) ) + iterator.RemoveCurrent ( ); else iterator.Advance(); } } - // Handle new connections while ((newtcp = tcplistener->NewQueuePop())) { newworld = new LWorld(newtcp); newworld->SetID(0); AddInitiateWorld(newworld); - - struct in_addr in; + struct in_addr in; in.s_addr = newtcp->GetrIP(); LogWrite(LOGIN__INFO, 0, "Login", "New Server connection: %s port %i", inet_ntoa(in), ntohs(newtcp->GetrPort())); net.numservers++; net.UpdateWindowTitle(); world_list.UpdateWorldList(); } - - // Process active world servers - map::iterator map_list; - for (map_list = worldmap.begin(); map_list != worldmap.end(); ) { + map::iterator map_list; + for( map_list = worldmap.begin(); map_list != worldmap.end(); ) { LWorld* world = map_list->second; + int32 account_id = world->GetAccountID(); if (world->IsKicked() && !world->IsNeverKick()) { map_list++; - worldmap.erase(account_id); + worldmap.erase ( account_id ); net.numservers--; net.UpdateWindowTitle(); - safe_delete(world); + safe_delete ( world ); continue; - } else if (!world->Process()) { + } + else if (!world->Process()) { + //struct in_addr in; + //in.s_addr = world->GetIP(); if (world->GetAccountID() == 0 || !(world->ShowDown()) || world->GetType() == Chat) { map_list++; - worldmap.erase(account_id); + worldmap.erase ( account_id ); + net.numservers--; net.UpdateWindowTitle(); - - if (account_id > 0) { + if(account_id > 0){ LWorld* world2 = FindByID(account_id); - if (world2) + if(world2) world2->ShowDownActive(true); } SendWorldChanged(account_id, true); - safe_delete(world); + safe_delete ( world ); continue; - } else { + } + else { world->ChangeToPlaceholder(); } } @@ -1240,11 +1005,10 @@ void LWorldList::Process() } } -// Send packet to all world and chat servers -void LWorldList::SendPacket(ServerPacket* pack, LWorld* butnotme) -{ - map::iterator map_list; - for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { +// Sends packet to all World and Chat servers, local and remote (but not to remote login server's ::Process()) +void LWorldList::SendPacket(ServerPacket* pack, LWorld* butnotme) { + map::iterator map_list; + for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { LWorld* world = map_list->second; if (world != butnotme) { if (world->GetType() == Login) { @@ -1256,64 +1020,59 @@ void LWorldList::SendPacket(ServerPacket* pack, LWorld* butnotme) memcpy(seps->data, pack->pBuffer, pack->size); world->SendPacket(outpack); delete outpack; - } else if (world->GetRemoteID() == 0) { + } + else if (world->GetRemoteID() == 0) { world->SendPacket(pack); } } } } -// Send packet to all local TCP connections -void LWorldList::SendPacketLocal(ServerPacket* pack, LWorld* butnotme) -{ - map::iterator map_list; - for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { +// Sends a packet to every local TCP Connection, all types +void LWorldList::SendPacketLocal(ServerPacket* pack, LWorld* butnotme) { + map::iterator map_list; + for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { LWorld* world = map_list->second; - if (world != butnotme && world->GetRemoteID() == 0) + if (world != butnotme && world->GetRemoteID() == 0) { world->SendPacket(pack); + } } } -// Send packet to all login servers -void LWorldList::SendPacketLogin(ServerPacket* pack, LWorld* butnotme) -{ - map::iterator map_list; - for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { +// Sends the packet to all login servers +void LWorldList::SendPacketLogin(ServerPacket* pack, LWorld* butnotme) { + map::iterator map_list; + for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++ ) { LWorld* world = map_list->second; - if (world != butnotme && world->GetType() == Login) + if (world != butnotme && world->GetType() == Login) { world->SendPacket(pack); + } } } -// Trigger world list update for all servers -void LWorldList::UpdateWorldList(LWorld* to) -{ - map::iterator map_list; - for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { +void LWorldList::UpdateWorldList(LWorld* to) { + map::iterator map_list; + for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { LWorld* world = map_list->second; if (net.GetLoginMode() != Mesh || world->GetRemoteID() == 0) world->UpdateWorldList(to); } } -// Find world server by ID -LWorld* LWorldList::FindByID(int32 LWorldID) -{ - if (worldmap.count(LWorldID) > 0) +LWorld* LWorldList::FindByID(int32 LWorldID) { + if(worldmap.count(LWorldID) > 0) return worldmap[LWorldID]; return 0; } -// Find world server by IP address -LWorld* LWorldList::FindByIP(int32 ip) -{ - map::iterator map_list; +LWorld* LWorldList::FindByIP(int32 ip) { + map::iterator map_list; LWorld* world = 0; LWorld* ret = 0; MWorldMap.readlock(); - for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { + for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { world = map_list->second; - if (world->GetIP() == ip) { + if (world->GetIP() == ip){ ret = world; break; } @@ -1322,16 +1081,14 @@ LWorld* LWorldList::FindByIP(int32 ip) return ret; } -// Find world server by address string -LWorld* LWorldList::FindByAddress(char* address) -{ - map::iterator map_list; +LWorld* LWorldList::FindByAddress(char* address) { + map::iterator map_list; LWorld* world = 0; LWorld* ret = 0; MWorldMap.readlock(); - for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { + for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { world = map_list->second; - if (strcasecmp(world->GetAddress(), address) == 0) { + if (strcasecmp(world->GetAddress(), address) == 0){ ret = world; break; } @@ -1340,19 +1097,16 @@ LWorld* LWorldList::FindByAddress(char* address) return ret; } -// Find world server by TCP connection and ID -LWorld* LWorldList::FindByLink(TCPConnection* in_link, int32 in_id) -{ +LWorld* LWorldList::FindByLink(TCPConnection* in_link, int32 in_id) { if (in_link == 0) return 0; - LWorld* world = 0; LWorld* ret = 0; - map::iterator map_list; + map::iterator map_list; MWorldMap.readlock(); - for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { + for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { world = map_list->second; - if (world->GetLink() == in_link && world->GetRemoteID() == in_id) { + if (world->GetLink() == in_link && world->GetRemoteID() == in_id){ ret = world; break; } @@ -1361,19 +1115,16 @@ LWorld* LWorldList::FindByLink(TCPConnection* in_link, int32 in_id) return ret; } -// Find world server by account ID and type -LWorld* LWorldList::FindByAccount(int32 in_accountid, ConType in_type) -{ +LWorld* LWorldList::FindByAccount(int32 in_accountid, ConType in_type) { if (in_accountid == 0) return 0; - LWorld* world = 0; LWorld* ret = 0; - map::iterator map_list; + map::iterator map_list; MWorldMap.readlock(); - for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { + for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { world = map_list->second; - if (world->GetAccountID() == in_accountid && world->GetType() == in_type) { + if (world->GetAccountID() == in_accountid && world->GetType() == in_type){ ret = world; break; } @@ -1382,22 +1133,39 @@ LWorld* LWorldList::FindByAccount(int32 in_accountid, ConType in_type) return ret; } -// Get server list update packets for specific client version -vector* LWorldList::GetServerListUpdate(int16 version) +int8 LWorld::GetWorldStatus(){ + if(IsDevelServer() && IsLocked() == false) + return 1; + else if(IsInit && IsLocked() == false) + return 0; + else + return 2; +} + +void LWorld::SendDeleteCharacter ( int32 char_id , int32 account_id ) { + ServerPacket* outpack = new ServerPacket(ServerOP_BasicCharUpdate, sizeof(CharDataUpdate_Struct)); + CharDataUpdate_Struct* cdu = (CharDataUpdate_Struct*)outpack->pBuffer; + cdu->account_id = account_id; + cdu->char_id = char_id; + cdu->update_field = DELETE_UPDATE_FLAG; + cdu->update_data = 1; + SendPacket(outpack); +} + +vector* LWorldList::GetServerListUpdate(int16 version){ vector* ret = new vector; - map::iterator map_list; + map::iterator map_list; PacketStruct* packet = 0; - MWorldMap.readlock(); - for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { + for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { LWorld* world = map_list->second; if ((world->IsInit || (world->ShowDown() && world->ShowDownActive())) && world->GetType() == World) { packet = configReader.getStruct("LS_WorldUpdate", version); - if (packet) { + if(packet){ packet->setDataByName("server_id", world->GetID()); packet->setDataByName("up", 1); - if (world->IsLocked()) + if(world->IsLocked()) packet->setDataByName("locked", 1); ret->push_back(packet); } @@ -1407,27 +1175,39 @@ vector* LWorldList::GetServerListUpdate(int16 version) return ret; } -// Create server list packet for client -EQ2Packet* LWorldList::MakeServerListPacket(int8 lsadmin, int16 version) -{ +EQ2Packet* LWorldList::MakeServerListPacket(int8 lsadmin, int16 version) { + + // if the latest world list has already been loaded, just return the string MWorldMap.readlock(); - if (!UpdateServerList && ServerListData.count(version)) { + if (!UpdateServerList && ServerListData.count(version)) + { MWorldMap.releasereadlock(); return ServerListData[version]; } + //LWorld* world = 0; + int32 ServerNum = 0; + /* while(iterator.MoreElements()){ + world = iterator.GetData(); + if ((world->IsInit || (world->ShowDown() && world->ShowDownActive())) && world->GetType() == World) + ServerNum++; + iterator.Advance(); + } + ServerNum+=3; + */ uint32 tmpCount = 0; map::iterator map_list; for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { LWorld* world = map_list->second; - if ((world->IsInit || (world->ShowDown() && world->ShowDownActive())) && world->GetType() == World) + if ((world->IsInit || (world->ShowDown() && world->ShowDownActive())) && world->GetType() == World) { tmpCount++; + } } PacketStruct* packet = configReader.getStruct("LS_WorldList", version); packet->setArrayLengthByName("num_worlds", tmpCount); - int32 ServerNum = 0; + string world_data; for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { LWorld* world = map_list->second; if ((world->IsInit || (world->ShowDown() && world->ShowDownActive())) && world->GetType() == World) { @@ -1443,27 +1223,31 @@ EQ2Packet* LWorldList::MakeServerListPacket(int8 lsadmin, int16 version) packet->setArrayDataByName("unknown2", 1, ServerNum - 1); packet->setArrayDataByName("unknown3", 1, ServerNum - 1); packet->setArrayDataByName("load", world->GetWorldStatus(), ServerNum - 1); - } else { + } + else + { if (version < 1212) packet->setArrayDataByName("allowed_races", 0xFFFFFFFF, ServerNum - 1); else if (version < 60006) packet->setArrayDataByName("allowed_races", 0x000FFFFF, ServerNum - 1); // + Freeblood else - packet->setArrayDataByName("allowed_races", 0x001FFFFF, ServerNum - 1); // + Aerakyn + packet->setArrayDataByName("allowed_races", 0x001FFFFF, ServerNum - 1); // + Aerakyn packet->setArrayDataByName("number_online_flag", 1, ServerNum - 1); packet->setArrayDataByName("num_players", world->GetPlayerNum(), ServerNum - 1); packet->setArrayDataByName("name", world->GetName(), ServerNum - 1); packet->setArrayDataByName("name2", world->GetName(), ServerNum - 1); packet->setArrayDataByName("feature_set", 0, ServerNum - 1); + packet->setArrayDataByName("load", world->GetWorldStatus(), ServerNum - 1); - if (world->IsLocked()) packet->setArrayDataByName("locked", 1, ServerNum - 1); + if (world->ShowDown()) packet->setArrayDataByName("tag", 0, ServerNum - 1); else packet->setArrayDataByName("tag", 1, ServerNum - 1); + if (version < 1212) packet->setArrayDataByName("unknown", ServerNum, ServerNum - 1); } @@ -1471,8 +1255,13 @@ EQ2Packet* LWorldList::MakeServerListPacket(int8 lsadmin, int16 version) } EQ2Packet* pack = packet->serialize(); - - if (ServerListData.count(version)) { + #ifdef DEBUG + //Only dump these for people trying to debug this... + printf("WorldList:\n"); + DumpPacket(pack->pBuffer, pack->size); + #endif + if (ServerListData.count(version)) + { map::iterator it = ServerListData.find(version); EQ2Packet* tmpPack = ServerListData[version]; safe_delete(tmpPack); @@ -1482,154 +1271,144 @@ EQ2Packet* LWorldList::MakeServerListPacket(int8 lsadmin, int16 version) MWorldMap.releasereadlock(); SetUpdateServerList(false); + return ServerListData[version]; } -// Send world status information to admin -void LWorldList::SendWorldStatus(LWorld* chat, char* adminname) -{ - struct in_addr in; +void LWorldList::SendWorldStatus(LWorld* chat, char* adminname) { + struct in_addr in; + int32 count = 0; - - map::iterator map_list; - for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { + map::iterator map_list; + for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { LWorld* world = map_list->second; if (world->GetIP() != 0 && world->GetType() == World) { chat->Message(adminname, "Name: %s", world->GetName()); in.s_addr = world->GetIP(); - - if (world->GetAccountID() != 0) + if (world->GetAccountID() != 0) { chat->Message(adminname, " Account: %s", world->GetAccount()); + } chat->Message(adminname, " Number of Zones: %i", world->GetZoneNum()); chat->Message(adminname, " Number of Players: %i", world->GetPlayerNum()); chat->Message(adminname, " IP: %s", inet_ntoa(in)); - - if (!world->IsAddressIP()) + if (!world->IsAddressIP()) { chat->Message(adminname, " Address: %s", world->GetAddress()); + } count++; } } chat->Message(adminname, "%i worlds listed.", count); } -// Remove world servers by TCP connection -void LWorldList::RemoveByLink(TCPConnection* in_link, int32 in_id, LWorld* ButNotMe) -{ +void LWorldList::RemoveByLink(TCPConnection* in_link, int32 in_id, LWorld* ButNotMe) { if (in_link == 0) return; - - map::iterator map_list; - for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { + map::iterator map_list; + for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { LWorld* world = map_list->second; - if (world != ButNotMe && world->GetLink() == in_link && - (in_id == 0 || world->GetRemoteID() == in_id)) { + if (world != ButNotMe && world->GetLink() == in_link && (in_id == 0 || world->GetRemoteID() == in_id)) { + // world->Link = 0; map_list++; - worldmap.erase(world->GetID()); - safe_delete(world); + worldmap.erase ( world->GetID ( ) ); + safe_delete ( world ); continue; } } } -// Remove world server by ID -void LWorldList::RemoveByID(int32 in_id) -{ +void LWorldList::RemoveByID(int32 in_id) { if (in_id == 0) return; LWorld* existWorld = FindByID(in_id); - if (existWorld != NULL) { + if ( existWorld != NULL ) + { MWorldMap.writelock(); - worldmap.erase(in_id); + worldmap.erase ( in_id ); MWorldMap.releasewritelock(); - safe_delete(existWorld); + safe_delete ( existWorld ); } } -// Initialize TCP listener -bool LWorldList::Init() -{ - database.ResetWorldStats(); - if (!tcplistener->IsOpen()) +bool LWorldList::Init() { + + database.ResetWorldStats ( ); + + if (!tcplistener->IsOpen()) { return tcplistener->Open(net.GetPort()); + } + return false; } -// Initialize placeholder worlds from database -void LWorldList::InitWorlds() -{ +void LWorldList::InitWorlds(){ vector server_list; database.GetServerAccounts(&server_list); - vector::iterator iter; int i = 0; - for (iter = server_list.begin(); iter != server_list.end(); iter++, i++) { + for(iter = server_list.begin(); iter != server_list.end(); iter++, i++){ LWorld* world = FindByID(server_list[i]->GetAccountID()); - if (!world) { + if(!world){ server_list[i]->ShowDown(true); server_list[i]->ShowDownActive(true); - server_list[i]->SetID(server_list[i]->GetAccountID()); - Add(server_list[i]); + server_list[i]->SetID ( server_list[i]->GetAccountID ( ) ); + Add ( server_list[i] ); } } } -// Get count of servers by type -int32 LWorldList::GetCount(ConType type) -{ +int32 LWorldList::GetCount(ConType type) { int32 count = 0; - map::iterator map_list; - for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { + map::iterator map_list; + for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { LWorld* world = map_list->second; - if (world->GetType() == type) + if (world->GetType() == type) { count++; + } } return count; } -// List all worlds to console -void LWorldList::ListWorldsToConsole() -{ +void LWorldList::ListWorldsToConsole() { struct in_addr in; + cout << "World List:" << endl; - cout << "============================" << endl; - - map::iterator map_list; - for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { + cout << "============================" << endl; + map::iterator map_list; + for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { LWorld* world = map_list->second; in.s_addr = world->GetIP(); - if (world->GetType() == World) { if (world->GetRemoteID() == 0) cout << "ID: " << world->GetID() << ", Name: " << world->GetName() << ", Local, IP: " << inet_ntoa(in) << ":" << world->GetPort() << ", Status: " << world->GetStatus() << ", AccID: " << world->GetAccountID() << endl; else cout << "ID: " << world->GetID() << ", Name: " << world->GetName() << ", RemoteID: " << world->GetRemoteID() << ", LinkWorldID: " << world->GetLinkWorldID() << ", IP: " << inet_ntoa(in) << ":" << world->GetPort() << ", Status: " << world->GetStatus() << ", AccID: " << world->GetAccountID() << endl; - } else if (world->GetType() == Chat) { + } + else if (world->GetType() == Chat) { cout << "ID: " << world->GetID() << ", Chat Server, IP: " << inet_ntoa(in) << ":" << world->GetPort() << ", AccID: " << world->GetAccountID() << endl; - } else if (world->GetType() == Login) { + } + else if (world->GetType() == Login) { if (world->IsOutgoingUplink()) { if (world->Connected()) cout << "ID: " << world->GetID() << ", Login Server (out), IP: " << inet_ntoa(in) << ":" << world->GetPort() << ", AccID: " << world->GetAccountID() << endl; else cout << "ID: " << world->GetID() << ", Login Server (nc), IP: " << inet_ntoa(in) << ":" << world->GetPort() << ", AccID: " << world->GetAccountID() << endl; - } else { - cout << "ID: " << world->GetID() << ", Login Server (in), IP: " << inet_ntoa(in) << ":" << world->GetPort() << " (" << world->GetClientPort() << "), AccID: " << world->GetAccountID() << endl; } - } else { + else + cout << "ID: " << world->GetID() << ", Login Server (in), IP: " << inet_ntoa(in) << ":" << world->GetPort() << " (" << world->GetClientPort() << "), AccID: " << world->GetAccountID() << endl; + } + else { cout << "ID: " << world->GetID() << ", Unknown Type, Name: " << world->GetName() << ", IP: " << inet_ntoa(in) << ":" << world->GetPort() << ", AccID: " << world->GetAccountID() << endl; } } cout << "============================" << endl; } -// Add zone updates from world server -void LWorldList::AddServerZoneUpdates(LWorld* world, map updates) -{ +void LWorldList::AddServerZoneUpdates(LWorld* world, map updates){ int32 server_id = world->GetID(); map::iterator itr; - - for (itr = updates.begin(); itr != updates.end(); itr++) { - if (zone_updates_already_used.size() >= 1500 || zone_updates_already_used[server_id].count(itr->first) > 0) { + for(itr = updates.begin(); itr != updates.end(); itr++){ + if(zone_updates_already_used.size() >= 1500 || zone_updates_already_used[server_id].count(itr->first) > 0){ world->Kick("Hacking attempt."); return; } @@ -1637,25 +1416,28 @@ void LWorldList::AddServerZoneUpdates(LWorld* world, map } server_zone_updates.Put(server_id, updates); } +//devn00b temp -// Add equipment updates from world server -void LWorldList::AddServerEquipmentUpdates(LWorld* world, map updates) -{ +void LWorldList::AddServerEquipmentUpdates(LWorld* world, map updates){ int32 server_id = world->GetID(); map::iterator itr; - - for (itr = updates.begin(); itr != updates.end(); itr++) { + for(itr = updates.begin(); itr != updates.end(); itr++){ LogWrite(MISC__TODO, 1, "TODO", "JA: Until we learn what this does, can't risk worlds being kicked performing login appearance updates...\n%s, func: %s, line: %i", __FILE__, __FUNCTION__, __LINE__); + + /*if(equip_updates_already_used.size() >= 1500 || equip_updates_already_used[server_id].count(itr->first) > 0) + { + LogWrite(LOGIN__ERROR, 0, "Login", "World ID '%i': Hacking attempt. (function: %s, line: %i", world->GetAccountID(), __FUNCTION__, __LINE__); + world->Kick("Hacking attempt."); + return; + }*/ equip_updates_already_used[server_id][itr->first] = true; } server_equip_updates.Put(server_id, updates); } -// Request zone updates from world server -void LWorldList::RequestServerUpdates(LWorld* world) -{ - if (world) { - ServerPacket* pack = new ServerPacket(ServerOP_ZoneUpdates, sizeof(ZoneUpdateRequest_Struct)); +void LWorldList::RequestServerUpdates(LWorld* world){ + if(world){ + ServerPacket* pack = new ServerPacket(ServerOP_ZoneUpdates, sizeof(ZoneUpdateRequest_Struct)); ZoneUpdateRequest_Struct* request = (ZoneUpdateRequest_Struct*)pack->pBuffer; request->max_per_batch = MAX_UPDATE_COUNT; world->SendPacket(pack); @@ -1664,91 +1446,93 @@ void LWorldList::RequestServerUpdates(LWorld* world) } } -// Process server zone and equipment updates -void LWorldList::ProcessServerUpdates() -{ - // Process zone updates - MutexMap>::iterator itr = server_zone_updates.begin(); - while (itr.Next()) { - if (itr->second.size() > 0) { - database.SetServerZoneDescriptions(itr->first, itr->second); - if (itr->second.size() == MAX_UPDATE_COUNT) - awaiting_zone_update.Put(itr->first, Timer::GetCurrentTime2() + 10000); +void LWorldList::ProcessServerUpdates(){ + MutexMap >::iterator itr = server_zone_updates.begin(); + while(itr.Next()){ + if(itr->second.size() > 0){ + database.SetServerZoneDescriptions(itr->first, itr->second); + if(itr->second.size() == MAX_UPDATE_COUNT) + awaiting_zone_update.Put(itr->first, Timer::GetCurrentTime2() + 10000); //only process 20 updates in a 10 second period to avoid network problems server_zone_updates.erase(itr->first); } - if (zone_update_timeouts.count(itr->first) == 0 || zone_update_timeouts.Get(itr->first) <= Timer::GetCurrentTime2()) { + if(zone_update_timeouts.count(itr->first) == 0 || zone_update_timeouts.Get(itr->first) <= Timer::GetCurrentTime2()){ zone_update_timeouts.erase(itr->first); server_zone_updates.erase(itr->first); } } - LWorld* world = 0; MWorldMap.readlock(); map::iterator map_itr; - for (map_itr = worldmap.begin(); map_itr != worldmap.end(); map_itr++) { + for(map_itr = worldmap.begin(); map_itr != worldmap.end(); map_itr++){ world = map_itr->second; - if (world && world->GetID()) { - if (last_updated.count(world) == 0 || last_updated.Get(world) <= Timer::GetCurrentTime2()) { + if(world && world->GetID()){ + if(last_updated.count(world) == 0 || last_updated.Get(world) <= Timer::GetCurrentTime2()){ zone_updates_already_used[world->GetID()].clear(); RequestServerUpdates(world); last_updated.Put(world, Timer::GetCurrentTime2() + 21600000); } - if (awaiting_zone_update.count(world->GetID()) > 0 && awaiting_zone_update.Get(world->GetID()) <= Timer::GetCurrentTime2()) { + if(awaiting_zone_update.count(world->GetID()) > 0 && awaiting_zone_update.Get(world->GetID()) <= Timer::GetCurrentTime2()){ awaiting_zone_update.erase(world->GetID()); - RequestServerUpdates(world); + RequestServerUpdates(world); } } } ProcessLSEquipUpdates(); MWorldMap.releasereadlock(); -} -// Request equipment updates from world server + +} void LWorldList::RequestServerEquipUpdates(LWorld* world) { - if (world) { - ServerPacket* pack_equip = new ServerPacket(ServerOP_LoginEquipment, sizeof(EquipmentUpdateRequest_Struct)); - EquipmentUpdateRequest_Struct* request_equip = (EquipmentUpdateRequest_Struct*)pack_equip->pBuffer; - request_equip->max_per_batch = MAX_LOGIN_APPEARANCE_COUNT; + if(world) + { + ServerPacket *pack_equip = new ServerPacket(ServerOP_LoginEquipment, sizeof(EquipmentUpdateRequest_Struct)); + EquipmentUpdateRequest_Struct *request_equip = (EquipmentUpdateRequest_Struct *)pack_equip->pBuffer; + request_equip->max_per_batch = MAX_LOGIN_APPEARANCE_COUNT; // item appearance data smaller, request more at a time? LogWrite(LOGIN__DEBUG, 1, "Login", "Sending equipment update requests to world: (%s)... (Batch Size: %i)", world->GetName(), request_equip->max_per_batch); world->SendPacket(pack_equip); delete pack_equip; equip_update_timeouts.Put(world->GetID(), Timer::GetCurrentTime2() + 30000); } } - -// Process login equipment updates void LWorldList::ProcessLSEquipUpdates() { - MutexMap>::iterator itr_equip = server_equip_updates.begin(); - while (itr_equip.Next()) { - if (itr_equip->second.size() > 0) { + // process login_equipment updates + MutexMap >::iterator itr_equip = server_equip_updates.begin(); + while(itr_equip.Next()) + { + if(itr_equip->second.size() > 0) + { LogWrite(LOGIN__DEBUG, 1, "Login", "Setting Login Appearances..."); database.SetServerEquipmentAppearances(itr_equip->first, itr_equip->second); - if (itr_equip->second.size() == MAX_LOGIN_APPEARANCE_COUNT) - awaiting_equip_update.Put(itr_equip->first, Timer::GetCurrentTime2() + 10000); + if(itr_equip->second.size() == MAX_LOGIN_APPEARANCE_COUNT) + awaiting_equip_update.Put(itr_equip->first, Timer::GetCurrentTime2() + 10000); //only process 100 updates in a 10 second period to avoid network problems server_equip_updates.erase(itr_equip->first); } - if (equip_update_timeouts.count(itr_equip->first) == 0 || equip_update_timeouts.Get(itr_equip->first) <= Timer::GetCurrentTime2()) { + if(equip_update_timeouts.count(itr_equip->first) == 0 || equip_update_timeouts.Get(itr_equip->first) <= Timer::GetCurrentTime2()) + { LogWrite(LOGIN__DEBUG, 1, "Login", "Clearing Login Appearances Update Timers..."); equip_update_timeouts.erase(itr_equip->first); server_equip_updates.erase(itr_equip->first); } } - LWorld* world = 0; MWorldMap.readlock(); map::iterator map_itr; - for (map_itr = worldmap.begin(); map_itr != worldmap.end(); map_itr++) { + for(map_itr = worldmap.begin(); map_itr != worldmap.end(); map_itr++) + { world = map_itr->second; - if (world && world->GetID()) { - if (last_equip_updated.count(world) == 0 || last_equip_updated.Get(world) <= Timer::GetCurrentTime2()) { + if(world && world->GetID()) + { + if(last_equip_updated.count(world) == 0 || last_equip_updated.Get(world) <= Timer::GetCurrentTime2()) + { LogWrite(LOGIN__DEBUG, 1, "Login", "Clearing Login Appearances Update Counters..."); equip_updates_already_used[world->GetID()].clear(); RequestServerEquipUpdates(world); last_equip_updated.Put(world, Timer::GetCurrentTime2() + 900000); // every 15 mins } - if (awaiting_equip_update.count(world->GetID()) > 0 && awaiting_equip_update.Get(world->GetID()) <= Timer::GetCurrentTime2()) { + if( awaiting_equip_update.count(world->GetID()) > 0 && awaiting_equip_update.Get(world->GetID()) <= Timer::GetCurrentTime2()) + { LogWrite(LOGIN__DEBUG, 1, "Login", "Erase awaiting equip updates..."); awaiting_equip_update.erase(world->GetID()); RequestServerEquipUpdates(world); @@ -1758,19 +1542,20 @@ void LWorldList::ProcessLSEquipUpdates() MWorldMap.releasereadlock(); } -// Server update thread main loop -void* ServerUpdateLoop(void* tmp) -{ + +ThreadReturnType ServerUpdateLoop(void* tmp) { +#ifdef WIN32 + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); +#endif if (tmp == 0) { ThrowError("ServerUpdateLoop(): tmp = 0!"); - return NULL; + THREAD_RETURN(NULL); } - LWorldList* worldList = (LWorldList*) tmp; while (worldList->ContinueServerUpdates()) { - std::this_thread::sleep_for(std::chrono::seconds(1)); + Sleep(1000); worldList->ProcessServerUpdates(); } worldList->ResetServerUpdates(); - return NULL; -} \ No newline at end of file + THREAD_RETURN(NULL); +} diff --git a/old/LoginServer/LWorld.h b/old/LoginServer/LWorld.h new file mode 100644 index 0000000..d9e3361 --- /dev/null +++ b/old/LoginServer/LWorld.h @@ -0,0 +1,253 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. +*/ +#ifndef LWORLD_H +#define LWORLD_H + +#include "../common/Mutex.h" + +#define ERROR_BADPASSWORD "Bad password" +#define INVALID_ACCOUNT "Invalid Server Account." +#define ERROR_BADVERSION "Incorrect version" +#define ERROR_UNNAMED "Unnamed servers not allowed to connect to full login servers" +#define ERROR_NOTMASTER "Not a master server" +#define ERROR_NOTMESH "Not a mesh server" +#define ERROR_GHOST "Ghost kick" +#define ERROR_UnknownServerType "Unknown Server Type" +#define ERROR_BADNAME_SERVER "Bad server name, name may not contain the word \"Server\" (case sensitive)" +#define ERROR_BADNAME "Bad server name. Unknown reason." + +#define WORLD_NAME_SUFFIX " Server" + +#include +#include +#include +#include +#include +#include +#include + +namespace beast = boost::beast; // from +namespace http = beast::http; // from + +#include "../common/linked_list.h" +#include "../WorldServer/MutexList.h" +#include "../WorldServer/MutexMap.h" +#include "../common/timer.h" +#include "../common/types.h" +#include "../common/queue.h" +#include "../common/servertalk.h" +#include "../common/TCPConnection.h" +#include "client.h" + +#define MAX_UPDATE_COUNT 20 +#define MAX_LOGIN_APPEARANCE_COUNT 100 + +#ifdef WIN32 + void ServerUpdateLoop(void* tmp); +#else + void* ServerUpdateLoop(void* tmp); +#endif + +enum ConType { UnknownW, World, Chat, Login }; + +class LWorld +{ +public: + LWorld(TCPConnection* in_con, bool OutgoingLoginUplink = false, int32 iIP = 0, int16 iPort = 0, bool iNeverKick = false); + LWorld(int32 in_accountid, char* in_accountname, char* in_worldname, int32 in_admin_id); + LWorld(TCPConnection* in_RemoteLink, int32 in_ip, int32 in_RemoteID, int32 in_accountid, char* in_accountname, char* in_worldname, char* in_address, sint32 in_status, int32 in_adminid, bool in_showdown, int8 in_authlevel, bool in_placeholder, int32 iLinkWorldID); + ~LWorld(); + + static bool CheckServerName(const char* name); + + bool Process(); + void SendPacket(ServerPacket* pack); + void Message(const char* to, const char* message, ...); + + bool SetupWorld(char* in_worldname, char* in_worldaddress, char* in_account, char* in_password, char* in_version); + void UpdateStatus(sint32 in_status, sint32 in_players, sint32 in_zones, int8 in_level) { + // we don't want the server list to update unless something has changed + if(status != in_status || num_players != in_players || num_zones != in_zones || world_max_level != in_level) + { + status = in_status; + num_players = in_players; + num_zones = in_zones; + world_max_level = in_level; + UpdateWorldList(); + } + } + void UpdateWorldList(LWorld* to = 0); + void SetRemoteInfo(int32 in_ip, int32 in_accountid, char* in_account, char* in_name, char* in_address, int32 in_status, int32 in_adminid, sint32 in_players, sint32 in_zones); + + inline bool IsPlaceholder() { return pPlaceholder; } + inline int32 GetAccountID() { return accountid; } + inline char* GetAccount() { return account; } + inline char* GetAddress() { return address; } + inline int16 GetClientPort() { return pClientPort; } + inline bool IsAddressIP() { return isaddressip; } + inline char* GetName() { return worldname; } + inline sint32 GetStatus() { return status; } + bool IsLocked() { return status==-2; } + inline int32 GetIP() { return ip; } + inline int16 GetPort() { return port; } + inline int32 GetID() { return ID; } + inline int32 GetAdmin() { return admin_id; } + inline bool ShowDown() { return pshowdown; } + inline bool ShowDownActive(){ return show_down_active; } + void ShowDownActive(bool show){ show_down_active = show; } + void ShowDown(bool show){ pshowdown = show; } + inline bool Connected() { return pConnected; } + int8 GetWorldStatus(); + + void ChangeToPlaceholder(); + void Kick(const char* message = ERROR_GHOST, bool iSetKickedFlag = true); + inline bool IsKicked() { return kicked; } + inline bool IsNeverKick() { return pNeverKick; } + inline ConType GetType() { return ptype; } + inline bool IsOutgoingUplink() { return OutgoingUplink; } + inline TCPConnection* GetLink() { return Link; } + inline int32 GetRemoteID() { return RemoteID; } + inline int32 GetLinkWorldID() { return LinkWorldID; } + inline sint32 GetZoneNum() { return num_zones; } + inline sint32 GetPlayerNum() { return num_players; } + void SetID(int32 new_id) { ID = new_id; } + + void SendDeleteCharacter( int32 char_id, int32 account_id ); + bool IsDevelServer(){ return devel_server; } + + inline int8 GetMaxWorldLevel() { return world_max_level; } + + bool IsInit; +protected: + friend class LWorldList; + TCPConnection* Link; + Timer* pReconnectTimer; + Timer* pStatsTimer; +private: + int32 ID; + int32 ip; + char IPAddr[64]; + int16 port; + bool kicked; + bool pNeverKick; + bool pPlaceholder; + bool devel_server; + + int32 accountid; + char account[30]; + char address[250]; + bool isAuthenticated; + int16 pClientPort; + bool isaddressip; + char worldname[200]; + sint32 status; + int32 admin_id; + bool pshowdown; + bool show_down_active; + ConType ptype; + bool OutgoingUplink; + bool pConnected; + sint32 num_players; + sint32 num_zones; + int32 RemoteID; + int32 LinkWorldID; + int8 world_max_level; + +}; + +class LWorldList +{ +public: + + LWorldList(); + ~LWorldList(); + + LWorld* FindByID(int32 WorldID); + LWorld* FindByIP(int32 ip); + LWorld* FindByAddress(char* address); + LWorld* FindByLink(TCPConnection* in_link, int32 in_id); + LWorld* FindByAccount(int32 in_accountid, ConType in_type = World); + + void Add(LWorld* worldserver); + void AddInitiateWorld ( LWorld* world ); + void Process(); + void ReceiveData(); + void SendPacket(ServerPacket* pack, LWorld* butnotme = 0); + void SendPacketLocal(ServerPacket* pack, LWorld* butnotme = 0); + void SendPacketLogin(ServerPacket* pack, LWorld* butnotme = 0); + void SendWorldChanged(int32 server_id, bool sendtoallclients=false, Client* sendto = 0); + vector* GetServerListUpdate(int16 version); + EQ2Packet* MakeServerListPacket(int8 lsadmin, int16 version); + + void UpdateWorldList(LWorld* to = 0); + void UpdateWorldStats(); + void KickGhost(ConType in_type, int32 in_accountid = 0, LWorld* ButNotMe = 0); + void KickGhostIP(int32 ip, LWorld* NotMe = 0, int16 iClientPort = 0); + void RemoveByLink(TCPConnection* in_link, int32 in_id = 0, LWorld* ButNotMe = 0); + void RemoveByID(int32 in_id); + + void SendWorldStatus(LWorld* chat, char* adminname); + + void ConnectUplink(); + bool Init(); + void InitWorlds(); + void Shutdown(); + bool WriteXML(); + + int32 GetCount(ConType type); + void PopulateWorldList(http::response& res); + + void ListWorldsToConsole(); + //devn00b temp + void AddServerEquipmentUpdates(LWorld* world, map updates); + void ProcessLSEquipUpdates(); + void RequestServerEquipUpdates(LWorld* world); + + void SetUpdateServerList ( bool var ) { UpdateServerList = var; } + bool ContinueServerUpdates(){ return server_update_thread; } + void ResetServerUpdates(){server_update_thread = true;} + void ProcessServerUpdates(); + void RequestServerUpdates(LWorld* world); + void AddServerZoneUpdates(LWorld* world, map updates); + +protected: + friend class LWorld; + int32 GetNextID() { return NextID++; } + +private: + Mutex MWorldMap; + map > zone_updates_already_used; //used to determine if someone is trying to DOS us + MutexMap zone_update_timeouts; + MutexMap awaiting_zone_update; + MutexMap last_updated; + MutexMap > server_zone_updates; + bool server_update_thread; + int32 NextID; + + LinkedList list; + + map worldmap; + + TCPServer* tcplistener; + TCPConnection* OutLink; + + //devn00b temp + // JohnAdams: login appearances, copied from above + map > equip_updates_already_used; + MutexMap equip_update_timeouts; + MutexMap awaiting_equip_update; + MutexMap last_equip_updated; + MutexMap > server_equip_updates; + // + /// + + // holds the world server list so we don't have to create it for every character + // logging in + map ServerListData; + bool UpdateServerList; +}; +#endif diff --git a/old/LoginServer/LoginAccount.cpp b/old/LoginServer/LoginAccount.cpp new file mode 100644 index 0000000..228f0b5 --- /dev/null +++ b/old/LoginServer/LoginAccount.cpp @@ -0,0 +1,58 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. +*/ +#include "LoginAccount.h" + +LoginAccount::LoginAccount(){ +} +LoginAccount::~LoginAccount(){ + vector::iterator iter; + for(iter = charlist.begin(); iter != charlist.end(); iter++){ + safe_delete(*iter); + } +} + +void LoginAccount::flushCharacters ( ) +{ + vector::iterator iter; + for(iter = charlist.begin(); iter != charlist.end(); iter++){ + safe_delete(*iter); + } + + charlist.clear ( ); +} + +CharSelectProfile* LoginAccount::getCharacter(char* name){ + vector::iterator char_iterator; + CharSelectProfile* profile = 0; + EQ2_16BitString temp; + for(char_iterator = charlist.begin(); char_iterator != charlist.end(); char_iterator++){ + profile = *char_iterator; + temp = profile->packet->getType_EQ2_16BitString_ByName("name"); + if(strcmp(temp.data.c_str(), name)==0) + return profile; + } + return 0; +} +void LoginAccount::removeCharacter(char* name, int16 version){ + vector::iterator iter; + CharSelectProfile* profile = 0; + EQ2_16BitString temp; + for(iter = charlist.begin(); iter != charlist.end(); iter++){ + profile = *iter; + temp = profile->packet->getType_EQ2_16BitString_ByName("name"); + if(strcmp(temp.data.c_str(), name)==0){ + if(version <= 561) { + profile->deleted = true; // workaround for char select crash on old clients + } + else { + safe_delete(*iter); + charlist.erase(iter); + } + return; + } + } +} \ No newline at end of file diff --git a/old/LoginServer/LoginAccount.h b/old/LoginServer/LoginAccount.h new file mode 100644 index 0000000..b5bb540 --- /dev/null +++ b/old/LoginServer/LoginAccount.h @@ -0,0 +1,54 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. +*/ +#ifndef _LOGINACCOUNT_ +#define _LOGINACCOUNT_ + +#include +#include +#include +#include "../common/linked_list.h" +#include "PacketHeaders.h" +#include "../common/PacketStruct.h" + +using namespace std; +class LoginAccount { +public: + LoginAccount(); + LoginAccount(int32 id, const char* in_name, const char* in_pass){ + account_id = id; + strcpy(name, in_name); + strcpy(password, in_pass); + } + ~LoginAccount(); + bool SaveAccount(LoginAccount* acct); + vector charlist; + void setName(const char* in_name) { strcpy(name, in_name); } + void setPassword(const char* in_pass) { strcpy(password, in_pass); } + void setAuthenticated(bool in_auth) { authenticated=in_auth; } + void setAccountID(int32 id){ account_id = id; } + void addCharacter(CharSelectProfile* profile){ + charlist.push_back(profile); + } + void removeCharacter(PacketStruct* profile); + void removeCharacter(char* name, int16 version); + void serializeCharacter(uchar* buffer, CharSelectProfile* profile); + + void flushCharacters ( ); + + CharSelectProfile* getCharacter(char* name); + int32 getLoginAccountID(){ return account_id; } + char* getLoginName() { return name; } + char* getLoginPassword() { return password; } + bool getLoginAuthenticated() { return authenticated; } + +private: + int32 account_id; + char name[32]; + char password[32]; + bool authenticated; +}; +#endif \ No newline at end of file diff --git a/old/LoginServer/LoginDatabase.cpp b/old/LoginServer/LoginDatabase.cpp new file mode 100644 index 0000000..80bfe98 --- /dev/null +++ b/old/LoginServer/LoginDatabase.cpp @@ -0,0 +1,1083 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. +*/ +#include "../common/debug.h" + +#include +#include +#include +using namespace std; + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#define snprintf _snprintf +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#else +#include "../common/unix.h" +#include +#endif + +#include "../common/Log.h" +#include "../common/DatabaseNew.h" +#include "LoginDatabase.h" +#include "LoginAccount.h" +#include "../common/MiscFunctions.h" +#include "../common/packet_functions.h" +#include "../common/packet_dump.h" +#include "LWorld.h" + +extern LoginDatabase database; +extern LWorldList world_list; + + +bool LoginDatabase::ConnectNewDatabase() { + return dbLogin.Connect(); +} + +void LoginDatabase::RemoveDeletedCharacterData() +{ + dbLogin.Query("DELETE FROM login_char_colors WHERE login_characters_id IN (SELECT id FROM login_characters WHERE deleted = 1)"); + dbLogin.Query("DELETE FROM login_equipment WHERE login_characters_id IN (SELECT id FROM login_characters WHERE deleted = 1)"); +} + +void LoginDatabase::SetZoneInformation(int32 server_id, int32 zone_id, int32 version, PacketStruct* packet){ + if(packet){ + Query query; + MYSQL_RES* result = 0; + + if ( version >= 1212 ) + result = query.RunQuery2(Q_SELECT, "SELECT name, description from ls_world_zones where server_id=%i and zone_id=%i", server_id, zone_id); + + MYSQL_ROW row; + if(result && (row = mysql_fetch_row(result))) { + if (row[0]) + packet->setMediumStringByName("zone", row[0]); + else + packet->setMediumStringByName("zone", " "); + if(row[1]) + packet->setMediumStringByName("zonedesc", row[1]); + else + packet->setMediumStringByName("zonedesc", " "); + } + else{ + Query query2; + MYSQL_RES* result2 = 0; + + if (version < 1212) + result2 = query2.RunQuery2(Q_SELECT, "SELECT file, description from zones where id=%i", zone_id); + else + result2 = query2.RunQuery2(Q_SELECT, "SELECT name, description from zones where id=%i", zone_id); + + MYSQL_ROW row2; + if(result2 && (row2 = mysql_fetch_row(result2))) { + + if (version < 546 && version > 561 && version < 1212) + { + if (row2[0]) + { + int len = strlen(row2[0]); + char* zoneName = new char[len + 2]; + strncpy(zoneName, row2[0], len); + zoneName[len] = 0x2E; + zoneName[len + 1] = 0x30; + + packet->setMediumStringByName("zone", zoneName); + safe_delete_array(zoneName); + } + else + packet->setMediumStringByName("zone", ".0"); + } + else + { + if (row2[0]) + packet->setMediumStringByName("zone", row2[0]); + else + packet->setMediumStringByName("zone", " "); + } + if(row2[1]) + packet->setMediumStringByName("zonedesc", row2[1]); + else + packet->setMediumStringByName("zonedesc", " "); + } + } + packet->setMediumStringByName("zonename2"," "); + } +} + +string LoginDatabase::GetZoneDescription(char* name){ + string ret; + Query query; + query.escaped_name = getEscapeString(name); + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT description from zones where file=substring_index('%s', '.', 1)", query.escaped_name); + MYSQL_ROW row; + if((row = mysql_fetch_row(result))) { + ret = string(row[0]); + } + return ret; +} + + +int32 LoginDatabase::GetLoginCharacterIDFromWorldCharID(int32 server_id, int32 char_id) +{ + int32 ret; + Query query; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id FROM login_characters WHERE server_id = %u AND char_id = %u AND deleted = 0 LIMIT 0,1", server_id, char_id); + MYSQL_ROW row; + if((row = mysql_fetch_row(result))) { + ret = atoi(row[0]); + } + + return ret; +} + +void LoginDatabase::SetServerEquipmentAppearances(int32 server_id, map equip_updates) +{ + + if(equip_updates.size() > 0) + { + + LogWrite(LOGIN__DEBUG, 0, "Login", "Saving appearance info from world %u...", server_id); + + map::iterator equip_itr; + stringstream ss; + ss << "replace into login_equipment (login_characters_id, equip_type, red, green, blue, highlight_red, highlight_green, highlight_blue, slot) values"; + int count=0; + int32 char_id = 0; + + for(equip_itr = equip_updates.begin(); equip_itr != equip_updates.end(); equip_itr++) + { + char_id = GetLoginCharacterIDFromWorldCharID(server_id, (int32)equip_itr->second.world_char_id); + + if( char_id == 0 ) // invalid character/world match + continue; + + LogWrite(LOGIN__DEBUG, 5, "Login", "--Processing character %u, slot %i", char_id, (int32)equip_itr->second.slot); + + if(count > 0) + ss << ", "; + + ss << "(" << char_id << ", "; + ss << (int32)equip_itr->second.equip_type << ", "; + ss << (int32)equip_itr->second.red << ", "; + ss << (int32)equip_itr->second.green << ", "; + ss << (int32)equip_itr->second.blue << ", "; + ss << (int32)equip_itr->second.highlight_red << ", "; + ss << (int32)equip_itr->second.highlight_green << ", "; + ss << (int32)equip_itr->second.highlight_blue << ", "; + ss << (int32)equip_itr->second.slot << ")"; + + count++; + } + + Query query; + query.RunQuery2(ss.str(), Q_REPLACE); + + if (query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF) + LogWrite(LOGIN__ERROR, 0, "Login", "Error saving login_equipment data Error: ", query.GetError()); + } +} + + +void LoginDatabase::SetServerZoneDescriptions(int32 server_id, map zone_descriptions){ + if(zone_descriptions.size() > 0){ + map::iterator zone_itr; + string query_string = "replace into ls_world_zones (server_id, zone_id, name, description) values"; + int count=0; + char server_id_str[12] = {0}; + sprintf(server_id_str, "%i", server_id); + for(zone_itr = zone_descriptions.begin(); zone_itr != zone_descriptions.end(); zone_itr++, count++){ + char zone_id_str[12] = {0}; + sprintf(zone_id_str, "%i", zone_itr->first); + if(count > 0) + query_string.append(", "); + query_string.append("(").append(server_id_str).append(","); + query_string.append(zone_id_str).append(","); + query_string.append("'").append(getSafeEscapeString(zone_itr->second.name.c_str()).c_str()).append("', '"); + query_string.append(getSafeEscapeString(zone_itr->second.description.c_str()).c_str()).append("')"); + } + Query query; + query.RunQuery2(query_string, Q_REPLACE); + } +} + +//this is really just for the version that doesn't send the server id in its play request +int32 LoginDatabase::GetServer(int32 accountID, int32 charID, string name) { + int32 id = 0; + Query query; + MYSQL_ROW row; + query.escaped_name = getEscapeString(name.c_str()); + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT server_id from login_characters where account_id=%i and char_id=%i and name='%s'", accountID, charID, query.escaped_name); + if (result && mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + id = atoi(row[0]); + } + return id; +} + +void LoginDatabase::LoadCharacters(LoginAccount* acct, int16 version){ + if(acct != NULL) + acct->flushCharacters ( ); + + Query query; + Query query2; + int32 id = 0; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT lc.char_id, lc.server_id, lc.name, lc.race, lc.class, lc.gender, lc.deity, lc.body_size, lc.body_age, lc.current_zone_id, lc.level, lc.soga_wing_type, lc.soga_chest_type, lc.soga_legs_type, lc.soga_hair_type, lc.legs_type, lc.chest_type, lc.wing_type, lc.hair_type, unix_timestamp(lc.created_date), unix_timestamp(lc.last_played), lc.id, lw.name, lc.facial_hair_type, lc.soga_facial_hair_type, lc.soga_model_type, lc.model_type from login_characters lc, login_worldservers lw where lw.id = lc.server_id and lc.account_id=%i and lc.deleted=0",acct->getLoginAccountID()); + if(result) { + MYSQL_ROW row; + MYSQL_ROW row2; + MYSQL_ROW row3; + while ((row = mysql_fetch_row(result))) { + CharSelectProfile* player = new CharSelectProfile(version); + id = atoul(row[0]); + //for (int i = 0; i < 10; i++) + // player->packet->setDataByName("hair_type", 0, i); + //player->packet->setDataByName("test23", 413); + //player->packet->setDataByName("test24", 414); + player->packet->setDataByName("charid", id); + player->packet->setDataByName("server_id", atoul(row[1])); + player->packet->setMediumStringByName("name", row[2]); + player->packet->setDataByName("race", atoi(row[3])); + player->packet->setDataByName("class", atoi(row[4])); + player->packet->setDataByName("gender", atoi(row[5])); + player->packet->setDataByName("deity", atoi(row[6])); + player->packet->setDataByName("body_size", atof(row[7])); + player->packet->setDataByName("body_age", atof(row[8])); + SetZoneInformation(atoi(row[1]), atoi(row[9]), version, player->packet); + player->packet->setDataByName("level", atoi(row[10])); + if(atoi(row[11]) > 0) + player->packet->setDataByName("soga_wing_type", atoi(row[11])); + else + player->packet->setDataByName("soga_wing_type", atoi(row[17])); + if(atoi(row[12]) > 0) + player->packet->setDataByName("soga_chest_type", atoi(row[12])); + else + player->packet->setDataByName("soga_chest_type", atoi(row[16])); + if(atoi(row[13]) > 0) + player->packet->setDataByName("soga_legs_type", atoi(row[13])); + else + player->packet->setDataByName("soga_legs_type", atoi(row[15])); + if(atoi(row[14]) > 0) + player->packet->setDataByName("soga_hair_type", atoi(row[14])); + else + player->packet->setDataByName("soga_hair_type", atoi(row[18])); + player->packet->setDataByName("legs_type", atoi(row[15])); + player->packet->setDataByName("chest_type", atoi(row[16])); + player->packet->setDataByName("wing_type", atoi(row[17])); + player->packet->setDataByName("hair_type", atoi(row[18])); + player->packet->setDataByName("created_date", atol(row[19])); + if (row[20]) + player->packet->setDataByName("last_played", atol(row[20])); + if(version == 546 || version == 561) + player->packet->setDataByName("version", 11); + else if(version >= 887) + player->packet->setDataByName("version", 6); + else + player->packet->setDataByName("version", 5); + player->packet->setDataByName("account_id", acct->getLoginAccountID()); + player->packet->setDataByName("account_id2", acct->getLoginAccountID()); + + LoadAppearanceData(atoul(row[21]), player->packet); + + if(row[22]) + player->packet->setMediumStringByName("server_name", row[22]); + player->packet->setDataByName("hair_face_type", atoi(row[23])); + if(atoi(row[24]) > 0) + player->packet->setDataByName("soga_hair_face_type", atoi(row[24])); + else + player->packet->setDataByName("soga_hair_face_type", atoi(row[23])); + if(atoi(row[25]) > 0) + player->packet->setDataByName("soga_race_type", atoi(row[25])); + else + player->packet->setDataByName("soga_race_type", atoi(row[26])); + player->packet->setDataByName("race_type", atoi(row[26])); + + player->packet->setDataByName("unknown3", 57); + player->packet->setDataByName("unknown4", 56); + player->packet->setDataByName("unknown6", 1, 1); //if not here will not display character + player->packet->setDataByName("unknown8", 15); + player->packet->setDataByName("unknown13", 212); + player->packet->setColorByName("unknown14", 0xFF, 0xFF, 0xFF); + + uchar tmp[] = {0xFF, 0xFF, 0xFF, 0x61, 0x00, 0x2C, 0x04, 0xA5, 0x09, 0x02, 0x0F, 0x00, 0x00}; + for(size_t y=0;ypacket->setDataByName("unknown11", tmp[y], y); + MYSQL_RES* result3 = query2.RunQuery2(Q_SELECT, "SELECT slot, equip_type, red, green, blue, highlight_red, highlight_green, highlight_blue from login_equipment where login_characters_id=%i order by slot",atoi(row[21])); + if(result3){ + for(int i=0;(row3 = mysql_fetch_row(result3)) && i<24; i++){ + player->packet->setEquipmentByName("equip", atoi(row3[1]), atoi(row3[2]), atoi(row3[3]), atoi(row3[4]), atoi(row3[5]), atoi(row3[6]), atoi(row3[7]), atoi(row3[0])); + } + } + player->packet->setDataByName("mount", 1377); + player->packet->setDataByName("mount_color1", 57); + /* + enum NetAppearance::NetAppearanceFlags + { + NAF_INVISIBLE=1, + NAF_SHOW_HOOD=2 + }; + */ + acct->addCharacter(player); + } + } + else + LogWrite(LOGIN__ERROR, 0, "Login", "Error in LoadCharacters query '%s': %s", query.GetQuery(), query.GetError()); + +} + +void LoginDatabase::CheckCharacterTimeStamps(LoginAccount* acct){ + Query query; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT char_id, unix_timestamp from login_characters where account_id=%i",acct->getLoginAccountID()); + if(result && mysql_num_rows(result) > 0) { + MYSQL_ROW row; + + ServerPacket* outpack = new ServerPacket(ServerOP_CharTimeStamp, sizeof(CharacterTimeStamp_Struct)); + CharacterTimeStamp_Struct* cts = (CharacterTimeStamp_Struct*) outpack->pBuffer; + cts->account_id = acct->getLoginAccountID(); + int32 server_id = 0; + LWorld* world_server = 0; + while ((row = mysql_fetch_row(result))) { + server_id = atoi(row[1]); + if(server_id != 0) + world_server = world_list.FindByAccount(server_id, World); + if(world_server) // If the pointer is 0, the world server must be down, we can't do any updates... + { + cts->char_id = atoi(row[0]); + cts->unix_timestamp = atoi(row[1]); + world_server->SendPacket(outpack); + //Reset for next character + world_server = 0; + server_id = 0; + } + } + safe_delete(outpack); + } +} + +void LoginDatabase::SaveCharacterFloats(int32 char_id, char* type, float float1, float float2, float float3,float multiplier){ + Query query; + string create_char = string("insert into login_char_colors (login_characters_id, type, red, green, blue, signed_value) values(%i,'%s',%i,%i,%i, 1)"); + query.RunQuery2(Q_INSERT, create_char.c_str(), char_id, type, (sint8)(float1*multiplier), (sint8)(float2*multiplier), (sint8)(float3*multiplier)); +} + +void LoginDatabase::SaveCharacterColors(int32 char_id, char* type, EQ2_Color color){ + Query query; + string create_char = string("insert into login_char_colors (login_characters_id, type, red, green, blue) values(%i,'%s',%i,%i,%i)"); + query.RunQuery2(Q_INSERT, create_char.c_str(), char_id, type, color.red, color.green, color.blue); +} + +void LoginDatabase::LoadAppearanceData(int32 char_id, PacketStruct* char_select_packet){ + Query query; + MYSQL_ROW row; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT type, signed_value, red, green, blue from login_char_colors where login_characters_id = %i",char_id); + while((row = mysql_fetch_row(result))){ + if(atoi(row[1]) == 0) + char_select_packet->setColorByName(row[0], atoul(row[2]), atoul(row[3]), atoul(row[4])); + else{ + char_select_packet->setDataByName(row[0], atoi(row[2]), 0); + char_select_packet->setDataByName(row[0], atoi(row[3]), 1); + char_select_packet->setDataByName(row[0], atoi(row[4]), 2); + } + } +} +int16 LoginDatabase::GetAppearanceID(string name){ + int32 id = 0; + Query query; + MYSQL_ROW row; + query.escaped_name = getEscapeString(name.c_str()); + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT appearance_id from appearances where name='%s'", query.escaped_name); + if(result && mysql_num_rows(result) == 1){ + row = mysql_fetch_row(result); + id = atoi(row[0]); + } + return id; +} + +void LoginDatabase::DeactivateCharID(int32 server_id, int32 char_id, int32 exception_id){ + Query query; + query.RunQuery2(Q_UPDATE, "update login_characters set deleted=1 where char_id=%u and server_id=%u and id!=%u",char_id,server_id,exception_id); +} + +int32 LoginDatabase::SaveCharacter(PacketStruct* create, LoginAccount* acct, int32 world_charid, int32 client_version){ + int32 ret_id = 0; + Query query; + string create_char = + string("Insert into login_characters (account_id, server_id, char_id, name, race, class, gender, deity, body_size, body_age, soga_wing_type, soga_chest_type, soga_legs_type, soga_hair_type, soga_facial_hair_type, legs_type, chest_type, wing_type, hair_type, facial_hair_type, soga_model_type, model_type)" + " values(%i, %i, %i, '%s', %i, %i, %i, %i, %f, %f, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i)"); + query.RunQuery2(Q_INSERT, create_char.c_str(), + acct->getLoginAccountID(), + create->getType_int32_ByName("server_id"), world_charid, + create->getType_EQ2_16BitString_ByName("name").data.c_str(), + create->getType_int8_ByName("race"), + create->getType_int8_ByName("class"), + create->getType_int8_ByName("gender"), + create->getType_int8_ByName("deity"), + create->getType_float_ByName("body_size"), + create->getType_float_ByName("body_age"), + GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_wing_file").data), + GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_chest_file").data), + GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_legs_file").data), + GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_hair_file").data), + GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_face_file").data), + GetAppearanceID(create->getType_EQ2_16BitString_ByName("legs_file").data), + GetAppearanceID(create->getType_EQ2_16BitString_ByName("chest_file").data), + GetAppearanceID(create->getType_EQ2_16BitString_ByName("wing_file").data), + GetAppearanceID(create->getType_EQ2_16BitString_ByName("hair_file").data), + GetAppearanceID(create->getType_EQ2_16BitString_ByName("face_file").data), + GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_race_file").data), + GetAppearanceID(create->getType_EQ2_16BitString_ByName("race_file").data)); + if(query.GetError() && strlen(query.GetError()) > 0){ + LogWrite(LOGIN__ERROR, 0, "Login", "Error in SaveCharacter query '%s': %s", query.GetQuery(), query.GetError()); + return 0; + } + + int32 last_insert_id = query.GetLastInsertedID(); + + //mark any remaining characters with same id as deleted (creates problems if world deleted their db and started assigning new char ids) + DeactivateCharID(create->getType_int32_ByName("server_id"), world_charid, last_insert_id); + int32 char_id = last_insert_id; + if (client_version <= 561) { + float classic_multiplier = 250.0f; + SaveCharacterFloats(char_id, "skin_color", create->getType_float_ByName("skin_color", 0), create->getType_float_ByName("skin_color", 1), create->getType_float_ByName("skin_color", 2), classic_multiplier); + SaveCharacterFloats(char_id, "eye_color", create->getType_float_ByName("eye_color", 0), create->getType_float_ByName("eye_color", 1), create->getType_float_ByName("eye_color", 2), classic_multiplier); + SaveCharacterFloats(char_id, "hair_color1", create->getType_float_ByName("hair_color1", 0), create->getType_float_ByName("hair_color1", 1), create->getType_float_ByName("hair_color1", 2), classic_multiplier); + SaveCharacterFloats(char_id, "hair_color2", create->getType_float_ByName("hair_color2", 0), create->getType_float_ByName("hair_color2", 1), create->getType_float_ByName("hair_color2", 2), classic_multiplier); + SaveCharacterFloats(char_id, "hair_highlight", create->getType_float_ByName("hair_highlight", 0), create->getType_float_ByName("hair_highlight", 1), create->getType_float_ByName("hair_highlight", 2), classic_multiplier); + SaveCharacterFloats(char_id, "hair_type_color", create->getType_float_ByName("hair_type_color", 0), create->getType_float_ByName("hair_type_color", 1), create->getType_float_ByName("hair_type_color", 2), classic_multiplier); + SaveCharacterFloats(char_id, "hair_type_highlight_color", create->getType_float_ByName("hair_type_highlight_color", 0), create->getType_float_ByName("hair_type_highlight_color", 1), create->getType_float_ByName("hair_type_highlight_color", 2), classic_multiplier); + SaveCharacterFloats(char_id, "hair_type_color", create->getType_float_ByName("hair_type_color", 0), create->getType_float_ByName("hair_type_color", 1), create->getType_float_ByName("hair_type_color", 2), classic_multiplier); + SaveCharacterFloats(char_id, "hair_type_highlight_color", create->getType_float_ByName("hair_type_highlight_color", 0), create->getType_float_ByName("hair_type_highlight_color", 1), create->getType_float_ByName("hair_type_highlight_color", 2), classic_multiplier); + SaveCharacterFloats(char_id, "hair_face_color", create->getType_float_ByName("hair_face_color", 0), create->getType_float_ByName("hair_face_color", 1), create->getType_float_ByName("hair_face_color", 2), classic_multiplier); + SaveCharacterFloats(char_id, "hair_face_highlight_color", create->getType_float_ByName("hair_face_highlight_color", 0), create->getType_float_ByName("hair_face_highlight_color", 1), create->getType_float_ByName("hair_face_highlight_color", 2), classic_multiplier); + SaveCharacterFloats(char_id, "shirt_color", create->getType_float_ByName("shirt_color", 0), create->getType_float_ByName("shirt_color", 1), create->getType_float_ByName("shirt_color", 2), classic_multiplier); + SaveCharacterFloats(char_id, "unknown_chest_color", create->getType_float_ByName("unknown_chest_color", 0), create->getType_float_ByName("unknown_chest_color", 1), create->getType_float_ByName("unknown_chest_color", 2), classic_multiplier); + SaveCharacterFloats(char_id, "pants_color", create->getType_float_ByName("pants_color", 0), create->getType_float_ByName("pants_color", 1), create->getType_float_ByName("pants_color", 2), classic_multiplier); + SaveCharacterFloats(char_id, "unknown_legs_color", create->getType_float_ByName("unknown_legs_color", 0), create->getType_float_ByName("unknown_legs_color", 1), create->getType_float_ByName("unknown_legs_color", 2), classic_multiplier); + SaveCharacterFloats(char_id, "unknown9", create->getType_float_ByName("unknown9", 0), create->getType_float_ByName("unknown9", 1), create->getType_float_ByName("unknown9", 2), classic_multiplier); + } + else { + SaveCharacterColors(char_id, "skin_color", create->getType_EQ2_Color_ByName("skin_color")); + SaveCharacterColors(char_id, "model_color", create->getType_EQ2_Color_ByName("model_color")); + SaveCharacterColors(char_id, "eye_color", create->getType_EQ2_Color_ByName("eye_color")); + SaveCharacterColors(char_id, "hair_color1", create->getType_EQ2_Color_ByName("hair_color1")); + SaveCharacterColors(char_id, "hair_color2", create->getType_EQ2_Color_ByName("hair_color2")); + SaveCharacterColors(char_id, "hair_highlight", create->getType_EQ2_Color_ByName("hair_highlight")); + SaveCharacterColors(char_id, "hair_type_color", create->getType_EQ2_Color_ByName("hair_type_color")); + SaveCharacterColors(char_id, "hair_type_highlight_color", create->getType_EQ2_Color_ByName("hair_type_highlight_color")); + SaveCharacterColors(char_id, "hair_face_color", create->getType_EQ2_Color_ByName("hair_face_color")); + SaveCharacterColors(char_id, "hair_face_highlight_color", create->getType_EQ2_Color_ByName("hair_face_highlight_color")); + SaveCharacterColors(char_id, "wing_color1", create->getType_EQ2_Color_ByName("wing_color1")); + SaveCharacterColors(char_id, "wing_color2", create->getType_EQ2_Color_ByName("wing_color2")); + SaveCharacterColors(char_id, "shirt_color", create->getType_EQ2_Color_ByName("shirt_color")); + SaveCharacterColors(char_id, "unknown_chest_color", create->getType_EQ2_Color_ByName("unknown_chest_color")); + SaveCharacterColors(char_id, "pants_color", create->getType_EQ2_Color_ByName("pants_color")); + SaveCharacterColors(char_id, "unknown_legs_color", create->getType_EQ2_Color_ByName("unknown_legs_color")); + SaveCharacterColors(char_id, "unknown9", create->getType_EQ2_Color_ByName("unknown9")); + + SaveCharacterColors(char_id, "soga_skin_color", create->getType_EQ2_Color_ByName("soga_skin_color")); + SaveCharacterColors(char_id, "soga_model_color", create->getType_EQ2_Color_ByName("soga_model_color")); + SaveCharacterColors(char_id, "soga_eye_color", create->getType_EQ2_Color_ByName("soga_eye_color")); + SaveCharacterColors(char_id, "soga_hair_color1", create->getType_EQ2_Color_ByName("soga_hair_color1")); + SaveCharacterColors(char_id, "soga_hair_color2", create->getType_EQ2_Color_ByName("soga_hair_color2")); + SaveCharacterColors(char_id, "soga_hair_highlight", create->getType_EQ2_Color_ByName("soga_hair_highlight")); + SaveCharacterColors(char_id, "soga_hair_type_color", create->getType_EQ2_Color_ByName("soga_hair_type_color")); + SaveCharacterColors(char_id, "soga_hair_type_highlight_color", create->getType_EQ2_Color_ByName("soga_hair_type_highlight_color")); + SaveCharacterColors(char_id, "soga_hair_face_color", create->getType_EQ2_Color_ByName("soga_hair_face_color")); + SaveCharacterColors(char_id, "soga_hair_face_highlight_color", create->getType_EQ2_Color_ByName("soga_hair_face_highlight_color")); + SaveCharacterColors(char_id, "soga_wing_color1", create->getType_EQ2_Color_ByName("soga_wing_color1")); + SaveCharacterColors(char_id, "soga_wing_color2", create->getType_EQ2_Color_ByName("soga_wing_color2")); + SaveCharacterColors(char_id, "soga_shirt_color", create->getType_EQ2_Color_ByName("soga_shirt_color")); + SaveCharacterColors(char_id, "soga_unknown_chest_color", create->getType_EQ2_Color_ByName("soga_unknown_chest_color")); + SaveCharacterColors(char_id, "soga_pants_color", create->getType_EQ2_Color_ByName("soga_pants_color")); + SaveCharacterColors(char_id, "soga_unknown_legs_color", create->getType_EQ2_Color_ByName("soga_unknown_legs_color")); + SaveCharacterColors(char_id, "soga_unknown13", create->getType_EQ2_Color_ByName("soga_unknown13")); + SaveCharacterFloats(char_id, "soga_eye_type", create->getType_float_ByName("soga_eyes2", 0), create->getType_float_ByName("soga_eyes2", 1), create->getType_float_ByName("soga_eyes2", 2)); + SaveCharacterFloats(char_id, "soga_ear_type", create->getType_float_ByName("soga_ears", 0), create->getType_float_ByName("soga_ears", 1), create->getType_float_ByName("soga_ears", 2)); + SaveCharacterFloats(char_id, "soga_eye_brow_type", create->getType_float_ByName("soga_eye_brows", 0), create->getType_float_ByName("soga_eye_brows", 1), create->getType_float_ByName("soga_eye_brows", 2)); + SaveCharacterFloats(char_id, "soga_cheek_type", create->getType_float_ByName("soga_cheeks", 0), create->getType_float_ByName("soga_cheeks", 1), create->getType_float_ByName("soga_cheeks", 2)); + SaveCharacterFloats(char_id, "soga_lip_type", create->getType_float_ByName("soga_lips", 0), create->getType_float_ByName("soga_lips", 1), create->getType_float_ByName("soga_lips", 2)); + SaveCharacterFloats(char_id, "soga_chin_type", create->getType_float_ByName("soga_chin", 0), create->getType_float_ByName("soga_chin", 1), create->getType_float_ByName("soga_chin", 2)); + SaveCharacterFloats(char_id, "soga_nose_type", create->getType_float_ByName("soga_nose", 0), create->getType_float_ByName("soga_nose", 1), create->getType_float_ByName("soga_nose", 2)); + } + SaveCharacterFloats(char_id, "eye_type", create->getType_float_ByName("eyes2", 0), create->getType_float_ByName("eyes2", 1), create->getType_float_ByName("eyes2", 2)); + SaveCharacterFloats(char_id, "ear_type", create->getType_float_ByName("ears", 0), create->getType_float_ByName("ears", 1), create->getType_float_ByName("ears", 2)); + SaveCharacterFloats(char_id, "eye_brow_type", create->getType_float_ByName("eye_brows", 0), create->getType_float_ByName("eye_brows", 1), create->getType_float_ByName("eye_brows", 2)); + SaveCharacterFloats(char_id, "cheek_type", create->getType_float_ByName("cheeks", 0), create->getType_float_ByName("cheeks", 1), create->getType_float_ByName("cheeks", 2)); + SaveCharacterFloats(char_id, "lip_type", create->getType_float_ByName("lips", 0), create->getType_float_ByName("lips", 1), create->getType_float_ByName("lips", 2)); + SaveCharacterFloats(char_id, "chin_type", create->getType_float_ByName("chin", 0), create->getType_float_ByName("chin", 1), create->getType_float_ByName("chin", 2)); + SaveCharacterFloats(char_id, "nose_type", create->getType_float_ByName("nose", 0), create->getType_float_ByName("nose", 1), create->getType_float_ByName("nose", 2)); + SaveCharacterFloats(char_id, "body_size", create->getType_float_ByName("body_size", 0), 0, 0); + return ret_id; +} + +bool LoginDatabase::DeleteCharacter(int32 account_id, int32 character_id, int32 server_id){ + Query query; + string delete_char = string("delete from login_characters where char_id=%i and account_id=%i and server_id=%i"); + query.RunQuery2(Q_DELETE, delete_char.c_str(),character_id,account_id,server_id); + if(!query.GetAffectedRows()) + { + //No error just in case ppl try doing stupid stuff + return false; + } + + return true; +} + +string LoginDatabase::GetCharacterName(int32 char_id, int32 server_id, int32 account_id){ + Query query; + MYSQL_ROW row; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name from login_characters where char_id=%lu and server_id=%lu and account_id=%lu and deleted = 0 limit 1", char_id, server_id, account_id); + + if(result && mysql_num_rows(result) == 1){ + row = mysql_fetch_row(result); + return string(row[0]); + } + return string(""); +} + +bool LoginDatabase::UpdateCharacterTimeStamp(int32 account_id, int32 character_id, int32 timestamp_update, int32 server_id){ + Query query; + string update_charts = string("update login_characters set unix_timestamp=%lu where char_id=%lu and account_id=%lu and server_id=%lu"); + query.RunQuery2(Q_UPDATE, update_charts.c_str(),timestamp_update,character_id,account_id,server_id); + if(!query.GetAffectedRows()) + { + LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterTimeStamp query '%s': %s", query.GetQuery(), query.GetError()); + return false; + } + + return true; +} + +bool LoginDatabase::UpdateCharacterLevel(int32 account_id, int32 character_id, int8 in_level, int32 server_id){ + Query query; + string update_charts = string("update login_characters set level=%i where char_id=%lu and account_id=%lu and server_id=%lu"); + query.RunQuery2(Q_UPDATE, update_charts.c_str(),in_level,character_id,account_id,server_id); + if(!query.GetAffectedRows()) + { + LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterLevel query '%s': %s", query.GetQuery(), query.GetError()); + return false; + } + + return true; +} + +bool LoginDatabase::UpdateCharacterRace(int32 account_id, int32 character_id, int16 in_racetype, int8 in_race, int32 server_id){ + Query query; + string update_charts = string("update login_characters set race_type=%i, race=%i where char_id=%lu and account_id=%lu and server_id=%lu"); + query.RunQuery2(Q_UPDATE, update_charts.c_str(),in_racetype,in_race,character_id,account_id,server_id); + if(!query.GetAffectedRows()) + { + LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterRace query '%s': %s", query.GetQuery(), query.GetError()); + return false; + } + + return true; +} + +bool LoginDatabase::UpdateCharacterZone(int32 account_id, int32 character_id, int32 zone_id, int32 server_id){ + Query query; + string update_chars = string("update login_characters set current_zone_id=%i where char_id=%lu and account_id=%lu and server_id=%lu"); + query.RunQuery2(Q_UPDATE, update_chars.c_str(), zone_id, character_id, account_id, server_id); + if(!query.GetAffectedRows()) + { + LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterZone query '%s': %s", query.GetQuery(), query.GetError()); + return false; + } + + return true; +} + +bool LoginDatabase::UpdateCharacterClass(int32 account_id, int32 character_id, int8 in_class, int32 server_id){ + Query query; + string update_charts = string("update login_characters set class=%i where char_id=%lu and account_id=%lu and server_id=%lu"); + query.RunQuery2(Q_UPDATE, update_charts.c_str(),in_class,character_id,account_id,server_id); + if(!query.GetAffectedRows()) + { + LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterClass query '%s': %s", query.GetQuery(), query.GetError()); + return false; + } + + return true; +} + +bool LoginDatabase::UpdateCharacterGender(int32 account_id, int32 character_id, int8 in_gender, int32 server_id){ + Query query; + string update_charts = string("update login_characters set gender=%i where char_id=%lu and account_id=%lu and server_id=%lu"); + query.RunQuery2(Q_UPDATE, update_charts.c_str(),in_gender,character_id,account_id,server_id); + if(!query.GetAffectedRows()) + { + LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterClass query '%s': %s", query.GetQuery(), query.GetError()); + return false; + } + + return true; +} + +bool LoginDatabase::UpdateCharacterName(int32 account_id, int32 character_id, char* newName, int32 server_id){ + Query query; + string update_charts = string("update login_characters set name='%s' where char_id=%lu and account_id=%lu and server_id=%lu"); + query.RunQuery2(Q_UPDATE, update_charts.c_str(),newName,character_id,account_id,server_id); + if(!query.GetAffectedRows()) + { + LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterName query '%s': %s", query.GetQuery(), query.GetError()); + return false; + } + + return true; +} + +LoginAccount* LoginDatabase::LoadAccount(const char* name, const char* password, bool attemptAccountCreation){ + LoginAccount* acct = NULL; + Query query; + query.escaped_name = getEscapeString(name); + query.escaped_pass = getEscapeString(password); + time_t now = time(0); //get the current epoc time + MYSQL_ROW row; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id from account where name='%s' and passwd=sha2('%s',512)", query.escaped_name, query.escaped_pass); + if(result){ + if (mysql_num_rows(result) == 1){ + row = mysql_fetch_row(result); + + int32 id = atol(row[0]); + + acct = new LoginAccount(id, name, password); + acct->setAuthenticated(true); + } + else if(mysql_num_rows(result) > 0) + LogWrite(LOGIN__ERROR, 0, "Login", "Error in LoginAccount: more than one account returned for '%s'", name); + else if (attemptAccountCreation && !database.GetAccountIDByName(name)) + { + Query newquery; + newquery.RunQuery2(Q_INSERT, "insert into account set name='%s',passwd=sha2('%s',512), created_date=%i", query.escaped_name, query.escaped_pass, now); + // re-run the query for select only not account creation + return LoadAccount(name, password, false); + } + + } + return acct; +} + +int32 LoginDatabase::GetAccountIDByName(const char* name) { + int32 id = 0; + Query query; + MYSQL_ROW row; + query.escaped_name = getEscapeString(name); + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id from account where name='%s'", query.escaped_name); + if (result && mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + id = atoi(row[0]); + } + return id; +} + +int32 LoginDatabase::CheckServerAccount(char* name, char* passwd){ + int32 id = 0; + Query query; + MYSQL_ROW row; + query.escaped_name = getEscapeString(name); + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT lower(password), id from login_worldservers where account='%s' and disabled = 0", query.escaped_name); + + LogWrite(LOGIN__INFO, 0, "Login", "WorldServer CheckServerAccount Account=%s\nSHA=%s", (char*)query.escaped_name, passwd); + if(result && mysql_num_rows(result) == 1){ + row = mysql_fetch_row(result); + + LogWrite(LOGIN__INFO, 0, "Login", "WorldServer CheckServerAccountResult Account=%s\nPassword=%s", (char*)query.escaped_name, (row && row[0]) ? row[0] : "(NULL)"); + + if (memcmp(row[0], passwd, strnlen(row[0], 256)) == 0) + { + LogWrite(LOGIN__INFO, 0, "Login", "WorldServer CheckServerAccountResultMatch Account=%s", (char*)query.escaped_name); + id = atoi(row[1]); + } + } + return id; +} + +bool LoginDatabase::IsServerAccountDisabled(char* name){ + Query query; + MYSQL_ROW row; + query.escaped_name = getEscapeString(name); + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id from login_worldservers where account='%s' and disabled = 1", query.escaped_name); + + LogWrite(LOGIN__DEBUG, 0, "Login", "WorldServer IsServerAccountDisabled Account=%s", (char*)query.escaped_name); + if(result && mysql_num_rows(result) > 0){ + row = mysql_fetch_row(result); + + LogWrite(LOGIN__INFO, 0, "Login", "WorldServer IsServerAccountDisabled Match Account=%s", (char*)query.escaped_name); + + return true; + } + return false; +} + +bool LoginDatabase::IsIPBanned(char* ipaddr){ + if(!ipaddr) + return false; + + Query query; + MYSQL_ROW row; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT ip from login_bannedips where '%s' LIKE CONCAT(ip ,'%%')", ipaddr); + + LogWrite(LOGIN__DEBUG, 0, "Login", "WorldServer IsServerIPBanned IPPartial=%s", (char*)ipaddr); + if(result && mysql_num_rows(result) > 0){ + row = mysql_fetch_row(result); + + LogWrite(LOGIN__INFO, 0, "Login", "WorldServer IsServerIPBanned Match IPBan=%s", row[0]); + + return true; + } + return false; +} + +void LoginDatabase::GetServerAccounts(vector* server_list){ + Query query; + MYSQL_ROW row; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, account, name, admin_id from login_worldservers"); + while((row = mysql_fetch_row(result))){ + LWorld* world = new LWorld(atol(row[0]), row[1], row[2], atoi(row[3])); + world->SetID(world->GetAccountID()); + server_list->push_back(world); + } +} +void LoginDatabase::SaveClientLog(const char* type, const char* message, const char* player_name, int16 version){ + Query query; + query.escaped_data1 = getEscapeString(message); + query.escaped_name = getEscapeString(player_name); + query.RunQuery2(Q_INSERT, "insert into log_messages (type, message, name, version) values('%s', '%s', '%s', %i)", type, query.escaped_data1, query.escaped_name, version); +} +bool LoginDatabase::VerifyDelete(int32 account_id, int32 character_id, const char* name){ + Query query; + query.escaped_name = getEscapeString(name); + query.RunQuery2(Q_UPDATE, "update login_characters set deleted = 1 where char_id=%i and account_id=%i and name='%s'", character_id, account_id, query.escaped_name); + if(query.GetAffectedRows() == 1) + return true; + else + return false; +} +char* LoginDatabase::GetServerAccountName(int32 id){ + Query query; + MYSQL_ROW row; + char* name = 0; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name from login_worldservers where id=%lu", id); + if(result && mysql_num_rows(result) == 1){ + row = mysql_fetch_row(result); + if(strlen(row[0]) > 0){ + name = new char[strlen(row[0])+1]; + strcpy(name, row[0]); + } + } + return name; +} +int32 LoginDatabase::GetRaceID(char* name){ + int32 ret = 1487; + Query query; + MYSQL_ROW row; + query.escaped_name = getEscapeString(name); + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT race_type from login_races where name='%s'", query.escaped_name); + if(result && mysql_num_rows(result) == 1){ + row = mysql_fetch_row(result); + ret = atol(row[0]); + } + else if(!result || mysql_num_rows(result) == 0) + UpdateRaceID(query.escaped_name); + return ret; +} +void LoginDatabase::UpdateRaceID(char* name){ + Query query; + query.RunQuery2(Q_UPDATE, "insert into login_races (name) values('%s')", name); +} +bool LoginDatabase::CheckVersion(char* in_version){ + Query query; + query.escaped_data1 = getEscapeString(in_version); + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id from login_versions where version='%s' or version='*'", query.escaped_data1); + if(result && mysql_num_rows(result) > 0) + return true; + else + return false; +} +void LoginDatabase::GetLatestTableVersions(LatestTableVersions* table_versions){ + Query query; + MYSQL_ROW row; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name, max(version) from login_table_versions group by name order by id"); + if(result && mysql_num_rows(result) > 0){ + table_versions->SetTableSize(mysql_num_rows(result)); + } + else // we need to return if theres no result, otherwise it will crash attempting to loop through rows + + return; + while((row = mysql_fetch_row(result))){ + if(VerifyDataTable(row[0])) + table_versions->AddTable(row[0], atoi(row[1]), GetDataVersion(row[0])); + else + table_versions->AddTable(row[0], atoi(row[1]), 0); + } +} +bool LoginDatabase::VerifyDataTable(char* name){ + Query query; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT table_name from download_tables where table_name='%s'", name); + if(result && mysql_num_rows(result) > 0) + return true; + return false; +} +string LoginDatabase::GetColumnNames(char* name){ + Query query; + MYSQL_ROW row; + string columns = "("; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "show columns from %s", name); + if(result && mysql_num_rows(result) > 0){ + int16 i = 0; + while((row = mysql_fetch_row(result))){ + if(strcmp(row[0], "table_data_version") != 0){ + if(i>0) + columns.append(","); + columns.append(row[0]); + i++; + } + } + } + columns.append(") "); + return columns; +} +TableDataQuery* LoginDatabase::GetTableDataQuery(int32 server_ip, char* name, int16 version){ + Query query; + MYSQL_ROW row; + query.escaped_name = getEscapeString(name); + TableDataQuery* table_query = 0; + MYSQL_RES* result = 0; + string columns; + + if(VerifyDataTable(query.escaped_name)){ + result = query.RunQuery2(Q_SELECT, "SELECT * from %s where table_data_version > %i", query.escaped_name, version); + columns = GetColumnNames(query.escaped_name); + } + if(result && mysql_num_rows(result) > 0){ + table_query = new TableDataQuery(query.escaped_name); + table_query->num_queries = mysql_num_rows(result); + table_query->columns_size = columns.length() + 1; + table_query->columns = new char[table_query->columns_size + 1]; + table_query->version = GetDataVersion(query.escaped_name); + strcpy(table_query->columns, (char*)columns.c_str()); + string query_data; + MYSQL_FIELD* field; + int* int_list = new int[mysql_num_fields(result)]; + int16 ndx = 0; + while((field = mysql_fetch_field(result))){ + int_list[ndx] = IS_NUM(field->type); + if(strcmp(field->name,"table_data_version") == 0) + int_list[ndx] = 2; + ndx++; + } + ndx = 0; + while((row = mysql_fetch_row(result))){ + query_data = ""; + for(int i=0;i0) + query_data.append(","); + if(!int_list[i]){ + query_data.append("'").append(getEscapeString(row[i])).append("'"); + } + else + query_data.append(row[i]); + } + } + TableData* new_query = new TableData; + new_query->size = query_data.length() + 1; + new_query->query = new char[query_data.length() + 1]; + strcpy(new_query->query, query_data.c_str()); + table_query->queries.push_back(new_query); + ndx++; + } + safe_delete_array(int_list); + } + else{ + string query2 = string("The user tried to download the following table: ").append(query.escaped_name); + SaveClientLog("Possible Hacking Attempt", (char*)query2.c_str(), "Hacking Data", server_ip); + } + return table_query; +} +TableQuery* LoginDatabase::GetLatestTableQuery(int32 server_ip, char* name, int16 version){ + Query query; + MYSQL_ROW row; + query.escaped_name = getEscapeString(name); + TableQuery* table_query = 0; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT query, version from login_table_versions where name = '%s' and version>=%i order by version", query.escaped_name, version + 1); + if(result && mysql_num_rows(result) > 0){ + int16 i = 0; + table_query = new TableQuery; + while((row = mysql_fetch_row(result))){ + char* rowdata = row[0]; + if(strstr(rowdata, ";")){ + char* token = strtok(rowdata,";"); + while(token){ + char* new_query = new char[strlen(token) + 1]; + strcpy(new_query, token); + table_query->AddQuery(new_query); + token = strtok(NULL, ";"); + } + } + else + table_query->AddQuery(rowdata); + table_query->latest_version = atoi(row[1]); + } + strcpy(table_query->tablename, name); + table_query->your_version = version; + } + else{ + string query2 = string("The following was the DB Query: ").append(query.GetQuery()); + SaveClientLog("Possible Hacking Attempt", (char*)query2.c_str(), "Hacking Query", server_ip); + } + return table_query; +} +sint16 LoginDatabase::GetDataVersion(char* name){ + Query query; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT max(table_data_version) from %s", name); + sint16 ret_version = 0; + if(result && mysql_num_rows(result) > 0) { + MYSQL_ROW row; + row = mysql_fetch_row(result); + if(row[0]) + ret_version = atoi(row[0]); + } + return ret_version; +} + +void LoginDatabase::RemoveOldWorldServerStats(){ + Query query; + query.RunQuery2(Q_DELETE, "delete from login_worldstats where (UNIX_TIMESTAMP(NOW())-UNIX_TIMESTAMP(last_update)) > 86400"); +} + + +void LoginDatabase::UpdateWorldServerStats(LWorld* world, sint32 status) +{ + Query query; + query.RunQuery2(Q_INSERT, "insert into login_worldstats (world_id, world_status, current_players, current_zones, last_update, world_max_level) values(%u, %i, %i, %i, NOW(), %i) ON DUPLICATE KEY UPDATE current_players=%i,current_zones=%i,world_max_level=%i,world_status=%i,last_update=NOW()", + world->GetAccountID(), status, world->GetPlayerNum(), world->GetZoneNum(), world->GetMaxWorldLevel(), world->GetPlayerNum(), world->GetZoneNum(), world->GetMaxWorldLevel(), status); + + string update_stats = string("update login_worldservers set lastseen=%u where id=%i"); + query.RunQuery2(Q_UPDATE, update_stats.c_str(), Timer::GetUnixTimeStamp(), world->GetAccountID()); +} + +bool LoginDatabase::ResetWorldServerStatsConnectedTime(LWorld* world){ + if(!world || world->GetAccountID() == 0) + return false; + + Query query; + string update_stats = string("update login_worldstats set connected_time=now() where world_id=%i and (UNIX_TIMESTAMP(NOW())-UNIX_TIMESTAMP(last_update)) > 300"); + query.RunQuery2(Q_UPDATE, update_stats.c_str(),world->GetAccountID()); + + return true; +} + +void LoginDatabase::ResetWorldStats ( ) +{ + Query query; + string update_stats = string("update login_worldstats set world_status=-4, current_players=0, current_zones=0"); + query.RunQuery2(update_stats.c_str(), Q_UPDATE); +} + +void LoginDatabase::SaveBugReport(int32 world_id, char* category, char* subcategory, char* causes_crash, char* reproducible, char* summary, char* description, char* version, char* player, int32 account_id, char* spawn_name, int32 spawn_id, int32 zone_id){ + Query query; + string bug_report = string("insert into bugs (world_id, category, subcategory, causes_crash, reproducible, summary, description, version, player, account_id, spawn_name, spawn_id, zone_id) values(%lu, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %lu, '%s', %lu, %lu)"); + query.RunQuery2(Q_INSERT, bug_report.c_str(), world_id, database.getSafeEscapeString(category).c_str(), database.getSafeEscapeString(subcategory).c_str(), + database.getSafeEscapeString(causes_crash).c_str(), database.getSafeEscapeString(reproducible).c_str(), database.getSafeEscapeString(summary).c_str(), + database.getSafeEscapeString(description).c_str(), database.getSafeEscapeString(version).c_str(), database.getSafeEscapeString(player).c_str(), account_id, + database.getSafeEscapeString(spawn_name).c_str(), spawn_id, zone_id); + FixBugReport(); +} + +void LoginDatabase::FixBugReport(){ + Query query; + string bug_report = string("update bugs set description = REPLACE(description,SUBSTRING(description,INSTR(description,'%'), 3),char(CONV(SUBSTRING(description,INSTR(description,'%')+1, 2), 16, 10))), summary = REPLACE(summary,SUBSTRING(summary,INSTR(summary,'%'), 3),char(CONV(SUBSTRING(summary,INSTR(summary,'%')+1, 2), 16, 10)))"); + query.RunQuery2(bug_report.c_str(), Q_UPDATE); +} + +void LoginDatabase::UpdateWorldIPAddress(int32 world_id, int32 address){ + struct in_addr in; + in.s_addr = address; + Query query; + query.RunQuery2(Q_UPDATE, "update login_worldservers set ip_address='%s' where id=%lu", inet_ntoa(in), world_id); +} + +void LoginDatabase::UpdateAccountIPAddress(int32 account_id, int32 address){ + struct in_addr in; + in.s_addr = address; + Query query; + query.RunQuery2(Q_UPDATE, "update account set ip_address='%s' where id=%lu", inet_ntoa(in), account_id); +} + +//devn00b: There is no rulesystem for login, so im going to use login_config for future things like this. +//devn00b: Returns the number of characters a player may create per account. This should be set by server owners -> login, +//devn00b: However, better semi-working for now than not working at all. +//devn00b: TODO: EQ2World sends max char per acct. +int8 LoginDatabase::GetMaxCharsSetting() { + //live defaults to 7 for GOLD members. + int8 max_chars = 7; + Query query; + MYSQL_ROW row; + + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "select config_value from login_config where config_name='max_characters_per_account'"); + if (result && mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + if (row[0]) + max_chars = atoi(row[0]); + } + //if nothing else return the default. + return max_chars; +} + +int16 LoginDatabase::GetAccountBonus(int32 acct_id) { + int32 bonus = 0; + int16 world_id = 0; + Query query; + MYSQL_ROW row; + Query query2; + MYSQL_ROW row2; + + //get the world ID for the character. TODO: Support multi server characters. + MYSQL_RES* result2 = query2.RunQuery2(Q_SELECT, "select server_id from login_characters where account_id=%i", acct_id); + + if (result2 && mysql_num_rows(result2) >= 1) { + row2 = mysql_fetch_row(result2); + if (row2[0]) + world_id = atoi(row2[0]); + } + + //pull all characters greater than the max level from the server + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT COUNT(id) FROM login_characters WHERE LEVEL >= (select world_max_level from login_worldstats where world_id=%i) AND account_id=%i", world_id, acct_id); + + if (result && mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + if(row[0]) + bonus = atoi(row[0]); + } + return bonus; +} + +void LoginDatabase::UpdateWorldVersion(int32 world_id, char* version) { + Query query; + query.RunQuery2(Q_UPDATE, "update login_worldservers set login_version='%s' where id=%u", version, world_id); +} + +void LoginDatabase::UpdateAccountClientDataVersion(int32 account_id, int16 version) +{ + Query query; + query.RunQuery2(Q_UPDATE, "UPDATE account SET last_client_version='%i' WHERE id = %u", version, account_id); +} + +//devn00b todo: finish this. +void LoginDatabase::SaveCharacterPicture(int32 account_id, int32 character_id, int32 server_id, int16 picture_size, uchar* picture) { + stringstream ss_hex; + stringstream ss_query; + ss_hex.flags(ios::hex); + for (int32 i = 0; i < picture_size; i++) + ss_hex << setfill('0') << setw(2) << (int32)picture[i]; + + ss_query << "INSERT INTO `ls_character_picture` (`server_id`, `account_id`, `character_id`, `picture`) VALUES (" << server_id << ", " << account_id << ", " << character_id << ", '" << ss_hex.str() << "') ON DUPLICATE KEY UPDATE `picture` = '" << ss_hex.str() << "'"; + + if (!dbLogin.Query(ss_query.str().c_str())) + LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", dbLogin.GetError(), dbLogin.GetErrorMsg()); +} \ No newline at end of file diff --git a/old/LoginServer/LoginDatabase.h b/old/LoginServer/LoginDatabase.h new file mode 100644 index 0000000..27ba85b --- /dev/null +++ b/old/LoginServer/LoginDatabase.h @@ -0,0 +1,96 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. +*/ +#ifndef EQ2LOGIN_EMU_DATABASE_H +#define EQ2LOGIN_EMU_DATABASE_H + +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN + #include + #include +#endif +#include +#include +#include + +#include "../common/database.h" +#include "../common/DatabaseNew.h" +#include "../common/types.h" +#include "../common/MiscFunctions.h" +#include "../common/servertalk.h" +#include "../common/Mutex.h" +#include "PacketHeaders.h" +#include "LoginAccount.h" +#include "LWorld.h" +#include "../common/PacketStruct.h" + +using namespace std; +#pragma pack() +class LoginDatabase : public Database +{ +public: + void FixBugReport(); + void UpdateAccountIPAddress(int32 account_id, int32 address); + void UpdateWorldIPAddress(int32 world_id, int32 address); + void SaveBugReport(int32 world_id, char* category, char* subcategory, char* causes_crash, char* reproducible, char* summary, char* description, char* version, char* player, int32 account_id, char* spawn_name, int32 spawn_id, int32 zone_id); + LoginAccount* LoadAccount(const char* name, const char* password, bool attemptAccountCreation=true); + int32 GetAccountIDByName(const char* name); + int32 CheckServerAccount(char* name, char* passwd); + bool IsServerAccountDisabled(char* name); + bool IsIPBanned(char* ipaddr); + void GetServerAccounts(vector* server_list); + char* GetServerAccountName(int32 id); + bool VerifyDelete(int32 account_id, int32 character_id, const char* name); + void SetServerZoneDescriptions(int32 server_id, map zone_descriptions); + int32 GetServer(int32 accountID, int32 charID, string name); + void LoadCharacters(LoginAccount* acct, int16 version); + void CheckCharacterTimeStamps(LoginAccount* acct); + string GetCharacterName(int32 char_id , int32 server_id, int32 account_id); + void SaveCharacterColors(int32 char_id, char* type, EQ2_Color color); + void SaveCharacterFloats(int32 char_id, char* type, float float1, float float2, float float3, float multiplier=100.0f); + int16 GetAppearanceID(string name); + void DeactivateCharID(int32 server_id, int32 char_id, int32 exception_id); + int32 SaveCharacter(PacketStruct* create, LoginAccount* acct, int32 world_charid, int32 client_version); + void LoadAppearanceData(int32 char_id, PacketStruct* char_select_packet); + bool UpdateCharacterTimeStamp(int32 account_id, int32 character_id, int32 timestamp_update, int32 server_id); + bool UpdateCharacterLevel(int32 account_id, int32 character_id, int8 in_level, int32 server_id); + bool UpdateCharacterRace(int32 account_id, int32 character_id, int16 in_racetype, int8 in_race, int32 server_id); + bool UpdateCharacterClass(int32 account_id, int32 character_id, int8 in_class, int32 server_id); + bool UpdateCharacterName(int32 account_id, int32 character_id, char* newName, int32 server_id); + bool UpdateCharacterZone(int32 account_id, int32 character_id, int32 zone_id, int32 server_id); + bool UpdateCharacterGender(int32 account_id, int32 character_id, int8 in_gender, int32 server_id); + int32 GetRaceID(char* name); + void UpdateRaceID(char* name); + bool DeleteCharacter(int32 account_id, int32 character_id, int32 server_id); + void SaveClientLog(const char* type, const char* message, const char* player_name, int16 version); + bool CheckVersion(char* version); + void GetLatestTableVersions(LatestTableVersions* table_versions); + TableQuery* GetLatestTableQuery(int32 server_ip, char* name, int16 version); + bool VerifyDataTable(char* name); + sint16 GetDataVersion(char* name); + void SetZoneInformation(int32 server_id, int32 zone_id, int32 version, PacketStruct* packet); + string GetZoneDescription(char* name); + string GetColumnNames(char* name); + TableDataQuery* GetTableDataQuery(int32 server_ip, char* name, int16 version); + + void UpdateWorldServerStats( LWorld* world, sint32 status); + bool ResetWorldServerStatsConnectedTime( LWorld* world ); + void RemoveOldWorldServerStats(); + void ResetWorldStats(); + //devn00b temp + bool ConnectNewDatabase(); + void SetServerEquipmentAppearances(int32 server_id, map equip_updates); // JohnAdams: login appearances + int32 GetLoginCharacterIDFromWorldCharID(int32 server_id, int32 char_id); // JohnAdams: login appearances + void RemoveDeletedCharacterData(); + int8 GetMaxCharsSetting(); + int16 GetAccountBonus(int32 acct_id); + void UpdateWorldVersion(int32 world_id, char* version); + void UpdateAccountClientDataVersion(int32 account_id, int16 version); + void SaveCharacterPicture(int32 account_id, int32 character_id, int32 server_id, int16 picture_size, uchar* picture); + + DatabaseNew dbLogin; +}; +#endif \ No newline at end of file diff --git a/old/LoginServer/PacketHeaders.cpp b/old/LoginServer/PacketHeaders.cpp new file mode 100644 index 0000000..4511f6a --- /dev/null +++ b/old/LoginServer/PacketHeaders.cpp @@ -0,0 +1,88 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. +*/ +#include "PacketHeaders.h" +#include "../common/MiscFunctions.h" +#include "LoginDatabase.h" +#include "LWorld.h" + +extern LWorldList world_list; +extern LoginDatabase database; + +void LS_DeleteCharacterRequest::loadData(EQApplicationPacket* packet){ + InitializeLoadData(packet->pBuffer, packet->size); + LoadData(character_number); + LoadData(server_id); + LoadData(spacer); + LoadDataString(name); +} + +EQ2Packet* LS_CharSelectList::serialize(int16 version){ + Clear(); + AddData(num_characters); + AddData(char_data); + if (version <= 561) { + LS_CharListAccountInfoEarlyClient account_info; + account_info.account_id = account_id; + account_info.unknown1 = 0xFFFFFFFF; + account_info.unknown2 = 0; + account_info.maxchars = 7; //live has a max of 7 on gold accounts base. + account_info.unknown4 = 0; + AddData(account_info); + } + else { + LS_CharListAccountInfo account_info; + account_info.account_id = account_id; + account_info.unknown1 = 0xFFFFFFFF; + account_info.unknown2 = 0; + account_info.maxchars = database.GetMaxCharsSetting(); + account_info.vet_adv_bonus = database.GetAccountBonus(account_id); + account_info.vet_trade_bonus = 0; + account_info.unknown4 = 0; + for (int i = 0; i < 3; i++) + account_info.unknown5[i] = 0xFFFFFFFF; + account_info.unknown5[3] = 0; + + AddData(account_info); + } + return new EQ2Packet(OP_AllCharactersDescReplyMsg, getData(), getDataSize()); +} + +void LS_CharSelectList::addChar(uchar* data, int16 size){ + char_data.append((char*)data, size); +} + +void LS_CharSelectList::loadData(int32 account, vector charlist, int16 version){ + vector::iterator itr; + account_id = account; + num_characters = 0; + char_data = ""; + CharSelectProfile* character = 0; + for(itr = charlist.begin();itr != charlist.end();itr++){ + character = *itr; + int32 serverID = character->packet->getType_int32_ByName("server_id"); + if(character->deleted) { // workaround for old clients <= 561 that crash if you delete a char (Doesn't refresh the char panel correctly) + character->packet->setDataByName("name", "(deleted)"); + character->packet->setDataByName("charid", 0xFFFFFFFF); + character->packet->setDataByName("name", 0xFFFFFFFF); + character->packet->setDataByName("server_id", 0xFFFFFFFF); + character->packet->setDataByName("created_date", 0xFFFFFFFF); + character->packet->setDataByName("unknown1", 0xFFFFFFFF); + character->packet->setDataByName("unknown2", 0xFFFFFFFF); + character->packet->setDataByName("flags", 0xFF); + } + else if(serverID == 0 || !world_list.FindByID(serverID)) + continue; + num_characters++; + character->SaveData(version); + addChar(character->getData(), character->getDataSize()); + } +} + +void CharSelectProfile::SaveData(int16 in_version){ + Clear(); + AddData(*packet->serializeString()); +} diff --git a/old/LoginServer/PacketHeaders.h b/old/LoginServer/PacketHeaders.h new file mode 100644 index 0000000..69a30c5 --- /dev/null +++ b/old/LoginServer/PacketHeaders.h @@ -0,0 +1,61 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. +*/ +#ifndef __PACKET_HEADERS__ +#define __PACKET_HEADERS__ + +#include "../common/types.h" +#include "../common/EQPacket.h" +#include "../common/EQ2_Common_Structs.h" +#include "login_structs.h" +#include "../common/DataBuffer.h" +#include "../common/GlobalHeaders.h" +#include "../common/ConfigReader.h" +#include + +extern ConfigReader configReader; + +class CharSelectProfile : public DataBuffer{ +public: + CharSelectProfile(int16 version){ + deleted = false; + packet = configReader.getStruct("CharSelectProfile",version); + for(int8 i=0;i<24;i++){ + packet->setEquipmentByName("equip",0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,i); + } + } + + ~CharSelectProfile(){ + safe_delete(packet); + } + PacketStruct* packet; + + void SaveData(int16 in_version); + void Data(); + int16 size; + bool deleted; +}; + +class LS_CharSelectList : public DataBuffer { +public: + int8 num_characters; + int32 account_id; + + EQ2Packet* serialize(int16 version); + void addChar(uchar* data, int16 size); + string char_data; + void loadData(int32 account, vector charlist, int16 version); +}; + +class LS_DeleteCharacterRequest : public DataBuffer{ +public: + int32 character_number; + int32 server_id; + int32 spacer; + EQ2_16BitString name; + void loadData(EQApplicationPacket* packet); +}; +#endif \ No newline at end of file diff --git a/old/LoginServer/Web/LoginWeb.cpp b/old/LoginServer/Web/LoginWeb.cpp new file mode 100644 index 0000000..03eb554 --- /dev/null +++ b/old/LoginServer/Web/LoginWeb.cpp @@ -0,0 +1,67 @@ +#include "../net.h" +#include "../LWorld.h" + +#include +#include +#include + +extern ClientList client_list; +extern LWorldList world_list; +extern NetConnection net; + +void NetConnection::Web_loginhandle_status(const http::request& req, http::response& res) { + res.set(http::field::content_type, "application/json"); + boost::property_tree::ptree pt; + + pt.put("web_status", "online"); + pt.put("login_status", net.login_running ? "online" : "offline"); + pt.put("login_uptime", (getCurrentTimestamp() - net.login_uptime)); + auto [days, hours, minutes, seconds] = convertTimestampDuration((getCurrentTimestamp() - net.login_uptime)); + std::string uptime_str("Days: " + std::to_string(days) + ", " + "Hours: " + std::to_string(hours) + ", " + "Minutes: " + std::to_string(minutes) + ", " + "Seconds: " + std::to_string(seconds)); + pt.put("login_uptime_string", uptime_str); + pt.put("world_count", world_list.GetCount(ConType::World)); + pt.put("client_count", net.numclients); + + std::ostringstream oss; + boost::property_tree::write_json(oss, pt); + std::string json = oss.str(); + res.body() = json; + res.prepare_payload(); +} + +void NetConnection::Web_loginhandle_worlds(const http::request& req, http::response& res) { + world_list.PopulateWorldList(res); +} + +void LWorldList::PopulateWorldList(http::response& res) { + + struct in_addr in; + res.set(http::field::content_type, "application/json"); + boost::property_tree::ptree maintree; + + std::ostringstream oss; + + map::iterator map_list; + for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { + LWorld* world = map_list->second; + in.s_addr = world->GetIP(); + if (world->GetType() == World) { + + boost::property_tree::ptree pt; + pt.put("id", world->GetID()); + pt.put("world_name", world->GetName()); + pt.put("status", (world->GetStatus() == 1) ? "online" : "offline"); + pt.put("num_players", world->GetPlayerNum()); + pt.put("ip_addr", inet_ntoa(in)); + maintree.push_back(std::make_pair("", pt)); + } + } + + boost::property_tree::ptree result; + result.add_child("WorldServers", maintree); + boost::property_tree::write_json(oss, result); + std::string json = oss.str(); + res.body() = json; + res.prepare_payload(); +} + diff --git a/old/LoginServer/client.cpp b/old/LoginServer/client.cpp new file mode 100644 index 0000000..d41728f --- /dev/null +++ b/old/LoginServer/client.cpp @@ -0,0 +1,813 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. +*/ +#include "../common/debug.h" +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#else +#include +#include +#include +#include +#endif +#include +#include +#include +#include + +#include "net.h" +#include "client.h" +#include "../common/EQStream.h" +#include "../common/packet_dump.h" +#include "../common/packet_functions.h" +#include "../common/emu_opcodes.h" +#include "../common/MiscFunctions.h" +#include "LWorld.h" +#include "LoginDatabase.h" +#include "../common/ConfigReader.h" +#include "../common/Log.h" + +extern NetConnection net; +extern LWorldList world_list; +extern ClientList client_list; +extern LoginDatabase database; +extern mapEQOpcodeManager; +extern ConfigReader configReader; +using namespace std; +Client::Client(EQStream* ieqnc) { + eqnc = ieqnc; + ip = eqnc->GetrIP(); + port = ntohs(eqnc->GetrPort()); + account_id = 0; + lsadmin = 0; + worldadmin = 0; + lsstatus = 0; + version = 0; + kicked = false; + verified = false; + memset(bannedreason, 0, sizeof(bannedreason)); + //worldresponse_timer = new Timer(10000); + //worldresponse_timer->Disable(); + memset(key,0,10); + LoginMode = None; + num_updates = 0; + updatetimer = new Timer(500); + updatelisttimer = new Timer(10000); + //keepalive = new Timer(5000); + //logintimer = new Timer(500); // Give time for the servers to send updates + //keepalive->Start(); + //updatetimer->Start(); + //logintimer->Disable(); + disconnectTimer = 0; + memset(ClientSession,0,25); + request_num = 0; + login_account = 0; + createRequest = 0; + playWaitTimer = NULL; + start = false; + update_position = 0; + update_packets = 0; + needs_world_list = true; + sent_character_list = false; +} + +Client::~Client() { + //safe_delete(worldresponse_timer); + //safe_delete(logintimer); + safe_delete(login_account); + eqnc->Close(); + safe_delete(playWaitTimer); + safe_delete(createRequest); + safe_delete(disconnectTimer); + safe_delete(updatetimer); +} + +bool Client::Process() { + if(!start && !eqnc->CheckActive()){ + if(!playWaitTimer) + playWaitTimer = new Timer(5000); + else if(playWaitTimer->Check()){ + safe_delete(playWaitTimer); + return false; + } + return true; + } + else if(!start){ + safe_delete(playWaitTimer); + start = true; + } + + if (disconnectTimer && disconnectTimer->Check()) + { + safe_delete(disconnectTimer); + getConnection()->SendDisconnect(); + } + + if (!kicked) { + /************ Get all packets from packet manager out queue and process them ************/ + EQApplicationPacket *app = 0; + /*if(logintimer && logintimer->Check()) + { + database.LoadCharacters(GetLoginAccount()); + SendLoginAccepted(); + logintimer->Disable(); + }*/ + /*if(worldresponse_timer && worldresponse_timer->Check()) + { + FatalError(WorldDownErrorMessage); + worldresponse_timer->Disable(); + }*/ + + if(playWaitTimer != NULL && playWaitTimer->Check ( ) ) + { + SendPlayFailed(PLAY_ERROR_SERVER_TIMEOUT); + safe_delete(playWaitTimer); + } + if(!needs_world_list && updatetimer && updatetimer->Check()){ + if(updatelisttimer && updatelisttimer->Check()){ + if(num_updates >= 180){ //30 minutes + getConnection()->SendDisconnect(); + } + else{ + vector::iterator itr; + if(update_packets){ + for(itr = update_packets->begin(); itr != update_packets->end(); itr++){ + safe_delete(*itr); + } + } + safe_delete(update_packets); + update_packets = world_list.GetServerListUpdate(version); + } + num_updates++; + } + else{ + if(!update_packets){ + update_packets = world_list.GetServerListUpdate(version); + } + else{ + if(update_position < update_packets->size()){ + QueuePacket(update_packets->at(update_position)->serialize()); + update_position++; + } + else + update_position = 0; + } + } + } + + while(app = eqnc->PopPacket()) + { + switch(app->GetOpcode()) + { + case OP_LoginRequestMsg:{ + DumpPacket(app); + PacketStruct* packet = configReader.getStruct("LS_LoginRequest", 1); + if(packet && packet->LoadPacketData(app->pBuffer,app->size)){ + version = packet->getType_int16_ByName("version"); + LogWrite(LOGIN__DEBUG, 0, "Login", "Classic Client Version Provided: %i", version); + + if (version == 0 || EQOpcodeManager.count(GetOpcodeVersion(version)) == 0) + { + safe_delete(packet); + packet = configReader.getStruct("LS_LoginRequest", 1208); + if (packet && packet->LoadPacketData(app->pBuffer, app->size)) { + version = packet->getType_int16_ByName("version"); + } + else + break; + } + //[7:19 PM] Kirmmin: Well, I very quickly learned that unknown3 in LS_LoginRequest packet is the same value as cl_eqversion in the eq2_defaults.ini file. + + LogWrite(LOGIN__DEBUG, 0, "Login", "New Client Version Provided: %i", version); + + if (EQOpcodeManager.count(GetOpcodeVersion(version)) == 0) { + LogWrite(LOGIN__ERROR, 0, "Login", "Incompatible client version provided: %i", version); + SendLoginDenied(); + return false; + } + + if(EQOpcodeManager.count(GetOpcodeVersion(version)) > 0 && getConnection()){ + getConnection()->SetClientVersion(GetVersion()); + EQ2_16BitString username = packet->getType_EQ2_16BitString_ByName("username"); + EQ2_16BitString password = packet->getType_EQ2_16BitString_ByName("password"); + LoginAccount* acct = database.LoadAccount(username.data.c_str(),password.data.c_str(), net.IsAllowingAccountCreation()); + if(acct){ + Client* otherclient = client_list.FindByLSID(acct->getLoginAccountID()); + if(otherclient) + otherclient->getConnection()->SendDisconnect(); // This person is already logged in, we don't want them logged in twice, kick the previous client as it might be a ghost + } + if(acct){ + SetAccountName(username.data.c_str()); + database.UpdateAccountIPAddress(acct->getLoginAccountID(), getConnection()->GetrIP()); + database.UpdateAccountClientDataVersion(acct->getLoginAccountID(), version); + LogWrite(LOGIN__INFO, 0, "Login", "%s successfully logged in.", (char*)username.data.c_str()); + } + else + { + if (username.size > 0) + LogWrite(LOGIN__ERROR, 0, "Login", "%s login failed!", (char*)username.data.c_str()); + else + LogWrite(LOGIN__ERROR, 0, "Login", "[UNKNOWN USER] login failed!"); + } + + if(!acct) + SendLoginDenied(); + else{ + needs_world_list = true; + SetLoginAccount(acct); + SendLoginAccepted(); + } + } + else{ + cout << "Error bad version: " << version << endl; + SendLoginDeniedBadVersion(); + } + } + else{ + cout << "Error loading LS_LoginRequest packet: \n"; + //DumpPacket(app); + } + safe_delete(packet); + break; + } + case OP_KeymapLoadMsg:{ + // cout << "Received OP_KeymapNoneMsg\n"; + //dunno what this is for + break; + } + case OP_AllWSDescRequestMsg:{ + SendWorldList(); + needs_world_list = false; + if(!sent_character_list) { + database.LoadCharacters(GetLoginAccount(), GetVersion()); + sent_character_list = true; + } + SendCharList(); + break; + } + case OP_LsClientCrashlogReplyMsg:{ +// DumpPacket(app); + SaveErrorsToDB(app, "Crash Log", GetVersion()); + break; + } + case OP_LsClientVerifylogReplyMsg:{ +// DumpPacket(app); + SaveErrorsToDB(app, "Verify Log", GetVersion()); + break; + } + case OP_LsClientAlertlogReplyMsg:{ +// DumpPacket(app); + SaveErrorsToDB(app, "Alert Log", GetVersion()); + break; + } + case OP_LsClientBaselogReplyMsg:{ +// DumpPacket(app); + SaveErrorsToDB(app, "Base Log", GetVersion()); + break; + } + case OP_AllCharactersDescRequestMsg:{ + break; + } + case OP_CreateCharacterRequestMsg:{ + PacketStruct* packet = configReader.getStruct("CreateCharacter", GetVersion()); + + DumpPacket(app); + playWaitTimer = new Timer ( 15000 ); + playWaitTimer->Start ( ); + + LogWrite(WORLD__INFO, 1, "World", "Character creation request from account %s", GetAccountName()); + if(packet->LoadPacketData(app->pBuffer,app->size, GetVersion() <= 561 ? false : true)){ + DumpPacket(app->pBuffer, app->size); + packet->setDataByName("account_id",GetAccountID()); + LWorld* world_server = world_list.FindByID(packet->getType_int32_ByName("server_id")); + if(!world_server) + { + DumpPacket(app->pBuffer, app->size); + cout << GetAccountName() << " attempted creation of character with an invalid server id of: " << packet->getType_int32_ByName("server_id") << "\n"; + break; + } + else + { + createRequest = packet; + ServerPacket* outpack = new ServerPacket(ServerOP_CharacterCreate, app->size+sizeof(int16)); + int16 out_version = GetVersion(); + memcpy(outpack->pBuffer, &out_version, sizeof(int16)); + memcpy(outpack->pBuffer + sizeof(int16), app->pBuffer, app->size); + uchar* tmp = outpack->pBuffer; + + if(out_version<=283) + tmp+=2; + else if(out_version == 373) { + tmp += 6; + } + else + tmp += 7; + + int32 account_id = GetAccountID(); + memcpy(tmp, &account_id, sizeof(int32)); + world_server->SendPacket(outpack); + safe_delete(outpack); + } + } + else{ + LogWrite(WORLD__ERROR, 1, "World", "Error in character creation request from account %s!", GetAccountName()); + safe_delete(packet); + } + // world_list.SendWorldChanged(create.profile.server_id, false, this); + break; + } + case OP_PlayCharacterRequestMsg:{ + int32 char_id = 0; + int32 server_id = 0; + PacketStruct* request = configReader.getStruct("LS_PlayRequest",GetVersion()); + if(request && request->LoadPacketData(app->pBuffer,app->size)){ + char_id = request->getType_int32_ByName("char_id"); + if (GetVersion() <= 283) { + server_id = database.GetServer(GetAccountID(), char_id, request->getType_EQ2_16BitString_ByName("name").data); + } + else { + server_id = request->getType_int32_ByName("server_id"); + } + LWorld* world = world_list.FindByID(server_id); + string name = database.GetCharacterName(char_id,server_id,GetAccountID()); + if(world && name.length() > 0){ + pending_play_char_id = char_id; + ServerPacket* outpack = new ServerPacket(ServerOP_UsertoWorldReq, sizeof(UsertoWorldRequest_Struct)); + UsertoWorldRequest_Struct* req = (UsertoWorldRequest_Struct*)outpack->pBuffer; + req->char_id = char_id; + req->lsaccountid = GetAccountID(); + req->worldid = server_id; + + struct in_addr in; + in.s_addr = GetIP(); + strcpy(req->ip_address, inet_ntoa(in)); + world->SendPacket(outpack); + delete outpack; + + safe_delete(playWaitTimer); + + playWaitTimer = new Timer ( 5000 ); + playWaitTimer->Start ( ); + } + else{ + cout << GetAccountName() << " sent invalid Play Request: \n"; + SendPlayFailed(PLAY_ERROR_PROBLEM); + DumpPacket(app); + } + } + safe_delete(request); + break; + } + case OP_DeleteCharacterRequestMsg:{ + PacketStruct* request = configReader.getStruct("LS_DeleteCharacterRequest", GetVersion()); + PacketStruct* response = configReader.getStruct("LS_DeleteCharacterResponse", GetVersion()); + if(request && response && request->LoadPacketData(app->pBuffer,app->size)){ + EQ2_16BitString name = request->getType_EQ2_16BitString_ByName("name"); + int32 acct_id = GetAccountID(); + int32 char_id = request->getType_int32_ByName("char_id"); + int32 server_id = request->getType_int32_ByName("server_id"); + if(database.VerifyDelete(acct_id, char_id, name.data.c_str())){ + response->setDataByName("response", 1); + GetLoginAccount()->removeCharacter((char*)name.data.c_str(), GetVersion()); + LWorld* world_server = world_list.FindByID(server_id); + if(world_server != NULL) + world_server->SendDeleteCharacter ( char_id , acct_id ); + } + else + response->setDataByName("response", 0); + response->setDataByName("server_id", server_id); + response->setDataByName("char_id", char_id); + response->setDataByName("account_id", account_id); + response->setMediumStringByName("name", (char*)name.data.c_str()); + response->setDataByName("max_characters", 10); + + EQ2Packet* outapp = response->serialize(); + QueuePacket(outapp); + + this->SendCharList(); + } + safe_delete(request); + safe_delete(response); + break; + } + default: { + const char* name = app->GetOpcodeName(); + if (name) + LogWrite(OPCODE__DEBUG, 1, "Opcode", "%s Received %04X (%i)", name, app->GetRawOpcode(), app->GetRawOpcode()); + else + LogWrite(OPCODE__DEBUG, 1, "Opcode", "Received %04X (%i)", app->GetRawOpcode(), app->GetRawOpcode()); + } + } + delete app; + } + } + + if (!eqnc->CheckActive()) { + return false; + } + + return true; +} + +void Client::SaveErrorsToDB(EQApplicationPacket* app, char* type, int32 version){ + int32 size = 0; + z_stream zstream; + if (version >= 546) { + memcpy(&size, app->pBuffer + sizeof(int32), sizeof(int32)); + zstream.next_in = app->pBuffer + 8; + zstream.avail_in = app->size - 8; + } + else { //box set + size = 0xFFFF; + zstream.next_in = app->pBuffer + 2; + zstream.avail_in = app->size - 2; + } + size++; + char* message = new char[size]; + memset(message, 0, size); + + int zerror = 0; + + zstream.next_out = (BYTE*)message; + zstream.avail_out = size; + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + + zerror = inflateInit( &zstream); + if(zerror != Z_OK) { + safe_delete_array(message); + return; + } + zerror = inflate( &zstream, 0 ); + if(message && strlen(message) > 0) + database.SaveClientLog(type, message, GetLoginAccount()->getLoginName(), GetVersion()); + safe_delete_array(message); +} + +void Client::CharacterApproved(int32 server_id,int32 char_id) +{ + if(createRequest && server_id == createRequest->getType_int32_ByName("server_id")){ + LWorld* world_server = world_list.FindByID(server_id); + if(!world_server) + return; + + PacketStruct* packet = configReader.getStruct("LS_CreateCharacterReply", GetVersion()); + if(packet){ + packet->setDataByName("account_id", GetAccountID()); + packet->setDataByName("unknown", 0xFFFFFFFF); + packet->setDataByName("response", CREATESUCCESS_REPLY); + packet->setMediumStringByName("name", (char*)createRequest->getType_EQ2_16BitString_ByName("name").data.c_str()); + EQ2Packet* outapp = packet->serialize(); + QueuePacket(outapp); + safe_delete(packet); + database.SaveCharacter(createRequest, GetLoginAccount(),char_id, GetVersion()); + + // refresh characters for this account + database.LoadCharacters(GetLoginAccount(), GetVersion()); + + SendCharList(); + + if (GetVersion() <= 561) + { + pending_play_char_id = char_id; + ServerPacket* outpack = new ServerPacket(ServerOP_UsertoWorldReq, sizeof(UsertoWorldRequest_Struct)); + UsertoWorldRequest_Struct* req = (UsertoWorldRequest_Struct*)outpack->pBuffer; + req->char_id = char_id; + req->lsaccountid = GetAccountID(); + req->worldid = server_id; + + struct in_addr in; + in.s_addr = GetIP(); + strcpy(req->ip_address, inet_ntoa(in)); + world_server->SendPacket(outpack); + delete outpack; + } + } + } + else{ + cout << GetAccountName() << " received invalid CharacterApproval from server: " << server_id << endl; + } + safe_delete(createRequest); +} + +void Client::CharacterRejected(int8 reason_number) +{ + PacketStruct* packet = configReader.getStruct("LS_CreateCharacterReply", GetVersion()); + if(createRequest && packet){ + packet->setDataByName("account_id", GetAccountID()); + int8 clientReasonNum = reason_number; + // reason numbers change and instead of updating the world server + // the login server will hold the up to date #'s +/* + switch(reason_number) + { + // these error codes seem to be removed now, they shutdown the client rather immediately + // for now we are just going to play a joke on them and say they can't create a new character. + case INVALIDRACE_REPLY: + case INVALIDGENDER_REPLY: + clientReasonNum = 8; + break; + case BADNAMELENGTH_REPLY: + clientReasonNum = 9; + break; + case NAMEINVALID_REPLY: + clientReasonNum = 10; + break; + case NAMEFILTER_REPLY: + clientReasonNum = 11; + break; + case NAMETAKEN_REPLY: + clientReasonNum = 12; + break; + case OVERLOADEDSERVER_REPLY: + clientReasonNum = 13; + break; + } +*/ + packet->setDataByName("response", clientReasonNum); + packet->setMediumStringByName("name", ""); + EQ2Packet* outapp = packet->serialize(); + QueuePacket(outapp); + safe_delete(packet); + } + /*LS_CreateCharacterReply reply(GetAccountID(), reason_number, create.profile.name.data); + EQ2Packet* outapp = reply.serialize(); + QueuePacket(outapp); + create.Clear();*/ +} + +void Client::SendCharList(){ + /*PacketStruct* packet = configReader.getStruct("LS_CreateCharacterReply"); + packet->setDataByName("account_id", GetAccountID()); + packet->setDataByName("response", reason_number); + packet->setDataByName("name", &create.profile.name); + EQ2Packet* outapp = packet->serialize(); + QueuePacket(outapp); + safe_delete(packet);*/ + LogWrite(LOGIN__INFO, 0, "Login", "[%s] sending character list.", GetAccountName()); + LS_CharSelectList list; + list.loadData(GetAccountID(), GetLoginAccount()->charlist, GetVersion()); + EQ2Packet* outapp = list.serialize(GetVersion()); + DumpPacket(outapp->pBuffer, outapp->size); + QueuePacket(outapp); + +} +void Client::SendLoginDeniedBadVersion(){ + EQ2Packet* app = new EQ2Packet(OP_LoginReplyMsg, 0, sizeof(LS_LoginResponse)); + LS_LoginResponse* ls_response = (LS_LoginResponse*)app->pBuffer; + ls_response->reply_code = 6; + ls_response->unknown03 = 0xFFFFFFFF; + ls_response->unknown04 = 0xFFFFFFFF; + QueuePacket(app); + StartDisconnectTimer(); +} +void Client::SendLoginDenied(){ + EQ2Packet* app = new EQ2Packet(OP_LoginReplyMsg, 0, sizeof(LS_LoginResponse)); + LS_LoginResponse* ls_response = (LS_LoginResponse*)app->pBuffer; + ls_response->reply_code = 1; + // reply_codes for AoM: + /* 1 = Login rejected: Invalid username or password. Please try again. + 2 = Login rejected: Server thinks your account is currently playing; you may have to wait " + "a few minutes for it to clear, then try again + 6 = Login rejected: The client's version does not match the server's. Please re-run the patcher. + 7 = Login rejected: You have no scheduled playtimes. + 8 = Your account does not have the features required to play on this server. + 11 = The client's build does not match the server's. Please re-run the patcher. + 12 = You must update your password in order to log in. Pressing OK will op" + "en your web browser to the SOE password management page + Other Value > 1 = Login rejected for an unknown reason. + */ + ls_response->unknown03 = 0xFFFFFFFF; + ls_response->unknown04 = 0xFFFFFFFF; + QueuePacket(app); + StartDisconnectTimer(); +} + +void Client::SendLoginAccepted(int32 account_id, int8 login_response) { + PacketStruct* packet = configReader.getStruct("LS_LoginReplyMsg", GetVersion()); + int i = 0; + if (packet) + { + packet->setDataByName("account_id", account_id); + + packet->setDataByName("login_response", login_response); + + packet->setDataByName("do_not_force_soga", 1); + + // sub_level 0xFFFFFFFF = blacks out all portraits for class alignments, considered non membership + // sub_level > 0 = class alignments still required, but portraits are viewable and race selectable + // sub_level = 2 membership, you can 'create characters on time locked servers' vs standard + // sub_level = 0 forces popup on close to web browser + packet->setDataByName("sub_level", net.GetDefaultSubscriptionLevel()); + packet->setDataByName("race_flag", 0x1FFFFF); + packet->setDataByName("class_flag", 0x7FFFFFE); + packet->setMediumStringByName("username", GetAccountName()); + packet->setMediumStringByName("password", GetAccountName()); + + // unknown5 + // full support = 0x7CFF + // 1 << 12 (-4096) = missing echoes of faydwer, disables Fae and Arasai (black portraits) and kelethin as starting city + // 1 << 13 (-8192) = disables sarnak (black portraits) and gorowyn as starting city + packet->setDataByName("unknown5", net.GetExpansionFlag()); + packet->setDataByName("unknown6", 0xFF); + packet->setDataByName("unknown6", 0xFF, 1); + packet->setDataByName("unknown6", 0xFF, 2); + + // controls class access / playable characters + packet->setDataByName("unknown10", 0xFF); + + // packet->setDataByName("unknown7a", 0x0101); + // packet->setDataByName("race_unknown", 0x01); + packet->setDataByName("unknown7", net.GetEnabledRaces()); // 0x01-0xFF disable extra races FAE(16) ARASAI (17) SARNAK (18) -- with 4096/8192 flags, no visibility of portraits + packet->setDataByName("unknown7a", 0xEE); + packet->setDataByName("unknown8", net.GetCitiesFlag(), 1); // dword_1ECBA18 operand for race flag packs (sublevel 0,1,2?) -- (sublevel -1) controls starting zones omission 0xEE vs 0xCF (CF misses halas) + + /* + 1 = city of qeynos + 2 = city of freeport + 4 = city of kelethin + 8 = city of neriak + 16 = gorowyn + 32 = new halas + 64 = queens colony + 128 = outpost overlord + */ + + EQ2Packet* outapp = packet->serialize(); + QueuePacket(outapp); + safe_delete(packet); + } +} + +void Client::SendWorldList(){ + EQ2Packet* pack = world_list.MakeServerListPacket(lsadmin, version); + EQ2Packet* dupe = pack->Copy(); + DumpPacket(dupe->pBuffer,dupe->size); + QueuePacket(dupe); + + SendLoginAccepted(0, 10); // triggers a different code path in the client to set certain flags + return; +} + +void Client::QueuePacket(EQ2Packet* app){ + eqnc->EQ2QueuePacket(app); +} + +void Client::WorldResponse(int32 worldid, int8 response, char* ip_address, int32 port, int32 access_key) +{ + LWorld* world = world_list.FindByID(worldid); + if(world == 0) { + FatalError(0); + return; + } + if(response != 1){ + if(response == PLAY_ERROR_CHAR_NOT_LOADED){ + string pending_play_char_name = database.GetCharacterName(pending_play_char_id, worldid, GetAccountID()); + if(database.VerifyDelete(GetAccountID(), pending_play_char_id, pending_play_char_name.c_str())){ + GetLoginAccount()->removeCharacter((char*)pending_play_char_name.c_str(), GetVersion()); + } + } + FatalError(response); + return; + } + + PacketStruct* response_packet = configReader.getStruct("LS_PlayResponse", GetVersion()); + if(response_packet){ + safe_delete(playWaitTimer); + response_packet->setDataByName("response", 1); + response_packet->setSmallStringByName("server", ip_address); + response_packet->setDataByName("port", port); + response_packet->setDataByName("account_id", GetAccountID()); + response_packet->setDataByName("access_code", access_key); + EQ2Packet* outapp = response_packet->serialize(); + QueuePacket(outapp); + safe_delete(response_packet); + } + return; +} +void Client::FatalError(int8 response) { + safe_delete(playWaitTimer); + SendPlayFailed(response); +} + +void Client::SendPlayFailed(int8 response){ + PacketStruct* response_packet = configReader.getStruct("LS_PlayResponse", GetVersion()); + if(response_packet){ + response_packet->setDataByName("response", response); + response_packet->setSmallStringByName("server", ""); + response_packet->setDataByName("port", 0); + response_packet->setDataByName("account_id", GetAccountID()); + response_packet->setDataByName("access_code", 0); + EQ2Packet* outapp = response_packet->serialize(); + QueuePacket(outapp); + safe_delete(response_packet); + } +} + +void ClientList::Add(Client* client) { + MClientList.writelock(); + client_list[client] = true; + MClientList.releasewritelock(); +} + +Client* ClientList::Get(int32 ip, int16 port) { + Client* ret = 0; + map::iterator itr; + MClientList.readlock(); + for(itr = client_list.begin(); itr != client_list.end(); itr++){ + if(itr->first->GetIP() == ip && itr->first->GetPort() == port){ + ret = itr->first; + break; + } + } + MClientList.releasereadlock(); + return ret; +} + +void ClientList::FindByCreateRequest(){ + Client* client = 0; + map::iterator itr; + MClientList.readlock(); + for(itr = client_list.begin(); itr != client_list.end(); itr++){ + if(itr->first->AwaitingCharCreationRequest()){ + if(!client) + client = itr->first; + else{ + client = 0;//more than 1 character waiting, dont want to send rejection to wrong one + break; + } + } + } + MClientList.releasereadlock(); + if(client) + client->CharacterRejected(UNKNOWNERROR_REPLY); +} + +Client* ClientList::FindByLSID(int32 lsaccountid) { + Client* client = 0; + map::iterator itr; + MClientList.readlock(); + for(itr = client_list.begin(); itr != client_list.end(); itr++){ + if(itr->first->GetAccountID() == lsaccountid){ + client = itr->first; + break; + } + } + MClientList.releasereadlock(); + return client; +} +void ClientList::SendPacketToAllClients(EQ2Packet* app){ + Client* client = 0; + map::iterator itr; + MClientList.readlock(); + if(client_list.size() > 0){ + for(itr = client_list.begin(); itr != client_list.end(); itr++){ + itr->first->QueuePacket(app->Copy()); + } + } + safe_delete(app); + MClientList.releasereadlock(); +} +void ClientList::Process() { + Client* client = 0; + vector erase_list; + map::iterator itr; + MClientList.readlock(); + for(itr = client_list.begin(); itr != client_list.end(); itr++){ + client = itr->first; + if(!client->Process()) + erase_list.push_back(client); + } + MClientList.releasereadlock(); + if(erase_list.size() > 0){ + vector::iterator erase_itr; + MClientList.writelock(); + for(erase_itr = erase_list.begin(); erase_itr != erase_list.end(); erase_itr++){ + client = *erase_itr; + struct in_addr in; + in.s_addr = client->getConnection()->GetRemoteIP(); + net.numclients--; + LogWrite(LOGIN__INFO, 0, "Login", "Removing client from ip: %s on port %i, Account Name: %s", inet_ntoa(in), ntohs(client->getConnection()->GetRemotePort()), client->GetAccountName()); + client->getConnection()->Close(); + net.UpdateWindowTitle(); + client_list.erase(client); + } + MClientList.releasewritelock(); + } +} + + +void Client::StartDisconnectTimer() { + if (!disconnectTimer) + { + disconnectTimer = new Timer(1000); + disconnectTimer->Start(); + } +} diff --git a/old/LoginServer/client.h b/old/LoginServer/client.h new file mode 100644 index 0000000..4d3936e --- /dev/null +++ b/old/LoginServer/client.h @@ -0,0 +1,131 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. +*/ +#ifndef CLIENT_H +#define CLIENT_H + +#include "../common/linked_list.h" +#include "../common/timer.h" +#include "../common/TCPConnection.h" +#include "login_structs.h" +#include "LoginAccount.h" +#include "../common/PacketStruct.h" +#include +#include + +enum eLoginMode { None, Normal, Registration }; +class DelayQue; +class Client +{ +public: + Client(EQStream* ieqnc); + ~Client(); + void SendLoginDenied(); + void SendLoginDeniedBadVersion(); + void SendLoginAccepted(int32 account_id = 1, int8 login_response = 0); + void SendWorldList(); + void SendCharList(); + int16 AddWorldToList2(uchar* buffer, char* name, int32 id, int16* flags); + void GenerateChecksum(EQApplicationPacket* outapp); + int8 LoginKey[10]; + int8 ClientSession[25]; + bool Process(); + void SaveErrorsToDB(EQApplicationPacket* app, char* type, int32 version); + void CharacterApproved(int32 server_id,int32 char_id); + void CharacterRejected(int8 reason_number); + EQStream* getConnection() { return eqnc; } + LoginAccount* GetLoginAccount() { return login_account; } + void SetLoginAccount(LoginAccount* in_account) { + login_account = in_account; + if(in_account) + account_id = in_account->getLoginAccountID(); + } + int16 GetVersion(){ return version; } + char* GetKey() { return key; } + void SetKey(char* in_key) { strcpy(key,in_key); } + int32 GetIP() { return ip; } + int16 GetPort() { return port; } + int32 GetAccountID() { return account_id; } + const char* GetAccountName(){ return (char*)account_name.c_str(); } + void SetAccountName(const char* name){ account_name = string(name); } + void ProcessLogin(char* name, char* pass,int seq=0); + void QueuePacket(EQ2Packet* app); + void FatalError(int8 response); + void WorldResponse(int32 worldid, int8 response, char* ip_address, int32 port, int32 access_key); + bool AwaitingCharCreationRequest(){ + if(createRequest) + return true; + else + return false; + } + Timer* updatetimer; + Timer* updatelisttimer; + Timer* disconnectTimer; + //Timer* keepalive; + //Timer* logintimer; + int16 packettotal; + int32 requested_server_id; + int32 request_num; + LinkedList delay_que; + void SendPlayFailed(int8 response); + + void StartDisconnectTimer(); +private: + string pending_play_char_name; + int32 pending_play_char_id; + int8 update_position; + int16 num_updates; + vector* update_packets; + LoginAccount* login_account; + EQStream* eqnc; + + int32 ip; + int16 port; + + int32 account_id; + string account_name; + char key[10]; + int8 lsadmin; + sint16 worldadmin; + int lsstatus; + bool kicked; + bool verified; + bool start; + bool needs_world_list; + int16 version; + char bannedreason[30]; + bool sent_character_list; + eLoginMode LoginMode; + PacketStruct* createRequest; + Timer* playWaitTimer; +}; + +class ClientList +{ +public: + ClientList() {} + ~ClientList() {} + + void Add(Client* client); + Client* Get(int32 ip, int16 port); + Client* FindByLSID(int32 lsaccountid); + void FindByCreateRequest(); + void SendPacketToAllClients(EQ2Packet* app); + void Process(); +private: + Mutex MClientList; + map client_list; +}; +class DelayQue { +public: + DelayQue(Timer* in_timer, EQApplicationPacket* in_packet){ + timer = in_timer; + packet = in_packet; + }; + Timer* timer; + EQApplicationPacket* packet; +}; +#endif diff --git a/old/LoginServer/client.hpp b/old/LoginServer/client.hpp deleted file mode 100644 index 9b6a237..0000000 --- a/old/LoginServer/client.hpp +++ /dev/null @@ -1,851 +0,0 @@ -// EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team - GPL License - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "net.hpp" -#include "lworld.hpp" -#include "login_structs.hpp" -#include "login_account.hpp" -#include "login_database.hpp" -#include "../common/tcp.hpp" -#include "../common/timer.hpp" -#include "../common/linked_list.hpp" -#include "../common/log.hpp" -#include "../common/debug.hpp" -#include "../common/config_reader.hpp" -#include "../common/misc_functions.hpp" -#include "../common/stream/eq_stream.hpp" -#include "../common/packet/packet_dump.hpp" -#include "../common/packet/packet_struct.hpp" -#include "../common/packet/packet_functions.hpp" -#include "../common/opcodes/emu_opcodes.hpp" - -using namespace std; - -enum eLoginMode { None, Normal, Registration }; - -extern NetConnection net; -extern LWorldList world_list; -extern ClientList client_list; -extern LoginDatabase database; -extern map EQOpcodeManager; -extern ConfigReader configReader; - -class DelayQue -{ -public: - // Constructor initializes timer and packet for delayed queue processing - DelayQue(Timer* in_timer, EQApplicationPacket* in_packet) - { - timer = in_timer; - packet = in_packet; - } - - Timer* timer; // Timer for delay processing - EQApplicationPacket* packet; // Packet to be processed after delay -}; - -class Client -{ -public: - // Constructor initializes client connection and default values - Client(EQStream* ieqnc) - { - eqnc = ieqnc; - ip = eqnc->GetrIP(); - port = ntohs(eqnc->GetrPort()); - account_id = 0; - lsadmin = 0; - worldadmin = 0; - lsstatus = 0; - version = 0; - kicked = false; - verified = false; - memset(bannedreason, 0, sizeof(bannedreason)); - memset(key,0,10); - LoginMode = None; - num_updates = 0; - updatetimer = new Timer(500); - updatelisttimer = new Timer(10000); - disconnectTimer = 0; - memset(ClientSession,0,25); - request_num = 0; - login_account = 0; - createRequest = 0; - playWaitTimer = NULL; - start = false; - update_position = 0; - update_packets = 0; - needs_world_list = true; - sent_character_list = false; - } - - // Destructor cleans up allocated resources and closes connection - ~Client() - { - safe_delete(login_account); - eqnc->Close(); - safe_delete(playWaitTimer); - safe_delete(createRequest); - safe_delete(disconnectTimer); - safe_delete(updatetimer); - } - - // Main processing loop for client connections and packet handling - bool Process() - { - if(!start && !eqnc->CheckActive()) { - if(!playWaitTimer) - playWaitTimer = new Timer(5000); - else if(playWaitTimer->Check()) { - safe_delete(playWaitTimer); - return false; - } - return true; - } - else if(!start) { - safe_delete(playWaitTimer); - start = true; - } - - if(disconnectTimer && disconnectTimer->Check()) { - safe_delete(disconnectTimer); - getConnection()->SendDisconnect(); - } - - if(!kicked) { - EQApplicationPacket *app = 0; - - if(playWaitTimer != NULL && playWaitTimer->Check()) { - SendPlayFailed(PLAY_ERROR_SERVER_TIMEOUT); - safe_delete(playWaitTimer); - } - - if(!needs_world_list && updatetimer && updatetimer->Check()) { - if(updatelisttimer && updatelisttimer->Check()) { - if(num_updates >= 180) { // 30 minutes - getConnection()->SendDisconnect(); - } - else { - vector::iterator itr; - if(update_packets) { - for(itr = update_packets->begin(); itr != update_packets->end(); itr++) { - safe_delete(*itr); - } - } - safe_delete(update_packets); - update_packets = world_list.GetServerListUpdate(version); - } - num_updates++; - } - else { - if(!update_packets) { - update_packets = world_list.GetServerListUpdate(version); - } - else { - if(update_position < update_packets->size()) { - QueuePacket(update_packets->at(update_position)->serialize()); - update_position++; - } - else - update_position = 0; - } - } - } - - while(app = eqnc->PopPacket()) { - switch(app->GetOpcode()) { - case OP_LoginRequestMsg: - HandleLoginRequest(app); - break; - case OP_KeymapLoadMsg: - // Keymap message - currently unused - break; - case OP_AllWSDescRequestMsg: - HandleWorldServerListRequest(); - break; - case OP_LsClientCrashlogReplyMsg: - SaveErrorsToDB(app, "Crash Log", GetVersion()); - break; - case OP_LsClientVerifylogReplyMsg: - SaveErrorsToDB(app, "Verify Log", GetVersion()); - break; - case OP_LsClientAlertlogReplyMsg: - SaveErrorsToDB(app, "Alert Log", GetVersion()); - break; - case OP_LsClientBaselogReplyMsg: - SaveErrorsToDB(app, "Base Log", GetVersion()); - break; - case OP_AllCharactersDescRequestMsg: - break; - case OP_CreateCharacterRequestMsg: - HandleCreateCharacterRequest(app); - break; - case OP_PlayCharacterRequestMsg: - HandlePlayCharacterRequest(app); - break; - case OP_DeleteCharacterRequestMsg: - HandleDeleteCharacterRequest(app); - break; - default: - HandleUnknownOpcode(app); - } - delete app; - } - } - - if(!eqnc->CheckActive()) { - return false; - } - - return true; - } - - // Handles login request packets and validates credentials - void HandleLoginRequest(EQApplicationPacket* app) - { - DumpPacket(app); - PacketStruct* packet = configReader.getStruct("LS_LoginRequest", 1); - if(packet && packet->LoadPacketData(app->pBuffer,app->size)) { - version = packet->getType_int16_ByName("version"); - LogWrite(LOGIN__DEBUG, 0, "Login", "Classic Client Version Provided: %i", version); - - if(version == 0 || EQOpcodeManager.count(GetOpcodeVersion(version)) == 0) { - safe_delete(packet); - packet = configReader.getStruct("LS_LoginRequest", 1208); - if(packet && packet->LoadPacketData(app->pBuffer, app->size)) { - version = packet->getType_int16_ByName("version"); - } - else - return; - } - - LogWrite(LOGIN__DEBUG, 0, "Login", "New Client Version Provided: %i", version); - - if(EQOpcodeManager.count(GetOpcodeVersion(version)) == 0) { - LogWrite(LOGIN__ERROR, 0, "Login", "Incompatible client version provided: %i", version); - SendLoginDenied(); - safe_delete(packet); - return; - } - - if(EQOpcodeManager.count(GetOpcodeVersion(version)) > 0 && getConnection()) { - getConnection()->SetClientVersion(GetVersion()); - EQ2_16BitString username = packet->getType_EQ2_16BitString_ByName("username"); - EQ2_16BitString password = packet->getType_EQ2_16BitString_ByName("password"); - LoginAccount* acct = database.LoadAccount(username.data.c_str(),password.data.c_str(), net.IsAllowingAccountCreation()); - if(acct) { - Client* otherclient = client_list.FindByLSID(acct->getLoginAccountID()); - if(otherclient) - otherclient->getConnection()->SendDisconnect(); // Prevent double login - } - if(acct) { - SetAccountName(username.data.c_str()); - database.UpdateAccountIPAddress(acct->getLoginAccountID(), getConnection()->GetrIP()); - database.UpdateAccountClientDataVersion(acct->getLoginAccountID(), version); - LogWrite(LOGIN__INFO, 0, "Login", "%s successfully logged in.", (char*)username.data.c_str()); - } - else { - if(username.size > 0) - LogWrite(LOGIN__ERROR, 0, "Login", "%s login failed!", (char*)username.data.c_str()); - else - LogWrite(LOGIN__ERROR, 0, "Login", "[UNKNOWN USER] login failed!"); - } - - if(!acct) - SendLoginDenied(); - else { - needs_world_list = true; - SetLoginAccount(acct); - SendLoginAccepted(); - } - } - else { - cout << "Error bad version: " << version << endl; - SendLoginDeniedBadVersion(); - } - } - else { - cout << "Error loading LS_LoginRequest packet: \n"; - } - safe_delete(packet); - } - - // Handles world server list request and sends character list - void HandleWorldServerListRequest() - { - SendWorldList(); - needs_world_list = false; - if(!sent_character_list) { - database.LoadCharacters(GetLoginAccount(), GetVersion()); - sent_character_list = true; - } - SendCharList(); - } - - // Handles character creation requests from clients - void HandleCreateCharacterRequest(EQApplicationPacket* app) - { - PacketStruct* packet = configReader.getStruct("CreateCharacter", GetVersion()); - DumpPacket(app); - playWaitTimer = new Timer(15000); - playWaitTimer->Start(); - - LogWrite(WORLD__INFO, 1, "World", "Character creation request from account %s", GetAccountName()); - if(packet->LoadPacketData(app->pBuffer,app->size, GetVersion() <= 561 ? false : true)) { - DumpPacket(app->pBuffer, app->size); - packet->setDataByName("account_id",GetAccountID()); - LWorld* world_server = world_list.FindByID(packet->getType_int32_ByName("server_id")); - if(!world_server) { - DumpPacket(app->pBuffer, app->size); - cout << GetAccountName() << " attempted creation of character with an invalid server id of: " << packet->getType_int32_ByName("server_id") << "\n"; - } - else { - createRequest = packet; - ServerPacket* outpack = new ServerPacket(ServerOP_CharacterCreate, app->size+sizeof(int16)); - int16 out_version = GetVersion(); - memcpy(outpack->pBuffer, &out_version, sizeof(int16)); - memcpy(outpack->pBuffer + sizeof(int16), app->pBuffer, app->size); - uchar* tmp = outpack->pBuffer; - - if(out_version<=283) - tmp+=2; - else if(out_version == 373) { - tmp += 6; - } - else - tmp += 7; - - int32 account_id = GetAccountID(); - memcpy(tmp, &account_id, sizeof(int32)); - world_server->SendPacket(outpack); - safe_delete(outpack); - } - } - else { - LogWrite(WORLD__ERROR, 1, "World", "Error in character creation request from account %s!", GetAccountName()); - safe_delete(packet); - } - } - - // Handles character play requests and forwards to world server - void HandlePlayCharacterRequest(EQApplicationPacket* app) - { - int32 char_id = 0; - int32 server_id = 0; - PacketStruct* request = configReader.getStruct("LS_PlayRequest",GetVersion()); - if(request && request->LoadPacketData(app->pBuffer,app->size)) { - char_id = request->getType_int32_ByName("char_id"); - if(GetVersion() <= 283) { - server_id = database.GetServer(GetAccountID(), char_id, request->getType_EQ2_16BitString_ByName("name").data); - } - else { - server_id = request->getType_int32_ByName("server_id"); - } - LWorld* world = world_list.FindByID(server_id); - string name = database.GetCharacterName(char_id,server_id,GetAccountID()); - if(world && name.length() > 0) { - pending_play_char_id = char_id; - ServerPacket* outpack = new ServerPacket(ServerOP_UsertoWorldReq, sizeof(UsertoWorldRequest_Struct)); - UsertoWorldRequest_Struct* req = (UsertoWorldRequest_Struct*)outpack->pBuffer; - req->char_id = char_id; - req->lsaccountid = GetAccountID(); - req->worldid = server_id; - - struct in_addr in; - in.s_addr = GetIP(); - strcpy(req->ip_address, inet_ntoa(in)); - world->SendPacket(outpack); - delete outpack; - - safe_delete(playWaitTimer); - playWaitTimer = new Timer(5000); - playWaitTimer->Start(); - } - else { - cout << GetAccountName() << " sent invalid Play Request: \n"; - SendPlayFailed(PLAY_ERROR_PROBLEM); - DumpPacket(app); - } - } - safe_delete(request); - } - - // Handles character deletion requests - void HandleDeleteCharacterRequest(EQApplicationPacket* app) - { - PacketStruct* request = configReader.getStruct("LS_DeleteCharacterRequest", GetVersion()); - PacketStruct* response = configReader.getStruct("LS_DeleteCharacterResponse", GetVersion()); - if(request && response && request->LoadPacketData(app->pBuffer,app->size)) { - EQ2_16BitString name = request->getType_EQ2_16BitString_ByName("name"); - int32 acct_id = GetAccountID(); - int32 char_id = request->getType_int32_ByName("char_id"); - int32 server_id = request->getType_int32_ByName("server_id"); - if(database.VerifyDelete(acct_id, char_id, name.data.c_str())) { - response->setDataByName("response", 1); - GetLoginAccount()->removeCharacter((char*)name.data.c_str(), GetVersion()); - LWorld* world_server = world_list.FindByID(server_id); - if(world_server != NULL) - world_server->SendDeleteCharacter(char_id, acct_id); - } - else - response->setDataByName("response", 0); - response->setDataByName("server_id", server_id); - response->setDataByName("char_id", char_id); - response->setDataByName("account_id", account_id); - response->setMediumStringByName("name", (char*)name.data.c_str()); - response->setDataByName("max_characters", 10); - - EQ2Packet* outapp = response->serialize(); - QueuePacket(outapp); - - this->SendCharList(); - } - safe_delete(request); - safe_delete(response); - } - - // Handles unknown opcodes and logs them for debugging - void HandleUnknownOpcode(EQApplicationPacket* app) - { - const char* name = app->GetOpcodeName(); - if(name) - LogWrite(OPCODE__DEBUG, 1, "Opcode", "%s Received %04X (%i)", name, app->GetRawOpcode(), app->GetRawOpcode()); - else - LogWrite(OPCODE__DEBUG, 1, "Opcode", "Received %04X (%i)", app->GetRawOpcode(), app->GetRawOpcode()); - } - - // Saves client error logs to database after decompression - void SaveErrorsToDB(EQApplicationPacket* app, char* type, int32 version) - { - int32 size = 0; - z_stream zstream; - if(version >= 546) { - memcpy(&size, app->pBuffer + sizeof(int32), sizeof(int32)); - zstream.next_in = app->pBuffer + 8; - zstream.avail_in = app->size - 8; - } - else { // box set - size = 0xFFFF; - zstream.next_in = app->pBuffer + 2; - zstream.avail_in = app->size - 2; - } - size++; - char* message = new char[size]; - memset(message, 0, size); - - int zerror = 0; - - zstream.next_out = (BYTE*)message; - zstream.avail_out = size; - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - - zerror = inflateInit(&zstream); - if(zerror != Z_OK) { - safe_delete_array(message); - return; - } - zerror = inflate(&zstream, 0); - if(message && strlen(message) > 0) - database.SaveClientLog(type, message, GetLoginAccount()->getLoginName(), GetVersion()); - safe_delete_array(message); - } - - // Handles character creation approval from world server - void CharacterApproved(int32 server_id,int32 char_id) - { - if(createRequest && server_id == createRequest->getType_int32_ByName("server_id")) { - LWorld* world_server = world_list.FindByID(server_id); - if(!world_server) - return; - - PacketStruct* packet = configReader.getStruct("LS_CreateCharacterReply", GetVersion()); - if(packet) { - packet->setDataByName("account_id", GetAccountID()); - packet->setDataByName("unknown", 0xFFFFFFFF); - packet->setDataByName("response", CREATESUCCESS_REPLY); - packet->setMediumStringByName("name", (char*)createRequest->getType_EQ2_16BitString_ByName("name").data.c_str()); - EQ2Packet* outapp = packet->serialize(); - QueuePacket(outapp); - safe_delete(packet); - database.SaveCharacter(createRequest, GetLoginAccount(),char_id, GetVersion()); - - // refresh characters for this account - database.LoadCharacters(GetLoginAccount(), GetVersion()); - - SendCharList(); - - if(GetVersion() <= 561) { - pending_play_char_id = char_id; - ServerPacket* outpack = new ServerPacket(ServerOP_UsertoWorldReq, sizeof(UsertoWorldRequest_Struct)); - UsertoWorldRequest_Struct* req = (UsertoWorldRequest_Struct*)outpack->pBuffer; - req->char_id = char_id; - req->lsaccountid = GetAccountID(); - req->worldid = server_id; - - struct in_addr in; - in.s_addr = GetIP(); - strcpy(req->ip_address, inet_ntoa(in)); - world_server->SendPacket(outpack); - delete outpack; - } - } - } - else { - cout << GetAccountName() << " received invalid CharacterApproval from server: " << server_id << endl; - } - safe_delete(createRequest); - } - - // Handles character creation rejection from world server - void CharacterRejected(int8 reason_number) - { - PacketStruct* packet = configReader.getStruct("LS_CreateCharacterReply", GetVersion()); - if(createRequest && packet) { - packet->setDataByName("account_id", GetAccountID()); - int8 clientReasonNum = reason_number; - packet->setDataByName("response", clientReasonNum); - packet->setMediumStringByName("name", ""); - EQ2Packet* outapp = packet->serialize(); - QueuePacket(outapp); - safe_delete(packet); - } - } - - // Sends character list to client - void SendCharList() - { - LogWrite(LOGIN__INFO, 0, "Login", "[%s] sending character list.", GetAccountName()); - LS_CharSelectList list; - list.loadData(GetAccountID(), GetLoginAccount()->charlist, GetVersion()); - EQ2Packet* outapp = list.serialize(GetVersion()); - DumpPacket(outapp->pBuffer, outapp->size); - QueuePacket(outapp); - } - - // Sends login denied response with bad version error - void SendLoginDeniedBadVersion() - { - EQ2Packet* app = new EQ2Packet(OP_LoginReplyMsg, 0, sizeof(LS_LoginResponse)); - LS_LoginResponse* ls_response = (LS_LoginResponse*)app->pBuffer; - ls_response->reply_code = 6; - ls_response->unknown03 = 0xFFFFFFFF; - ls_response->unknown04 = 0xFFFFFFFF; - QueuePacket(app); - StartDisconnectTimer(); - } - - // Sends login denied response for invalid credentials - void SendLoginDenied() - { - EQ2Packet* app = new EQ2Packet(OP_LoginReplyMsg, 0, sizeof(LS_LoginResponse)); - LS_LoginResponse* ls_response = (LS_LoginResponse*)app->pBuffer; - ls_response->reply_code = 1; - ls_response->unknown03 = 0xFFFFFFFF; - ls_response->unknown04 = 0xFFFFFFFF; - QueuePacket(app); - StartDisconnectTimer(); - } - - // Sends login accepted response with account details - void SendLoginAccepted(int32 account_id = 1, int8 login_response = 0) - { - PacketStruct* packet = configReader.getStruct("LS_LoginReplyMsg", GetVersion()); - if(packet) { - packet->setDataByName("account_id", account_id); - packet->setDataByName("login_response", login_response); - packet->setDataByName("do_not_force_soga", 1); - packet->setDataByName("sub_level", net.GetDefaultSubscriptionLevel()); - packet->setDataByName("race_flag", 0x1FFFFF); - packet->setDataByName("class_flag", 0x7FFFFFE); - packet->setMediumStringByName("username", GetAccountName()); - packet->setMediumStringByName("password", GetAccountName()); - packet->setDataByName("unknown5", net.GetExpansionFlag()); - packet->setDataByName("unknown6", 0xFF); - packet->setDataByName("unknown6", 0xFF, 1); - packet->setDataByName("unknown6", 0xFF, 2); - packet->setDataByName("unknown10", 0xFF); - packet->setDataByName("unknown7", net.GetEnabledRaces()); - packet->setDataByName("unknown7a", 0xEE); - packet->setDataByName("unknown8", net.GetCitiesFlag(), 1); - - EQ2Packet* outapp = packet->serialize(); - QueuePacket(outapp); - safe_delete(packet); - } - } - - // Sends world server list to client - void SendWorldList() - { - EQ2Packet* pack = world_list.MakeServerListPacket(lsadmin, version); - EQ2Packet* dupe = pack->Copy(); - DumpPacket(dupe->pBuffer,dupe->size); - QueuePacket(dupe); - SendLoginAccepted(0, 10); // triggers a different code path in the client to set certain flags - } - - // Queues packet for transmission to client - void QueuePacket(EQ2Packet* app) - { - eqnc->EQ2QueuePacket(app); - } - - // Handles world server response for character play requests - void WorldResponse(int32 worldid, int8 response, char* ip_address, int32 port, int32 access_key) - { - LWorld* world = world_list.FindByID(worldid); - if(world == 0) { - FatalError(0); - return; - } - if(response != 1) { - if(response == PLAY_ERROR_CHAR_NOT_LOADED) { - string pending_play_char_name = database.GetCharacterName(pending_play_char_id, worldid, GetAccountID()); - if(database.VerifyDelete(GetAccountID(), pending_play_char_id, pending_play_char_name.c_str())) { - GetLoginAccount()->removeCharacter((char*)pending_play_char_name.c_str(), GetVersion()); - } - } - FatalError(response); - return; - } - - PacketStruct* response_packet = configReader.getStruct("LS_PlayResponse", GetVersion()); - if(response_packet) { - safe_delete(playWaitTimer); - response_packet->setDataByName("response", 1); - response_packet->setSmallStringByName("server", ip_address); - response_packet->setDataByName("port", port); - response_packet->setDataByName("account_id", GetAccountID()); - response_packet->setDataByName("access_code", access_key); - EQ2Packet* outapp = response_packet->serialize(); - QueuePacket(outapp); - safe_delete(response_packet); - } - } - - // Handles fatal errors and sends play failed response - void FatalError(int8 response) - { - safe_delete(playWaitTimer); - SendPlayFailed(response); - } - - // Sends play failed response to client - void SendPlayFailed(int8 response) - { - PacketStruct* response_packet = configReader.getStruct("LS_PlayResponse", GetVersion()); - if(response_packet) { - response_packet->setDataByName("response", response); - response_packet->setSmallStringByName("server", ""); - response_packet->setDataByName("port", 0); - response_packet->setDataByName("account_id", GetAccountID()); - response_packet->setDataByName("access_code", 0); - EQ2Packet* outapp = response_packet->serialize(); - QueuePacket(outapp); - safe_delete(response_packet); - } - } - - // Starts timer for delayed client disconnection - void StartDisconnectTimer() - { - if(!disconnectTimer) { - disconnectTimer = new Timer(1000); - disconnectTimer->Start(); - } - } - - // Accessor methods - EQStream* getConnection() { return eqnc; } - LoginAccount* GetLoginAccount() { return login_account; } - void SetLoginAccount(LoginAccount* in_account) { - login_account = in_account; - if(in_account) - account_id = in_account->getLoginAccountID(); - } - int16 GetVersion() { return version; } - char* GetKey() { return key; } - void SetKey(char* in_key) { strcpy(key,in_key); } - int32 GetIP() { return ip; } - int16 GetPort() { return port; } - int32 GetAccountID() { return account_id; } - const char* GetAccountName() { return (char*)account_name.c_str(); } - void SetAccountName(const char* name) { account_name = string(name); } - bool AwaitingCharCreationRequest() { - if(createRequest) - return true; - else - return false; - } - - // Public member variables - int8 LoginKey[10]; // Login encryption key - int8 ClientSession[25]; // Client session identifier - Timer* updatetimer; // Timer for client updates - Timer* updatelisttimer; // Timer for update list processing - Timer* disconnectTimer; // Timer for delayed disconnection - int16 packettotal; // Total packet count - int32 requested_server_id; // Server ID requested by client - int32 request_num; // Request number counter - LinkedList delay_que; // Queue for delayed packet processing - -private: - string pending_play_char_name; // Character name for pending play request - int32 pending_play_char_id; // Character ID for pending play request - int8 update_position; // Position in update packet list - int16 num_updates; // Number of updates sent - vector* update_packets; // List of update packets - LoginAccount* login_account; // Associated login account - EQStream* eqnc; // Network connection stream - int32 ip; // Client IP address - int16 port; // Client port number - int32 account_id; // Account ID - string account_name; // Account name - char key[10]; // Client authentication key - int8 lsadmin; // Login server admin level - sint16 worldadmin; // World server admin level - int lsstatus; // Login server status - bool kicked; // Whether client has been kicked - bool verified; // Whether client is verified - bool start; // Whether client has started - bool needs_world_list; // Whether client needs world list - int16 version; // Client version - char bannedreason[30]; // Reason for ban if applicable - bool sent_character_list; // Whether character list has been sent - eLoginMode LoginMode; // Current login mode - PacketStruct* createRequest; // Pending character creation request - Timer* playWaitTimer; // Timer for play request timeout -}; - -class ClientList -{ -public: - // Constructor and destructor for client list management - ClientList() {} - ~ClientList() {} - - // Adds a new client to the list - void Add(Client* client) - { - MClientList.writelock(); - client_list[client] = true; - MClientList.releasewritelock(); - } - - // Finds client by IP address and port - Client* Get(int32 ip, int16 port) - { - Client* ret = 0; - map::iterator itr; - MClientList.readlock(); - for(itr = client_list.begin(); itr != client_list.end(); itr++) { - if(itr->first->GetIP() == ip && itr->first->GetPort() == port) { - ret = itr->first; - break; - } - } - MClientList.releasereadlock(); - return ret; - } - - // Finds clients waiting for character creation and handles rejections - void FindByCreateRequest() - { - Client* client = 0; - map::iterator itr; - MClientList.readlock(); - for(itr = client_list.begin(); itr != client_list.end(); itr++) { - if(itr->first->AwaitingCharCreationRequest()) { - if(!client) - client = itr->first; - else { - client = 0; // more than 1 character waiting, dont want to send rejection to wrong one - break; - } - } - } - MClientList.releasereadlock(); - if(client) - client->CharacterRejected(UNKNOWNERROR_REPLY); - } - - // Finds client by login server account ID - Client* FindByLSID(int32 lsaccountid) - { - Client* client = 0; - map::iterator itr; - MClientList.readlock(); - for(itr = client_list.begin(); itr != client_list.end(); itr++) { - if(itr->first->GetAccountID() == lsaccountid) { - client = itr->first; - break; - } - } - MClientList.releasereadlock(); - return client; - } - - // Sends packet to all connected clients - void SendPacketToAllClients(EQ2Packet* app) - { - Client* client = 0; - map::iterator itr; - MClientList.readlock(); - if(client_list.size() > 0) { - for(itr = client_list.begin(); itr != client_list.end(); itr++) { - itr->first->QueuePacket(app->Copy()); - } - } - safe_delete(app); - MClientList.releasereadlock(); - } - - // Processes all clients and removes inactive ones - void Process() - { - Client* client = 0; - vector erase_list; - map::iterator itr; - MClientList.readlock(); - for(itr = client_list.begin(); itr != client_list.end(); itr++) { - client = itr->first; - if(!client->Process()) - erase_list.push_back(client); - } - MClientList.releasereadlock(); - if(erase_list.size() > 0) { - vector::iterator erase_itr; - MClientList.writelock(); - for(erase_itr = erase_list.begin(); erase_itr != erase_list.end(); erase_itr++) { - client = *erase_itr; - struct in_addr in; - in.s_addr = client->getConnection()->GetRemoteIP(); - net.numclients--; - LogWrite(LOGIN__INFO, 0, "Login", "Removing client from ip: %s on port %i, Account Name: %s", inet_ntoa(in), ntohs(client->getConnection()->GetRemotePort()), client->GetAccountName()); - client->getConnection()->Close(); - net.UpdateWindowTitle(); - client_list.erase(client); - } - MClientList.releasewritelock(); - } - } - -private: - Mutex MClientList; // Mutex for thread-safe client list access - map client_list; // Map of active clients -}; \ No newline at end of file diff --git a/old/LoginServer/login_account.hpp b/old/LoginServer/login_account.hpp deleted file mode 100644 index 68d0d53..0000000 --- a/old/LoginServer/login_account.hpp +++ /dev/null @@ -1,164 +0,0 @@ -// EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) - GPL License - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "packet_headers.hpp" -#include "../common/linked_list.hpp" -#include "../common/packet/packet_struct.hpp" - -using namespace std; - -/** - * LoginAccount class manages user account data and associated character profiles - * Handles authentication, character management, and account operations - */ -class LoginAccount -{ -public: - // Default constructor - initializes account with default values - LoginAccount(); - - // Parameterized constructor - creates account with specified ID, name, and password - LoginAccount(int32 id, const char* in_name, const char* in_pass); - - // Destructor - cleans up all character profiles and allocated memory - ~LoginAccount(); - - // Saves account data to persistent storage - bool SaveAccount(LoginAccount* acct); - - // Vector containing all character profiles associated with this account - vector charlist; - - // Sets the account name from C-string input - void setName(const char* in_name) { strcpy(name, in_name); } - - // Sets the account password from C-string input - void setPassword(const char* in_pass) { strcpy(password, in_pass); } - - // Sets the authentication status for this account - void setAuthenticated(bool in_auth) { authenticated = in_auth; } - - // Sets the unique account identifier - void setAccountID(int32 id) { account_id = id; } - - // Adds a character profile to the account's character list - void addCharacter(CharSelectProfile* profile) - { - charlist.push_back(profile); - } - - // Removes character by PacketStruct profile reference - void removeCharacter(PacketStruct* profile); - - // Removes character by name with version-specific handling - void removeCharacter(char* name, int16 version); - - // Serializes character data into provided buffer for network transmission - void serializeCharacter(uchar* buffer, CharSelectProfile* profile); - - // Removes and deallocates all character profiles from the account - void flushCharacters(); - - // Retrieves character profile by name, returns nullptr if not found - CharSelectProfile* getCharacter(char* name); - - // Returns the unique account identifier - int32 getLoginAccountID() { return account_id; } - - // Returns pointer to account name string - char* getLoginName() { return name; } - - // Returns pointer to account password string - char* getLoginPassword() { return password; } - - // Returns current authentication status - bool getLoginAuthenticated() { return authenticated; } - -private: - int32 account_id; // Unique identifier for this account - char name[32]; // Account name (limited to 31 characters + null terminator) - char password[32]; // Account password (limited to 31 characters + null terminator) - bool authenticated; // Current authentication state -}; - -// Default constructor implementation - initializes account with default values -inline LoginAccount::LoginAccount() -{ - account_id = 0; - memset(name, 0, sizeof(name)); - memset(password, 0, sizeof(password)); - authenticated = false; -} - -// Parameterized constructor implementation - sets up account with provided credentials -inline LoginAccount::LoginAccount(int32 id, const char* in_name, const char* in_pass) -{ - account_id = id; - strcpy(name, in_name); - strcpy(password, in_pass); - authenticated = false; -} - -// Destructor implementation - properly cleans up all character profiles -inline LoginAccount::~LoginAccount() -{ - for(auto iter = charlist.begin(); iter != charlist.end(); iter++) { - delete(*iter); // Clean up character profile memory - } -} - -// Removes and deallocates all character profiles from memory -inline void LoginAccount::flushCharacters() -{ - for(auto iter = charlist.begin(); iter != charlist.end(); iter++) { - delete(*iter); // Clean up each character profile - } - charlist.clear(); // Clear the vector -} - -// Searches for and returns character profile by name -inline CharSelectProfile* LoginAccount::getCharacter(char* name) -{ - CharSelectProfile* profile = nullptr; - EQ2_16BitString temp; - - // Iterate through character list to find matching name - for(auto char_iterator = charlist.begin(); char_iterator != charlist.end(); char_iterator++) { - profile = *char_iterator; - temp = profile->packet->getType_EQ2_16BitString_ByName("name"); - if(strcmp(temp.data.c_str(), name) == 0) - return profile; // Return matching profile - } - return nullptr; // Character not found -} - -// Removes character from account by name with client version handling -inline void LoginAccount::removeCharacter(char* name, int16 version) -{ - CharSelectProfile* profile = nullptr; - EQ2_16BitString temp; - - // Search for character with matching name - for(auto iter = charlist.begin(); iter != charlist.end(); iter++) { - profile = *iter; - temp = profile->packet->getType_EQ2_16BitString_ByName("name"); - if(strcmp(temp.data.c_str(), name) == 0) { - if(version <= 561) { - profile->deleted = true; // Workaround for character select crash on legacy clients - } - else { - delete(*iter); // Clean up memory - charlist.erase(iter); // Remove from vector - } - return; - } - } -} \ No newline at end of file diff --git a/old/LoginServer/login_database.hpp b/old/LoginServer/login_database.hpp deleted file mode 100644 index f3c5fe7..0000000 --- a/old/LoginServer/login_database.hpp +++ /dev/null @@ -1,1199 +0,0 @@ -// EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) - GPLv2 License - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lworld.hpp" -#include "login_account.hpp" -#include "packet_headers.hpp" -#include "../common/log.hpp" -#include "../common/unix.hpp" -#include "../common/types.hpp" -#include "../common/debug.hpp" -#include "../common/servertalk.hpp" -#include "../common/misc_functions.hpp" -#include "../common/database/query.hpp" -#include "../common/database/database.hpp" -#include "../common/database/database_core.hpp" -#include "../common/database/database_result.hpp" -#include "../common/packet/packet_functions.hpp" -#include "../common/packet/packet_dump.hpp" -#include "../common/packet/packet_struct.hpp" - -using namespace std; - -extern LoginDatabase database; -extern LWorldList world_list; - -class LoginDatabase : public Database -{ -public: - // Connect to the login database using new database connection - bool ConnectNewDatabase() - { - return dbLogin.Connect(); - } - - // Remove character data for characters marked as deleted to clean up orphaned records - void RemoveDeletedCharacterData() - { - dbLogin.Query("DELETE FROM login_char_colors WHERE login_characters_id IN (SELECT id FROM login_characters WHERE deleted = 1)"); - dbLogin.Query("DELETE FROM login_equipment WHERE login_characters_id IN (SELECT id FROM login_characters WHERE deleted = 1)"); - } - - // Set zone information in character select packet based on server and zone IDs - void SetZoneInformation(int32 server_id, int32 zone_id, int32 version, PacketStruct* packet) - { - if(!packet) return; - - Query query; - MYSQL_RES* result = nullptr; - - if (version >= 1212) - result = query.RunQuery2(Q_SELECT, "SELECT name, description from ls_world_zones where server_id=%i and zone_id=%i", server_id, zone_id); - - MYSQL_ROW row; - if(result && (row = mysql_fetch_row(result))) { - if (row[0]) - packet->setMediumStringByName("zone", row[0]); - else - packet->setMediumStringByName("zone", " "); - - if(row[1]) - packet->setMediumStringByName("zonedesc", row[1]); - else - packet->setMediumStringByName("zonedesc", " "); - } - else { - Query query2; - MYSQL_RES* result2 = nullptr; - - if (version < 1212) - result2 = query2.RunQuery2(Q_SELECT, "SELECT file, description from zones where id=%i", zone_id); - else - result2 = query2.RunQuery2(Q_SELECT, "SELECT name, description from zones where id=%i", zone_id); - - MYSQL_ROW row2; - if(result2 && (row2 = mysql_fetch_row(result2))) { - if (version < 546 && version > 561 && version < 1212) { - if (row2[0]) { - int len = strlen(row2[0]); - char* zoneName = new char[len + 2]; - strncpy(zoneName, row2[0], len); - zoneName[len] = 0x2E; - zoneName[len + 1] = 0x30; - - packet->setMediumStringByName("zone", zoneName); - safe_delete_array(zoneName); - } - else - packet->setMediumStringByName("zone", ".0"); - } - else { - if (row2[0]) - packet->setMediumStringByName("zone", row2[0]); - else - packet->setMediumStringByName("zone", " "); - } - - if(row2[1]) - packet->setMediumStringByName("zonedesc", row2[1]); - else - packet->setMediumStringByName("zonedesc", " "); - } - } - packet->setMediumStringByName("zonename2"," "); - } - - // Get zone description by zone file name - string GetZoneDescription(char* name) - { - string ret; - Query query; - query.escaped_name = getEscapeString(name); - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT description from zones where file=substring_index('%s', '.', 1)", query.escaped_name); - MYSQL_ROW row; - if((row = mysql_fetch_row(result))) { - ret = string(row[0]); - } - return ret; - } - - // Convert world character ID to login character ID for equipment updates - int32 GetLoginCharacterIDFromWorldCharID(int32 server_id, int32 char_id) - { - int32 ret = 0; - Query query; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id FROM login_characters WHERE server_id = %u AND char_id = %u AND deleted = 0 LIMIT 0,1", server_id, char_id); - MYSQL_ROW row; - if((row = mysql_fetch_row(result))) { - ret = atoi(row[0]); - } - return ret; - } - - // Update character equipment appearance data from world server - void SetServerEquipmentAppearances(int32 server_id, map equip_updates) - { - if(equip_updates.size() == 0) return; - - LogWrite(LOGIN__DEBUG, 0, "Login", "Saving appearance info from world %u...", server_id); - - map::iterator equip_itr; - stringstream ss; - ss << "replace into login_equipment (login_characters_id, equip_type, red, green, blue, highlight_red, highlight_green, highlight_blue, slot) values"; - int count = 0; - int32 char_id = 0; - - for(equip_itr = equip_updates.begin(); equip_itr != equip_updates.end(); equip_itr++) { - char_id = GetLoginCharacterIDFromWorldCharID(server_id, (int32)equip_itr->second.world_char_id); - - if(char_id == 0) continue; // invalid character/world match - - LogWrite(LOGIN__DEBUG, 5, "Login", "--Processing character %u, slot %i", char_id, (int32)equip_itr->second.slot); - - if(count > 0) ss << ", "; - - ss << "(" << char_id << ", "; - ss << (int32)equip_itr->second.equip_type << ", "; - ss << (int32)equip_itr->second.red << ", "; - ss << (int32)equip_itr->second.green << ", "; - ss << (int32)equip_itr->second.blue << ", "; - ss << (int32)equip_itr->second.highlight_red << ", "; - ss << (int32)equip_itr->second.highlight_green << ", "; - ss << (int32)equip_itr->second.highlight_blue << ", "; - ss << (int32)equip_itr->second.slot << ")"; - - count++; - } - - Query query; - query.RunQuery2(ss.str(), Q_REPLACE); - - if (query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF) - LogWrite(LOGIN__ERROR, 0, "Login", "Error saving login_equipment data Error: ", query.GetError()); - } - - // Update zone descriptions for a specific server - void SetServerZoneDescriptions(int32 server_id, map zone_descriptions) - { - if(zone_descriptions.size() == 0) return; - - map::iterator zone_itr; - string query_string = "replace into ls_world_zones (server_id, zone_id, name, description) values"; - int count = 0; - char server_id_str[12] = {0}; - sprintf(server_id_str, "%i", server_id); - - for(zone_itr = zone_descriptions.begin(); zone_itr != zone_descriptions.end(); zone_itr++, count++) { - char zone_id_str[12] = {0}; - sprintf(zone_id_str, "%i", zone_itr->first); - - if(count > 0) query_string.append(", "); - - query_string.append("(").append(server_id_str).append(","); - query_string.append(zone_id_str).append(","); - query_string.append("'").append(getSafeEscapeString(zone_itr->second.name.c_str()).c_str()).append("', '"); - query_string.append(getSafeEscapeString(zone_itr->second.description.c_str()).c_str()).append("')"); - } - - Query query; - query.RunQuery2(query_string, Q_REPLACE); - } - - // Get server ID for character when not provided in play request (legacy support) - int32 GetServer(int32 accountID, int32 charID, string name) - { - int32 id = 0; - Query query; - MYSQL_ROW row; - query.escaped_name = getEscapeString(name.c_str()); - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT server_id from login_characters where account_id=%i and char_id=%i and name='%s'", accountID, charID, query.escaped_name); - - if (result && mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - id = atoi(row[0]); - } - return id; - } - - // Load all characters for an account and populate character selection data - void LoadCharacters(LoginAccount* acct, int16 version) - { - if(acct != nullptr) acct->flushCharacters(); - - Query query; - Query query2; - int32 id = 0; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT lc.char_id, lc.server_id, lc.name, lc.race, lc.class, lc.gender, lc.deity, lc.body_size, lc.body_age, lc.current_zone_id, lc.level, lc.soga_wing_type, lc.soga_chest_type, lc.soga_legs_type, lc.soga_hair_type, lc.legs_type, lc.chest_type, lc.wing_type, lc.hair_type, unix_timestamp(lc.created_date), unix_timestamp(lc.last_played), lc.id, lw.name, lc.facial_hair_type, lc.soga_facial_hair_type, lc.soga_model_type, lc.model_type from login_characters lc, login_worldservers lw where lw.id = lc.server_id and lc.account_id=%i and lc.deleted=0", acct->getLoginAccountID()); - - if(!result) { - LogWrite(LOGIN__ERROR, 0, "Login", "Error in LoadCharacters query '%s': %s", query.GetQuery(), query.GetError()); - return; - } - - MYSQL_ROW row; - MYSQL_ROW row2; - MYSQL_ROW row3; - - while ((row = mysql_fetch_row(result))) { - CharSelectProfile* player = new CharSelectProfile(version); - id = atoul(row[0]); - - player->packet->setDataByName("charid", id); - player->packet->setDataByName("server_id", atoul(row[1])); - player->packet->setMediumStringByName("name", row[2]); - player->packet->setDataByName("race", atoi(row[3])); - player->packet->setDataByName("class", atoi(row[4])); - player->packet->setDataByName("gender", atoi(row[5])); - player->packet->setDataByName("deity", atoi(row[6])); - player->packet->setDataByName("body_size", atof(row[7])); - player->packet->setDataByName("body_age", atof(row[8])); - - SetZoneInformation(atoi(row[1]), atoi(row[9]), version, player->packet); - - player->packet->setDataByName("level", atoi(row[10])); - - // Handle SOGA appearance fallback to regular appearance if not set - if(atoi(row[11]) > 0) - player->packet->setDataByName("soga_wing_type", atoi(row[11])); - else - player->packet->setDataByName("soga_wing_type", atoi(row[17])); - - if(atoi(row[12]) > 0) - player->packet->setDataByName("soga_chest_type", atoi(row[12])); - else - player->packet->setDataByName("soga_chest_type", atoi(row[16])); - - if(atoi(row[13]) > 0) - player->packet->setDataByName("soga_legs_type", atoi(row[13])); - else - player->packet->setDataByName("soga_legs_type", atoi(row[15])); - - if(atoi(row[14]) > 0) - player->packet->setDataByName("soga_hair_type", atoi(row[14])); - else - player->packet->setDataByName("soga_hair_type", atoi(row[18])); - - player->packet->setDataByName("legs_type", atoi(row[15])); - player->packet->setDataByName("chest_type", atoi(row[16])); - player->packet->setDataByName("wing_type", atoi(row[17])); - player->packet->setDataByName("hair_type", atoi(row[18])); - player->packet->setDataByName("created_date", atol(row[19])); - - if (row[20]) player->packet->setDataByName("last_played", atol(row[20])); - - // Set version-specific packet data - if(version == 546 || version == 561) - player->packet->setDataByName("version", 11); - else if(version >= 887) - player->packet->setDataByName("version", 6); - else - player->packet->setDataByName("version", 5); - - player->packet->setDataByName("account_id", acct->getLoginAccountID()); - player->packet->setDataByName("account_id2", acct->getLoginAccountID()); - - LoadAppearanceData(atoul(row[21]), player->packet); - - if(row[22]) player->packet->setMediumStringByName("server_name", row[22]); - - player->packet->setDataByName("hair_face_type", atoi(row[23])); - - if(atoi(row[24]) > 0) - player->packet->setDataByName("soga_hair_face_type", atoi(row[24])); - else - player->packet->setDataByName("soga_hair_face_type", atoi(row[23])); - - if(atoi(row[25]) > 0) - player->packet->setDataByName("soga_race_type", atoi(row[25])); - else - player->packet->setDataByName("soga_race_type", atoi(row[26])); - - player->packet->setDataByName("race_type", atoi(row[26])); - - // Set unknown packet fields required for proper display - player->packet->setDataByName("unknown3", 57); - player->packet->setDataByName("unknown4", 56); - player->packet->setDataByName("unknown6", 1, 1); - player->packet->setDataByName("unknown8", 15); - player->packet->setDataByName("unknown13", 212); - player->packet->setColorByName("unknown14", 0xFF, 0xFF, 0xFF); - - uchar tmp[] = {0xFF, 0xFF, 0xFF, 0x61, 0x00, 0x2C, 0x04, 0xA5, 0x09, 0x02, 0x0F, 0x00, 0x00}; - for(size_t y = 0; y < sizeof(tmp); y++) - player->packet->setDataByName("unknown11", tmp[y], y); - - // Load equipment appearance data - MYSQL_RES* result3 = query2.RunQuery2(Q_SELECT, "SELECT slot, equip_type, red, green, blue, highlight_red, highlight_green, highlight_blue from login_equipment where login_characters_id=%i order by slot", atoi(row[21])); - - if(result3) { - for(int i = 0; (row3 = mysql_fetch_row(result3)) && i < 24; i++) { - player->packet->setEquipmentByName("equip", atoi(row3[1]), atoi(row3[2]), atoi(row3[3]), atoi(row3[4]), atoi(row3[5]), atoi(row3[6]), atoi(row3[7]), atoi(row3[0])); - } - } - - player->packet->setDataByName("mount", 1377); - player->packet->setDataByName("mount_color1", 57); - - acct->addCharacter(player); - } - } - - // Check character timestamps and notify world servers for validation - void CheckCharacterTimeStamps(LoginAccount* acct) - { - Query query; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT char_id, unix_timestamp from login_characters where account_id=%i", acct->getLoginAccountID()); - - if(!result || mysql_num_rows(result) <= 0) return; - - MYSQL_ROW row; - ServerPacket* outpack = new ServerPacket(ServerOP_CharTimeStamp, sizeof(CharacterTimeStamp_Struct)); - CharacterTimeStamp_Struct* cts = (CharacterTimeStamp_Struct*) outpack->pBuffer; - cts->account_id = acct->getLoginAccountID(); - int32 server_id = 0; - LWorld* world_server = nullptr; - - while ((row = mysql_fetch_row(result))) { - server_id = atoi(row[1]); - if(server_id != 0) - world_server = world_list.FindByAccount(server_id, World); - - if(world_server) { // If the pointer is 0, the world server must be down, we can't do any updates... - cts->char_id = atoi(row[0]); - cts->unix_timestamp = atoi(row[1]); - world_server->SendPacket(outpack); - - // Reset for next character - world_server = nullptr; - server_id = 0; - } - } - safe_delete(outpack); - } - - // Save character appearance color/float data for classic client versions - void SaveCharacterFloats(int32 char_id, char* type, float float1, float float2, float float3, float multiplier = 100.0f) - { - Query query; - string create_char = string("insert into login_char_colors (login_characters_id, type, red, green, blue, signed_value) values(%i,'%s',%i,%i,%i, 1)"); - query.RunQuery2(Q_INSERT, create_char.c_str(), char_id, type, (sint8)(float1*multiplier), (sint8)(float2*multiplier), (sint8)(float3*multiplier)); - } - - // Save character appearance color data for newer client versions - void SaveCharacterColors(int32 char_id, char* type, EQ2_Color color) - { - Query query; - string create_char = string("insert into login_char_colors (login_characters_id, type, red, green, blue) values(%i,'%s',%i,%i,%i)"); - query.RunQuery2(Q_INSERT, create_char.c_str(), char_id, type, color.red, color.green, color.blue); - } - - // Load character appearance data and apply to character select packet - void LoadAppearanceData(int32 char_id, PacketStruct* char_select_packet) - { - Query query; - MYSQL_ROW row; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT type, signed_value, red, green, blue from login_char_colors where login_characters_id = %i", char_id); - - while((row = mysql_fetch_row(result))) { - if(atoi(row[1]) == 0) - char_select_packet->setColorByName(row[0], atoul(row[2]), atoul(row[3]), atoul(row[4])); - else { - char_select_packet->setDataByName(row[0], atoi(row[2]), 0); - char_select_packet->setDataByName(row[0], atoi(row[3]), 1); - char_select_packet->setDataByName(row[0], atoi(row[4]), 2); - } - } - } - - // Get appearance ID by name from appearances table - int16 GetAppearanceID(string name) - { - int32 id = 0; - Query query; - MYSQL_ROW row; - query.escaped_name = getEscapeString(name.c_str()); - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT appearance_id from appearances where name='%s'", query.escaped_name); - - if(result && mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - id = atoi(row[0]); - } - return id; - } - - // Mark characters with duplicate world character IDs as deleted (except the specified exception) - void DeactivateCharID(int32 server_id, int32 char_id, int32 exception_id) - { - Query query; - query.RunQuery2(Q_UPDATE, "update login_characters set deleted=1 where char_id=%u and server_id=%u and id!=%u", char_id, server_id, exception_id); - } - - // Save new character data to database during character creation - int32 SaveCharacter(PacketStruct* create, LoginAccount* acct, int32 world_charid, int32 client_version) - { - Query query; - string create_char = - string("Insert into login_characters (account_id, server_id, char_id, name, race, class, gender, deity, body_size, body_age, soga_wing_type, soga_chest_type, soga_legs_type, soga_hair_type, soga_facial_hair_type, legs_type, chest_type, wing_type, hair_type, facial_hair_type, soga_model_type, model_type)" - " values(%i, %i, %i, '%s', %i, %i, %i, %i, %f, %f, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i)"); - - query.RunQuery2(Q_INSERT, create_char.c_str(), - acct->getLoginAccountID(), - create->getType_int32_ByName("server_id"), world_charid, - create->getType_EQ2_16BitString_ByName("name").data.c_str(), - create->getType_int8_ByName("race"), - create->getType_int8_ByName("class"), - create->getType_int8_ByName("gender"), - create->getType_int8_ByName("deity"), - create->getType_float_ByName("body_size"), - create->getType_float_ByName("body_age"), - GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_wing_file").data), - GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_chest_file").data), - GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_legs_file").data), - GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_hair_file").data), - GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_face_file").data), - GetAppearanceID(create->getType_EQ2_16BitString_ByName("legs_file").data), - GetAppearanceID(create->getType_EQ2_16BitString_ByName("chest_file").data), - GetAppearanceID(create->getType_EQ2_16BitString_ByName("wing_file").data), - GetAppearanceID(create->getType_EQ2_16BitString_ByName("hair_file").data), - GetAppearanceID(create->getType_EQ2_16BitString_ByName("face_file").data), - GetAppearanceID(create->getType_EQ2_16BitString_ByName("soga_race_file").data), - GetAppearanceID(create->getType_EQ2_16BitString_ByName("race_file").data)); - - if(query.GetError() && strlen(query.GetError()) > 0) { - LogWrite(LOGIN__ERROR, 0, "Login", "Error in SaveCharacter query '%s': %s", query.GetQuery(), query.GetError()); - return 0; - } - - int32 last_insert_id = query.GetLastInsertedID(); - - // Mark any remaining characters with same id as deleted (creates problems if world deleted their db and started assigning new char ids) - DeactivateCharID(create->getType_int32_ByName("server_id"), world_charid, last_insert_id); - int32 char_id = last_insert_id; - - // Save appearance data based on client version - if (client_version <= 561) { - float classic_multiplier = 250.0f; - SaveCharacterFloats(char_id, "skin_color", create->getType_float_ByName("skin_color", 0), create->getType_float_ByName("skin_color", 1), create->getType_float_ByName("skin_color", 2), classic_multiplier); - SaveCharacterFloats(char_id, "eye_color", create->getType_float_ByName("eye_color", 0), create->getType_float_ByName("eye_color", 1), create->getType_float_ByName("eye_color", 2), classic_multiplier); - SaveCharacterFloats(char_id, "hair_color1", create->getType_float_ByName("hair_color1", 0), create->getType_float_ByName("hair_color1", 1), create->getType_float_ByName("hair_color1", 2), classic_multiplier); - SaveCharacterFloats(char_id, "hair_color2", create->getType_float_ByName("hair_color2", 0), create->getType_float_ByName("hair_color2", 1), create->getType_float_ByName("hair_color2", 2), classic_multiplier); - SaveCharacterFloats(char_id, "hair_highlight", create->getType_float_ByName("hair_highlight", 0), create->getType_float_ByName("hair_highlight", 1), create->getType_float_ByName("hair_highlight", 2), classic_multiplier); - SaveCharacterFloats(char_id, "hair_type_color", create->getType_float_ByName("hair_type_color", 0), create->getType_float_ByName("hair_type_color", 1), create->getType_float_ByName("hair_type_color", 2), classic_multiplier); - SaveCharacterFloats(char_id, "hair_type_highlight_color", create->getType_float_ByName("hair_type_highlight_color", 0), create->getType_float_ByName("hair_type_highlight_color", 1), create->getType_float_ByName("hair_type_highlight_color", 2), classic_multiplier); - SaveCharacterFloats(char_id, "hair_face_color", create->getType_float_ByName("hair_face_color", 0), create->getType_float_ByName("hair_face_color", 1), create->getType_float_ByName("hair_face_color", 2), classic_multiplier); - SaveCharacterFloats(char_id, "hair_face_highlight_color", create->getType_float_ByName("hair_face_highlight_color", 0), create->getType_float_ByName("hair_face_highlight_color", 1), create->getType_float_ByName("hair_face_highlight_color", 2), classic_multiplier); - SaveCharacterFloats(char_id, "shirt_color", create->getType_float_ByName("shirt_color", 0), create->getType_float_ByName("shirt_color", 1), create->getType_float_ByName("shirt_color", 2), classic_multiplier); - SaveCharacterFloats(char_id, "unknown_chest_color", create->getType_float_ByName("unknown_chest_color", 0), create->getType_float_ByName("unknown_chest_color", 1), create->getType_float_ByName("unknown_chest_color", 2), classic_multiplier); - SaveCharacterFloats(char_id, "pants_color", create->getType_float_ByName("pants_color", 0), create->getType_float_ByName("pants_color", 1), create->getType_float_ByName("pants_color", 2), classic_multiplier); - SaveCharacterFloats(char_id, "unknown_legs_color", create->getType_float_ByName("unknown_legs_color", 0), create->getType_float_ByName("unknown_legs_color", 1), create->getType_float_ByName("unknown_legs_color", 2), classic_multiplier); - SaveCharacterFloats(char_id, "unknown9", create->getType_float_ByName("unknown9", 0), create->getType_float_ByName("unknown9", 1), create->getType_float_ByName("unknown9", 2), classic_multiplier); - } - else { - SaveCharacterColors(char_id, "skin_color", create->getType_EQ2_Color_ByName("skin_color")); - SaveCharacterColors(char_id, "model_color", create->getType_EQ2_Color_ByName("model_color")); - SaveCharacterColors(char_id, "eye_color", create->getType_EQ2_Color_ByName("eye_color")); - SaveCharacterColors(char_id, "hair_color1", create->getType_EQ2_Color_ByName("hair_color1")); - SaveCharacterColors(char_id, "hair_color2", create->getType_EQ2_Color_ByName("hair_color2")); - SaveCharacterColors(char_id, "hair_highlight", create->getType_EQ2_Color_ByName("hair_highlight")); - SaveCharacterColors(char_id, "hair_type_color", create->getType_EQ2_Color_ByName("hair_type_color")); - SaveCharacterColors(char_id, "hair_type_highlight_color", create->getType_EQ2_Color_ByName("hair_type_highlight_color")); - SaveCharacterColors(char_id, "hair_face_color", create->getType_EQ2_Color_ByName("hair_face_color")); - SaveCharacterColors(char_id, "hair_face_highlight_color", create->getType_EQ2_Color_ByName("hair_face_highlight_color")); - SaveCharacterColors(char_id, "wing_color1", create->getType_EQ2_Color_ByName("wing_color1")); - SaveCharacterColors(char_id, "wing_color2", create->getType_EQ2_Color_ByName("wing_color2")); - SaveCharacterColors(char_id, "shirt_color", create->getType_EQ2_Color_ByName("shirt_color")); - SaveCharacterColors(char_id, "unknown_chest_color", create->getType_EQ2_Color_ByName("unknown_chest_color")); - SaveCharacterColors(char_id, "pants_color", create->getType_EQ2_Color_ByName("pants_color")); - SaveCharacterColors(char_id, "unknown_legs_color", create->getType_EQ2_Color_ByName("unknown_legs_color")); - SaveCharacterColors(char_id, "unknown9", create->getType_EQ2_Color_ByName("unknown9")); - - SaveCharacterColors(char_id, "soga_skin_color", create->getType_EQ2_Color_ByName("soga_skin_color")); - SaveCharacterColors(char_id, "soga_model_color", create->getType_EQ2_Color_ByName("soga_model_color")); - SaveCharacterColors(char_id, "soga_eye_color", create->getType_EQ2_Color_ByName("soga_eye_color")); - SaveCharacterColors(char_id, "soga_hair_color1", create->getType_EQ2_Color_ByName("soga_hair_color1")); - SaveCharacterColors(char_id, "soga_hair_color2", create->getType_EQ2_Color_ByName("soga_hair_color2")); - SaveCharacterColors(char_id, "soga_hair_highlight", create->getType_EQ2_Color_ByName("soga_hair_highlight")); - SaveCharacterColors(char_id, "soga_hair_type_color", create->getType_EQ2_Color_ByName("soga_hair_type_color")); - SaveCharacterColors(char_id, "soga_hair_type_highlight_color", create->getType_EQ2_Color_ByName("soga_hair_type_highlight_color")); - SaveCharacterColors(char_id, "soga_hair_face_color", create->getType_EQ2_Color_ByName("soga_hair_face_color")); - SaveCharacterColors(char_id, "soga_hair_face_highlight_color", create->getType_EQ2_Color_ByName("soga_hair_face_highlight_color")); - SaveCharacterColors(char_id, "soga_wing_color1", create->getType_EQ2_Color_ByName("soga_wing_color1")); - SaveCharacterColors(char_id, "soga_wing_color2", create->getType_EQ2_Color_ByName("soga_wing_color2")); - SaveCharacterColors(char_id, "soga_shirt_color", create->getType_EQ2_Color_ByName("soga_shirt_color")); - SaveCharacterColors(char_id, "soga_unknown_chest_color", create->getType_EQ2_Color_ByName("soga_unknown_chest_color")); - SaveCharacterColors(char_id, "soga_pants_color", create->getType_EQ2_Color_ByName("soga_pants_color")); - SaveCharacterColors(char_id, "soga_unknown_legs_color", create->getType_EQ2_Color_ByName("soga_unknown_legs_color")); - SaveCharacterColors(char_id, "soga_unknown13", create->getType_EQ2_Color_ByName("soga_unknown13")); - - SaveCharacterFloats(char_id, "soga_eye_type", create->getType_float_ByName("soga_eyes2", 0), create->getType_float_ByName("soga_eyes2", 1), create->getType_float_ByName("soga_eyes2", 2)); - SaveCharacterFloats(char_id, "soga_ear_type", create->getType_float_ByName("soga_ears", 0), create->getType_float_ByName("soga_ears", 1), create->getType_float_ByName("soga_ears", 2)); - SaveCharacterFloats(char_id, "soga_eye_brow_type", create->getType_float_ByName("soga_eye_brows", 0), create->getType_float_ByName("soga_eye_brows", 1), create->getType_float_ByName("soga_eye_brows", 2)); - SaveCharacterFloats(char_id, "soga_cheek_type", create->getType_float_ByName("soga_cheeks", 0), create->getType_float_ByName("soga_cheeks", 1), create->getType_float_ByName("soga_cheeks", 2)); - SaveCharacterFloats(char_id, "soga_lip_type", create->getType_float_ByName("soga_lips", 0), create->getType_float_ByName("soga_lips", 1), create->getType_float_ByName("soga_lips", 2)); - SaveCharacterFloats(char_id, "soga_chin_type", create->getType_float_ByName("soga_chin", 0), create->getType_float_ByName("soga_chin", 1), create->getType_float_ByName("soga_chin", 2)); - SaveCharacterFloats(char_id, "soga_nose_type", create->getType_float_ByName("soga_nose", 0), create->getType_float_ByName("soga_nose", 1), create->getType_float_ByName("soga_nose", 2)); - } - - SaveCharacterFloats(char_id, "eye_type", create->getType_float_ByName("eyes2", 0), create->getType_float_ByName("eyes2", 1), create->getType_float_ByName("eyes2", 2)); - SaveCharacterFloats(char_id, "ear_type", create->getType_float_ByName("ears", 0), create->getType_float_ByName("ears", 1), create->getType_float_ByName("ears", 2)); - SaveCharacterFloats(char_id, "eye_brow_type", create->getType_float_ByName("eye_brows", 0), create->getType_float_ByName("eye_brows", 1), create->getType_float_ByName("eye_brows", 2)); - SaveCharacterFloats(char_id, "cheek_type", create->getType_float_ByName("cheeks", 0), create->getType_float_ByName("cheeks", 1), create->getType_float_ByName("cheeks", 2)); - SaveCharacterFloats(char_id, "lip_type", create->getType_float_ByName("lips", 0), create->getType_float_ByName("lips", 1), create->getType_float_ByName("lips", 2)); - SaveCharacterFloats(char_id, "chin_type", create->getType_float_ByName("chin", 0), create->getType_float_ByName("chin", 1), create->getType_float_ByName("chin", 2)); - SaveCharacterFloats(char_id, "nose_type", create->getType_float_ByName("nose", 0), create->getType_float_ByName("nose", 1), create->getType_float_ByName("nose", 2)); - SaveCharacterFloats(char_id, "body_size", create->getType_float_ByName("body_size", 0), 0, 0); - - return last_insert_id; - } - - // Delete a character from the database - bool DeleteCharacter(int32 account_id, int32 character_id, int32 server_id) - { - Query query; - string delete_char = string("delete from login_characters where char_id=%i and account_id=%i and server_id=%i"); - query.RunQuery2(Q_DELETE, delete_char.c_str(), character_id, account_id, server_id); - - return query.GetAffectedRows() > 0; - } - - // Get character name by character ID, server ID, and account ID - string GetCharacterName(int32 char_id, int32 server_id, int32 account_id) - { - Query query; - MYSQL_ROW row; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name from login_characters where char_id=%lu and server_id=%lu and account_id=%lu and deleted = 0 limit 1", char_id, server_id, account_id); - - if(result && mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - return string(row[0]); - } - return string(""); - } - - // Update character's last played timestamp - bool UpdateCharacterTimeStamp(int32 account_id, int32 character_id, int32 timestamp_update, int32 server_id) - { - Query query; - string update_chars = string("update login_characters set unix_timestamp=%lu where char_id=%lu and account_id=%lu and server_id=%lu"); - query.RunQuery2(Q_UPDATE, update_chars.c_str(), timestamp_update, character_id, account_id, server_id); - - if(!query.GetAffectedRows()) { - LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterTimeStamp query '%s': %s", query.GetQuery(), query.GetError()); - return false; - } - return true; - } - - // Update character's level - bool UpdateCharacterLevel(int32 account_id, int32 character_id, int8 in_level, int32 server_id) - { - Query query; - string update_chars = string("update login_characters set level=%i where char_id=%lu and account_id=%lu and server_id=%lu"); - query.RunQuery2(Q_UPDATE, update_chars.c_str(), in_level, character_id, account_id, server_id); - - if(!query.GetAffectedRows()) { - LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterLevel query '%s': %s", query.GetQuery(), query.GetError()); - return false; - } - return true; - } - - // Update character's race information - bool UpdateCharacterRace(int32 account_id, int32 character_id, int16 in_racetype, int8 in_race, int32 server_id) - { - Query query; - string update_chars = string("update login_characters set race_type=%i, race=%i where char_id=%lu and account_id=%lu and server_id=%lu"); - query.RunQuery2(Q_UPDATE, update_chars.c_str(), in_racetype, in_race, character_id, account_id, server_id); - - if(!query.GetAffectedRows()) { - LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterRace query '%s': %s", query.GetQuery(), query.GetError()); - return false; - } - return true; - } - - // Update character's current zone - bool UpdateCharacterZone(int32 account_id, int32 character_id, int32 zone_id, int32 server_id) - { - Query query; - string update_chars = string("update login_characters set current_zone_id=%i where char_id=%lu and account_id=%lu and server_id=%lu"); - query.RunQuery2(Q_UPDATE, update_chars.c_str(), zone_id, character_id, account_id, server_id); - - if(!query.GetAffectedRows()) { - LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterZone query '%s': %s", query.GetQuery(), query.GetError()); - return false; - } - return true; - } - - // Update character's class - bool UpdateCharacterClass(int32 account_id, int32 character_id, int8 in_class, int32 server_id) - { - Query query; - string update_chars = string("update login_characters set class=%i where char_id=%lu and account_id=%lu and server_id=%lu"); - query.RunQuery2(Q_UPDATE, update_chars.c_str(), in_class, character_id, account_id, server_id); - - if(!query.GetAffectedRows()) { - LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterClass query '%s': %s", query.GetQuery(), query.GetError()); - return false; - } - return true; - } - - // Update character's gender - bool UpdateCharacterGender(int32 account_id, int32 character_id, int8 in_gender, int32 server_id) - { - Query query; - string update_chars = string("update login_characters set gender=%i where char_id=%lu and account_id=%lu and server_id=%lu"); - query.RunQuery2(Q_UPDATE, update_chars.c_str(), in_gender, character_id, account_id, server_id); - - if(!query.GetAffectedRows()) { - LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterGender query '%s': %s", query.GetQuery(), query.GetError()); - return false; - } - return true; - } - - // Update character's name - bool UpdateCharacterName(int32 account_id, int32 character_id, char* newName, int32 server_id) - { - Query query; - string update_chars = string("update login_characters set name='%s' where char_id=%lu and account_id=%lu and server_id=%lu"); - query.RunQuery2(Q_UPDATE, update_chars.c_str(), newName, character_id, account_id, server_id); - - if(!query.GetAffectedRows()) { - LogWrite(LOGIN__ERROR, 0, "Login", "Error in UpdateCharacterName query '%s': %s", query.GetQuery(), query.GetError()); - return false; - } - return true; - } - - // Load account credentials and authenticate user login - LoginAccount* LoadAccount(const char* name, const char* password, bool attemptAccountCreation = true) - { - LoginAccount* acct = nullptr; - Query query; - query.escaped_name = getEscapeString(name); - query.escaped_pass = getEscapeString(password); - time_t now = time(0); - MYSQL_ROW row; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id from account where name='%s' and passwd=sha2('%s',512)", query.escaped_name, query.escaped_pass); - - if(!result) return nullptr; - - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - int32 id = atol(row[0]); - acct = new LoginAccount(id, name, password); - acct->setAuthenticated(true); - } - else if(mysql_num_rows(result) > 0) { - LogWrite(LOGIN__ERROR, 0, "Login", "Error in LoginAccount: more than one account returned for '%s'", name); - } - else if (attemptAccountCreation && !GetAccountIDByName(name)) { - Query newquery; - newquery.RunQuery2(Q_INSERT, "insert into account set name='%s',passwd=sha2('%s',512), created_date=%i", query.escaped_name, query.escaped_pass, now); - // re-run the query for select only not account creation - return LoadAccount(name, password, false); - } - - return acct; - } - - // Get account ID by account name - int32 GetAccountIDByName(const char* name) - { - int32 id = 0; - Query query; - MYSQL_ROW row; - query.escaped_name = getEscapeString(name); - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id from account where name='%s'", query.escaped_name); - - if (result && mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - id = atoi(row[0]); - } - return id; - } - - // Validate world server account credentials - int32 CheckServerAccount(char* name, char* passwd) - { - int32 id = 0; - Query query; - MYSQL_ROW row; - query.escaped_name = getEscapeString(name); - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT lower(password), id from login_worldservers where account='%s' and disabled = 0", query.escaped_name); - - LogWrite(LOGIN__INFO, 0, "Login", "WorldServer CheckServerAccount Account=%s\nSHA=%s", (char*)query.escaped_name, passwd); - - if(result && mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - LogWrite(LOGIN__INFO, 0, "Login", "WorldServer CheckServerAccountResult Account=%s\nPassword=%s", (char*)query.escaped_name, (row && row[0]) ? row[0] : "(NULL)"); - - if (memcmp(row[0], passwd, strnlen(row[0], 256)) == 0) { - LogWrite(LOGIN__INFO, 0, "Login", "WorldServer CheckServerAccountResultMatch Account=%s", (char*)query.escaped_name); - id = atoi(row[1]); - } - } - return id; - } - - // Check if world server account is disabled - bool IsServerAccountDisabled(char* name) - { - Query query; - MYSQL_ROW row; - query.escaped_name = getEscapeString(name); - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id from login_worldservers where account='%s' and disabled = 1", query.escaped_name); - - LogWrite(LOGIN__DEBUG, 0, "Login", "WorldServer IsServerAccountDisabled Account=%s", (char*)query.escaped_name); - - if(result && mysql_num_rows(result) > 0) { - row = mysql_fetch_row(result); - LogWrite(LOGIN__INFO, 0, "Login", "WorldServer IsServerAccountDisabled Match Account=%s", (char*)query.escaped_name); - return true; - } - return false; - } - - // Check if IP address is banned - bool IsIPBanned(char* ipaddr) - { - if(!ipaddr) return false; - - Query query; - MYSQL_ROW row; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT ip from login_bannedips where '%s' LIKE CONCAT(ip ,'%%')", ipaddr); - - LogWrite(LOGIN__DEBUG, 0, "Login", "WorldServer IsServerIPBanned IPPartial=%s", (char*)ipaddr); - - if(result && mysql_num_rows(result) > 0) { - row = mysql_fetch_row(result); - LogWrite(LOGIN__INFO, 0, "Login", "WorldServer IsServerIPBanned Match IPBan=%s", row[0]); - return true; - } - return false; - } - - // Load all configured world server accounts - void GetServerAccounts(vector* server_list) - { - Query query; - MYSQL_ROW row; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, account, name, admin_id from login_worldservers"); - - while((row = mysql_fetch_row(result))) { - LWorld* world = new LWorld(atol(row[0]), row[1], row[2], atoi(row[3])); - world->SetID(world->GetAccountID()); - server_list->push_back(world); - } - } - - // Save client log messages for debugging and support - void SaveClientLog(const char* type, const char* message, const char* player_name, int16 version) - { - Query query; - query.escaped_data1 = getEscapeString(message); - query.escaped_name = getEscapeString(player_name); - query.RunQuery2(Q_INSERT, "insert into log_messages (type, message, name, version) values('%s', '%s', '%s', %i)", type, query.escaped_data1, query.escaped_name, version); - } - - // Verify character deletion request by matching account, character, and name - bool VerifyDelete(int32 account_id, int32 character_id, const char* name) - { - Query query; - query.escaped_name = getEscapeString(name); - query.RunQuery2(Q_UPDATE, "update login_characters set deleted = 1 where char_id=%i and account_id=%i and name='%s'", character_id, account_id, query.escaped_name); - - return query.GetAffectedRows() == 1; - } - - // Get world server name by server ID - char* GetServerAccountName(int32 id) - { - Query query; - MYSQL_ROW row; - char* name = nullptr; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name from login_worldservers where id=%lu", id); - - if(result && mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - if(strlen(row[0]) > 0) { - name = new char[strlen(row[0])+1]; - strcpy(name, row[0]); - } - } - return name; - } - - // Get race type ID by race name, create if not found - int32 GetRaceID(char* name) - { - int32 ret = 1487; - Query query; - MYSQL_ROW row; - query.escaped_name = getEscapeString(name); - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT race_type from login_races where name='%s'", query.escaped_name); - - if(result && mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - ret = atol(row[0]); - } - else if(!result || mysql_num_rows(result) == 0) { - UpdateRaceID(query.escaped_name); - } - return ret; - } - - // Add new race to race table if not exists - void UpdateRaceID(char* name) - { - Query query; - query.RunQuery2(Q_UPDATE, "insert into login_races (name) values('%s')", name); - } - - // Validate client version against allowed versions - bool CheckVersion(char* in_version) - { - Query query; - query.escaped_data1 = getEscapeString(in_version); - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id from login_versions where version='%s' or version='*'", query.escaped_data1); - - return result && mysql_num_rows(result) > 0; - } - - // Get latest table versions for client update system - void GetLatestTableVersions(LatestTableVersions* table_versions) - { - Query query; - MYSQL_ROW row; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT name, max(version) from login_table_versions group by name order by id"); - - if(!result || mysql_num_rows(result) <= 0) return; - - table_versions->SetTableSize(mysql_num_rows(result)); - - while((row = mysql_fetch_row(result))) { - if(VerifyDataTable(row[0])) - table_versions->AddTable(row[0], atoi(row[1]), GetDataVersion(row[0])); - else - table_versions->AddTable(row[0], atoi(row[1]), 0); - } - } - - // Verify if table exists in downloadable tables list - bool VerifyDataTable(char* name) - { - Query query; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT table_name from download_tables where table_name='%s'", name); - return result && mysql_num_rows(result) > 0; - } - - // Get column names for a table, excluding internal version column - string GetColumnNames(char* name) - { - Query query; - MYSQL_ROW row; - string columns = "("; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "show columns from %s", name); - - if(result && mysql_num_rows(result) > 0) { - int16 i = 0; - while((row = mysql_fetch_row(result))) { - if(strcmp(row[0], "table_data_version") != 0) { - if(i > 0) columns.append(","); - columns.append(row[0]); - i++; - } - } - } - columns.append(") "); - return columns; - } - - // Get table data for client downloads with version filtering - TableDataQuery* GetTableDataQuery(int32 server_ip, char* name, int16 version) - { - Query query; - MYSQL_ROW row; - query.escaped_name = getEscapeString(name); - TableDataQuery* table_query = nullptr; - MYSQL_RES* result = nullptr; - string columns; - - if(VerifyDataTable(query.escaped_name)) { - result = query.RunQuery2(Q_SELECT, "SELECT * from %s where table_data_version > %i", query.escaped_name, version); - columns = GetColumnNames(query.escaped_name); - } - - if(result && mysql_num_rows(result) > 0) { - table_query = new TableDataQuery(query.escaped_name); - table_query->num_queries = mysql_num_rows(result); - table_query->columns_size = columns.length() + 1; - table_query->columns = new char[table_query->columns_size + 1]; - table_query->version = GetDataVersion(query.escaped_name); - strcpy(table_query->columns, (char*)columns.c_str()); - - string query_data; - MYSQL_FIELD* field; - int* int_list = new int[mysql_num_fields(result)]; - int16 ndx = 0; - - while((field = mysql_fetch_field(result))) { - int_list[ndx] = IS_NUM(field->type); - if(strcmp(field->name,"table_data_version") == 0) - int_list[ndx] = 2; - ndx++; - } - - ndx = 0; - while((row = mysql_fetch_row(result))) { - query_data = ""; - for(int i = 0; i < mysql_num_fields(result); i++) { - if(int_list[i] < 2) { - if(i > 0) query_data.append(","); - - if(!int_list[i]) { - query_data.append("'").append(getEscapeString(row[i])).append("'"); - } - else - query_data.append(row[i]); - } - } - - TableData* new_query = new TableData; - new_query->size = query_data.length() + 1; - new_query->query = new char[query_data.length() + 1]; - strcpy(new_query->query, query_data.c_str()); - table_query->queries.push_back(new_query); - ndx++; - } - safe_delete_array(int_list); - } - else { - string query2 = string("The user tried to download the following table: ").append(query.escaped_name); - SaveClientLog("Possible Hacking Attempt", (char*)query2.c_str(), "Hacking Data", server_ip); - } - return table_query; - } - - // Get table update queries for client patching system - TableQuery* GetLatestTableQuery(int32 server_ip, char* name, int16 version) - { - Query query; - MYSQL_ROW row; - query.escaped_name = getEscapeString(name); - TableQuery* table_query = nullptr; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT query, version from login_table_versions where name = '%s' and version>=%i order by version", query.escaped_name, version + 1); - - if(result && mysql_num_rows(result) > 0) { - table_query = new TableQuery; - - while((row = mysql_fetch_row(result))) { - char* rowdata = row[0]; - if(strstr(rowdata, ";")) { - char* token = strtok(rowdata,";"); - while(token) { - char* new_query = new char[strlen(token) + 1]; - strcpy(new_query, token); - table_query->AddQuery(new_query); - token = strtok(nullptr, ";"); - } - } - else - table_query->AddQuery(rowdata); - - table_query->latest_version = atoi(row[1]); - } - - strcpy(table_query->tablename, name); - table_query->your_version = version; - } - else { - string query2 = string("The following was the DB Query: ").append(query.GetQuery()); - SaveClientLog("Possible Hacking Attempt", (char*)query2.c_str(), "Hacking Query", server_ip); - } - return table_query; - } - - // Get current data version for a table - sint16 GetDataVersion(char* name) - { - Query query; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT max(table_data_version) from %s", name); - sint16 ret_version = 0; - - if(result && mysql_num_rows(result) > 0) { - MYSQL_ROW row; - row = mysql_fetch_row(result); - if(row[0]) ret_version = atoi(row[0]); - } - return ret_version; - } - - // Clean up old world server statistics older than 24 hours - void RemoveOldWorldServerStats() - { - Query query; - query.RunQuery2(Q_DELETE, "delete from login_worldstats where (UNIX_TIMESTAMP(NOW())-UNIX_TIMESTAMP(last_update)) > 86400"); - } - - // Update world server statistics including player count and zone count - void UpdateWorldServerStats(LWorld* world, sint32 status) - { - Query query; - query.RunQuery2(Q_INSERT, "insert into login_worldstats (world_id, world_status, current_players, current_zones, last_update, world_max_level) values(%u, %i, %i, %i, NOW(), %i) ON DUPLICATE KEY UPDATE current_players=%i,current_zones=%i,world_max_level=%i,world_status=%i,last_update=NOW()", - world->GetAccountID(), status, world->GetPlayerNum(), world->GetZoneNum(), world->GetMaxWorldLevel(), world->GetPlayerNum(), world->GetZoneNum(), world->GetMaxWorldLevel(), status); - - string update_stats = string("update login_worldservers set lastseen=%u where id=%i"); - query.RunQuery2(Q_UPDATE, update_stats.c_str(), Timer::GetUnixTimeStamp(), world->GetAccountID()); - } - - // Reset world server connection time if server has been offline too long - bool ResetWorldServerStatsConnectedTime(LWorld* world) - { - if(!world || world->GetAccountID() == 0) return false; - - Query query; - string update_stats = string("update login_worldstats set connected_time=now() where world_id=%i and (UNIX_TIMESTAMP(NOW())-UNIX_TIMESTAMP(last_update)) > 300"); - query.RunQuery2(Q_UPDATE, update_stats.c_str(), world->GetAccountID()); - - return true; - } - - // Reset all world server statistics to offline state - void ResetWorldStats() - { - Query query; - string update_stats = string("update login_worldstats set world_status=-4, current_players=0, current_zones=0"); - query.RunQuery2(update_stats.c_str(), Q_UPDATE); - } - - // Save bug report submitted by players - void SaveBugReport(int32 world_id, char* category, char* subcategory, char* causes_crash, char* reproducible, char* summary, char* description, char* version, char* player, int32 account_id, char* spawn_name, int32 spawn_id, int32 zone_id) - { - Query query; - string bug_report = string("insert into bugs (world_id, category, subcategory, causes_crash, reproducible, summary, description, version, player, account_id, spawn_name, spawn_id, zone_id) values(%lu, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %lu, '%s', %lu, %lu)"); - query.RunQuery2(Q_INSERT, bug_report.c_str(), world_id, database.getSafeEscapeString(category).c_str(), database.getSafeEscapeString(subcategory).c_str(), - database.getSafeEscapeString(causes_crash).c_str(), database.getSafeEscapeString(reproducible).c_str(), database.getSafeEscapeString(summary).c_str(), - database.getSafeEscapeString(description).c_str(), database.getSafeEscapeString(version).c_str(), database.getSafeEscapeString(player).c_str(), account_id, - database.getSafeEscapeString(spawn_name).c_str(), spawn_id, zone_id); - FixBugReport(); - } - - // Fix URL encoding in bug reports by converting hex codes to characters - void FixBugReport() - { - Query query; - string bug_report = string("update bugs set description = REPLACE(description,SUBSTRING(description,INSTR(description,'%'), 3),char(CONV(SUBSTRING(description,INSTR(description,'%')+1, 2), 16, 10))), summary = REPLACE(summary,SUBSTRING(summary,INSTR(summary,'%'), 3),char(CONV(SUBSTRING(summary,INSTR(summary,'%')+1, 2), 16, 10)))"); - query.RunQuery2(bug_report.c_str(), Q_UPDATE); - } - - // Update world server IP address in database - void UpdateWorldIPAddress(int32 world_id, int32 address) - { - struct in_addr in; - in.s_addr = address; - Query query; - query.RunQuery2(Q_UPDATE, "update login_worldservers set ip_address='%s' where id=%lu", inet_ntoa(in), world_id); - } - - // Update player account IP address for tracking - void UpdateAccountIPAddress(int32 account_id, int32 address) - { - struct in_addr in; - in.s_addr = address; - Query query; - query.RunQuery2(Q_UPDATE, "update account set ip_address='%s' where id=%lu", inet_ntoa(in), account_id); - } - - // Get maximum characters allowed per account from configuration - int8 GetMaxCharsSetting() - { - int8 max_chars = 7; // live defaults to 7 for GOLD members - Query query; - MYSQL_ROW row; - - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "select config_value from login_config where config_name='max_characters_per_account'"); - - if (result && mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - if (row[0]) max_chars = atoi(row[0]); - } - return max_chars; - } - - // Calculate account bonus based on max level characters - int16 GetAccountBonus(int32 acct_id) - { - int32 bonus = 0; - int16 world_id = 0; - Query query; - MYSQL_ROW row; - Query query2; - MYSQL_ROW row2; - - // get the world ID for the character. TODO: Support multi server characters. - MYSQL_RES* result2 = query2.RunQuery2(Q_SELECT, "select server_id from login_characters where account_id=%i", acct_id); - - if (result2 && mysql_num_rows(result2) >= 1) { - row2 = mysql_fetch_row(result2); - if (row2[0]) world_id = atoi(row2[0]); - } - - // pull all characters greater than the max level from the server - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT COUNT(id) FROM login_characters WHERE LEVEL >= (select world_max_level from login_worldstats where world_id=%i) AND account_id=%i", world_id, acct_id); - - if (result && mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - if(row[0]) bonus = atoi(row[0]); - } - return bonus; - } - - // Update world server client version information - void UpdateWorldVersion(int32 world_id, char* version) - { - Query query; - query.RunQuery2(Q_UPDATE, "update login_worldservers set login_version='%s' where id=%u", version, world_id); - } - - // Update account's last used client version - void UpdateAccountClientDataVersion(int32 account_id, int16 version) - { - Query query; - query.RunQuery2(Q_UPDATE, "UPDATE account SET last_client_version='%i' WHERE id = %u", version, account_id); - } - - // Save character portrait picture data (base64 encoded) - void SaveCharacterPicture(int32 account_id, int32 character_id, int32 server_id, int16 picture_size, uchar* picture) - { - stringstream ss_hex; - stringstream ss_query; - ss_hex.flags(ios::hex); - - for (int32 i = 0; i < picture_size; i++) - ss_hex << setfill('0') << setw(2) << (int32)picture[i]; - - ss_query << "INSERT INTO `ls_character_picture` (`server_id`, `account_id`, `character_id`, `picture`) VALUES (" - << server_id << ", " << account_id << ", " << character_id << ", '" << ss_hex.str() - << "') ON DUPLICATE KEY UPDATE `picture` = '" << ss_hex.str() << "'"; - - if (!dbLogin.Query(ss_query.str().c_str())) - LogWrite(DATABASE__ERROR, 0, "DBNew", "MySQL Error %u: %s", dbLogin.GetError(), dbLogin.GetErrorMsg()); - } - - // Database connection instance for login operations - DatabaseNew dbLogin; -}; \ No newline at end of file diff --git a/old/LoginServer/login_opcodes.h b/old/LoginServer/login_opcodes.h new file mode 100644 index 0000000..734ae2f --- /dev/null +++ b/old/LoginServer/login_opcodes.h @@ -0,0 +1,52 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. +*/ + +#ifndef LOGIN_OPCODES_H +#define LOGIN_OPCODES_H +#define OP_Login2 0x0200 +#define OP_GetLoginInfo 0x0300 +#define OP_SendServersFragment 0x0D00 +#define OP_LoginInfo 0x0100 +#define OP_SessionId 0x0900 +#define OP_Disconnect 0x0500 +//#define OP_Reg_SendPricing 0x0400 +#define OP_AllFinish 0x0500 +#define OP_Ack5 0x1500 +#define OP_Chat_ChannelList 0x0600 +#define OP_Chat_JoinChannel 0x0700 +#define OP_Chat_PartChannel 0x0800 +#define OP_Chat_ChannelMessage 0x0930 +#define OP_Chat_Tell 0x0a00 +#define OP_Chat_SysMsg 0x0b00 +#define OP_Chat_CreateChannel 0x0c00 +#define OP_Chat_ChangeChannel 0x0d00 +#define OP_Chat_DeleteChannel 0x0e00 +#define OP_Chat_UserList 0x1000 +#define OP_Reg_GetPricing 0x1a00 // for new account signup +#define OP_Reg_SendPricing 0x1b00 +#define OP_RegisterAccount 0x2300 +#define OP_Chat_ChannelWelcome 0x2400 +#define OP_Chat_PopupMakeWindow 0x3000 +#define OP_BillingInfoAccepted 0x3300 // i THINK =p +#define OP_CheckGameCardValid 0x3400 +#define OP_GameCardTimeLeft 0x3600 +#define OP_AccountExpired 0x4200 +#define OP_Reg_GetPricing2 0x4400 // for re-registering +#define OP_ChangePassword 0x4500 +#define OP_ServerList 0x4600 +#define OP_SessionKey 0x4700 +#define OP_RequestServerStatus 0x4800 +#define OP_SendServerStatus 0x4A00 +#define OP_Reg_ChangeAcctLogin 0x5100 +#define OP_LoginBanner 0x5200 +#define OP_Chat_GuildsList 0x5500 +#define OP_Chat_GuildEdit 0x5700 +#define OP_Version 0x5900 +#define OP_RenewAccountBillingInfo 0x7a00 + +#endif /* LOGIN_OPCODES_H */ + diff --git a/old/LoginServer/login_opcodes.hpp b/old/LoginServer/login_opcodes.hpp deleted file mode 100644 index 9303d25..0000000 --- a/old/LoginServer/login_opcodes.hpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team, GPL License - -#pragma once - -// Core login and session management opcodes -#define OP_Login2 0x0200 // Primary login authentication -#define OP_GetLoginInfo 0x0300 // Request login information from client -#define OP_LoginInfo 0x0100 // Response with login details -#define OP_SessionId 0x0900 // Session identifier assignment -#define OP_SessionKey 0x4700 // Session key exchange -#define OP_Disconnect 0x0500 // Connection termination -#define OP_AllFinish 0x0500 // Process completion acknowledgment -#define OP_Ack5 0x1500 // Generic acknowledgment packet - -// Server list and status management -#define OP_SendServersFragment 0x0D00 // Fragment of server list data -#define OP_ServerList 0x4600 // Complete server list -#define OP_RequestServerStatus 0x4800 // Request current server status -#define OP_SendServerStatus 0x4A00 // Response with server status -#define OP_Version 0x5900 // Client/server version verification - -// Chat system opcodes -#define OP_Chat_ChannelList 0x0600 // Available chat channels -#define OP_Chat_JoinChannel 0x0700 // Join a chat channel -#define OP_Chat_PartChannel 0x0800 // Leave a chat channel -#define OP_Chat_ChannelMessage 0x0930 // Message to channel -#define OP_Chat_Tell 0x0a00 // Private message -#define OP_Chat_SysMsg 0x0b00 // System message -#define OP_Chat_CreateChannel 0x0c00 // Create new chat channel -#define OP_Chat_ChangeChannel 0x0d00 // Modify channel settings -#define OP_Chat_DeleteChannel 0x0e00 // Remove chat channel -#define OP_Chat_UserList 0x1000 // Users in channel -#define OP_Chat_ChannelWelcome 0x2400 // Welcome message for channel -#define OP_Chat_PopupMakeWindow 0x3000 // Create popup chat window -#define OP_Chat_GuildsList 0x5500 // Available guilds list -#define OP_Chat_GuildEdit 0x5700 // Guild modification - -// Account registration and management -#define OP_RegisterAccount 0x2300 // New account creation -#define OP_Reg_GetPricing 0x1a00 // Get pricing for new account signup -//#define OP_Reg_SendPricing 0x0400 // Legacy pricing send (unused) -#define OP_Reg_SendPricing 0x1b00 // Send pricing information -#define OP_Reg_GetPricing2 0x4400 // Get pricing for re-registration -#define OP_Reg_ChangeAcctLogin 0x5100 // Change account login credentials -#define OP_ChangePassword 0x4500 // Password modification -#define OP_LoginBanner 0x5200 // Login banner display - -// Billing and subscription management -#define OP_BillingInfoAccepted 0x3300 // Billing information accepted -#define OP_CheckGameCardValid 0x3400 // Validate game card -#define OP_GameCardTimeLeft 0x3600 // Remaining game card time -#define OP_AccountExpired 0x4200 // Account expiration notification -#define OP_RenewAccountBillingInfo 0x7a00 // Billing renewal information \ No newline at end of file diff --git a/old/LoginServer/login_structs.h b/old/LoginServer/login_structs.h new file mode 100644 index 0000000..bd8e9b9 --- /dev/null +++ b/old/LoginServer/login_structs.h @@ -0,0 +1,61 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. +*/ +#ifndef LOGIN_STRUCTS_H +#define LOGIN_STRUCTS_H + +#include "../common/types.h" +#include "PacketHeaders.h" + +#pragma pack(1) +struct LS_LoginRequest{ + EQ2_16BitString AccessCode; + EQ2_16BitString unknown1; + EQ2_16BitString username; + EQ2_16BitString password; + EQ2_16BitString unknown2[4]; + int16 unknown3; + int32 unknown4[2]; +}; +struct LS_WorldStatusChanged{ + int32 server_id; + int8 up; + int8 locked; + int8 hidden; +}; +struct LS_PlayCharacterRequest{ + int32 character_id; + int32 server_id; + int16 unknown1; +}; +struct LS_OLDPlayCharacterRequest{ + int32 character_id; + EQ2_16BitString name; +}; + +struct LS_CharListAccountInfoEarlyClient { + int32 account_id; + int32 unknown1; + int16 unknown2; + int32 maxchars; + int8 unknown4; // 15 bytes total + // int8 unknown7; // adds 'free' option.. +}; + +struct LS_CharListAccountInfo{ + int32 account_id; + int32 unknown1; + int16 unknown2; + int32 maxchars; + // DoF does not have the following data + int8 unknown4; + int32 unknown5[4]; + int8 vet_adv_bonus; // sets Veteran Bonus under 'Select Character' yellow (vs greyed out), adventure/tradeskill bonus 200% + int8 vet_trade_bonus; // when 1 (count?) provides free upgrade option for character to lvl 90 (heroic character) -- its a green 'Free' up arrow next to the character that is selected in char select +}; // 33 bytes +#pragma pack() + +#endif diff --git a/old/LoginServer/login_structs.hpp b/old/LoginServer/login_structs.hpp deleted file mode 100644 index 976c1d4..0000000 --- a/old/LoginServer/login_structs.hpp +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL License -#pragma once - -#include "packet_headers.hpp" -#include "../common/types.hpp" - -#pragma pack(1) - -// Login request packet structure containing user credentials and access information -struct LS_LoginRequest -{ - EQ2_16BitString AccessCode; // Access code for login validation - EQ2_16BitString unknown1; // Unknown field - possibly session data - EQ2_16BitString username; // Player username - EQ2_16BitString password; // Player password - EQ2_16BitString unknown2[4]; // Unknown array - possibly additional auth data - int16 unknown3; // Unknown 16-bit field - int32 unknown4[2]; // Unknown 32-bit array -}; - -// World server status change notification structure -struct LS_WorldStatusChanged -{ - int32 server_id; // Unique identifier for the world server - int8 up; // Server online status (1 = up, 0 = down) - int8 locked; // Server locked status (1 = locked, 0 = unlocked) - int8 hidden; // Server visibility status (1 = hidden, 0 = visible) -}; - -// Character play request for newer client versions -struct LS_PlayCharacterRequest -{ - int32 character_id; // Unique character identifier - int32 server_id; // Target server identifier - int16 unknown1; // Unknown field - possibly additional flags -}; - -// Character play request for older client versions -struct LS_OLDPlayCharacterRequest -{ - int32 character_id; // Unique character identifier - EQ2_16BitString name; // Character name string -}; - -// Account information structure for early client versions -struct LS_CharListAccountInfoEarlyClient -{ - int32 account_id; // Unique account identifier - int32 unknown1; // Unknown field - possibly subscription data - int16 unknown2; // Unknown 16-bit field - int32 maxchars; // Maximum characters allowed on account - int8 unknown4; // Unknown field - padding or flags (15 bytes total) -}; - -// Complete account information structure for character list -struct LS_CharListAccountInfo -{ - int32 account_id; // Unique account identifier - int32 unknown1; // Unknown field - possibly subscription type - int16 unknown2; // Unknown 16-bit field - int32 maxchars; // Maximum characters allowed on account - int8 unknown4; // Unknown field - DoF compatibility marker - int32 unknown5[4]; // Unknown array - extended account data - int8 vet_adv_bonus; // Veteran adventure bonus flag (enables 200% bonus) - int8 vet_trade_bonus; // Veteran tradeskill bonus flag (enables free lvl 90 upgrade) -}; // Total structure size: 33 bytes - -#pragma pack() \ No newline at end of file diff --git a/old/LoginServer/main.cpp b/old/LoginServer/main.cpp deleted file mode 100644 index aab96af..0000000 --- a/old/LoginServer/main.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (C) 2007-2021 EQ2Emulator Development Team, GPL v3 License - -#include -#include - -#include "net.hpp" - -EQStreamFactory eqsf(LoginStream); -std::map EQOpcodeManager; -NetConnection net; -ClientList client_list; -LWorldList world_list; -LoginDatabase database; -ConfigReader configReader; -std::map EQOpcodeVersions; -Timer statTimer(60000); -volatile bool RunLoops = true; - -void CatchSignal(int sig_num) -{ - std::cout << "Got signal " << sig_num << std::endl; - RunLoops = false; -} - -int main(int argc, char** argv) -{ - if (signal(SIGINT, CatchSignal) == SIG_ERR) { - std::cerr << "Could not set signal handler" << std::endl; - } - - LogStart(); - LogParseConfigs(); - net.WelcomeHeader(); - - srand(time(NULL)); - - if (!net.ReadLoginConfig()) - return 1; - - net.InitWebServer(net.GetWebLoginAddress(), net.GetWebLoginPort(), net.GetWebCertFile(), net.GetWebKeyFile(), net.GetWebKeyPassword(), net.GetWebHardcodeUser(), net.GetWebHardcodePassword()); - - const char* structList[] = { "CommonStructs.xml", "LoginStructs.xml" }; - - for (int s = 0; s < sizeof(structList) / sizeof(const char*); s++) { - LogWrite(INIT__INFO, 0, "Init", "Loading Structs File %s..", structList[s]); - if (configReader.processXML_Elements(structList[s])) - LogWrite(INIT__INFO, 0, "Init", "Loading Structs File %s completed..", structList[s]); - else { - LogWrite(INIT__ERROR, 0, "Init", "Loading Structs File %s FAILED!", structList[s]); - return 1; - } - } - - LogWrite(INIT__INFO, 0, "Init", "Initialize World List.."); - world_list.Init(); - - if (eqsf.listen_ip_address) - LogWrite(INIT__INFO, 0, "Init", "Login server listening on %s port %i", eqsf.listen_ip_address, net.GetPort()); - else - LogWrite(INIT__INFO, 0, "Init", "Login server listening on port %i", net.GetPort()); - - if (!eqsf.Open(net.GetPort())) { - LogWrite(INIT__ERROR, 0, "Init", "Failed to open port %i.", net.GetPort()); - return 1; - } - - net.login_running = true; - net.login_uptime = getCurrentTimestamp(); - net.UpdateWindowTitle(); - - EQStream* eqs; - Timer* TimeoutTimer = new Timer(5000); - TimeoutTimer->Start(); - - while (RunLoops) { - Timer::SetCurrentTime(); - - while ((eqs = eqsf.Pop())) { - struct in_addr in; - in.s_addr = eqs->GetRemoteIP(); - - LogWrite(LOGIN__INFO, 0, "Login", "New client from IP: %s on port %i", inet_ntoa(in), ntohs(eqs->GetRemotePort())); - Client* client = new Client(eqs); - eqs->SetClientVersion(0); - client_list.Add(client); - net.numclients++; - net.UpdateWindowTitle(); - } - - if (TimeoutTimer->Check()) { - eqsf.CheckTimeout(); - } - - if (statTimer.Check()) { - world_list.UpdateWorldStats(); - database.RemoveOldWorldServerStats(); - database.FixBugReport(); - } - - client_list.Process(); - world_list.Process(); - - Sleep(1); - } - - eqsf.Close(); - world_list.Shutdown(); - - delete TimeoutTimer; - - return 0; -} \ No newline at end of file diff --git a/old/LoginServer/net.cpp b/old/LoginServer/net.cpp new file mode 100644 index 0000000..fe6b8cd --- /dev/null +++ b/old/LoginServer/net.cpp @@ -0,0 +1,363 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. +*/ +#include "../common/debug.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "../common/queue.h" +#include "../common/timer.h" + +#include "../common/seperator.h" + +#include "net.h" +#include "client.h" + +#include "LoginDatabase.h" +#include "LWorld.h" +#include "../common/packet_functions.h" +#include "../common/EQStreamFactory.h" +#include "../common/MiscFunctions.h" +#include "../common/version.h" + +#include "../common/PacketStruct.h" +#include "../common/DataBuffer.h" +#include "../common/ConfigReader.h" +#include "../common/Log.h" +#include "../common/JsonParser.h" +#include "../common/Common_Defines.h" + +#ifdef WIN32 + #define snprintf _snprintf + #define vsnprintf _vsnprintf + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + #include +#else + #include + #include "../common/unix.h" +#endif +EQStreamFactory eqsf(LoginStream); +mapEQOpcodeManager; +//TCPServer eqns(5999); +NetConnection net; +ClientList client_list; +LWorldList world_list; +LoginDatabase database; +ConfigReader configReader; +map EQOpcodeVersions; +Timer statTimer(60000); + +volatile bool RunLoops = true; +bool ReadLoginConfig(); + +#ifdef PUBLICLOGIN +char version[200], consoletitle[200]; +#endif +#include "../common/timer.h" + +#include "../common/CRC16.h" +#include + +int main(int argc, char** argv){ +#ifdef _DEBUG + _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); +#endif + if (signal(SIGINT, CatchSignal) == SIG_ERR) { + cerr << "Could not set signal handler" << endl; + } + + LogStart(); + + LogParseConfigs(); + net.WelcomeHeader(); + + srand(time(NULL)); + + if(!net.ReadLoginConfig()) + return 1; + + net.InitWebServer(net.GetWebLoginAddress(), net.GetWebLoginPort(), net.GetWebCertFile(), net.GetWebKeyFile(), net.GetWebKeyPassword(), net.GetWebHardcodeUser(), net.GetWebHardcodePassword()); + + const char* structList[] = { "CommonStructs.xml", "LoginStructs.xml" }; + + for (int s = 0; s < sizeof(structList) / sizeof(const char*); s++) + { + LogWrite(INIT__INFO, 0, "Init", "Loading Structs File %s..", structList[s]); + if (configReader.processXML_Elements(structList[s])) + LogWrite(INIT__INFO, 0, "Init", "Loading Structs File %s completed..", structList[s]); + else + { + LogWrite(INIT__ERROR, 0, "Init", "Loading Structs File %s FAILED!", structList[s]); + return 1; + } + } + + + LogWrite(INIT__INFO, 0, "Init", "Initialize World List.."); + world_list.Init(); + + if(eqsf.listen_ip_address) + LogWrite(INIT__INFO, 0, "Init", "Login server listening on %s port %i", eqsf.listen_ip_address, net.GetPort()); + else + LogWrite(INIT__INFO, 0, "Init", "Login server listening on port %i", net.GetPort()); + /*} + else { + cout << "EQNetworkServer.Open() error" << endl; + return 1; + }*/ + if (!eqsf.Open(net.GetPort())) { + LogWrite(INIT__ERROR, 0, "Init", "Failed to open port %i.", net.GetPort()); + return 1; + } + net.login_running = true; + net.login_uptime = getCurrentTimestamp(); + + net.UpdateWindowTitle(); + EQStream* eqs; + Timer* TimeoutTimer = new Timer(5000); + TimeoutTimer->Start(); + while(RunLoops) { + Timer::SetCurrentTime(); + while ((eqs = eqsf.Pop())) { + struct in_addr in; + in.s_addr = eqs->GetRemoteIP(); + + LogWrite(LOGIN__INFO, 0, "Login", "New client from IP: %s on port %i", inet_ntoa(in), ntohs(eqs->GetRemotePort())); + Client* client = new Client(eqs); + eqs->SetClientVersion(0); + client_list.Add(client); + net.numclients++; + net.UpdateWindowTitle(); + } + if(TimeoutTimer->Check()){ + eqsf.CheckTimeout(); + } + if(statTimer.Check()){ + world_list.UpdateWorldStats(); + database.RemoveOldWorldServerStats(); + database.FixBugReport(); + } + client_list.Process(); + world_list.Process(); +#ifdef WIN32 + if(kbhit()) + { + int hitkey = getch(); + net.HitKey(hitkey); + } +#endif + Sleep(1); + } + //close + //eqns.Close(); + eqsf.Close(); + world_list.Shutdown(); + return 0; +} +#ifdef WIN32 +void NetConnection::HitKey(int keyhit) +{ + switch(keyhit) + { + case 'l': + case 'L': { + world_list.ListWorldsToConsole(); + break; + } + case 'v': + case 'V': + { + printf("========Version Info=========\n"); + printf("%s %s\n", EQ2EMU_MODULE, CURRENT_VERSION); + printf("Last Compiled on %s %s\n", COMPILE_DATE, COMPILE_TIME); + printf("=============================\n\n"); + break; + } + case 'H': + case 'h': { + printf("===========Help=============\n"); + printf("Available Commands:\n"); + printf("l = Listing of World Servers\n"); + printf("v = Login Version\n"); +// printf("0 = Kick all connected world servers\n"); + printf("============================\n\n"); + break; + } + default: + printf("Invalid Command.\n"); + break; + } +} +#endif + +void CatchSignal(int sig_num) { + cout << "Got signal " << sig_num << endl; + RunLoops = false; +} + +bool NetConnection::ReadLoginConfig() { + JsonParser parser(MAIN_CONFIG_FILE); + if(!parser.IsLoaded()) { + LogWrite(INIT__ERROR, 0, "Init", "Failed to find %s in server directory..", MAIN_CONFIG_FILE); + return false; + } + std::string serverport = parser.getValue("loginconfig.serverport"); + std::string serverip = parser.getValue("loginconfig.serverip"); + + if (!parser.convertStringToUnsignedShort(serverport, port)) { + LogWrite(INIT__ERROR, 0, "Init", "Failed to translate loginconfig.serverport.."); + return false; + } + + if(serverip.size() > 0) { + eqsf.listen_ip_address = new char[serverip.size() + 1]; + strcpy(eqsf.listen_ip_address, serverip.c_str()); + } + else { + safe_delete(eqsf.listen_ip_address); + eqsf.listen_ip_address = nullptr; + } + + std::string acctcreate_str = parser.getValue("loginconfig.accountcreation"); + int16 allow_acct = 0; + parser.convertStringToUnsignedShort(acctcreate_str, allow_acct); + allowAccountCreation = allow_acct > 0 ? true : false; + + std::string expflag_str = parser.getValue("loginconfig.expansionflag"); + parser.convertStringToUnsignedInt(expflag_str, expansionFlag); + + std::string citiesflag_str = parser.getValue("loginconfig.citiesflag"); + parser.convertStringToUnsignedChar(citiesflag_str, citiesFlag); + + std::string defaultsublevel_str = parser.getValue("loginconfig.defaultsubscriptionlevel"); + parser.convertStringToUnsignedInt(defaultsublevel_str, defaultSubscriptionLevel); + + std::string enableraces_str = parser.getValue("loginconfig.enabledraces"); + parser.convertStringToUnsignedInt(enableraces_str, enabledRaces); + + web_loginaddress = parser.getValue("loginconfig.webloginaddress"); + web_certfile = parser.getValue("loginconfig.webcertfile"); + web_keyfile = parser.getValue("loginconfig.webkeyfile"); + web_keypassword = parser.getValue("loginconfig.webkeypassword"); + web_hardcodeuser = parser.getValue("loginconfig.webhardcodeuser"); + web_hardcodepassword = parser.getValue("loginconfig.webhardcodepassword"); + + std::string webloginport_str = parser.getValue("loginconfig.webloginport"); + parser.convertStringToUnsignedShort(webloginport_str, web_loginport); + + LogWrite(INIT__INFO, 0, "Init", "%s loaded..", MAIN_CONFIG_FILE); + + + LogWrite(INIT__INFO, 0, "Init", "Database init begin.."); + //remove this when all database calls are using the new database class + if (!database.Init()) { + LogWrite(INIT__ERROR, 0, "Init", "Database init FAILED!"); + LogStop(); + return false; + } + + LogWrite(INIT__INFO, 0, "Init", "Loading opcodes 2.0.."); + EQOpcodeVersions = database.GetVersions(); + map::iterator version_itr2; + int16 version1 = 0; + for (version_itr2 = EQOpcodeVersions.begin(); version_itr2 != EQOpcodeVersions.end(); version_itr2++) { + version1 = version_itr2->first; + EQOpcodeManager[version1] = new RegularOpcodeManager(); + map eq = database.GetOpcodes(version1); + if(!EQOpcodeManager[version1]->LoadOpcodes(&eq)) { + LogWrite(INIT__ERROR, 0, "Init", "Loading opcodes failed. Make sure you have sourced the opcodes.sql file!"); + return false; + } + } + + return true; +} + +void NetConnection::UpdateWindowTitle(char* iNewTitle) { +#ifdef WIN32 + char tmp[500]; + if (iNewTitle) { + snprintf(tmp, sizeof(tmp), "Login: %s", iNewTitle); + } + else { + snprintf(tmp, sizeof(tmp), "%s, Version: %s: %i Server(s), %i Client(s) Connected", EQ2EMU_MODULE, CURRENT_VERSION, net.numservers, net.numclients); + } + SetConsoleTitle(tmp); +#endif +} + +void NetConnection::WelcomeHeader() +{ +#ifdef _WIN32 + HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE); + SetConsoleTextAttribute(console, FOREGROUND_WHITE_BOLD); +#endif + printf("Module: %s, Version: %s", EQ2EMU_MODULE, CURRENT_VERSION); +#ifdef _WIN32 + SetConsoleTextAttribute(console, FOREGROUND_YELLOW_BOLD); +#endif + printf("\n\nCopyright (C) 2007-2021 EQ2Emulator. https://www.eq2emu.com \n\n"); + printf("EQ2Emulator is free software: you can redistribute it and/or modify\n"); + printf("it under the terms of the GNU General Public License as published by\n"); + printf("the Free Software Foundation, either version 3 of the License, or\n"); + printf("(at your option) any later version.\n\n"); + printf("EQ2Emulator is distributed in the hope that it will be useful,\n"); + printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n"); + printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"); + printf("GNU General Public License for more details.\n\n"); +#ifdef _WIN32 + SetConsoleTextAttribute(console, FOREGROUND_GREEN_BOLD); +#endif + printf(" /$$$$$$$$ /$$$$$$ /$$$$$$ /$$$$$$$$ \n"); + printf("| $$_____/ /$$__ $$ /$$__ $$| $$_____/ \n"); + printf("| $$ | $$ \\ $$|__/ \\ $$| $$ /$$$$$$/$$$$ /$$ /$$\n"); + printf("| $$$$$ | $$ | $$ /$$$$$$/| $$$$$ | $$_ $$_ $$| $$ | $$\n"); + printf("| $$__/ | $$ | $$ /$$____/ | $$__/ | $$ \\ $$ \\ $$| $$ | $$\n"); + printf("| $$ | $$/$$ $$| $$ | $$ | $$ | $$ | $$| $$ | $$\n"); + printf("| $$$$$$$$| $$$$$$/| $$$$$$$$| $$$$$$$$| $$ | $$ | $$| $$$$$$/\n"); + printf("|________/ \\____ $$$|________/|________/|__/ |__/ |__/ \\______/ \n"); + printf(" \\__/ \n\n"); +#ifdef _WIN32 + SetConsoleTextAttribute(console, FOREGROUND_MAGENTA_BOLD); +#endif + printf(" Website : https://eq2emu.com \n"); + printf(" Wiki : https://wiki.eq2emu.com \n"); + printf(" Git : https://git.eq2emu.com \n"); + printf(" Discord : https://discord.gg/5Cavm9NYQf \n\n"); +#ifdef _WIN32 + SetConsoleTextAttribute(console, FOREGROUND_WHITE_BOLD); +#endif + printf("For more detailed logging, modify 'Level' param the log_config.xml file.\n\n"); +#ifdef _WIN32 + SetConsoleTextAttribute(console, FOREGROUND_WHITE); +#endif + + fflush(stdout); +} + +void NetConnection::InitWebServer(std::string web_ipaddr, int16 web_port, std::string cert_file, std::string key_file, std::string key_password, std::string hardcode_user, std::string hardcode_password) { + if(web_ipaddr.size() > 0 && web_port > 0) { + try { + login_webserver = new WebServer(web_ipaddr, web_port, cert_file, key_file, key_password, hardcode_user, hardcode_password); + + login_webserver->register_route("/status", NetConnection::Web_loginhandle_status); + login_webserver->register_route("/worlds", NetConnection::Web_loginhandle_worlds); + login_webserver->run(); + LogWrite(INIT__INFO, 0, "Init", "Login Web Server is listening on %s:%u..", web_ipaddr.c_str(), web_port); + } + catch (const std::exception& e) { + LogWrite(INIT__ERROR, 0, "Init", "Login Web Server failed to listen on %s:%u due to reason %s", web_ipaddr.c_str(), web_port, e.what()); + } + } +} \ No newline at end of file diff --git a/old/LoginServer/net.h b/old/LoginServer/net.h new file mode 100644 index 0000000..0f7b628 --- /dev/null +++ b/old/LoginServer/net.h @@ -0,0 +1,147 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. +*/ +#ifdef WIN32 +#define WIN32_LEAN_AND_MEAN + #include + #include +#else + #include + #include + #include + #include +#endif + +//#include +#include +#include +#include + +#include "../common/types.h" +#include "../common/Web/WebServer.h" +#include "../common/MiscFunctions.h" + +void CatchSignal(int sig_num); +enum eServerMode { Standalone, Master, Slave, Mesh }; + +class NetConnection +{ +public: + NetConnection() { + port = 5999; + listening_socket = 0; + memset(masteraddress, 0, sizeof(masteraddress)); + uplinkport = 0; + memset(uplinkaccount, 0, sizeof(uplinkaccount)); + memset(uplinkpassword, 0, sizeof(uplinkpassword)); + LoginMode = Standalone; + Uplink_WrongVersion = false; + numclients = 0; + numservers = 0; + allowAccountCreation = true; + + // full support = 0x7CFF + // 1 << 12 (-4096) = missing echoes of faydwer, disables Fae and Arasai (black portraits) and kelethin as starting city + // 1 << 13 (-8192) = disables sarnak (black portraits) and gorowyn as starting city + expansionFlag = 0x7CFF; // 0x4CF5 + + /* dword_1ECBA18 operand for race flag packs (sublevel 0,1,2?) -- (sublevel -1) controls starting zones omission 0xEE vs 0xCF (CF misses halas) + 1 = city of qeynos + 2 = city of freeport + 4 = city of kelethin + 8 = city of neriak + 16 = gorowyn + 32 = new halas + 64 = queens colony + 128 = outpost overlord + */ + citiesFlag = 0xFF; + + // sub_level 0xFFFFFFFF = blacks out all portraits for class alignments, considered non membership + // sub_level > 0 = class alignments still required, but portraits are viewable and race selectable + // sub_level = 2 membership, you can 'create characters on time locked servers' vs standard + // sub_level = 0 forces popup on close to web browser + defaultSubscriptionLevel = 0xFFFFFFFF; + + // disable extra races FAE(16) ARASAI (17) SARNAK (18) -- with 4096/8192 flags, no visibility of portraits + enabledRaces = 0xFFFF; // 0xCFFF + + web_loginport = 0; + + login_webserver = nullptr; + + login_running = false; + login_uptime = getCurrentTimestamp(); + } + + ~NetConnection() { + safe_delete(login_webserver); + } + + void UpdateWindowTitle(char* iNewTitle = 0); + bool Init(); + void ListenNewClients(); + void HitKey(int keyhit); + char address[1024]; + int32 numclients; + int32 numservers; + + int16 GetPort() { return port; } + void SetPort(int16 in_port) { port = in_port; } + eServerMode GetLoginMode() { return LoginMode; } + + bool ReadLoginConfig(); + char* GetMasterAddress() { return masteraddress; } + int16 GetUplinkPort() { if (uplinkport != 0) return uplinkport; else return port; } + char* GetUplinkAccount() { return uplinkaccount; } + char* GetUplinkPassword() { return uplinkpassword; } + + bool IsAllowingAccountCreation() { return allowAccountCreation; } + int32 GetExpansionFlag() { return expansionFlag; } + int8 GetCitiesFlag() { return citiesFlag; } + int32 GetDefaultSubscriptionLevel() { return defaultSubscriptionLevel; } + int32 GetEnabledRaces() { return enabledRaces; } + std::string GetWebLoginAddress() { return web_loginaddress; } + inline int16 GetWebLoginPort() { return web_loginport; } + std::string GetWebCertFile() { return web_certfile; } + std::string GetWebKeyFile() { return web_keyfile; } + std::string GetWebKeyPassword() { return web_keypassword; } + std::string GetWebHardcodeUser() { return web_hardcodeuser; } + std::string GetWebHardcodePassword() { return web_hardcodepassword; } + void WelcomeHeader(); + + void InitWebServer(std::string web_ipaddr, int16 web_port, std::string cert_file, std::string key_file, std::string key_password, std::string hardcode_user, std::string hardcode_password); + + static void Web_loginhandle_status(const http::request& req, http::response& res); + static void Web_loginhandle_worlds(const http::request& req, http::response& res); + + bool login_running; + std::atomic login_uptime; +protected: + friend class LWorld; + bool Uplink_WrongVersion; +private: + int16 port; + int listening_socket; + char masteraddress[300]; + int16 uplinkport; + char uplinkaccount[300]; + char uplinkpassword[300]; + eServerMode LoginMode; + bool allowAccountCreation; + int32 expansionFlag; + int8 citiesFlag; + int32 defaultSubscriptionLevel; + int32 enabledRaces; + std::string web_loginaddress; + std::string web_certfile; + std::string web_keyfile; + std::string web_keypassword; + std::string web_hardcodeuser; + std::string web_hardcodepassword; + int16 web_loginport; + WebServer* login_webserver; +}; diff --git a/old/LoginServer/net.hpp b/old/LoginServer/net.hpp deleted file mode 100644 index d018a2a..0000000 --- a/old/LoginServer/net.hpp +++ /dev/null @@ -1,307 +0,0 @@ -#pragma once - -// Copyright (C) 2007-2021 EQ2Emulator Development Team, GPL v3 License - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lworld.hpp" -#include "client.hpp" -#include "login_database.hpp" -#include "../common/log.hpp" -#include "../common/unix.hpp" -#include "../common/crc16.hpp" -#include "../common/types.hpp" -#include "../common/debug.hpp" -#include "../common/queue.hpp" -#include "../common/timer.hpp" -#include "../common/version.hpp" -#include "../common/separator.hpp" -#include "../common/web_server.hpp" -#include "../common/json_parser.hpp" -#include "../common/data_buffer.hpp" -#include "../common/config_reader.hpp" -#include "../common/misc_functions.hpp" -#include "../common/common_defines.hpp" -#include "../common/packet/packet_struct.hpp" -#include "../common/packet/packet_functions.hpp" -#include "../common/stream/eq_stream_factory.hpp" - -void CatchSignal(int sig_num); - -enum eServerMode -{ - Standalone, - Master, - Slave, - Mesh -}; - -class NetConnection -{ -public: - // Constructor - initializes all network connection parameters and default values - NetConnection() - { - port = 5999; - listening_socket = 0; - memset(masteraddress, 0, sizeof(masteraddress)); - uplinkport = 0; - memset(uplinkaccount, 0, sizeof(uplinkaccount)); - memset(uplinkpassword, 0, sizeof(uplinkpassword)); - LoginMode = Standalone; - Uplink_WrongVersion = false; - numclients = 0; - numservers = 0; - allowAccountCreation = true; - - // Full expansion support flag - controls available expansions - expansionFlag = 0x7CFF; - - // Cities availability flag - controls which starting cities are available - citiesFlag = 0xFF; - - // Default subscription level - controls character creation restrictions - defaultSubscriptionLevel = 0xFFFFFFFF; - - // Enabled races flag - controls which races are selectable - enabledRaces = 0xFFFF; - - web_loginport = 0; - login_webserver = nullptr; - login_running = false; - login_uptime = getCurrentTimestamp(); - } - - // Destructor - cleans up web server resources - ~NetConnection() - { - safe_delete(login_webserver); - } - - // Updates console window title with server status information - void UpdateWindowTitle(char* iNewTitle = 0) - { - // Linux systems don't support console title updates like Windows - // This is a no-op on Linux systems - } - - // Reads and parses the login server configuration from JSON file - bool ReadLoginConfig() - { - JsonParser parser(MAIN_CONFIG_FILE); - if (!parser.IsLoaded()) { - LogWrite(INIT__ERROR, 0, "Init", "Failed to find %s in server directory..", MAIN_CONFIG_FILE); - return false; - } - - std::string serverport = parser.getValue("loginconfig.serverport"); - std::string serverip = parser.getValue("loginconfig.serverip"); - - if (!parser.convertStringToUnsignedShort(serverport, port)) { - LogWrite(INIT__ERROR, 0, "Init", "Failed to translate loginconfig.serverport.."); - return false; - } - - if (serverip.size() > 0) { - eqsf.listen_ip_address = new char[serverip.size() + 1]; - strcpy(eqsf.listen_ip_address, serverip.c_str()); - } - else { - safe_delete(eqsf.listen_ip_address); - eqsf.listen_ip_address = nullptr; - } - - std::string acctcreate_str = parser.getValue("loginconfig.accountcreation"); - int16 allow_acct = 0; - parser.convertStringToUnsignedShort(acctcreate_str, allow_acct); - allowAccountCreation = allow_acct > 0 ? true : false; - - std::string expflag_str = parser.getValue("loginconfig.expansionflag"); - parser.convertStringToUnsignedInt(expflag_str, expansionFlag); - - std::string citiesflag_str = parser.getValue("loginconfig.citiesflag"); - parser.convertStringToUnsignedChar(citiesflag_str, citiesFlag); - - std::string defaultsublevel_str = parser.getValue("loginconfig.defaultsubscriptionlevel"); - parser.convertStringToUnsignedInt(defaultsublevel_str, defaultSubscriptionLevel); - - std::string enableraces_str = parser.getValue("loginconfig.enabledraces"); - parser.convertStringToUnsignedInt(enableraces_str, enabledRaces); - - web_loginaddress = parser.getValue("loginconfig.webloginaddress"); - web_certfile = parser.getValue("loginconfig.webcertfile"); - web_keyfile = parser.getValue("loginconfig.webkeyfile"); - web_keypassword = parser.getValue("loginconfig.webkeypassword"); - web_hardcodeuser = parser.getValue("loginconfig.webhardcodeuser"); - web_hardcodepassword = parser.getValue("loginconfig.webhardcodepassword"); - - std::string webloginport_str = parser.getValue("loginconfig.webloginport"); - parser.convertStringToUnsignedShort(webloginport_str, web_loginport); - - LogWrite(INIT__INFO, 0, "Init", "%s loaded..", MAIN_CONFIG_FILE); - - LogWrite(INIT__INFO, 0, "Init", "Database init begin.."); - if (!database.Init()) { - LogWrite(INIT__ERROR, 0, "Init", "Database init FAILED!"); - LogStop(); - return false; - } - - LogWrite(INIT__INFO, 0, "Init", "Loading opcodes 2.0.."); - EQOpcodeVersions = database.GetVersions(); - std::map::iterator version_itr2; - int16 version1 = 0; - for (version_itr2 = EQOpcodeVersions.begin(); version_itr2 != EQOpcodeVersions.end(); version_itr2++) { - version1 = version_itr2->first; - EQOpcodeManager[version1] = new RegularOpcodeManager(); - std::map eq = database.GetOpcodes(version1); - if (!EQOpcodeManager[version1]->LoadOpcodes(&eq)) { - LogWrite(INIT__ERROR, 0, "Init", "Loading opcodes failed. Make sure you have sourced the opcodes.sql file!"); - return false; - } - } - - return true; - } - - // Displays the EQ2Emulator welcome header with ASCII art and information - void WelcomeHeader() - { - printf("Module: %s, Version: %s", EQ2EMU_MODULE, CURRENT_VERSION); - printf("\n\nCopyright (C) 2007-2021 EQ2Emulator. https://www.eq2emu.com \n\n"); - printf("EQ2Emulator is free software: you can redistribute it and/or modify\n"); - printf("it under the terms of the GNU General Public License as published by\n"); - printf("the Free Software Foundation, either version 3 of the License, or\n"); - printf("(at your option) any later version.\n\n"); - printf("EQ2Emulator is distributed in the hope that it will be useful,\n"); - printf("but WITHOUT ANY WARRANTY; without even the implied warranty of\n"); - printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"); - printf("GNU General Public License for more details.\n\n"); - - printf(" /$$$$$$$$ /$$$$$$ /$$$$$$ /$$$$$$$$ \n"); - printf("| $$_____/ /$$__ $$ /$$__ $$| $$_____/ \n"); - printf("| $$ | $$ \\ $$|__/ \\ $$| $$ /$$$$$$/$$$$ /$$ /$$\n"); - printf("| $$$$$ | $$ | $$ /$$$$$$/| $$$$$ | $$_ $$_ $$| $$ | $$\n"); - printf("| $$__/ | $$ | $$ /$$____/ | $$__/ | $$ \\ $$ \\ $$| $$ | $$\n"); - printf("| $$ | $$/$$ $$| $$ | $$ | $$ | $$ | $$| $$ | $$\n"); - printf("| $$$$$$$$| $$$$$$/| $$$$$$$$| $$$$$$$$| $$ | $$ | $$| $$$$$$/\n"); - printf("|________/ \\____ $$$|________/|________/|__/ |__/ |__/ \\______/ \n"); - printf(" \\__/ \n\n"); - - printf(" Website : https://eq2emu.com \n"); - printf(" Wiki : https://wiki.eq2emu.com \n"); - printf(" Git : https://git.eq2emu.com \n"); - printf(" Discord : https://discord.gg/5Cavm9NYQf \n\n"); - - printf("For more detailed logging, modify 'Level' param the log_config.xml file.\n\n"); - - fflush(stdout); - } - - // Initializes and starts the web server for login status and world information - void InitWebServer(std::string web_ipaddr, int16 web_port, std::string cert_file, std::string key_file, std::string key_password, std::string hardcode_user, std::string hardcode_password) - { - if (web_ipaddr.size() > 0 && web_port > 0) { - try { - login_webserver = new WebServer(web_ipaddr, web_port, cert_file, key_file, key_password, hardcode_user, hardcode_password); - - login_webserver->register_route("/status", NetConnection::Web_loginhandle_status); - login_webserver->register_route("/worlds", NetConnection::Web_loginhandle_worlds); - login_webserver->run(); - LogWrite(INIT__INFO, 0, "Init", "Login Web Server is listening on %s:%u..", web_ipaddr.c_str(), web_port); - } - catch (const std::exception& e) { - LogWrite(INIT__ERROR, 0, "Init", "Login Web Server failed to listen on %s:%u due to reason %s", web_ipaddr.c_str(), web_port, e.what()); - } - } - } - - // Accessor methods for configuration values - int16 GetPort() { return port; } - void SetPort(int16 in_port) { port = in_port; } - eServerMode GetLoginMode() { return LoginMode; } - char* GetMasterAddress() { return masteraddress; } - int16 GetUplinkPort() { if (uplinkport != 0) return uplinkport; else return port; } - char* GetUplinkAccount() { return uplinkaccount; } - char* GetUplinkPassword() { return uplinkpassword; } - bool IsAllowingAccountCreation() { return allowAccountCreation; } - int32 GetExpansionFlag() { return expansionFlag; } - int8 GetCitiesFlag() { return citiesFlag; } - int32 GetDefaultSubscriptionLevel() { return defaultSubscriptionLevel; } - int32 GetEnabledRaces() { return enabledRaces; } - std::string GetWebLoginAddress() { return web_loginaddress; } - int16 GetWebLoginPort() { return web_loginport; } - std::string GetWebCertFile() { return web_certfile; } - std::string GetWebKeyFile() { return web_keyfile; } - std::string GetWebKeyPassword() { return web_keypassword; } - std::string GetWebHardcodeUser() { return web_hardcodeuser; } - std::string GetWebHardcodePassword() { return web_hardcodepassword; } - - // Web server route handlers for status and world information - static void Web_loginhandle_status(const http::request& req, http::response& res); - static void Web_loginhandle_worlds(const http::request& req, http::response& res); - - // Public server state variables - char address[1024]; - int32 numclients; - int32 numservers; - bool login_running; - std::atomic login_uptime; - -protected: - friend class LWorld; - bool Uplink_WrongVersion; - -private: - // Network connection parameters - int16 port; - int listening_socket; - char masteraddress[300]; - int16 uplinkport; - char uplinkaccount[300]; - char uplinkpassword[300]; - eServerMode LoginMode; - - // Login server configuration flags - bool allowAccountCreation; - int32 expansionFlag; - int8 citiesFlag; - int32 defaultSubscriptionLevel; - int32 enabledRaces; - - // Web server configuration - std::string web_loginaddress; - std::string web_certfile; - std::string web_keyfile; - std::string web_keypassword; - std::string web_hardcodeuser; - std::string web_hardcodepassword; - int16 web_loginport; - WebServer* login_webserver; -}; - -// Global variables - external declarations -extern EQStreamFactory eqsf; -extern std::map EQOpcodeManager; -extern NetConnection net; -extern ClientList client_list; -extern LWorldList world_list; -extern LoginDatabase database; -extern ConfigReader configReader; -extern std::map EQOpcodeVersions; -extern Timer statTimer; -extern volatile bool RunLoops; \ No newline at end of file diff --git a/old/LoginServer/packet_headers.hpp b/old/LoginServer/packet_headers.hpp deleted file mode 100644 index 2116293..0000000 --- a/old/LoginServer/packet_headers.hpp +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) - GPLv3 - -#pragma once - -#include -#include - -#include "lworld.hpp" -#include "login_structs.hpp" -#include "login_database.hpp" -#include "../common/types.hpp" -#include "../common/data_buffer.hpp" -#include "../common/config_reader.hpp" -#include "../common/misc_functions.hpp" -#include "../common/global_headers.hpp" -#include "../common/eq_common_structs.hpp" -#include "../common/packet/eq_packet.hpp" - -extern ConfigReader configReader; -extern LWorldList world_list; -extern LoginDatabase database; - -using std::vector; -using std::string; - -// Character profile data for character selection screen -class CharSelectProfile : public DataBuffer -{ -public: - // Constructor - initializes character profile with version-specific packet structure - CharSelectProfile(int16 version) - { - deleted = false; - packet = configReader.getStruct("CharSelectProfile", version); - // Initialize all 24 equipment slots to default values - for (int8 i = 0; i < 24; i++) { - packet->setEquipmentByName("equip", 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, i); - } - } - - // Destructor - safely cleans up packet structure - ~CharSelectProfile() - { - safe_delete(packet); - } - - // Serializes character profile data for transmission - void SaveData(int16 in_version) - { - Clear(); - AddData(*packet->serializeString()); - } - - PacketStruct* packet; // Packet structure containing character data - int16 size; // Size of serialized data - bool deleted; // Flag indicating if character is marked for deletion -}; - -// Character selection list containing multiple character profiles -class LS_CharSelectList : public DataBuffer -{ -public: - // Serializes character list data with account information for specified client version - EQ2Packet* serialize(int16 version) - { - Clear(); - AddData(num_characters); - AddData(char_data); - - if (version <= 561) { - // Early client version uses simplified account info structure - LS_CharListAccountInfoEarlyClient account_info; - account_info.account_id = account_id; - account_info.unknown1 = 0xFFFFFFFF; - account_info.unknown2 = 0; - account_info.maxchars = 7; // Live has a max of 7 on gold accounts base - account_info.unknown4 = 0; - AddData(account_info); - } else { - // Later client versions use extended account info structure - LS_CharListAccountInfo account_info; - account_info.account_id = account_id; - account_info.unknown1 = 0xFFFFFFFF; - account_info.unknown2 = 0; - account_info.maxchars = database.GetMaxCharsSetting(); - account_info.vet_adv_bonus = database.GetAccountBonus(account_id); - account_info.vet_trade_bonus = 0; - account_info.unknown4 = 0; - for (int i = 0; i < 3; i++) - account_info.unknown5[i] = 0xFFFFFFFF; - account_info.unknown5[3] = 0; - - AddData(account_info); - } - return new EQ2Packet(OP_AllCharactersDescReplyMsg, getData(), getDataSize()); - } - - // Appends character data to the character list - void addChar(uchar* data, int16 size) - { - char_data.append(reinterpret_cast(data), size); - } - - // Loads character data from database and populates character list for specified account - void loadData(int32 account, vector charlist, int16 version) - { - account_id = account; - num_characters = 0; - char_data = ""; - CharSelectProfile* character = nullptr; - - for (auto itr = charlist.begin(); itr != charlist.end(); itr++) { - character = *itr; - int32 serverID = character->packet->getType_int32_ByName("server_id"); - - if (character->deleted) { - // Workaround for old clients <= 561 that crash if you delete a char - // Doesn't refresh the char panel correctly - character->packet->setDataByName("name", "(deleted)"); - character->packet->setDataByName("charid", 0xFFFFFFFF); - character->packet->setDataByName("name", 0xFFFFFFFF); - character->packet->setDataByName("server_id", 0xFFFFFFFF); - character->packet->setDataByName("created_date", 0xFFFFFFFF); - character->packet->setDataByName("unknown1", 0xFFFFFFFF); - character->packet->setDataByName("unknown2", 0xFFFFFFFF); - character->packet->setDataByName("flags", 0xFF); - } else if (serverID == 0 || !world_list.FindByID(serverID)) { - continue; - } - - num_characters++; - character->SaveData(version); - addChar(character->getData(), character->getDataSize()); - } - } - - int8 num_characters; // Number of characters in the list - int32 account_id; // Account ID for this character list - string char_data; // Serialized character data buffer -}; - -// Request packet for character deletion operations -class LS_DeleteCharacterRequest : public DataBuffer -{ -public: - // Loads character deletion request data from incoming packet - void loadData(EQApplicationPacket* packet) - { - InitializeLoadData(packet->pBuffer, packet->size); - LoadData(character_number); - LoadData(server_id); - LoadData(spacer); - LoadDataString(name); - } - - int32 character_number; // Character slot number to delete - int32 server_id; // Server ID where character exists - int32 spacer; // Padding/alignment bytes - EQ2_16BitString name; // Character name to delete -}; \ No newline at end of file diff --git a/old/LoginServer/web.hpp b/old/LoginServer/web.hpp deleted file mode 100644 index 7da99cd..0000000 --- a/old/LoginServer/web.hpp +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (C) 2007-2021 EQ2Emulator Development Team, GPL v3 - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "net.hpp" -#include "lworld.hpp" - -extern ClientList client_list; -extern LWorldList world_list; -extern NetConnection net; - -/** - * Handles web requests for login server status information. - * Generates JSON response containing login status, uptime, client count, and world count. - * @param req HTTP request object containing the incoming request - * @param res HTTP response object to populate with JSON status data - */ -void NetConnection::Web_loginhandle_status(const http::request& req, http::response& res) -{ - // Set response content type to JSON - res.set(http::field::content_type, "application/json"); - boost::property_tree::ptree pt; - - // Build status information tree - pt.put("web_status", "online"); - pt.put("login_status", net.login_running ? "online" : "offline"); - pt.put("login_uptime", (getCurrentTimestamp() - net.login_uptime)); - - // Convert uptime to human-readable format - auto [days, hours, minutes, seconds] = convertTimestampDuration((getCurrentTimestamp() - net.login_uptime)); - std::string uptime_str("Days: " + std::to_string(days) + ", " + "Hours: " + std::to_string(hours) + ", " + "Minutes: " + std::to_string(minutes) + ", " + "Seconds: " + std::to_string(seconds)); - pt.put("login_uptime_string", uptime_str); - pt.put("world_count", world_list.GetCount(ConType::World)); - pt.put("client_count", net.numclients); - - // Serialize to JSON and set response body - std::ostringstream oss; - boost::property_tree::write_json(oss, pt); - std::string json = oss.str(); - res.body() = json; - res.prepare_payload(); -} - -/** - * Handles web requests for world server list information. - * Delegates to LWorldList::PopulateWorldList to generate the response. - * @param req HTTP request object containing the incoming request - * @param res HTTP response object to be populated with world server data - */ -void NetConnection::Web_loginhandle_worlds(const http::request& req, http::response& res) -{ - world_list.PopulateWorldList(res); -} - -/** - * Populates HTTP response with JSON data containing all active world servers. - * Iterates through world map and builds JSON array of world server information - * including ID, name, status, player count, and IP address. - * @param res HTTP response object to populate with world server JSON data - */ -void LWorldList::PopulateWorldList(http::response& res) -{ - struct in_addr in; - res.set(http::field::content_type, "application/json"); - boost::property_tree::ptree maintree; - std::ostringstream oss; - - // Iterate through all worlds in the world map - std::map::iterator map_list; - for (map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) { - LWorld* world = map_list->second; - in.s_addr = world->GetIP(); - - // Only include World type servers in the response - if (world->GetType() == World) { - boost::property_tree::ptree pt; - pt.put("id", world->GetID()); - pt.put("world_name", world->GetName()); - pt.put("status", (world->GetStatus() == 1) ? "online" : "offline"); - pt.put("num_players", world->GetPlayerNum()); - pt.put("ip_addr", inet_ntoa(in)); // Convert IP to string format - maintree.push_back(std::make_pair("", pt)); - } - } - - // Build final JSON structure and serialize - boost::property_tree::ptree result; - result.add_child("WorldServers", maintree); - boost::property_tree::write_json(oss, result); - std::string json = oss.str(); - res.body() = json; - res.prepare_payload(); -} \ No newline at end of file diff --git a/old/WorldServer/Achievements/Achievements.cpp b/old/WorldServer/Achievements/Achievements.cpp new file mode 100644 index 0000000..0d65164 --- /dev/null +++ b/old/WorldServer/Achievements/Achievements.cpp @@ -0,0 +1,332 @@ +/* +EQ2Emulator: Everquest II Server Emulator +Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + +This file is part of EQ2Emulator. + +EQ2Emulator 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. + +EQ2Emulator 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 EQ2Emulator. If not, see . +*/ + +#include "Achievements.h" + +#include "../../common/Log.h" +#include "../../common/ConfigReader.h" +#include + +extern ConfigReader configReader; +extern MasterAchievementList master_achievement_list; + +Achievement::Achievement() { + id = 0; + memset(title, 0, sizeof(title)); + memset(uncompleted_text, 0, sizeof(uncompleted_text)); + memset(completed_text, 0, sizeof(completed_text)); + memset(category, 0, sizeof(category)); + memset(expansion, 0, sizeof(expansion)); + icon = 0; + point_value = 0; + qty_req = 0; + hide = false; + unknown3a = 0; + unknown3b = 0; +} + +Achievement::Achievement(Achievement *in) { + vector *requirements_in; + vector *rewards_in; + vector::iterator itr; + vector::iterator itr2; + struct AchievementRequirements *achievement_requirement; + struct AchievementRewards *achievement_reward; + + assert(in); + + id = in->GetID(); + strncpy(title, in->GetTitle(), sizeof(title)); + strncpy(uncompleted_text, in->GetUncompletedText(), sizeof(uncompleted_text)); + strncpy(completed_text, in->GetCompletedText(), sizeof(completed_text)); + strncpy(category, in->GetCategory(), sizeof(category)); + strncpy(expansion, in->GetExpansion(), sizeof(expansion)); + icon = in->GetIcon(); + point_value = in->GetPointValue(); + qty_req = in->GetQtyReq(); + hide = in->GetHide(); + unknown3a = in->GetUnknown3a(); + unknown3b = in->GetUnknown3b(); + + requirements_in = in->GetRequirements(); + for (itr = requirements_in->begin(); itr != requirements_in->end(); itr++) { + achievement_requirement = new struct AchievementRequirements; + achievement_requirement->achievement_id = (*itr)->achievement_id; + achievement_requirement->name = (*itr)->name; + achievement_requirement->qty_req = (*itr)->qty_req; + requirements.push_back(achievement_requirement); + } + + rewards_in = in->GetRewards(); + for (itr2 = rewards_in->begin(); itr2 != rewards_in->end(); itr2++) { + achievement_reward = new struct AchievementRewards; + achievement_reward->achievement_id = (*itr2)->achievement_id; + achievement_reward->reward = (*itr2)->reward; + rewards.push_back(achievement_reward); + } +} + +Achievement::~Achievement() { + vector::iterator itr; + vector::iterator itr2; + + for (itr = requirements.begin(); itr != requirements.end(); itr++) + safe_delete(*itr); + for (itr2 = rewards.begin(); itr2 != rewards.end(); itr2++) + safe_delete(*itr2); +} + +void Achievement::AddAchievementRequirement(struct AchievementRequirements *requirement) { + assert(requirement); + + requirements.push_back(requirement); +} + +void Achievement::AddAchievementReward(struct AchievementRewards *reward) { + assert(reward); + + rewards.push_back(reward); +} + +void AchievementUpdate::AddAchievementUpdateItems(struct AchievementUpdateItems *update_item) { + assert(update_item); + + update_items.push_back(update_item); +} + +MasterAchievementList::MasterAchievementList() { + m_packetsCreated = false; + masterPacket = 0; + mutex_achievements.SetName("MasterAchievementList::achievements"); +} + +MasterAchievementList::~MasterAchievementList() { + ClearAchievements(); +} + +bool MasterAchievementList::AddAchievement(Achievement *achievement) { + bool ret = false; + + assert(achievement); + + mutex_achievements.writelock(__FUNCTION__, __LINE__); + if (achievements.count(achievement->GetID()) == 0) { + achievements[achievement->GetID()] = achievement; + ret = true; + } + mutex_achievements.releasewritelock(__FUNCTION__, __LINE__); + + return ret; +} + +Achievement * MasterAchievementList::GetAchievement(int32 achievement_id) { + Achievement *achievement = 0; + + mutex_achievements.readlock(__FUNCTION__, __LINE__); + if (achievements.count(achievement_id) > 0) + achievement = achievements[achievement_id]; + mutex_achievements.releasereadlock(__FUNCTION__, __LINE__); + + return achievement; +} + +void MasterAchievementList::ClearAchievements() { + map::iterator itr; + + mutex_achievements.writelock(__FUNCTION__, __LINE__); + for (itr = achievements.begin(); itr != achievements.end(); itr++) + safe_delete(itr->second); + achievements.clear(); + mutex_achievements.releasewritelock(__FUNCTION__, __LINE__); +} + +int32 MasterAchievementList::Size() { + int32 size; + + mutex_achievements.readlock(__FUNCTION__, __LINE__); + size = achievements.size(); + mutex_achievements.releasereadlock(__FUNCTION__, __LINE__); + + return size; +} + +PlayerAchievementList::PlayerAchievementList() { +} + +PlayerAchievementList::~PlayerAchievementList() { + ClearAchievements(); +} + +bool PlayerAchievementList::AddAchievement(Achievement *achievement) { + assert(achievement); + + if (achievements.count(achievement->GetID()) == 0) { + achievements[achievement->GetID()] = achievement; + return true; + } + + return false; +} + +Achievement * PlayerAchievementList::GetAchievement(int32 achievement_id) { + if (achievements.count(achievement_id) > 0) + return achievements[achievement_id]; + + return 0; +} + +void PlayerAchievementList::ClearAchievements() { + map::iterator itr; + + for (itr = achievements.begin(); itr != achievements.end(); itr++) + safe_delete(itr->second); + achievements.clear(); +} + +int32 PlayerAchievementList::Size() { + return achievements.size(); +} + +AchievementUpdate::AchievementUpdate() { + id = 0; + completed_date = 0; + +} + +AchievementUpdate::AchievementUpdate(AchievementUpdate *in) { + vector *items_in; + vector::iterator itr; + struct AchievementUpdateItems *items; + + assert(in); + + id = in->GetID(); + completed_date = in->GetCompletedDate(); + + items_in = in->GetUpdateItems(); + for (itr = items_in->begin(); itr != items_in->end(); itr++) { + items = new struct AchievementUpdateItems; + items->achievement_id = (*itr)->achievement_id; + items->item_update = (*itr)->item_update; + update_items.push_back(items); + } +} + +AchievementUpdate::~AchievementUpdate() { + vector::iterator itr; + + for (itr = update_items.begin(); itr != update_items.end(); itr++) + safe_delete(*itr); +} + + +PlayerAchievementUpdateList::PlayerAchievementUpdateList() { + +} + +PlayerAchievementUpdateList::~PlayerAchievementUpdateList() { + ClearAchievementUpdates(); +} + +bool PlayerAchievementUpdateList::AddAchievementUpdate(AchievementUpdate *update) { + assert(update); + + if (achievement_updates.count(update->GetID()) == 0) { + achievement_updates[update->GetID()] = update; + return true; + } + return false; +} + +void PlayerAchievementUpdateList::ClearAchievementUpdates() { + map::iterator itr; + + for (itr = achievement_updates.begin(); itr != achievement_updates.end(); itr++) + safe_delete(itr->second); + achievement_updates.clear(); +} + +int32 PlayerAchievementUpdateList::Size() { + return achievement_updates.size(); +} + +void MasterAchievementList::CreateMasterAchievementListPacket() { + map::iterator itr; + Achievement *achievement; + vector *requirements = 0; + vector::iterator itr2; + AchievementRequirements *requirement; + vector *rewards = 0; + vector::iterator itr3; + AchievementRewards *reward; + PacketStruct *packet; + int16 i = 0; + int16 j = 0; + int16 k = 0; + int16 version = 1096; + + if (!(packet = configReader.getStruct("WS_CharacterAchievements", version))) { + return; + } + + packet->setArrayLengthByName("num_achievements" , achievements.size()); + for (itr = achievements.begin(); itr != achievements.end(); itr++) { + achievement = itr->second; + packet->setArrayDataByName("achievement_id", achievement->GetID(), i); + packet->setArrayDataByName("title", achievement->GetTitle(), i); + packet->setArrayDataByName("uncompleted_text", achievement->GetUncompletedText(), i); + packet->setArrayDataByName("completed_text", achievement->GetCompletedText(), i); + packet->setArrayDataByName("category", achievement->GetCategory(), i); + packet->setArrayDataByName("expansion", achievement->GetExpansion(), i); + packet->setArrayDataByName("icon", achievement->GetIcon(), i); + packet->setArrayDataByName("point_value", achievement->GetPointValue(), i); + packet->setArrayDataByName("qty_req", achievement->GetQtyReq(), i); + packet->setArrayDataByName("hide_achievement", achievement->GetHide(), i); + packet->setArrayDataByName("unknown3", achievement->GetUnknown3a(), i); + packet->setArrayDataByName("unknown3", achievement->GetUnknown3b(), i); + requirements = achievement->GetRequirements(); + rewards = achievement->GetRewards(); + j = 0; + k = 0; + packet->setSubArrayLengthByName("num_items", requirements->size(), i, j); + for (itr2 = requirements->begin(); itr2 != requirements->end(); itr2++) { + requirement = *itr2; + packet->setSubArrayDataByName("item_name", requirement->name.c_str(), i, j); + packet->setSubArrayDataByName("item_qty_req", requirement->qty_req, i, j); + j++; + } + packet->setSubArrayLengthByName("num_rewards", achievement->GetRewards()->size(), i, k); + for (itr3 = rewards->begin(); itr3 != rewards->end(); itr3++) { + reward = *itr3; + packet->setSubArrayDataByName("reward_item", reward->reward.c_str(), i, k); + k++; + } + i++; + } + + //packet->PrintPacket(); + EQ2Packet* data = packet->serialize(); + masterPacket = new EQ2Packet(OP_ClientCmdMsg, data->pBuffer, data->size); + safe_delete(packet); + safe_delete(data); + //DumpPacket(app); + + m_packetsCreated = true; +} diff --git a/old/WorldServer/Achievements/Achievements.h b/old/WorldServer/Achievements/Achievements.h new file mode 100644 index 0000000..7b1895c --- /dev/null +++ b/old/WorldServer/Achievements/Achievements.h @@ -0,0 +1,176 @@ +/* +EQ2Emulator: Everquest II Server Emulator +Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + +This file is part of EQ2Emulator. + +EQ2Emulator 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. + +EQ2Emulator 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 EQ2Emulator. If not, see . +*/ + +#ifndef ACHIEVEMENTS_H_ +#define ACHIEVEMENTS_H_ + +#include "../../common/types.h" +#include "../../common/Mutex.h" +#include "../Items/Items.h" +#include +#include + +using namespace std; + +struct AchievementRewards +{ + int32 achievement_id; + string reward; +}; + +struct AchievementRequirements +{ + int32 achievement_id; + string name; + int32 qty_req; +}; + +struct AchievementUpdateItems +{ + int32 achievement_id; + int32 item_update; +}; + +class Achievement { +public: + Achievement(); + Achievement(Achievement *in); + virtual ~Achievement(); + + void SetID(int32 id) {this->id = id;} + void SetTitle(const char *title) {strncpy(this->title, title, sizeof(this->title));} + void SetUncompletedText(const char *uncompleted_text) {strncpy(this->uncompleted_text, uncompleted_text, sizeof(this->uncompleted_text));} + void SetCompletedText(const char *completed_text) {strncpy(this->completed_text, completed_text, sizeof(this->completed_text));} + void SetCategory(const char *category) {strncpy(this->category, category, sizeof(this->category));} + void SetExpansion(const char *expansion) {strncpy(this->expansion, expansion, sizeof(this->expansion));} + void SetIcon(int16 icon) {this->icon = icon;} + void SetPointValue(int32 point_value) {this->point_value = point_value;} + void SetQtyReq(int32 qty_req) {this->qty_req = qty_req;} + void SetHide(bool hide) {this->hide = hide;} + void SetUnknown3a(int32 unknown3a) {this->unknown3a = unknown3a;} + void SetUnknown3b(int32 unknown3b) {this->unknown3b = unknown3b;} + + void AddAchievementRequirement(struct AchievementRequirements *requirements); + void AddAchievementReward(struct AchievementRewards *reward); + + int32 GetID() {return id;} + const char * GetTitle() {return title;} + const char * GetUncompletedText() {return uncompleted_text;} + const char * GetCompletedText() {return completed_text;} + const char * GetCategory() {return category;} + const char * GetExpansion() {return expansion;} + int16 GetIcon() {return icon;} + int32 GetPointValue() {return point_value;} + int32 GetQtyReq() {return qty_req;} + bool GetHide() {return hide;} + int32 GetUnknown3a() {return unknown3a;} + int32 GetUnknown3b() {return unknown3b;} + vector * GetRequirements() {return &requirements;} + vector * GetRewards() {return &rewards;} + +private: + int32 id; + char title[512]; + char uncompleted_text[512]; + char completed_text[512]; + char category[32]; + char expansion[32]; + int16 icon; + int32 point_value; + int32 qty_req; + bool hide; + int32 unknown3a; + int32 unknown3b; + vector requirements; + vector rewards; +}; + +class AchievementUpdate { +public: + AchievementUpdate(); + AchievementUpdate(AchievementUpdate *in); + virtual ~AchievementUpdate(); + + void SetID(int32 id) {this->id = id;} + void SetCompletedDate(int32 completed_date) {this->completed_date = completed_date;} + + void AddAchievementUpdateItems(struct AchievementUpdateItems *update_items); + + int32 GetID() {return id;} + int32 GetCompletedDate() {return completed_date;} + + vector * GetUpdateItems() {return &update_items;} + +private: + int32 id; + int32 completed_date; + vector update_items; +}; + +class MasterAchievementList { +public: + MasterAchievementList(); + virtual ~MasterAchievementList(); + + bool AddAchievement(Achievement *achievement); + Achievement * GetAchievement(int32 achievement_id); + void ClearAchievements(); + int32 Size(); + void CreateMasterAchievementListPacket(); + EQ2Packet * GetAchievementPacket() { return m_packetsCreated ? masterPacket : 0;} + EQ2Packet *masterPacket; +private: + Mutex mutex_achievements; + map achievements; + + bool m_packetsCreated; +}; + +class PlayerAchievementList { +public: + PlayerAchievementList(); + virtual ~PlayerAchievementList(); + + bool AddAchievement(Achievement *achievement); + Achievement * GetAchievement(int32 achievement_id); + void ClearAchievements(); + int32 Size(); + + map * GetAchievements() {return &achievements;} + +private: + map achievements; +}; + +class PlayerAchievementUpdateList { +public: + PlayerAchievementUpdateList(); + virtual ~PlayerAchievementUpdateList(); + + bool AddAchievementUpdate(AchievementUpdate *achievement_update); + void ClearAchievementUpdates(); + int32 Size(); + + map * GetAchievementUpdates() {return &achievement_updates;} + +private: + map achievement_updates; +}; +#endif \ No newline at end of file diff --git a/old/WorldServer/achievements/achievements_db.hpp b/old/WorldServer/Achievements/AchievementsDB.cpp similarity index 89% rename from old/WorldServer/achievements/achievements_db.hpp rename to old/WorldServer/Achievements/AchievementsDB.cpp index f68c7fa..b8fd9bd 100644 --- a/old/WorldServer/achievements/achievements_db.hpp +++ b/old/WorldServer/Achievements/AchievementsDB.cpp @@ -1,11 +1,32 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ + +#ifdef WIN32 + #include + #include +#endif #include #include - -#include "achievements.hpp" +#include "../../common/Log.h" #include "../WorldDatabase.h" -#include "../../common/log.hpp" +#include "Achievements.h" extern MasterAchievementList master_achievement_list; @@ -55,6 +76,7 @@ void WorldDatabase::LoadAchievements() LogWrite(ACHIEVEMENT__DEBUG, 0, "Achievements", "\tLoaded %u achievements", master_achievement_list.Size()); LogWrite(ACHIEVEMENT__DEBUG, 0, "Achievements", "\tLoaded %u achievement requirements", aReqs_total); LogWrite(ACHIEVEMENT__DEBUG, 0, "Achievements", "\tLoaded %u achievement rewards", aRewards_total); + } int32 WorldDatabase::LoadAchievementRequirements(Achievement *achievement) diff --git a/old/WorldServer/alt_advancement/AltAdvancement.cpp b/old/WorldServer/AltAdvancement/AltAdvancement.cpp similarity index 98% rename from old/WorldServer/alt_advancement/AltAdvancement.cpp rename to old/WorldServer/AltAdvancement/AltAdvancement.cpp index bf5b609..c48115f 100644 --- a/old/WorldServer/alt_advancement/AltAdvancement.cpp +++ b/old/WorldServer/AltAdvancement/AltAdvancement.cpp @@ -1,7 +1,25 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License +/* +EQ2Emulator: Everquest II Server Emulator +Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + +This file is part of EQ2Emulator. + +EQ2Emulator 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. + +EQ2Emulator 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 EQ2Emulator. If not, see . +*/ #include "AltAdvancement.h" -#include "../../common/config_reader.hpp" +#include "../../common/ConfigReader.h" #include "../../common/Log.h" #include "../Spells.h" #include "../classes.h" @@ -9,8 +27,8 @@ #include #include #include -#include "../../common/database_new.hpp" -#include "../Worlddatabase.hpp" +#include "../../common/DatabaseNew.h" +#include "../WorldDatabase.h" extern ConfigReader configReader; extern MasterSpellList master_spell_list; extern Classes classes; diff --git a/old/WorldServer/alt_advancement/AltAdvancement.h b/old/WorldServer/AltAdvancement/AltAdvancement.h similarity index 97% rename from old/WorldServer/alt_advancement/AltAdvancement.h rename to old/WorldServer/AltAdvancement/AltAdvancement.h index 36357ee..9b0ed93 100644 --- a/old/WorldServer/alt_advancement/AltAdvancement.h +++ b/old/WorldServer/AltAdvancement/AltAdvancement.h @@ -22,8 +22,8 @@ along with EQ2Emulator. If not, see . #define __AltAdvancement__ #include -#include "../../common/types.hpp" -#include "../../common/packet/eq_packet.hpp" +#include "../../common/types.h" +#include "../../common/EQPacket.h" #include "../client.h" // defines for AA tabs based on group # from DB diff --git a/old/WorldServer/alt_advancement/AltAdvancementDB.cpp b/old/WorldServer/AltAdvancement/AltAdvancementDB.cpp similarity index 98% rename from old/WorldServer/alt_advancement/AltAdvancementDB.cpp rename to old/WorldServer/AltAdvancement/AltAdvancementDB.cpp index f284da3..b7f3da9 100644 --- a/old/WorldServer/alt_advancement/AltAdvancementDB.cpp +++ b/old/WorldServer/AltAdvancement/AltAdvancementDB.cpp @@ -25,8 +25,8 @@ #include #include #include "../../common/Log.h" -#include "../../common/database_new.hpp" -#include "../Worlddatabase.hpp" +#include "../../common/DatabaseNew.h" +#include "../WorldDatabase.h" #include "AltAdvancement.h" extern MasterAAList master_aa_list; diff --git a/old/WorldServer/Bots/Bot.cpp b/old/WorldServer/Bots/Bot.cpp index b015f03..ad311de 100644 --- a/old/WorldServer/Bots/Bot.cpp +++ b/old/WorldServer/Bots/Bot.cpp @@ -2,7 +2,7 @@ #include "BotBrain.h" #include "../Trade.h" #include "../../common/Log.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "../classes.h" #include "../SpellProcess.h" diff --git a/old/WorldServer/Bots/BotCommands.cpp b/old/WorldServer/Bots/BotCommands.cpp index 7428bcc..10934bb 100644 --- a/old/WorldServer/Bots/BotCommands.cpp +++ b/old/WorldServer/Bots/BotCommands.cpp @@ -1,5 +1,5 @@ #include "../Commands/Commands.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "../classes.h" #include "../races.h" #include "../Bots/Bot.h" diff --git a/old/WorldServer/Bots/BotDB.cpp b/old/WorldServer/Bots/BotDB.cpp index c36afd7..86afc51 100644 --- a/old/WorldServer/Bots/BotDB.cpp +++ b/old/WorldServer/Bots/BotDB.cpp @@ -1,4 +1,4 @@ -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "../../common/Log.h" #include "Bot.h" #include "../classes.h" diff --git a/old/WorldServer/Broker/BrokerManager.cpp b/old/WorldServer/Broker/BrokerManager.cpp new file mode 100644 index 0000000..0e6f127 --- /dev/null +++ b/old/WorldServer/Broker/BrokerManager.cpp @@ -0,0 +1,957 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ + +#include "BrokerManager.h" +#include "../Items/Items.h" +#include "../../common/Log.h" +#include "../WorldDatabase.h" +#include "../World.h" +#include "../Web/PeerManager.h" +#include + +extern WorldDatabase database; +extern ZoneList zone_list; +extern PeerManager peer_manager; + +BrokerManager::BrokerManager() { + +} + +void BrokerManager::AddSeller(int32 cid, + const std::string& name, + int32 hid, + bool enabled, + bool invFlag) +{ + + int64 prevSession = 0, prevTotal = 0; + { + std::shared_lock lock(mtx_); + auto sit = players_.find(cid); + if (sit != players_.end()) { + prevSession = sit->second.coin_session; + prevTotal = sit->second.coin_total; + } + } + + SellerInfo info{cid, name, hid, enabled, invFlag, prevSession, prevTotal}; + { + std::unique_lock lock(mtx_); + players_[cid] = info; + } + + SavePlayerToDB(info); + peer_manager.sendPeersAddSeller(cid, hid, name, enabled, invFlag); +} + +void BrokerManager::LoadSeller(int32 cid, const std::string& name, + int32 hid, bool enabled, bool invFlag, + int64 coin_session, int64 coin_total) +{ + SellerInfo info{cid, name, hid, enabled, invFlag, coin_session, coin_total}; + { + std::unique_lock lock(mtx_); + players_[cid] = info; + } +} + +int64 BrokerManager::ResetSellerSessionCoins(int32 cid) { + database.ClearSellerSession(cid); + + int64 session_coin = 0; + { + std::unique_lock lock(mtx_); + auto it = players_.find(cid); + if (it == players_.end()) return 0; + session_coin = it->second.coin_session; + it->second.coin_total += it->second.coin_session; + it->second.coin_session = 0; + } + + return session_coin; +} + +void BrokerManager::AddSellerSessionCoins(int32 cid, uint64 session) { + database.AddToSellerSession(cid, session); + { + std::unique_lock lock(mtx_); + auto it = players_.find(cid); + if (it == players_.end()) return; + it->second.coin_session += session; + } +} + +void BrokerManager::RemoveSeller(int32 cid, bool peerCacheOnly) +{ + { + std::unique_lock lock(mtx_); + players_.erase(cid); + active_items_by_char_.erase(cid); + inactive_items_by_char_.erase(cid); + } + if(!peerCacheOnly) + peer_manager.sendPeersRemoveSeller(cid); +} + +void BrokerManager::AddItem(const SaleItem& item, bool peerCacheOnly) +{ + { + std::unique_lock lock(mtx_); + auto& a = active_items_by_char_[item.character_id]; + auto& i = inactive_items_by_char_[item.character_id]; + a.erase(item.unique_id); + i.erase(item.unique_id); + if (item.for_sale) a[item.unique_id] = item; + else i[item.unique_id] = item; + } + SaveItemToDB(item); + if(!peerCacheOnly) + peer_manager.sendPeersAddItemSale(item.character_id, item.house_id, item.item_id, item.unique_id, item.cost_copper, item.inv_slot_id, + item.slot_id, item.count, item.from_inventory, item.for_sale, item.creator); +} + +void BrokerManager::LoadItem(const SaleItem& item) +{ + std::unique_lock lock(mtx_); + if (item.for_sale) + active_items_by_char_[item.character_id][item.unique_id] = item; + else + inactive_items_by_char_[item.character_id][item.unique_id] = item; +} + +void BrokerManager::SetSaleStatus(int32 cid, int64 uid, bool for_sale) +{ + std::optional toUpdate; + { + std::unique_lock lock(mtx_); + auto& a = active_items_by_char_[cid]; + auto& i = inactive_items_by_char_[cid]; + + if (for_sale) { + LogWrite(PLAYER__ERROR, 5, "Broker", + "--SetSaleStatus: %u (%u), for_sale=%u", + cid, uid, for_sale + ); + if (auto it = i.find(uid); it != i.end()) { + LogWrite(PLAYER__ERROR, 5, "Broker", + "--SetSaleStatusFuckOff: %u (%u), for_sale=%u", + cid, uid, for_sale + ); + SaleItem copy = it->second; + copy.for_sale = true; + i.erase(it); + a[uid] = copy; + toUpdate = copy; + } + } else { + LogWrite(PLAYER__ERROR, 5, "Broker", + "--SetSaleStatusInactive: %u (%u), for_sale=%u", + cid, uid, for_sale + ); + if (auto it = a.find(uid); it != a.end()) { + LogWrite(PLAYER__ERROR, 5, "Broker", + "--SetSaleStatusFuckyou: %u (%u), for_sale=%u", + cid, uid, for_sale + ); + SaleItem copy = it->second; + copy.for_sale = false; + a.erase(it); + i[uid] = copy; + toUpdate = copy; + } + } + } + if (toUpdate) { + SaveItemToDB(*toUpdate); + peer_manager.sendPeersAddItemSale(toUpdate->character_id, toUpdate->house_id, toUpdate->item_id, toUpdate->unique_id, toUpdate->cost_copper, toUpdate->inv_slot_id, + toUpdate->slot_id, toUpdate->count, toUpdate->from_inventory, toUpdate->for_sale, toUpdate->creator); + } +} + +bool BrokerManager::IsItemListed(int32 cid, int64 uid) { + std::shared_lock lock(mtx_); + auto& active_map = active_items_by_char_[cid]; + auto& inactive_map = inactive_items_by_char_[cid]; + + auto it = inactive_map.find(uid); + if (it != inactive_map.end()) { + return true; + } + + auto it2 = active_map.find(uid); + if (it2 != active_map.end()) { + return true; + } + return false; +} + +void BrokerManager::SetSalePrice(int32 cid, int64 uid, int64 price) +{ + std::optional toUpdate; + { + std::unique_lock lock(mtx_); + if (auto ait = active_items_by_char_.find(cid); ait != active_items_by_char_.end()) { + if (auto it = ait->second.find(uid); it != ait->second.end()) { + + LogWrite(PLAYER__ERROR, 5, "Broker", + "--SetSalePriceActive: %u (%u), cost=%u", + cid, uid, price + ); + it->second.cost_copper = price; + toUpdate = it->second; + } + } + if (auto iit = inactive_items_by_char_.find(cid); iit != inactive_items_by_char_.end()) { + if (auto it = iit->second.find(uid); it != iit->second.end()) { + LogWrite(PLAYER__ERROR, 5, "Broker", + "--SetSalePriceInactive: %u (%u), cost=%u", + cid, uid, price + ); + it->second.cost_copper = price; + toUpdate = it->second; + } + } + } + if (toUpdate) { + SaveItemToDB(*toUpdate); + peer_manager.sendPeersAddItemSale(toUpdate->character_id, toUpdate->house_id, toUpdate->item_id, toUpdate->unique_id, toUpdate->cost_copper, toUpdate->inv_slot_id, + toUpdate->slot_id, toUpdate->count, toUpdate->from_inventory, toUpdate->for_sale, toUpdate->creator); + } +} + +void BrokerManager::RemoveItem(int32 cid, int64 uid, int16 qty, bool shouldDelete) +{ + bool didDelete = false; + std::optional snapshot; + { + std::unique_lock lock(mtx_); + if (auto ait = active_items_by_char_.find(cid); ait != active_items_by_char_.end()) { + auto& m = ait->second; + if (auto it = m.find(uid); it != m.end()) { + if(shouldDelete) + qty = it->second.count; + + it->second.count -= qty; + if (it->second.count <= 0) { + didDelete = true; + snapshot = it->second; + m.erase(it); + } else { + snapshot = it->second; + } + if (m.empty()) + active_items_by_char_.erase(ait); + } + } + } + if (didDelete || shouldDelete) { + DeleteItemFromDB(cid, uid); + peer_manager.sendPeersRemoveItemSale(cid, uid); + } + else if (snapshot) { + SaveItemToDB(*snapshot); + peer_manager.sendPeersAddItemSale(snapshot->character_id, snapshot->house_id, snapshot->item_id, snapshot->unique_id, snapshot->cost_copper, snapshot->inv_slot_id, + snapshot->slot_id, snapshot->count, snapshot->from_inventory, snapshot->for_sale, snapshot->creator); + } +} + +bool BrokerManager::BuyItem(Client* buyer, int32 seller_cid, int64 uid, int32 quantity) +{ + Client* seller = zone_list.GetClientByCharID(seller_cid); // establish if seller is online + + if(buyer && buyer->GetCharacterID() == seller_cid) { + buyer->Message(CHANNEL_COLOR_RED, "You cannot buy from yourself!"); + return false; + } + if (quantity <= 0 || !IsSaleEnabled(seller_cid) || !IsItemForSale(seller_cid, uid)) { + if(buyer) + buyer->Message(CHANNEL_COLOR_RED, "Quantity not provided (%u), sale is not enabled (sale enabled? %u) or item is not for sale (itemforsale? %u).", quantity, IsSaleEnabled(seller_cid), IsItemForSale(seller_cid, uid)); + return false; + } + int64 price = GetSalePrice(seller_cid, uid) * quantity; + + if(!buyer || !buyer->GetPlayer() || !buyer->GetPlayer()->RemoveCoins(price)) { + if(buyer) + buyer->Message(CHANNEL_COLOR_RED, "You do not have enough coin to purchase."); + return false; + } + + if (!database.RemoveBrokerItem(seller_cid, uid, quantity)) { + buyer->GetPlayer()->AddCoins(price); + buyer->Message(CHANNEL_COLOR_RED, "Failed to remove broker item from database."); + return false; + } + + Item* giveItem = nullptr; + int16 result_count = 0; + std::string creator; + bool deleteItem = false; + int16 quantity_left = 0; + // 2) Mirror in-memory + + std::optional toUpdate; + { + std::unique_lock lock(mtx_); + if (auto sit = active_items_by_char_.find(seller_cid); sit != active_items_by_char_.end()) { + auto& m = sit->second; + if (auto it = m.find(uid); it != m.end()) { + creator = it->second.creator; + giveItem = master_item_list.GetItem(it->second.item_id); + SaleItem copy = it->second; + toUpdate = copy; + if (it->second.count > quantity) { + it->second.count -= quantity; + quantity_left = it->second.count; + result_count = quantity; + if(seller && seller->GetPlayer()) { + seller->GetPlayer()->item_list.SetVaultItemUniqueIDCount(seller, uid, it->second.count); + } + } + else { + result_count = it->second.count; + if(seller && seller->GetPlayer()) { + seller->GetPlayer()->item_list.RemoveVaultItemFromUniqueID(seller, uid); + } + m.erase(it); + deleteItem = true; + } + + if (m.empty()) + active_items_by_char_.erase(sit); + } + } + } + + if(!giveItem) { + buyer->GetPlayer()->AddCoins(price); + buyer->Message(CHANNEL_COLOR_RED, "Failed to find item on broker memory."); + if(toUpdate) + AddItem(*toUpdate); + return false; + } + Item* resultItem = new Item(giveItem); + resultItem->details.count = result_count; + resultItem->creator = creator; + if (buyer->GetPlayer()->item_list.HasFreeSlot() || buyer->GetPlayer()->item_list.CanStack(resultItem, false)) { + if(!buyer->AddItem(resultItem, nullptr, AddItemType::BUY_FROM_BROKER)) { + buyer->GetPlayer()->AddCoins(price); + safe_delete(resultItem); + if(toUpdate) + AddItem(*toUpdate); + return false; + } + } + else { + buyer->Message(CHANNEL_COLOR_RED, "You have no free slot available."); + buyer->GetPlayer()->AddCoins(price); + safe_delete(resultItem); + if(toUpdate) + AddItem(*toUpdate); + return false; + } + + if(deleteItem) { + DeleteItemFromDB(seller_cid, uid); + DeleteCharacterItemFromDB(seller_cid, uid); + } + else if(quantity_left) { + UpdateCharacterItemDB(seller_cid, uid, quantity_left); + } + + AddSellerSessionCoins(seller_cid, price); + LogSale(seller_cid, std::string(buyer->GetPlayer()->GetName()), std::string(resultItem->name), result_count, price); + if(seller) { + std::string logMsg = GetShopPurchaseMessage(buyer->GetPlayer()->GetName(), std::string(resultItem->name), result_count, price); + auto seller_info = GetSellerInfo(seller_cid); + seller->SendHouseSaleLog(logMsg, 0, seller_info ? seller_info->coin_total : 0, 0); + seller->OpenShopWindow(nullptr); + } + return true; +} + +void BrokerManager::OnPeerRemoveItem(int32 cid, int64 uid) +{ + std::unique_lock lock(mtx_); + if (auto ait = active_items_by_char_.find(cid); ait != active_items_by_char_.end()) + ait->second.erase(uid); + if (auto iit = inactive_items_by_char_.find(cid); iit != inactive_items_by_char_.end()) + iit->second.erase(uid); +} + +bool BrokerManager::IsItemForSale(int32 cid, int64 uid) const +{ + std::shared_lock lock(mtx_); + if (auto pit = active_items_by_char_.find(cid); pit != active_items_by_char_.end()) + return pit->second.find(uid) != pit->second.end(); + return false; +} + +int64 BrokerManager::GetSalePrice(int32 cid, int64 uid) +{ + std::shared_lock lock(mtx_); + if (auto pit = active_items_by_char_.find(cid); pit != active_items_by_char_.end()) { + if (auto it = pit->second.find(uid); it != pit->second.end()) + return it->second.cost_copper; + } + if (auto pit2 = inactive_items_by_char_.find(cid); pit2 != inactive_items_by_char_.end()) { + if (auto it = pit2->second.find(uid); it != pit2->second.end()) + return it->second.cost_copper; + } + return 0; +} + +std::vector BrokerManager::GetActiveForSaleItems(int32 cid) const +{ + std::shared_lock lock(mtx_); + std::vector out; + if (auto pit = active_items_by_char_.find(cid); pit != active_items_by_char_.end()) { + for (auto const& kv : pit->second) + out.push_back(kv.second); + } + return out; +} + +std::optional BrokerManager::GetActiveItem(int32 cid, int64 uid) const +{ + std::shared_lock lock(mtx_); + auto pit = active_items_by_char_.find(cid); + if (pit == active_items_by_char_.end()) + return std::nullopt; + + auto it = pit->second.find(uid); + if (it == pit->second.end()) + return std::nullopt; + + return it->second; // copy out the SaleItem +} + +bool BrokerManager::IsSellingItems(int32 cid, bool vaultOnly) const +{ + // Grab shared lock for thread‐safe read + std::shared_lock lock(mtx_); + + // Find this character’s active sales + auto pit = active_items_by_char_.find(cid); + if (pit == active_items_by_char_.end()) + return false; // no items => not selling from vault + + // Scan through each SaleItem; if any has sell_from_inventory == false, + // that means it’s coming from the vault + for (auto const& kv : pit->second) { + const SaleItem& item = kv.second; + if ((item.for_sale && (!item.from_inventory || !vaultOnly))) + return true; + } + + return false; +} + +std::vector BrokerManager::GetInactiveItems(int32 cid) const +{ + std::shared_lock lock(mtx_); + std::vector out; + if (auto pit = inactive_items_by_char_.find(cid); pit != inactive_items_by_char_.end()) { + for (auto const& kv : pit->second) + out.push_back(kv.second); + } + return out; +} + +std::vector> +BrokerManager::GetUniqueIDsAndCost(int32 cid) const +{ + std::shared_lock lock(mtx_); + std::vector> out; + if (auto pit = active_items_by_char_.find(cid); pit != active_items_by_char_.end()) { + for (auto const& kv : pit->second) + out.emplace_back(kv.second.unique_id, kv.second.cost_copper); + } + return out; +} + +vector* BrokerManager::GetItems( + const std::string& name, + int64 itype, + int64 ltype, + int64 btype, + int64 minprice, + int64 maxprice, + int8 minskill, + int8 maxskill, + const std::string& seller, + const std::string& adornment, + int8 mintier, + int8 maxtier, + int16 minlevel, + int16 maxlevel, + int8 itemclass +) const { + vector* ret = new vector; + string lower_name = ::ToLower(string(name.c_str())); + std::shared_lock lock(mtx_); + std::vector out; + for (auto const& char_pair : active_items_by_char_) { + int32 cid = char_pair.first; + auto pit_player = players_.find(cid); + if (pit_player == players_.end()) + continue; + bool allowInv = pit_player->second.sell_from_inventory; + for (auto const& kv : char_pair.second) { + auto const& itm = kv.second; + + LogWrite(PLAYER__DEBUG, 5, "Broker", + "--GetItems: %u (selling: %u), allowinv: %u", + itm.unique_id, itm.for_sale, allowInv + ); + if (!itm.for_sale) continue; + if (!allowInv && itm.from_inventory) continue; + Item* def = master_item_list.GetItem(itm.item_id); + if (!def) continue; + + LogWrite(PLAYER__DEBUG, 5, "Broker", + "--GetItems#1: %u (selling: %u), allowinv: %u", + itm.unique_id, itm.for_sale, allowInv + ); + + if (!name.empty() && def->lowername.find(lower_name) == std::string::npos) continue; + if (itype!=ITEM_BROKER_TYPE_ANY && itype !=ITEM_BROKER_TYPE_ANY64BIT && !master_item_list.ShouldAddItemBrokerType(def, itype)) continue; + if (ltype!=ITEM_BROKER_SLOT_ANY && !master_item_list.ShouldAddItemBrokerSlot(def, ltype)) continue; + if (btype!=0xFFFFFFFF && !master_item_list.ShouldAddItemBrokerStat(def, btype)) continue; + + LogWrite(PLAYER__DEBUG, 5, "Broker", + "--GetItems#2: %u (cost_copper: %u), seller: %s", + itm.unique_id, itm.cost_copper, seller.c_str() + ); + + //if (itm.cost_copper < minprice || itm.cost_copper > maxprice) continue; + //if (!seller.empty() && pit_player->second.seller_name.find(seller)==std::string::npos) continue; + /*if(mintier > 1 && def->details.tier < mintier) + continue; + if(maxtier > 0 && def->details.tier > maxtier) + continue;*/ + + LogWrite(PLAYER__DEBUG, 5, "Broker", + "--GetItems#3: %u (selling: %u), allowinv: %u", + itm.unique_id, itm.for_sale, allowInv + ); + + if(def->generic_info.adventure_default_level == 0 && def->generic_info.tradeskill_default_level == 0 && minlevel > 0 && maxlevel > 0){ + if(def->details.recommended_level < minlevel) + continue; + if(def->details.recommended_level > maxlevel) + continue; + } + else{ + if(minlevel > 0 && ((def->generic_info.adventure_default_level == 0 && def->generic_info.tradeskill_default_level == 0) || (def->generic_info.adventure_default_level > 0 && def->generic_info.adventure_default_level < minlevel) || (def->generic_info.tradeskill_default_level > 0 && def->generic_info.tradeskill_default_level < minlevel))) + continue; + if(maxlevel > 0 && ((def->generic_info.adventure_default_level > 0 && def->generic_info.adventure_default_level > maxlevel) || (def->generic_info.tradeskill_default_level > 0 && def->generic_info.tradeskill_default_level > maxlevel))) + continue; + } + if (itemclass>0) { + int64 bit = ((int64)2 << (itemclass-1)); + if (!(def->generic_info.adventure_classes & bit) && + !(def->generic_info.tradeskill_classes & bit)) + continue; + } + + LogWrite(PLAYER__DEBUG, 5, "Broker", + "--GetItemsPass: %u (selling: %u), allowinv: %u", + itm.unique_id, itm.for_sale, allowInv + ); + ret->push_back(new Item(def, itm.unique_id, itm.creator, pit_player->second.seller_name, cid, itm.cost_copper, itm.count, pit_player->second.house_id, itm.from_inventory)); + } + } + return ret; +} + +std::string BrokerManager::GetSellerName(int32 cid) const { + std::shared_lock lock(mtx_); + auto it = players_.find(cid); + return (it != players_.end()) ? it->second.seller_name : std::string(); +} + +bool BrokerManager::IsSaleEnabled(int32 cid) const { + std::shared_lock lock(mtx_); + auto it = players_.find(cid); + return (it != players_.end()) ? it->second.sale_enabled : false; +} + +bool BrokerManager::CanSellFromInventory(int32 cid) const { + std::shared_lock lock(mtx_); + auto it = players_.find(cid); + return (it != players_.end()) ? it->second.sell_from_inventory : false; +} + +int32 BrokerManager::GetHouseID(int32 cid) const { + std::shared_lock lock(mtx_); + auto it = players_.find(cid); + return (it != players_.end()) ? it->second.house_id : -1; +} + +std::optional BrokerManager::GetSellerInfo(int32 cid) const { + std::shared_lock lock(mtx_); + auto it = players_.find(cid); + if (it == players_.end()) + return std::nullopt; + return it->second; +} + +void BrokerManager::SavePlayerToDB(const SellerInfo& p) { + Query q; + std::ostringstream oss; + oss << "INSERT INTO broker_sellers " + "(character_id,seller_name,house_id,sale_enabled,sell_from_inventory,coin_session,coin_total) " + "VALUES(" + << p.character_id << ",'" + << EscapeSQLString(p.seller_name) << "'," + << p.house_id << "," + << (p.sale_enabled ? 1 : 0) << "," + << (p.sell_from_inventory ? 1 : 0) << "," + << p.coin_session << "," + << p.coin_total + << ") ON DUPLICATE KEY UPDATE " + "seller_name=VALUES(seller_name)," + "house_id=VALUES(house_id)," + "sale_enabled=VALUES(sale_enabled)," + "sell_from_inventory=VALUES(sell_from_inventory)," + "coin_session=VALUES(coin_session), " + "coin_total=VALUES(coin_total)"; + + std::string sql = oss.str(); + + q.AddQueryAsync(p.character_id, &database, Q_INSERT, + sql.c_str() + ); +} +#include + +void BrokerManager::SaveItemToDB(const SaleItem& i) { + // 2) Build full SQL + std::ostringstream oss; + oss + << "INSERT INTO broker_items " + "(unique_id,character_id,house_id,item_id,cost_copper,for_sale," + "inv_slot_id,slot_id,`count`,from_inventory,creator) " + "VALUES(" + << i.unique_id << "," + << i.character_id << "," + << i.house_id << "," + << i.item_id << "," + << i.cost_copper << "," + << (i.for_sale ? 1 : 0) << "," + << i.inv_slot_id << "," + << i.slot_id << "," + << i.count << "," + << (i.from_inventory ? 1 : 0) << ",'" + << EscapeSQLString(i.creator) << "') " + "ON DUPLICATE KEY UPDATE " + "house_id=VALUES(house_id)," + "item_id=VALUES(item_id)," + "cost_copper=VALUES(cost_copper)," + "for_sale=VALUES(for_sale)," + "inv_slot_id=VALUES(inv_slot_id)," + "slot_id=VALUES(slot_id)," + "`count`=VALUES(`count`)," + "from_inventory=VALUES(from_inventory)," + "creator=VALUES(creator)"; + + std::string sql = oss.str(); + Query q; + q.AddQueryAsync(i.character_id, &database, Q_INSERT, sql.c_str()); +} + +void BrokerManager::UpdateItemInDB(const SaleItem& i) { + Query q; + std::ostringstream oss; + oss << "UPDATE broker_items SET " + << "house_id=" << i.house_id + << ",item_id=" << i.item_id + << ",cost_copper=" << i.cost_copper + << ",for_sale=" << (i.for_sale ? 1 : 0) + << ",inv_slot_id=" << i.inv_slot_id + << ",slot_id=" << i.slot_id + << ",count=" << i.count + << ",from_inventory=" << (i.from_inventory ? 1 : 0) + << ",creator='" << EscapeSQLString(i.creator) << "' " + << "WHERE unique_id=" << i.unique_id + << " AND character_id=" << i.character_id; + + std::string sql = oss.str(); + + q.AddQueryAsync(i.character_id, &database, Q_UPDATE, + sql.c_str() + ); +} + +void BrokerManager::DeleteItemFromDB(int32 cid, int64 uid) { + Query q; + q.AddQueryAsync(cid, &database, Q_DELETE, + "DELETE FROM broker_items WHERE unique_id=%llu AND character_id=%u", + uid, cid + ); +} + +void BrokerManager::DeleteCharacterItemFromDB(int32 cid, int64 uid) { + Query q; + q.AddQueryAsync(cid, &database, Q_DELETE, + "DELETE FROM character_items WHERE id=%llu AND char_id=%u", + uid, cid + ); +} + +void BrokerManager::UpdateCharacterItemDB(int32 cid, int64 uid, int16 count) { + Query q; + q.AddQueryAsync(cid, &database, Q_UPDATE, + "UPDATE character_items set count=%u WHERE id=%llu AND char_id=%u", + count, uid, cid + ); +} + +void BrokerManager::DeletePlayerFromDB(int32 cid) { + Query q; + // delete from broker_sellers + q.AddQueryAsync(cid, &database, Q_DELETE, + "DELETE FROM broker_sellers WHERE character_id=%u", cid); + // delete all their items + q.AddQueryAsync(cid, &database, Q_DELETE, + "DELETE FROM broker_items WHERE character_id=%u", cid); +} + +bool BrokerManager::IsItemFromInventory(int32 cid, int64 uid) const { + std::shared_lock lock(mtx_); + + // 1) Check active items for that character + auto pit = active_items_by_char_.find(cid); + if (pit != active_items_by_char_.end()) { + auto it = pit->second.find(uid); + if (it != pit->second.end()) + return it->second.from_inventory; + } + + // 2) Otherwise check inactive items + auto iit = inactive_items_by_char_.find(cid); + if (iit != inactive_items_by_char_.end()) { + auto it = iit->second.find(uid); + if (it != iit->second.end()) + return it->second.from_inventory; + } + + // 3) Not found → false + return false; +} + + +void BrokerManager::LockActiveItemsForClient(Client* client) const { + int32 cid = client->GetCharacterID(); + + auto infoOpt = GetSellerInfo(cid); + if (!infoOpt) + return; + const SellerInfo& info = *infoOpt; + + { + auto items = GetActiveForSaleItems(cid); + for (auto const& itm : items) { + if (!itm.for_sale) { + client->GetPlayer()->item_list.SetVaultItemLockUniqueID(client, itm.unique_id, false, true); + continue; + } + + LogWrite(PLAYER__ERROR, 5, "Broker", + "--LockActiveItemsForClient: %u (selling: %u), allowinv: %u, sellfrominv: %u", + itm.unique_id, itm.for_sale, itm.from_inventory, info.sell_from_inventory + ); + + if (!info.sell_from_inventory && itm.from_inventory) { + client->GetPlayer()->item_list.SetVaultItemLockUniqueID(client, itm.unique_id, false, true); + continue; + } + + client->GetPlayer()->item_list.SetVaultItemLockUniqueID(client, itm.unique_id, true, true); + } + } +} + + +void WorldDatabase::ClearSellerSession(int32 character_id) { + Query q; + std::ostringstream sql; + sql << "UPDATE broker_sellers SET coin_session = 0" + << " WHERE character_id = " << character_id; + + std::string full = sql.str(); + q.AddQueryAsync(character_id, &database, Q_INSERT, full.c_str()); +} + +void WorldDatabase::AddToSellerSession(int32 character_id, int64 amount) { + Query q; + std::ostringstream sql; + sql << "UPDATE broker_sellers SET coin_session = coin_session + " + << amount + << " WHERE character_id = " << character_id; + + std::string full = sql.str(); + q.AddQueryAsync(character_id, &database, Q_INSERT, full.c_str()); +} + +int64 WorldDatabase::GetSellerSession(int32 character_id) { + Query query; + MYSQL_RES* result = query.RunQuery2( + Q_SELECT, + "SELECT " + "coin_session " + "FROM broker_sellers " + "WHERE character_id = %d " + "LIMIT 1", + character_id + ); + + if (result) { + MYSQL_ROW row; + if ((row = mysql_fetch_row(result))) { + return strtoull(row[0], NULL, 0); + } + } + return 0; +} + +std::string BrokerManager::GetShopPurchaseMessage(const std::string& buyer_name, const std::string& item_desc, int16 quantity, int64 coin) { + int64 platinum = static_cast(coin / 1000000); + // 1) Break totalCopper into denominations + int64 rem = coin % 1000000; + int64 gold = static_cast(rem / 10000); + rem %= 10000; + int64 silver = static_cast(rem / 100); + int64 copper = static_cast(rem % 100); + + // 1) Timestamp + auto now = std::time(nullptr); + std::tm tm; + localtime_r(&now, &tm); + char timebuf[64]; + std::strftime(timebuf, sizeof(timebuf), + "%B %d, %Y, %I:%M:%S %p", &tm); + + // 2) Build the sale line + std::ostringstream msg; + msg << timebuf + << " " << buyer_name << " buys "; + if (quantity > 1) + msg << quantity << " "; + msg << item_desc + << " for "; + + // 3) Denominations + std::vector parts; + if (platinum > 0) parts.push_back(std::to_string(platinum) + " Platinum"); + if (gold > 0) parts.push_back(std::to_string(gold) + " Gold"); + if (silver > 0) parts.push_back(std::to_string(silver) + " Silver"); + if (copper > 0) parts.push_back(std::to_string(copper) + " Copper"); + + // If all are zero (unlikely), still print "0 Copper" + if (parts.empty()) + parts.push_back("0 Copper"); + + // Join with ", " + for (size_t i = 0; i < parts.size(); ++i) { + if (i) msg << ", "; + msg << parts[i]; + } + + return msg.str(); +} + +void BrokerManager::LogSale(int32 cid, + const std::string& buyer_name, + const std::string& item_desc, + int16 quantity, + int64 coin) +{ + std::string msg = GetShopPurchaseMessage(buyer_name, item_desc, quantity, coin); + + std::string esc = EscapeSQLString(msg.c_str()); + std::ostringstream sql; + sql << "INSERT INTO broker_seller_log " + "(character_id,log_time,message) VALUES (" + << cid << ",NOW(),'" + << esc << "')"; + + std::string full = sql.str(); + + Query q; + q.AddQueryAsync(cid, &database, Q_INSERT, full.c_str()); +} + +void BrokerManager::LogSaleMessage(int32 cid, + const std::string& log_message) +{ + auto now = std::time(nullptr); + std::tm tm; + localtime_r(&now, &tm); + char timebuf[64]; + std::strftime(timebuf, sizeof(timebuf), + "%B %d, %Y, %I:%M:%S %p", &tm); + + std::ostringstream msg; + msg << timebuf + << " " << log_message; + + std::string esc = EscapeSQLString(msg.str()); + std::ostringstream sql; + sql << "INSERT INTO broker_seller_log " + "(character_id,log_time,message) VALUES (" + << cid << ",NOW(),'" + << esc << "')"; + + std::string full = sql.str(); + + Query q; + q.AddQueryAsync(cid, &database, Q_INSERT, full.c_str()); +} + +std::vector BrokerManager::GetSellerLog(int32 cid) const { + std::vector out; + + Query query; + MYSQL_RES* result = query.RunQuery2( + Q_SELECT, + "SELECT " + "log_id, " + "DATE_FORMAT(log_time, '%%M %%d, %%Y, %%r') as ts, " + "message " + "FROM broker_seller_log " + "WHERE character_id=%u " + "ORDER BY log_time ASC", + cid + ); + + if (result) { + MYSQL_ROW row; + while ((row = mysql_fetch_row(result))) { + SellerLog entry; + entry.log_id = row[0] ? atoll(row[0]) : 0; + entry.timestamp = row[1] ? row[1] : ""; + entry.message = row[2] ? row[2] : ""; + out.push_back(std::move(entry)); + } + } + + return out; +} \ No newline at end of file diff --git a/old/WorldServer/Broker/BrokerManager.h b/old/WorldServer/Broker/BrokerManager.h new file mode 100644 index 0000000..afc1ea6 --- /dev/null +++ b/old/WorldServer/Broker/BrokerManager.h @@ -0,0 +1,182 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../common/types.h" + +// Key = (character_id, unique_id) +using Key = std::pair; + +class Item; +class Client; + +struct SaleItem { + int64 unique_id; + int32 character_id; + int32 house_id; + int64 item_id; + int64 cost_copper; + bool for_sale; + sint32 inv_slot_id; + int16 slot_id; + int16 count; + bool from_inventory; + string creator; +}; + +struct SellerInfo { + int32 character_id; + std::string seller_name; + int64 house_id; + bool sale_enabled; + bool sell_from_inventory; + int64 coin_session; + int64 coin_total; +}; + +struct SellerLog { + int64 log_id; // auto_increment PK + std::string timestamp; // e.g. "October 20, 2005, 05:29:33 AM" + std::string message; // the human-readable text +}; + +class BrokerManager { +public: + BrokerManager(); + + //–– Player management + void AddSeller(int32 cid, + const std::string& name, + int32 house_id, + bool sale_enabled, + bool sell_from_inventory); + + void LoadSeller(int32 cid, + const std::string& name, + int32 house_id, + bool sale_enabled, + bool sell_from_inventory, + int64 coin_session, + int64 coin_total); + + int64 ResetSellerSessionCoins(int32 cid); + void AddSellerSessionCoins(int32 cid, uint64 session); + void RemoveSeller(int32 character_id, bool peerCacheOnly = false); + + //–– Item management + void AddItem(const SaleItem& item, bool peerCacheOnly = false); + void LoadItem(const SaleItem& item); + + //–– Activate / deactivate sale flag + void SetSaleStatus(int32 cid, int64 uid, bool for_sale); + bool IsItemListed(int32 cid, int64 uid); + void SetSalePrice(int32 cid, int64 uid, int64 price); + int64 GetSalePrice(int32 cid, int64 uid); + + //–– Remove quantity + void RemoveItem(int32 cid, int64 uid, int16 quantity = 1, bool shouldDelete = false); + + //–– Attempt to buy (atomic DB + in-memory + broadcast) + bool BuyItem(Client* buyer, int32 seller_cid, int64 uid, int32 quantity); + bool IsItemForSale(int32 seller_cid, int64 uid) const; + + //–– Called when a peer notifies that an item was sold/removed (in-memory only) + void OnPeerRemoveItem(int32 character_id, int64 unique_id); + + //–– Queries + std::vector GetActiveForSaleItems(int32 cid) const; + std::optional GetActiveItem(int32 cid, int64 uid) const; + bool IsSellingItems(int32 cid, bool vaultOnly = false) const; + std::vector GetInactiveItems(int32 cid) const; + //–– Global search API (only active_items_) + vector* GetItems( + const std::string& name, + int64 itype, + int64 ltype, + int64 btype, + int64 minprice, + int64 maxprice, + int8 minskill, + int8 maxskill, + const std::string& seller, + const std::string& adornment, + int8 mintier, + int8 maxtier, + int16 minlevel, + int16 maxlevel, + int8 itemclass + ) const; + + //–– UI helper: get (unique_id, cost) for active items + std::vector> GetUniqueIDsAndCost(int32 cid) const; + + std::optional GetSellerInfo(int32 character_id) const; + //–– Lookup seller name + std::string GetSellerName(int32 cid) const; + bool IsSaleEnabled(int32 cid) const; + bool CanSellFromInventory(int32 cid) const; + int32 GetHouseID(int32 cid) const; + + bool IsItemFromInventory(int32 cid, int64 uid) const; + void LockActiveItemsForClient(Client* client) const; + + std::string GetShopPurchaseMessage(const std::string& buyer_name, const std::string& item_desc, int16 quantity, int64 coin); + void LogSale(int32 character_id, const std::string& buyer_name,const std::string& item_desc, int16 quantity, int64 coin); + void LogSaleMessage(int32 character_id,const std::string& log_message); + std::vector GetSellerLog(int32 character_id) const; + static std::string EscapeSQLString(const std::string& s) { + std::string out; + out.reserve(s.size() * 2); + for (char c : s) { + if (c == '\'') out += "''"; + else out += c; + } + return out; + } +private: + mutable std::shared_mutex mtx_; + std::unordered_map players_; + std::unordered_map< + int32, + std::unordered_map + > active_items_by_char_; + std::unordered_map< + int32, + std::unordered_map + > inactive_items_by_char_; + + //–– DB sync (async writes) + void SavePlayerToDB(const SellerInfo& p); + void SaveItemToDB(const SaleItem& i); + void UpdateItemInDB(const SaleItem& i); + void DeleteItemFromDB(int32 character_id, int64 unique_id); + void DeleteCharacterItemFromDB(int32 cid, int64 uid); + void UpdateCharacterItemDB(int32 cid, int64 uid, int16 count); + void DeletePlayerFromDB(int32 cid); +}; \ No newline at end of file diff --git a/old/WorldServer/Chat/Chat.cpp b/old/WorldServer/Chat/Chat.cpp index 219cfc7..8c41c25 100644 --- a/old/WorldServer/Chat/Chat.cpp +++ b/old/WorldServer/Chat/Chat.cpp @@ -20,7 +20,7 @@ #include "Chat.h" #include "../../common/Log.h" -#include "../../common/config_reader.hpp" +#include "../../common/ConfigReader.h" #include "../../common/PacketStruct.h" #include "../Rules/Rules.h" extern RuleManager rule_manager; diff --git a/old/WorldServer/Chat/Chat.h b/old/WorldServer/Chat/Chat.h index d4565f5..fe6c4e2 100644 --- a/old/WorldServer/Chat/Chat.h +++ b/old/WorldServer/Chat/Chat.h @@ -21,9 +21,9 @@ #define CHAT_CHAT_H_ #include -#include "../../common/types.hpp" -#include "../../common/packet/eq_packet.hpp" - +#include "../../common/types.h" +#include "../../common/EQPacket.h" +#include "../../common/Mutex.h" #include "../client.h" #include "ChatChannel.h" diff --git a/old/WorldServer/Chat/ChatChannel.cpp b/old/WorldServer/Chat/ChatChannel.cpp index 47a3215..e0fbf9e 100644 --- a/old/WorldServer/Chat/ChatChannel.cpp +++ b/old/WorldServer/Chat/ChatChannel.cpp @@ -1,6 +1,6 @@ #include #include "../../common/Log.h" -#include "../../common/config_reader.hpp" +#include "../../common/ConfigReader.h" #include "../../common/PacketStruct.h" #include "../World.h" #include "ChatChannel.h" diff --git a/old/WorldServer/Chat/ChatChannel.h b/old/WorldServer/Chat/ChatChannel.h index 3c3853d..8dce202 100644 --- a/old/WorldServer/Chat/ChatChannel.h +++ b/old/WorldServer/Chat/ChatChannel.h @@ -21,7 +21,7 @@ #ifndef CHAT_CHATCHANNEL_H_ #define CHAT_CHATCHANNEL_H_ -#include "../../common/types.hpp" +#include "../../common/types.h" #include "../client.h" #include diff --git a/old/WorldServer/Chat/ChatDB.cpp b/old/WorldServer/Chat/ChatDB.cpp index 65711ae..297ecf7 100644 --- a/old/WorldServer/Chat/ChatDB.cpp +++ b/old/WorldServer/Chat/ChatDB.cpp @@ -21,7 +21,7 @@ #include "../../common/Log.h" #include "Chat.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" extern Chat chat; diff --git a/old/WorldServer/ClientPacketFunctions.cpp b/old/WorldServer/ClientPacketFunctions.cpp index 406d734..51160c7 100644 --- a/old/WorldServer/ClientPacketFunctions.cpp +++ b/old/WorldServer/ClientPacketFunctions.cpp @@ -18,12 +18,12 @@ along with EQ2Emulator. If not, see . */ #include "ClientPacketFunctions.h" -#include "WorldDatabase.hpp" -#include "../common/config_reader.hpp" +#include "WorldDatabase.h" +#include "../common/ConfigReader.h" #include "Variables.h" #include "World.h" #include "classes.h" -#include "../common/log.hpp" +#include "../common/Log.h" #include "Traits/Traits.h" extern Classes classes; @@ -354,7 +354,7 @@ void ClientPacketFunctions::SendInstanceList(Client* client) { } void ClientPacketFunctions::SendMaintainedExamineUpdate(Client* client, int8 slot_pos, int32 update_value, int8 update_type){ - if (!client) + if (!client || client->GetVersion() <= 561) // KoS and earlier clients don't support this packet, best to just return and not spam logs w/ errors return; PacketStruct* packet = configReader.getStruct("WS_UpdateMaintainedExamine", client->GetVersion()); diff --git a/old/WorldServer/Collections/Collections.h b/old/WorldServer/Collections/Collections.h index 6f18913..eb39873 100644 --- a/old/WorldServer/Collections/Collections.h +++ b/old/WorldServer/Collections/Collections.h @@ -1,7 +1,8 @@ #ifndef COLLECTIONS_H_ #define COLLECTIONS_H_ -#include "../../common/types.hpp" +#include "../../common/types.h" +#include "../../common/Mutex.h" #include "../Items/Items.h" #include #include diff --git a/old/WorldServer/Collections/CollectionsDB.cpp b/old/WorldServer/Collections/CollectionsDB.cpp index f09ff10..3e39cbb 100644 --- a/old/WorldServer/Collections/CollectionsDB.cpp +++ b/old/WorldServer/Collections/CollectionsDB.cpp @@ -24,7 +24,7 @@ #include #include #include "../../common/Log.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "Collections.h" extern MasterCollectionList master_collection_list; diff --git a/old/WorldServer/Combat.cpp b/old/WorldServer/Combat.cpp index 5d8601a..b345009 100644 --- a/old/WorldServer/Combat.cpp +++ b/old/WorldServer/Combat.cpp @@ -19,10 +19,10 @@ */ #include "Combat.h" #include "client.h" -#include "../common/config_reader.hpp" +#include "../common/ConfigReader.h" #include "classes.h" -#include "../common/debug.hpp" -#include "../common/log.hpp" +#include "../common/debug.h" +#include "../common/Log.h" #include "zoneserver.h" #include "Skills.h" #include "classes.h" @@ -140,6 +140,10 @@ bool Entity::AttackAllowed(Entity* target, float distance, bool range_attack) { return false; } } + + if(attacker->IsNPC() && target->IsNPC() && attacker->GetFactionID() > 10 && attacker->GetFactionID() == target->GetFactionID()) { + return false; + } if (attacker->IsPlayer() && target->IsPlayer()) { @@ -275,6 +279,8 @@ void Entity::MeleeAttack(Spawn* victim, float distance, bool primary, bool multi CheckEncounterState((Entity*)victim); } + CheckProcs(PROC_TYPE_PHYSICAL_ATTEMPT, victim); + if(hit_result == DAMAGE_PACKET_RESULT_SUCCESSFUL){ /*if(GetAdventureClass() == MONK){ max_damage*=3; @@ -290,7 +296,7 @@ void Entity::MeleeAttack(Spawn* victim, float distance, bool primary, bool multi DamageSpawn((Entity*)victim, DAMAGE_PACKET_TYPE_SIMPLE_CRIT_DMG, damage_type, min_damage, max_damage, 0); } else*/ - DamageSpawn((Entity*)victim, DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE, damage_type, min_damage, max_damage, 0); + DamageSpawn((Entity*)victim, DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE, damage_type, min_damage, max_damage, 0, 0, false, false, false, false, nullptr, false, true); if (!multi_attack) { CheckProcs(PROC_TYPE_OFFENSIVE, victim); CheckProcs(PROC_TYPE_PHYSICAL_OFFENSIVE, victim); @@ -351,10 +357,13 @@ void Entity::MeleeAttack(Spawn* victim, float distance, bool primary, bool multi } } -void Entity::RangeAttack(Spawn* victim, float distance, Item* weapon, Item* ammo, bool multi_attack) { +bool Entity::RangeAttack(Spawn* victim, float distance, Item* weapon, Item* ammo, bool multi_attack) { if(!victim) - return; + return false; + CheckProcs(PROC_TYPE_PHYSICAL_ATTEMPT, victim); + + bool item_deleted = false; if(weapon && weapon->IsRanged() && ammo && ammo->IsAmmo() && ammo->IsThrown()) { if(weapon->ranged_info->range_low <= distance && (weapon->ranged_info->range_high + ammo->thrown_info->range) >= distance) { int8 hit_result = DetermineHit(victim, DAMAGE_PACKET_TYPE_RANGE_DAMAGE, ammo->thrown_info->damage_type, ammo->thrown_info->hit_bonus, false); @@ -367,7 +376,10 @@ void Entity::RangeAttack(Spawn* victim, float distance, Item* weapon, Item* ammo } else GetZone()->SendDamagePacket(this, victim, DAMAGE_PACKET_TYPE_RANGE_DAMAGE, hit_result, ammo->thrown_info->damage_type, 0, 0); - + + bool inv_slot_incl = false; + if(ammo->details.inv_slot_id >= 6) + inv_slot_incl = true; // If is a player subtract ammo if (IsPlayer()) { if (ammo->details.count > 1) { @@ -376,11 +388,15 @@ void Entity::RangeAttack(Spawn* victim, float distance, Item* weapon, Item* ammo } else { if(ammo->details.inv_slot_id >= 6) { - ((Player*)this)->equipment_list.RemoveItem(ammo->details.slot_id, false); - ((Player*)this)->item_list.DestroyItem(ammo->details.index); + ((Player*)this)->equipment_list.RemoveItem(ammo->details.slot_id); + ((Player*)this)->item_list.DestroyItem(ammo->details.index); + ammo = nullptr; // item is gone + item_deleted = true; } else { ((Player*)this)->equipment_list.RemoveItem(ammo->details.slot_id, true); + ammo = nullptr; + item_deleted = true; } } @@ -390,7 +406,7 @@ void Entity::RangeAttack(Spawn* victim, float distance, Item* weapon, Item* ammo if(outapp) client->QueuePacket(outapp); - if(ammo->details.inv_slot_id > 6) { + if(inv_slot_incl) { EQ2Packet* outapp = client->GetPlayer()->SendInventoryUpdate(client->GetVersion()); if (outapp) client->QueuePacket(outapp); @@ -433,26 +449,28 @@ void Entity::RangeAttack(Spawn* victim, float distance, Item* weapon, Item* ammo } } //Multi Attack roll - if(!multi_attack){ + if(!multi_attack && !item_deleted){ float multi_attack = info_struct.get_multi_attack(); if(multi_attack > 0){ float chance = multi_attack; if (multi_attack > 100){ int8 automatic_multi = (int8)floor((float)(multi_attack / 100)); chance = (multi_attack - (floor((float)(multi_attack / 100) * 100))); - while(automatic_multi > 0){ - RangeAttack(victim, 100, weapon, ammo, true); + while(!item_deleted && automatic_multi > 0){ + item_deleted = RangeAttack(victim, 100, weapon, ammo, true); automatic_multi--; } } - if (MakeRandomFloat(0, 100) <= chance) - RangeAttack(victim, 100, weapon, ammo, true); + if (!item_deleted && MakeRandomFloat(0, 100) <= chance) + item_deleted = RangeAttack(victim, 100, weapon, ammo, true); } } //Apply attack speed mods if(!multi_attack) SetAttackDelay(false, true); + + return item_deleted; } bool Entity::SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8 damage_type, int32 low_damage, int32 high_damage, int8 crit_mod, bool no_calcs, int8 override_packet_type, bool take_power){ @@ -481,6 +499,9 @@ bool Entity::SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8 if(victim->IsEntity()) { CheckEncounterState((Entity*)victim); } + + CheckProcs(PROC_TYPE_MAGICAL_ATTEMPT, victim); + bool successful_hit = true; if(hit_result == DAMAGE_PACKET_RESULT_SUCCESSFUL) { luaspell->last_spellattack_hit = true; @@ -495,26 +516,6 @@ bool Entity::SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8 CheckProcs(PROC_TYPE_OFFENSIVE, victim); CheckProcs(PROC_TYPE_MAGICAL_OFFENSIVE, victim); - - if(spell->GetSpellData()->success_message.length() > 0){ - Client* client = nullptr; - if(IsPlayer()) - client = ((Player*)this)->GetClient(); - if(client){ - string success_message = spell->GetSpellData()->success_message; - if(success_message.find("%t") < 0xFFFFFFFF) - success_message.replace(success_message.find("%t"), 2, victim->GetName()); - client->Message(CHANNEL_YOU_CAST, success_message.c_str()); - //commented out the following line as it was causing a duplicate message EmemJR 5/4/2019 - //GetZone()->SendDamagePacket(this, victim, DAMAGE_PACKET_TYPE_SPELL_DAMAGE, hit_result, damage_type, 0, spell->GetName()); - } - } - if(spell->GetSpellData()->effect_message.length() > 0){ - string effect_message = spell->GetSpellData()->effect_message; - if(effect_message.find("%t") < 0xFFFFFFFF) - effect_message.replace(effect_message.find("%t"), 2, victim->GetName()); - GetZone()->SimpleMessage(CHANNEL_SPELLS, effect_message.c_str(), victim, 50); - } } else { successful_hit = false; @@ -603,15 +604,15 @@ bool Entity::ProcAttack(Spawn* victim, int8 damage_type, int32 low_damage, int32 if(IsPlayer()) client = ((Player*)this)->GetClient(); if(client) { - if(success_msg.find("%t") < 0xFFFFFFFF) - success_msg.replace(success_msg.find("%t"), 2, victim->GetName()); - client->Message(CHANNEL_YOU_CAST, success_msg.c_str()); + std::string castMsg = std::string(success_msg); + SpellProcess::ReplaceEffectTokens(castMsg, (Spawn*)this, victim); + client->Message(CHANNEL_YOU_CAST, castMsg.c_str()); } } if (effect_msg.length() > 0) { - if(effect_msg.find("%t") < 0xFFFFFFFF) - effect_msg.replace(effect_msg.find("%t"), 2, victim->GetName()); - GetZone()->SimpleMessage(CHANNEL_SPELLS, effect_msg.c_str(), victim, 50); + std::string effectMsg = std::string(effect_msg); + SpellProcess::ReplaceEffectTokens(effectMsg, (Spawn*)this, victim); + GetZone()->SimpleMessage(CHANNEL_SPELLS, effectMsg.c_str(), victim, 50); } } else { @@ -1063,7 +1064,7 @@ Skill* Entity::GetSkillByWeaponType(int8 type, int8 damage_type, bool update) { return 0; } -bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_damage, int32 high_damage, const char* spell_name, int8 crit_mod, bool is_tick, bool no_calcs, bool ignore_attacker, bool take_power, LuaSpell* spell) { +bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_damage, int32 high_damage, const char* spell_name, int8 crit_mod, bool is_tick, bool no_calcs, bool ignore_attacker, bool take_power, LuaSpell* spell, bool skip_check_wards, bool is_melee_spawn) { if(spell) { spell->is_damage_spell = true; } @@ -1178,7 +1179,8 @@ bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_ if(damage) { int32 prevDmg = damage; - damage = victim->CheckWards(this, damage, damage_type); + if(!skip_check_wards) + damage = victim->CheckWards(this, damage, damage_type); if (damage < (sint64)prevDmg) useWards = true; @@ -1241,7 +1243,7 @@ bool Entity::DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_ } if (victim->GetHP() <= 0) - KillSpawn(victim, type, damage_type, blow_type); + KillSpawn(victim, type, damage_type, blow_type, is_melee_spawn); else { victim->CheckProcs(PROC_TYPE_DEFENSIVE, this); if (spell_name) @@ -1327,7 +1329,7 @@ float Entity::CalculateMitigation(int8 type, int8 damage_type, int16 effective_l void Entity::AddHate(Entity* attacker, sint32 hate, bool ignore_pet_behavior) { if(!attacker || GetHP() <= 0 || attacker->GetHP() <= 0) return; - + if(IsInSpawnGroup(attacker)) return; // can't aggro your own members @@ -1444,7 +1446,7 @@ bool Entity::CheckInterruptSpell(Entity* attacker) { return false; } -void Entity::KillSpawn(Spawn* dead, int8 type, int8 damage_type, int16 kill_blow_type) { +void Entity::KillSpawn(Spawn* dead, int8 type, int8 damage_type, int16 kill_blow_type, bool is_melee_spawn) { if(!dead) return; @@ -1484,7 +1486,7 @@ void Entity::KillSpawn(Spawn* dead, int8 type, int8 damage_type, int16 kill_blow dead->ClearRunningLocations(); dead->CalculateRunningLocation(true); - GetZone()->KillSpawn(true, dead, this, true, type, damage_type, kill_blow_type); + GetZone()->KillSpawn(is_melee_spawn, dead, this, true, type, damage_type, kill_blow_type); } void Entity::HandleDeathExperienceDebt(Spawn* killer) @@ -1553,11 +1555,11 @@ void NPC::ProcessCombat() { void Player::ProcessCombat() { // if not in combat OR casting a spell OR dazed OR feared return out - if (!EngagedInCombat() || IsCasting() || IsDazed() || IsFeared()) + if (!GetZone() || !EngagedInCombat() || IsCasting() || IsDazed() || IsFeared()) return; //If no target delete combat_target and return out - Spawn* Target = GetZone()->GetSpawnByID(target); + Spawn* Target = GetZone()->GetSpawnByID(target, true); if (!Target) { combat_target = 0; if (target > 0) { @@ -1931,7 +1933,10 @@ sint32 Entity::CalculateHateAmount(Spawn* target, sint32 amt) { amt = CalculateFormulaByStat(amt, ITEM_STAT_TAUNT_AND_COMBAT_ART_DAMAGE); amt = CalculateFormulaByStat(amt, ITEM_STAT_ABILITY_MODIFIER); - + + float multiplier = 1.0f + (GetInfoStruct()->get_hate_mod() / 100.0f); + amt = static_cast(amt * multiplier); + return amt; } diff --git a/old/WorldServer/Combat.h b/old/WorldServer/Combat.h index 4f6895c..f56a11b 100644 --- a/old/WorldServer/Combat.h +++ b/old/WorldServer/Combat.h @@ -20,7 +20,7 @@ #ifndef __EQ2_COMBAT_H__ #define __EQ2_COMBAT_H__ #include "Player.h" -#include "../common/timer.hpp" +#include "../common/timer.h" #include "NPC_AI.h" #include "MutexList.h" diff --git a/old/WorldServer/Commands/Commands.cpp b/old/WorldServer/Commands/Commands.cpp index 67103a9..9613be4 100644 --- a/old/WorldServer/Commands/Commands.cpp +++ b/old/WorldServer/Commands/Commands.cpp @@ -1,22 +1,23 @@ /* -EQ2Emulator: Everquest II Server Emulator -Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) -This file is part of EQ2Emulator. + This file is part of EQ2Emulator. -EQ2Emulator 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. + EQ2Emulator 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. -EQ2Emulator 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. + EQ2Emulator 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 EQ2Emulator. If not, see . + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . */ + #include #include #include @@ -26,11 +27,11 @@ along with EQ2Emulator. If not, see . #include "../../common/version.h" #include "../../common/seperator.h" #include "../../common/servertalk.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "../World.h" -#include "../../common/config_reader.hpp" +#include "../../common/ConfigReader.h" #include "../VisualStates.h" -#include "../../common/debug.hpp" +#include "../../common/debug.h" #include "../LuaInterface.h" #include "../Quests.h" #include "../client.h" @@ -39,7 +40,7 @@ along with EQ2Emulator. If not, see . #include "../SpellProcess.h" #include "../Tradeskills/Tradeskills.h" #include "../../common/Log.h" -#include "../../common/misc_functions.hpp" +#include "../../common/MiscFunctions.h" #include "../Languages.h" #include "../Traits/Traits.h" #include "../Chat/Chat.h" @@ -51,6 +52,7 @@ along with EQ2Emulator. If not, see . #include "../Bots/Bot.h" #include "../Web/PeerManager.h" #include "../../common/GlobalHeaders.h" +#include "../Broker/BrokerManager.h" extern WorldDatabase database; extern MasterSpellList master_spell_list; @@ -75,6 +77,7 @@ extern MasterAAList master_aa_list; extern MasterRaceTypeList race_types_list; extern Classes classes; extern PeerManager peer_manager; +extern BrokerManager broker; //devn00b: Fix for linux builds since we dont use stricmp we use strcasecmp #if defined(__GNUC__) @@ -1987,6 +1990,18 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!"); break; } + case COMMAND_RELOAD_PLAYERSCRIPTS: { + client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Player Scripts..."); + world.SetReloadingSubsystem("PlayerScripts"); + world.ResetPlayerScripts(); + world.LoadPlayerScripts(); + if (lua_interface) + lua_interface->DestroyPlayerScripts(); + world.RemoveReloadingSubSystem("PlayerScripts"); + peer_manager.sendPeersMessage("/reloadcommand", command->handler); + client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!"); + break; + } case COMMAND_RELOAD_ENTITYCOMMANDS: { client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Entity Commands..."); world.SetReloadingSubsystem("EntityCommands"); @@ -2180,14 +2195,35 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie LogWrite(COMMAND__ERROR, 0, "Command", "/info appearance: Unknown Index: %u", item_index); } else if(strcmp(sep->arg[0], "item") == 0 || strcmp(sep->arg[0], "merchant") == 0 || strcmp(sep->arg[0], "store") == 0 || strcmp(sep->arg[0], "buyback") == 0 || strcmp(sep->arg[0], "consignment") == 0){ - int32 item_id = atoul(sep->arg[1]); - Item* item = master_item_list.GetItem(item_id); - if(item){ + int64 item_id = strtoull(sep->arg[1], NULL, 0); + Item* item = nullptr; + + if (strcmp(sep->arg[0], "store") == 0) + item = client->GetPlayer()->item_list.GetVaultItemFromUniqueID(item_id, true); + else + item = master_item_list.GetItem(item_id); + + if(!item && client->GetMerchantTransactionID() && strcmp(sep->arg[0], "merchant") == 0) { + Spawn* merchant = client->GetPlayer()->GetZone()->GetSpawnByID(client->GetMerchantTransactionID()); + if(merchant && merchant->GetHouseCharacterID() && merchant->GetPickupUniqueItemID()) { + if(auto itemInfo = broker.GetActiveItem(merchant->GetHouseCharacterID(), item_id)) { + item = master_item_list.GetItem(itemInfo->item_id); + if(item) { + EQ2Packet* app = item->serialize(client->GetVersion(), true, client->GetPlayer()); + client->QueuePacket(app); + } + } + } + } + else if(!item && strcmp(sep->arg[0], "consignment") == 0) { + client->SendSellerItemByItemUniqueId(item_id); + } + else if(item){ EQ2Packet* app = item->serialize(client->GetVersion(), true, client->GetPlayer()); client->QueuePacket(app); } else - LogWrite(COMMAND__ERROR, 0, "Command", "/info item|merchant|store|buyback|consignment: Unknown Item ID: %u", item_id); + LogWrite(COMMAND__ERROR, 0, "Command", "/info item|merchant|store|buyback|consignment: Unknown Item ID: %u (full arguments %s)", item_id, sep->argplus[0]); } else if (strcmp(sep->arg[0], "spell") == 0) { sint32 spell_id = atol(sep->arg[1]); @@ -2679,7 +2715,8 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie break; } case COMMAND_BANK_DEPOSIT:{ - if(client->GetBanker() && sep && sep->arg[0]){ + Spawn* banker = client->GetCurrentZone()->GetSpawnByID(client->GetBanker()); + if(banker && sep && sep->arg[0]){ int64 amount = 0; string deposit = string(sep->arg[0]); amount = atoi64(deposit.c_str()); @@ -2688,7 +2725,8 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie break; } case COMMAND_BANK_WITHDRAWAL:{ - if(client->GetBanker() && sep && sep->arg[0] && sep->IsNumber(0)){ + Spawn* banker = client->GetCurrentZone()->GetSpawnByID(client->GetBanker()); + if(banker && sep && sep->arg[0] && sep->IsNumber(0)){ int64 amount = 0; string deposit = string(sep->arg[0]); amount = atoi64(deposit.c_str()); @@ -2891,7 +2929,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie } case COMMAND_CLASS:{ if(sep && sep->arg[ndx][0]){ - client->GetPlayer()->SetPlayerAdventureClass(atoi(sep->arg[ndx])); + client->GetPlayer()->SetPlayerAdventureClass(atoi(sep->arg[ndx]), true); }else client->SimpleMessage(CHANNEL_COLOR_YELLOW,"Usage: /class {class_id}"); break; @@ -3137,6 +3175,9 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie world.ResetZoneScripts(); database.LoadZoneScriptData(); + + world.ResetPlayerScripts(); + world.LoadPlayerScripts(); if(lua_interface) { lua_interface->DestroySpawnScripts(); @@ -3144,6 +3185,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie lua_interface->DestroyQuests(); lua_interface->DestroyItemScripts(); lua_interface->DestroyZoneScripts(); + lua_interface->DestroyPlayerScripts(); } int32 quest_count = database.LoadQuests(); @@ -3173,21 +3215,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie int32 quest_id = 0; if(sep && sep->arg[0] && sep->IsNumber(0)) quest_id = atoul(sep->arg[0]); - if(quest_id > 0){ - client->GetPlayer()->MPlayerQuests.readlock(__FUNCTION__, __LINE__); - if(lua_interface && client->GetPlayer()->player_quests.count(quest_id) > 0) { - Quest* quest = client->GetPlayer()->player_quests[quest_id]; - client->GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__); - if (quest && quest->CanDeleteQuest()) { - lua_interface->CallQuestFunction(quest, "Deleted", client->GetPlayer()); - client->RemovePlayerQuest(quest_id); - client->GetCurrentZone()->SendQuestUpdates(client); - } - } - else { - client->GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__); - } - } + client->DeleteQuest(quest_id); break; } case COMMAND_ACCEPT_QUEST:{ @@ -3895,8 +3923,12 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie if (!spawn || !client->HasOwnerOrEditAccess() || !spawn->GetPickupItemID()) break; - - if(client->AddItem(spawn->GetPickupItemID(), 1)) { + Item* tmpItem = client->GetPlayer()->item_list.GetVaultItemFromUniqueID(spawn->GetPickupUniqueItemID()); + if((tmpItem && tmpItem->generic_info.item_type == ITEM_TYPE_HOUSE_CONTAINER) || client->AddItem(spawn->GetPickupItemID(), 1)) { + if ( tmpItem && tmpItem->generic_info.item_type == ITEM_TYPE_HOUSE_CONTAINER ) { + tmpItem->TryUnlockItem(LockReason::LockReason_House); + client->QueuePacket(client->GetPlayer()->SendInventoryUpdate(client->GetVersion())); + } Query query; query.RunQuery2(Q_INSERT, "delete from spawn_instance_data where spawn_id = %u and spawn_location_id = %u and pickup_item_id = %u", spawn->GetDatabaseID(), spawn->GetSpawnLocationID(), spawn->GetPickupItemID()); @@ -4025,12 +4057,16 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie case COMMAND_PLACE_HOUSE_ITEM: { if (sep && sep->IsNumber(0)) { - int32 uniqueid = atoi(sep->arg[0]); + int64 uniqueid = strtoull(sep->arg[0], NULL, 0); - Item* item = client->GetPlayer()->item_list.GetItemFromUniqueID(uniqueid); - //Item* item = player->GetEquipmentList()->GetItem(slot); - if (item && item->IsHouseItem()) + Item* item = client->GetPlayer()->item_list.GetVaultItemFromUniqueID(uniqueid); + + if (item && (item->IsHouseItem() || item->IsHouseContainer())) { + if(item->IsHouseContainer() && item->details.inv_slot_id != InventorySlotType::HOUSE_VAULT) { // must be in base slot of vault in house for house containers + client->SimpleMessage(CHANNEL_COLOR_RED, "Must be in vault to place this item."); + break; + } if (!client->HasOwnerOrEditAccess()) { client->SimpleMessage(CHANNEL_COLOR_RED, "This is not your home!"); @@ -4057,7 +4093,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie Object* obj = new Object(); Spawn* spawn = (Spawn*)obj; memset(&spawn->appearance, 0, sizeof(spawn->appearance)); - strcpy(spawn->appearance.name, "temp"); + strcpy(spawn->appearance.name, item->name.c_str()); spawn->SetX(client->GetPlayer()->GetX()); spawn->SetY(client->GetPlayer()->GetY()); spawn->SetZ(client->GetPlayer()->GetZ()); @@ -4398,20 +4434,49 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie } case COMMAND_BUY_FROM_BROKER:{ if(sep && sep->arg[1][0] && sep->IsNumber(0) && sep->IsNumber(1)){ - int32 item_id = atoul(sep->arg[0]); + int64 item_id = strtoull(sep->arg[0], NULL, 0); int16 quantity = atoul(sep->arg[1]); - Item* item = master_item_list.GetItem(item_id); - if(item && item->generic_info.max_charges > 1) - quantity = item->generic_info.max_charges; - client->AddItem(item_id, quantity, AddItemType::BUY_FROM_BROKER); + if(client->IsGMStoreSearch()) { + Item* item = master_item_list.GetItem(item_id); + if(item && item->generic_info.max_charges > 1) + quantity = item->generic_info.max_charges; + client->AddItem(item_id, quantity, AddItemType::BUY_FROM_BROKER); + } + else { + client->BuySellerItemByItemUniqueId(item_id, quantity); + LogWrite(COMMAND__ERROR, 0, "Command", "BUY_FROM_BROKER. Item ID %u, Quantity %u, full args %s.", item_id, quantity, sep->argplus[0]); + } } break; } case COMMAND_SEARCH_STORES_PAGE:{ + LogWrite(COMMAND__ERROR, 0, "Command", "SearchStores: %s", sep && sep->arg[0] ? sep->argplus[0] : ""); if(sep && sep->arg[0][0] && sep->IsNumber(0)){ int32 page = atoul(sep->arg[0]); client->SearchStore(page); } + else { + client->SetGMStoreSearch(false); + PacketStruct* packet = configReader.getStruct("WS_StartBroker", client->GetVersion()); + if (packet) { + packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(client->GetPlayer())); + //packet->setDataByName("unknown", 1); + packet->setDataByName("unknown2", 5, 0); + packet->setDataByName("unknown2", 20, 1); + packet->setDataByName("unknown2", 58, 3); + packet->setDataByName("unknown2", 40, 4); + client->QueuePacket(packet->serialize()); + if(client->GetVersion() > 561) { + PacketStruct* packet2 = configReader.getStruct("WS_BrokerBags", client->GetVersion()); + if (packet2) { + packet2->setDataByName("char_id", client->GetCharacterID()); + client->QueuePacket(packet2->serialize()); //send this for now, needed to properly clear data + safe_delete(packet2); + } + safe_delete(packet); + } + } + } break; } case COMMAND_SEARCH_STORES:{ @@ -4419,11 +4484,11 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie const char* values = sep->argplus[0]; if(values){ LogWrite(ITEM__WARNING, 0, "Item", "SearchStores: %s", values); - map str_values = TranslateBrokerRequest(values); vector* items = master_item_list.GetItems(str_values, client); if(items){ - client->SetItemSearch(items); + client->SetItemSearch(items, str_values); + client->SetSearchPage(0); client->SearchStore(0); } } @@ -4820,6 +4885,17 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie client->Message(CHANNEL_COLOR_YELLOW, "Angle %f between player %s and target %s", spawnAngle, client->GetPlayer()->GetTarget() ? client->GetPlayer()->GetTarget()->GetName() : client->GetPlayer()->GetName(), client->GetPlayer()->GetName()); break; } + else if (ToLower(string(sep->arg[0])) == "hated") + { + if(client->GetPlayer()->GetTarget() && client->GetPlayer()->GetTarget()->IsEntity()) { + Entity* target = (Entity*)client->GetPlayer()->GetTarget(); + target->SendHatedByList(client); + } + else { + client->Message(CHANNEL_COLOR_YELLOW, "No target or target is not entity to display hated by list."); + } + break; + } } if (sep->IsNumber(0)) { @@ -4953,6 +5029,8 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie details3 += "STA / STABase: " + to_string(ent->GetInfoStruct()->get_sta()) + " / " + to_string(ent->GetInfoStruct()->get_sta_base()) + "\n"; details3 += "INT / INTBase: " + to_string(ent->GetInfoStruct()->get_intel()) + " / " + to_string(ent->GetInfoStruct()->get_intel_base()) + "\n"; details3 += "WIS / WISBase: " + to_string(ent->GetInfoStruct()->get_wis()) + " / " + to_string(ent->GetInfoStruct()->get_wis_base()) + "\n"; + details3 += "HPRegen: " + to_string(ent->GetInfoStruct()->get_hp_regen()) + "\n"; + details3 += "PowerRegen: " + to_string(ent->GetInfoStruct()->get_power_regen()) + "\n"; } string details4; @@ -5372,14 +5450,14 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie } case COMMAND_BROADCAST: { if (sep && sep->arg[0]) - zone_list.HandleGlobalBroadcast(sep->argplus[0]); + zone_list.TransmitBroadcast(sep->argplus[0]); else client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Usage: /broadcast {message}"); break; } case COMMAND_ANNOUNCE: { if (sep && sep->arg[0]) - zone_list.HandleGlobalAnnouncement(sep->argplus[0]); + zone_list.TransmitGlobalAnnouncement(sep->argplus[0]); else client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Usage: /announce {message}"); break; @@ -5401,6 +5479,8 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie database.LoadMerchantInformation(); // we skip if there is only a reload of single item not all items } + peer_manager.sendPeersMessage("/reloadcommand", command->handler, item_id); + if(item_id > 0) { client->Message(CHANNEL_COLOR_YELLOW, "Reloaded item %u.", item_id); } @@ -5417,6 +5497,13 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie } case COMMAND_ITEMSEARCH: case COMMAND_FROMBROKER:{ + + if(command->handler == COMMAND_ITEMSEARCH) { + client->SetGMStoreSearch(true); + } + else { + client->SetGMStoreSearch(false); + } PacketStruct* packet = configReader.getStruct("WS_StartBroker", client->GetVersion()); if (packet) { packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(client->GetPlayer())); @@ -5426,13 +5513,15 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie packet->setDataByName("unknown2", 58, 3); packet->setDataByName("unknown2", 40, 4); client->QueuePacket(packet->serialize()); - PacketStruct* packet2 = configReader.getStruct("WS_BrokerBags", client->GetVersion()); - if (packet2) { - packet2->setDataByName("char_id", client->GetCharacterID()); - client->QueuePacket(packet2->serialize()); //send this for now, needed to properly clear data - safe_delete(packet2); + if(client->GetVersion() > 561) { + PacketStruct* packet2 = configReader.getStruct("WS_BrokerBags", client->GetVersion()); + if (packet2) { + packet2->setDataByName("char_id", client->GetCharacterID()); + client->QueuePacket(packet2->serialize()); //send this for now, needed to properly clear data + safe_delete(packet2); + } + safe_delete(packet); } - safe_delete(packet); } break; } @@ -5831,6 +5920,15 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie case COMMAND_SPLIT: { Command_Split(client, sep); break; } case COMMAND_RAIDSAY: { Command_RaidSay(client, sep); break; } case COMMAND_RELOAD_ZONEINFO: { Command_ReloadZoneInfo(client, sep); break; } + case COMMAND_SLE: { Command_SetLocationEntry(client, sep); break; } + case COMMAND_STORE_LIST_ITEM: { Command_StoreListItem(client, sep); break; } + case COMMAND_STORE_SET_PRICE: { Command_StoreSetPrice(client, sep); break; } + case COMMAND_STORE_SET_PRICE_LOCAL: { Command_StoreSetPriceLocal(client, sep); break; } + case COMMAND_STORE_START_SELLING: { Command_StoreStartSelling(client, sep); break; } + case COMMAND_STORE_STOP_SELLING: { Command_StoreStopSelling(client, sep); break; } + case COMMAND_STORE_UNLIST_ITEM: { Command_StoreUnlistItem(client, sep); break; } + case COMMAND_CLOSE_STORE_KEEP_SELLING: { Command_CloseStoreKeepSelling(client, sep); break; } + case COMMAND_CANCEL_STORE: { Command_CancelStore(client, sep); break; } default: { LogWrite(COMMAND__WARNING, 0, "Command", "Unhandled command: %s", command->command.data.c_str()); @@ -6184,11 +6282,6 @@ void Commands::Command_DuelSurrender(Client* client, Seperator* sep) PrintSep(sep, "COMMAND_DUEL_SURRENDER"); LogWrite(MISC__TODO, 1, "Command", "TODO-Command: Surrender Duel Command"); client->Message(CHANNEL_COLOR_YELLOW, "You cannot duel other players (Not Implemented)"); - - // JA, just messin around ;) - char surrender[64]; - sprintf(surrender, "%s surrendered like a cowardly dog!", client->GetPlayer()->GetName()); - zone_list.HandleGlobalAnnouncement(surrender); } /* @@ -7022,7 +7115,7 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma if(item) { - if(item->details.item_locked) { + if(item->IsItemLocked()) { client->SimpleMessage(CHANNEL_COLOR_RED, "You cannot destroy the item in use."); return; } @@ -7034,11 +7127,15 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma client->SimpleMessage(CHANNEL_COLOR_RED, "You can't destroy this item."); return; } + if(client->GetPlayer()->item_list.IsItemInSlotType(item, InventorySlotType::HOUSE_VAULT) || client->GetPlayer()->item_list.IsItemInSlotType(item, InventorySlotType::BASE_INVENTORY)) { + broker.RemoveItem(client->GetPlayer()->GetCharacterID(), item->details.unique_id, item->details.count, true); + } if(item->GetItemScript() && lua_interface) lua_interface->RunItemScript(item->GetItemScript(), "destroyed", item, client->GetPlayer()); //reobtain item make sure it wasn't removed item = player->item_list.GetItemFromIndex(index); + int32 bag_id = 0; if(item){ bag_id = item->details.inv_slot_id; @@ -7047,6 +7144,8 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma client->GetPlayer()->item_list.DestroyItem(index); client->GetPlayer()->UpdateInventory(bag_id); client->GetPlayer()->CalculateApplyWeight(); + + client->OpenShopWindow(nullptr); // update the window if it is open } } else if(sep->arg[4][0] && strncasecmp("move", sep->arg[0], 4) == 0 && sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4)) @@ -7056,33 +7155,34 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma sint32 bag_id = atol(sep->arg[3]); int8 charges = atoi(sep->arg[4]); Item* item = client->GetPlayer()->item_list.GetItemFromIndex(from_index); - + int64 unique_id = 0; + int16 count = 0; if(!item) { client->SimpleMessage(CHANNEL_COLOR_RED, "You have no item."); return; } - - if(to_slot == item->details.slot_id && (bag_id < 0 || bag_id == item->details.inv_slot_id)) { + unique_id = item->details.unique_id; + count = item->details.count; + if(to_slot == item->details.slot_id && (bag_id == item->details.inv_slot_id)) { return; } - if(item->details.item_locked) + if(item->IsItemLocked()) { client->SimpleMessage(CHANNEL_COLOR_RED, "You cannot move the item in use."); return; } - if(bag_id == -4 && !client->GetPlayer()->item_list.SharedBankAddAllowed(item)) + if(bag_id == InventorySlotType::SHARED_BANK && !client->GetPlayer()->item_list.SharedBankAddAllowed(item)) { client->SimpleMessage(CHANNEL_COLOR_RED, "That item (or an item inside) cannot be shared."); return; } - sint32 old_inventory_id = 0; if(item) old_inventory_id = item->details.inv_slot_id; //autobank - if (bag_id == -3 && to_slot == -1) + if (bag_id == InventorySlotType::BANK && to_slot == -1) { if (player->HasFreeBankSlot()) to_slot = player->FindFreeBankSlot(); @@ -7127,6 +7227,16 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma } client->GetPlayer()->CalculateApplyWeight(); + if(item) { + if(!client->GetPlayer()->item_list.IsItemInSlotType(item, InventorySlotType::HOUSE_VAULT) && + !client->GetPlayer()->item_list.IsItemInSlotType(item, InventorySlotType::BASE_INVENTORY)) { + broker.RemoveItem(client->GetPlayer()->GetCharacterID(), unique_id, charges); + } + } + else { + broker.RemoveItem(client->GetPlayer()->GetCharacterID(), unique_id, count); + } + client->OpenShopWindow(nullptr); // update the window if it is open } else if(sep->arg[1][0] && strncasecmp("equip", sep->arg[0], 5) == 0 && sep->IsNumber(1)) { @@ -7157,6 +7267,7 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma client->QueuePacket(characterSheetPackets); client->GetPlayer()->CalculateBonuses(); + client->OpenShopWindow(nullptr); // update the window if it is open } else if (sep->arg[1][0] && strncasecmp("unpack", sep->arg[0], 6) == 0 && sep->IsNumber(1)) { @@ -7166,7 +7277,7 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma int16 index = atoi(sep->arg[1]); Item* item = client->GetPlayer()->item_list.GetItemFromIndex(index); if (item) { - if(item->details.item_locked) + if(item->IsItemLocked()) { client->SimpleMessage(CHANNEL_COLOR_RED, "You cannot unpack the item in use."); return; @@ -7184,6 +7295,7 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma } client->RemoveItem(item, 1); + client->OpenShopWindow(nullptr); // update the window if it is open } } @@ -7222,6 +7334,7 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma client->UnequipItem(index, bag_id, to_slot, appearance_equip); client->GetPlayer()->CalculateBonuses(); + client->OpenShopWindow(nullptr); // update the window if it is open } else if(sep->arg[2][0] && strncasecmp("swap_equip", sep->arg[0], 10) == 0 && sep->IsNumber(1) && sep->IsNumber(2)) { @@ -7274,9 +7387,10 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma // Send the inventory update packet client->QueuePacket(player->item_list.serialize(player, client->GetVersion())); + client->OpenShopWindow(nullptr); // update the window if it is open return; } - else if (bag_id == -3 && to_slot == -1) { + else if (bag_id == InventorySlotType::BANK && to_slot == -1) { // Auto Bank if (!player->item_list.GetFirstFreeBankSlot(&bag_id, &to_slot)) { client->SimpleMessage(CHANNEL_STATUS, "You do not have any free bank slots."); @@ -7289,14 +7403,15 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma player->item_list.RemoveOverflowItem(item); } client->QueuePacket(player->item_list.serialize(player, client->GetVersion())); + client->OpenShopWindow(nullptr); // update the window if it is open } - else if (bag_id == -4) { + else if (bag_id == InventorySlotType::SHARED_BANK) { // Shared Bank if (!player->item_list.SharedBankAddAllowed(item)) { client->SimpleMessage(CHANNEL_STATUS, "That item (or an item inside) cannot be shared."); return; } - Item* tmp_item = player->item_list.GetItem(-4, to_slot); + Item* tmp_item = player->item_list.GetItem(bag_id, to_slot); if (tmp_item) { client->SimpleMessage(CHANNEL_STATUS, "You can not place an overflow item into an occupied slot"); return; @@ -7309,6 +7424,7 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma player->item_list.RemoveOverflowItem(item); } client->QueuePacket(player->item_list.serialize(player, client->GetVersion())); + client->OpenShopWindow(nullptr); // update the window if it is open return; } } @@ -7328,6 +7444,7 @@ void Commands::Command_Inventory(Client* client, Seperator* sep, EQ2_RemoteComma player->item_list.RemoveOverflowItem(item); } client->QueuePacket(player->item_list.serialize(player, client->GetVersion())); + client->OpenShopWindow(nullptr); // update the window if it is open return; } } @@ -9974,14 +10091,18 @@ void Commands::Command_TradeAddItem(Client* client, Seperator* sep) int32 index = atoi(sep->arg[0]); item = client->GetPlayer()->GetPlayerItemList()->GetItemFromIndex(index); if (item) { - if(item->details.item_locked || item->details.equip_slot_id) { + if(item->IsItemLocked() || item->details.equip_slot_id) { client->SimpleMessage(CHANNEL_COLOR_RED, "You cannot trade an item currently in use."); return; } - else if(item->details.inv_slot_id == -3 || item->details.inv_slot_id == -4) { + else if(item->details.inv_slot_id == InventorySlotType::BANK || item->details.inv_slot_id == InventorySlotType::SHARED_BANK) { client->SimpleMessage(CHANNEL_COLOR_RED, "You cannot trade an item in the bank."); return; } + else if(client->GetPlayer()->item_list.IsItemInSlotType(item, InventorySlotType::HOUSE_VAULT)) { + client->SimpleMessage(CHANNEL_COLOR_RED, "You cannot trade an item in the house vault."); + return; + } int8 result = client->GetPlayer()->trade->AddItemToTrade(client->GetPlayer(), item, atoi(sep->arg[2]), atoi(sep->arg[1])); if (result == 1) @@ -11039,6 +11160,23 @@ void Commands::Command_Test(Client* client, EQ2_16BitString* command_parms) { else if(atoi(sep->arg[0]) == 38) { client->GetPlayer()->GetZone()->SendFlightPathsPackets(client); } + else if(atoi(sep->arg[0]) == 39) { + client->OpenShopWindow(nullptr, true); + } + else if(atoi(sep->arg[0]) == 40) { + + PacketStruct* packet2 = configReader.getStruct("WS_HouseStoreLog", client->GetVersion()); + if (packet2) { + packet2->setDataByName("data", sep->arg[1]); + packet2->setDataByName("coin_gain_session", atoul(sep->arg[2])); + packet2->setDataByName("coin_gain_alltime", atoul(sep->arg[3])); + packet2->setDataByName("sales_log_open", atoi(sep->arg[4])); + EQ2Packet* outapp = packet2->serialize(); + DumpPacket(outapp); + client->QueuePacket(outapp); + safe_delete(packet2); + } + } } else { PacketStruct* packet2 = configReader.getStruct("WS_ExaminePartialSpellInfo", client->GetVersion()); @@ -11501,37 +11639,8 @@ void Commands::Command_Wind(Client* client, Seperator* sep) { void Commands::Command_SendMerchantWindow(Client* client, Seperator* sep, bool sell) { Spawn* spawn = client->GetPlayer()->GetTarget(); - if(client->GetVersion() < 561) { - sell = false; // doesn't support in the same way as AoM just open the normal buy/sell window - } - if(spawn) { - client->SetMerchantTransaction(spawn); - if (spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(client)){ - client->SendHailCommand(spawn); - //MerchantFactionMultiplier* multiplier = world.GetMerchantMultiplier(spawn->GetMerchantID()); - //if(!multiplier || (multiplier && client->GetPlayer()->GetFactions()->GetFactionValue(multiplier->faction_id) >= multiplier->faction_min)){ - client->SendBuyMerchantList(sell); - if(!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY)) - client->SendSellMerchantList(sell); - if(!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY_BACK)) - client->SendBuyBackList(sell); - - if(client->GetVersion() > 561) { - PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", client->GetVersion()); - if (packet) { - packet->setDataByName("spawn_id", 0xFFFFFFFF); - packet->setDataByName("type", 16); - EQ2Packet* outapp = packet->serialize(); - if (outapp) - client->QueuePacket(outapp); - safe_delete(packet); - } - } - } - if (spawn->GetMerchantType() & MERCHANT_TYPE_REPAIR) - client->SendRepairList(); - } - // client->SimpleMessage(CHANNEL_COLOR_RED, "Your faction is too low to use this merchant."); + if(spawn) + client->SendMerchantWindow(spawn, sell); } @@ -12944,4 +13053,301 @@ void Commands::Command_RaidSay(Client* client, Seperator* sep) { */ void Commands::Command_ReloadZoneInfo(Client* client, Seperator* sep) { world.ClearZoneInfoCache(); +} + +// somewhere in your command‐handler: +void Commands::Command_SetLocationEntry(Client* client, Seperator* sep) { + if(client->GetCurrentZone()->GetInstanceType() == PERSONAL_HOUSE_INSTANCE) { + client->Message(CHANNEL_COLOR_YELLOW, "Use in a non house zone."); + return; + } + + if (!sep->IsSet(1) || (sep->IsNumber(0))) { + client->Message(CHANNEL_COLOR_YELLOW, "Usage: /sle "); + return; + } + + Spawn* target = client->GetPlayer()->GetTarget(); + + if(!target) { + client->Message(CHANNEL_COLOR_RED, "Target missing for set location entry, /sle "); + return; + } + + // GetSpawnEntryID for spawn_location_entry to set the spawnpercentage + int32 spawnEntryID = target->GetSpawnEntryID(); + int32 spawnPlacementID = target->GetSpawnLocationPlacementID(); + int32 spawnLocationID = target->GetSpawnLocationID(); + int32 dbID = target->GetDatabaseID(); + if (spawnPlacementID == 0 || spawnLocationID == 0 || spawnEntryID == 0 || dbID == 0) { + client->Message(CHANNEL_COLOR_RED, "Error: no valid spawn entry selected."); + return; + } + + // 2) Whitelist the allowed columns + static const std::unordered_set allowed = { + "x", + "y", + "z", + "x_offset", + "y_offset", + "z_offset", + "heading", + "pitch", + "roll", + "respawn", + "respawn_offset_low", + "respawn_offset_high", + "duplicated_spawn", + "expire_timer", + "expire_offset", + "grid_id", + "processed", + "instance_id", + "lvl_override", + "hp_override", + "mp_override", + "str_override", + "sta_override", + "wis_override", + "int_override", + "agi_override", + "heat_override", + "cold_override", + "magic_override", + "mental_override", + "divine_override", + "disease_override", + "poison_override", + "difficulty_override", + "spawnpercentage", + "condition" + }; + + + const std::string& field = std::string(sep->arg[0]); + if (!allowed.count(field)) { + client->Message(CHANNEL_COLOR_RED, "Error: column '%s' is not modifiable.", field.c_str()); + return; + } + + const std::string& val = std::string(sep->arg[1]); + + Query query; + if(field == "spawnpercentage" || field == "condition") { + query.AddQueryAsync(0, + &database, + Q_UPDATE, + // we embed the whitelisted field name directly in the format + "UPDATE spawn_location_entry " + "SET %s=%s " + "WHERE id=%u and spawn_location_id=%u and spawn_id=%u ", + field.c_str(), + val.c_str(), + spawnEntryID, + spawnLocationID, + dbID + ); + } + else { + query.AddQueryAsync(0, + &database, + Q_UPDATE, + // we embed the whitelisted field name directly in the format + "UPDATE spawn_location_placement " + "SET %s=%s " + "WHERE id=%u and spawn_location_id=%u ", + field.c_str(), + val.c_str(), + spawnPlacementID, + spawnLocationID + ); + } + + client->Message(CHANNEL_COLOR_YELLOW, "Modified %s to %s for row entry id %u, spawn placement id %u, related to location id %u and spawn database id %u.", + field.c_str(), val.c_str(), spawnEntryID, spawnPlacementID, spawnLocationID, dbID); +} + + + +void Commands::Command_StoreListItem(Client* client, Seperator* sep) { + if(!client->GetShopWindowStatus()) { + client->Message(CHANNEL_COLOR_RED, "Shop not available."); + return; + } + + if(sep && sep->arg[0]) { + auto info = broker.GetSellerInfo(client->GetPlayer()->GetCharacterID()); + if(!info) { + client->Message(CHANNEL_COLOR_RED, "Player %u is not in the broker database.", client->GetPlayer()->GetCharacterID()); + } + else if(sep->IsNumber(0)) { + int64 unique_id = atoll(sep->arg[0]); + if(client->GetPlayer()->item_list.CanStoreSellItem(unique_id, true)) { + Item* item = client->GetPlayer()->item_list.GetVaultItemFromUniqueID(unique_id, true); + if(item) { + bool isInv = !client->GetPlayer()->item_list.IsItemInSlotType(item, InventorySlotType::HOUSE_VAULT); + int64 cost = broker.GetSalePrice(client->GetPlayer()->GetCharacterID(), item->details.unique_id); + client->AddItemSale(item->details.unique_id, item->details.item_id, cost, item->details.inv_slot_id, item->details.slot_id, item->details.count, isInv, true, item->creator); + } + else + client->Message(CHANNEL_COLOR_RED, "Broker issue, cannot find item %u.", unique_id); + + client->SetItemSaleStatus(unique_id, true); + client->GetPlayer()->item_list.SetVaultItemLockUniqueID(client, unique_id, true, false); + client->SetSellerStatus(); + } + } + else { + client->Message(CHANNEL_COLOR_RED, "Invalid arguments for /store_list_item unique_id."); + + } + } +} +void Commands::Command_StoreSetPrice(Client* client, Seperator* sep) { + if(!client->GetShopWindowStatus()) { + client->Message(CHANNEL_COLOR_RED, "Shop not available."); + return; + } + + if(sep && sep->arg[0]) { + auto info = broker.GetSellerInfo(client->GetPlayer()->GetCharacterID()); + int64 unique_id = atoll(sep->arg[0]); + if(!info) { + client->Message(CHANNEL_COLOR_RED, "Player %u is not in the broker database.", client->GetPlayer()->GetCharacterID()); + } + else if(info->sell_from_inventory && broker.IsItemFromInventory(client->GetPlayer()->GetCharacterID(), unique_id)) { + client->Message(CHANNEL_COLOR_RED, "You cannot change the price while selling."); + } + else if(info->sell_from_inventory && !broker.IsItemFromInventory(client->GetPlayer()->GetCharacterID(), unique_id) && broker.IsItemForSale(client->GetPlayer()->GetCharacterID(), unique_id)) { + client->Message(CHANNEL_COLOR_RED, "You cannot change the price while selling."); + } + else if(sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4)) { + int32 plat = atoul(sep->arg[1]); + int32 gold = atoul(sep->arg[2]); + int32 silver = atoul(sep->arg[3]); + int32 copper = atoul(sep->arg[4]); + int64 price = plat * 1000000 + gold * 10000 + silver * 100 + copper; + + LogWrite(PLAYER__INFO, 5, "Broker", + "--StoreSetPrice: %u (%u), cost=%u", + client->GetPlayer()->GetCharacterID(), unique_id, price + ); + client->SetItemSaleCost(unique_id, plat, gold, silver, copper); + } + else { + client->Message(CHANNEL_COLOR_RED, "Invalid arguments for /store_set_price unique_id platinum gold silver copper."); + } + } +} +void Commands::Command_StoreSetPriceLocal(Client* client, Seperator* sep) { + if(!client->GetShopWindowStatus()) { + client->Message(CHANNEL_COLOR_RED, "Shop not available."); + return; + } + + if(sep && sep->arg[0]) { + auto info = broker.GetSellerInfo(client->GetPlayer()->GetCharacterID()); + if(!info) { + client->Message(CHANNEL_COLOR_RED, "Player %u is not in the broker database.", client->GetPlayer()->GetCharacterID()); + } + else if(info->sell_from_inventory) { + client->Message(CHANNEL_COLOR_RED, "You cannot change the price while selling."); + } + else if(sep->IsNumber(0) && sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4)) { + int64 unique_id = atoll(sep->arg[0]); + int32 plat = atoul(sep->arg[1]); + int32 gold = atoul(sep->arg[2]); + int32 silver = atoul(sep->arg[3]); + int32 copper = atoul(sep->arg[4]); + int64 price = plat * 1000000 + gold * 10000 + silver * 100 + copper; + LogWrite(PLAYER__INFO, 5, "Broker", + "--StoreSetLocalPrice: %u (%u), cost=%u", + client->GetPlayer()->GetCharacterID(), unique_id, price + ); + client->SetItemSaleCost(unique_id, plat, gold, silver, copper); + } + else { + client->Message(CHANNEL_COLOR_RED, "Invalid arguments for /store_set_price_local unique_id platinum gold silver copper."); + } + } + +} +void Commands::Command_StoreStartSelling(Client* client, Seperator* sep) { + if(!client->GetShopWindowStatus()) { + client->Message(CHANNEL_COLOR_RED, "Shop not available."); + return; + } + + broker.AddSeller(client->GetPlayer()->GetCharacterID(), std::string(client->GetPlayer()->GetName()), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), true, true); + client->OpenShopWindow(nullptr); + broker.LockActiveItemsForClient(client); +} + +void Commands::Command_StoreStopSelling(Client* client, Seperator* sep) { + if(!client->GetShopWindowStatus()) { + client->Message(CHANNEL_COLOR_RED, "Shop not available."); + return; + } + + broker.AddSeller(client->GetPlayer()->GetCharacterID(), std::string(client->GetPlayer()->GetName()), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), true, false); + client->OpenShopWindow(nullptr); + broker.LockActiveItemsForClient(client); +} + +void Commands::Command_StoreUnlistItem(Client* client, Seperator* sep) { + if(!client->GetShopWindowStatus()) { + client->Message(CHANNEL_COLOR_RED, "Shop not available."); + return; + } + if(sep && sep->arg[0]) { + auto info = broker.GetSellerInfo(client->GetPlayer()->GetCharacterID()); + if(!info) { + client->Message(CHANNEL_COLOR_RED, "Player %u is not in the broker database.", client->GetPlayer()->GetCharacterID()); + } + else if(sep->IsNumber(0)) { + int64 unique_id = atoll(sep->arg[0]); + client->SetItemSaleStatus(unique_id, false); + client->SetSellerStatus(); + client->GetPlayer()->item_list.SetVaultItemLockUniqueID(client, unique_id, false, false); + } + else { + client->Message(CHANNEL_COLOR_RED, "Invalid arguments for /store_unlist_item unique_id."); + + } + } +} + +void Commands::Command_CloseStoreKeepSelling(Client* client, Seperator* sep) { + if(!client->GetShopWindowStatus()) { + client->Message(CHANNEL_COLOR_RED, "Shop not available."); + return; + } + auto info = broker.GetSellerInfo(client->GetPlayer()->GetCharacterID()); + client->SetShopWindowStatus(false); + if(!info) { + client->Message(CHANNEL_COLOR_RED, "Player %u is not in the broker database.", client->GetPlayer()->GetCharacterID()); + } + else { + bool itemsSelling = broker.IsSellingItems(client->GetPlayer()->GetCharacterID()); + broker.AddSeller(client->GetPlayer()->GetCharacterID(), std::string(client->GetPlayer()->GetName()), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), itemsSelling, true); + } + broker.LockActiveItemsForClient(client); +} + +void Commands::Command_CancelStore(Client* client, Seperator* sep) { + if(!client->GetShopWindowStatus()) { + client->Message(CHANNEL_COLOR_RED, "Shop not available."); + return; + } + auto info = broker.GetSellerInfo(client->GetPlayer()->GetCharacterID()); + client->SetShopWindowStatus(false); + if(!info) { + client->Message(CHANNEL_COLOR_RED, "Player %u is not in the broker database.", client->GetPlayer()->GetCharacterID()); + } + else { + bool itemsSelling = broker.IsSellingItems(client->GetPlayer()->GetCharacterID(), true); + broker.AddSeller(client->GetPlayer()->GetCharacterID(), std::string(client->GetPlayer()->GetName()), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), itemsSelling, false); + } + broker.LockActiveItemsForClient(client); } \ No newline at end of file diff --git a/old/WorldServer/Commands/Commands.h b/old/WorldServer/Commands/Commands.h index c07253e..05bcec0 100644 --- a/old/WorldServer/Commands/Commands.h +++ b/old/WorldServer/Commands/Commands.h @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,16 +17,17 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #ifndef __EQ2_COMMANDS__ #define __EQ2_COMMANDS__ -#include "../../common/data_buffer.hpp" -#include "../../common/misc_functions.hpp" -#include "../../common/types.hpp" -#include "../../common/opcodes/opcode_manager.hpp" +#include "../../common/DataBuffer.h" +#include "../../common/MiscFunctions.h" +#include "../../common/types.h" +#include "../../common/opcodemgr.h" #include #include #include -#include "../../common/debug.hpp" +#include "../../common/debug.h" using namespace std; class Client; @@ -466,7 +467,16 @@ public: void Command_RaidSay(Client* client, Seperator* sep); void Command_ReloadZoneInfo(Client* client, Seperator* sep); - + void Command_SetLocationEntry(Client* client, Seperator* sep); + void Command_StoreListItem(Client* client, Seperator* sep); + void Command_StoreSetPrice(Client* client, Seperator* sep); + void Command_StoreSetPriceLocal(Client* client, Seperator* sep); + void Command_StoreStartSelling(Client* client, Seperator* sep); + void Command_StoreStopSelling(Client* client, Seperator* sep); + void Command_StoreUnlistItem(Client* client, Seperator* sep); + void Command_CloseStoreKeepSelling(Client* client, Seperator* sep); + void Command_CancelStore(Client* client, Seperator* sep); + // AA Commands void Get_AA_Xml(Client* client, Seperator* sep); void Add_AA(Client* client, Seperator* sep); @@ -981,9 +991,18 @@ private: #define CANCEL_AA_PROFILE 757 #define SAVE_AA_PROFILE 758 -#define COMMAND_MOOD 800 - - +#define COMMAND_MOOD 800 +#define COMMAND_RELOAD_PLAYERSCRIPTS 801 +#define COMMAND_SLE 802 +#define COMMAND_STORE_LIST_ITEM 803 +#define COMMAND_STORE_SET_PRICE 804 +#define COMMAND_STORE_SET_PRICE_LOCAL 805 +#define COMMAND_STORE_START_SELLING 806 +#define COMMAND_STORE_STOP_SELLING 807 +#define COMMAND_STORE_UNLIST_ITEM 808 +#define COMMAND_CLOSE_STORE_KEEP_SELLING 809 +#define COMMAND_CANCEL_STORE 810 + #define COMMAND_MODIFY 1000 // INSERT INTO `commands`(`id`,`type`,`command`,`subcommand`,`handler`,`required_status`) VALUES ( NULL,'1','modify','','1000','200'); #define COMMAND_MODIFY_CHARACTER 1001 #define COMMAND_MODIFY_FACTION 1002 diff --git a/old/WorldServer/Commands/CommandsDB.cpp b/old/WorldServer/Commands/CommandsDB.cpp index 6f1a3ae..f5ca257 100644 --- a/old/WorldServer/Commands/CommandsDB.cpp +++ b/old/WorldServer/Commands/CommandsDB.cpp @@ -1,6 +1,6 @@ -/* +/* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #ifdef WIN32 #include #include @@ -24,7 +25,7 @@ #include #include #include "../../common/Log.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "Commands.h" #include "ConsoleCommands.h" @@ -118,6 +119,9 @@ bool WorldDatabase::RemoveSpawnTemplate(int32 template_id) int32 WorldDatabase::CreateSpawnFromTemplateByID(Client* client, int32 template_id) { + if(client && client->GetCurrentZone() && client->GetCurrentZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE) { + return 0; + } Query query, query2, query3, query4, query5, query6; MYSQL_ROW row; int32 spawn_location_id = 0; diff --git a/old/WorldServer/Commands/ConsoleCommands.cpp b/old/WorldServer/Commands/ConsoleCommands.cpp index d06b1a6..b23991f 100644 --- a/old/WorldServer/Commands/ConsoleCommands.cpp +++ b/old/WorldServer/Commands/ConsoleCommands.cpp @@ -24,13 +24,13 @@ using namespace std; #include #include -#include "../../common/debug.hpp" +#include "../../common/debug.h" #include "../../common/Log.h" #include "../../common/seperator.h" #include "ConsoleCommands.h" #include "../World.h" #include "../Rules/Rules.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" extern volatile bool RunLoops; bool ContinueLoops = false; @@ -153,7 +153,7 @@ bool ConsoleBroadcastCommand(Seperator *sep) char message[4096]; snprintf(message, sizeof(message), "%s %s", "BROADCAST:", sep->argplus[1]); - zone_list.HandleGlobalBroadcast(message); + zone_list.TransmitBroadcast(message); return true; } @@ -366,7 +366,7 @@ bool ConsoleZoneCommand(Seperator *sep) printf("============================================================================================\n"); printf("| %30s | %10s | %42s |\n", "Zone", "Param", "Value"); printf("============================================================================================\n"); - printf("| %30s | %10s | %42s |\n", zone_details.zoneName, "locked", zone_details.lockState ? "true" : "false"); + printf("| %30s | %10s | %42s |\n", zone_details.zoneName.c_str(), "locked", zone_details.lockState ? "true" : "false"); } else { @@ -487,7 +487,7 @@ bool ConsoleShutdownCommand(Seperator *sep) // shutting down gracefully, warn players. char message[4096]; snprintf(message, sizeof(message), "BROADCAST: Server is shutting down in %s second(s)", sep->arg[1]); - zone_list.HandleGlobalBroadcast(message); + zone_list.TransmitBroadcast(message); Sleep(shutdown_delay * 1000); } else { diff --git a/old/WorldServer/Entity.cpp b/old/WorldServer/Entity.cpp index 01ce1b8..6838ee1 100644 --- a/old/WorldServer/Entity.cpp +++ b/old/WorldServer/Entity.cpp @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,12 +17,13 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #include "Entity.h" #include #include "Items/Items.h" #include "zoneserver.h" #include "World.h" -#include "../common/log.hpp" +#include "../common/Log.h" #include "Spells.h" #include "SpellProcess.h" #include "classes.h" @@ -31,6 +32,7 @@ #include "Skills.h" #include "Rules/Rules.h" #include "LuaInterface.h" +#include "WorldDatabase.h" extern World world; extern MasterItemList master_item_list; @@ -39,6 +41,7 @@ extern MasterSkillList master_skill_list; extern RuleManager rule_manager; extern Classes classes; extern LuaInterface* lua_interface; +extern WorldDatabase database; Entity::Entity(){ MapInfoStruct(); @@ -127,6 +130,7 @@ void Entity::DeleteSpellEffects(bool removeClient) if (IsPlayer()) GetInfoStruct()->maintained_effects[i].icon = 0xFFFF; GetInfoStruct()->maintained_effects[i].spell_id = 0xFFFFFFFF; + GetInfoStruct()->maintained_effects[i].inherited_spell_id = 0; GetInfoStruct()->maintained_effects[i].spell = nullptr; } } @@ -139,11 +143,17 @@ void Entity::DeleteSpellEffects(bool removeClient) } } GetInfoStruct()->spell_effects[i].spell_id = 0xFFFFFFFF; + GetInfoStruct()->spell_effects[i].inherited_spell_id = 0; + GetInfoStruct()->spell_effects[i].icon = 0; + GetInfoStruct()->spell_effects[i].icon_backdrop = 0; + GetInfoStruct()->spell_effects[i].tier = 0; + GetInfoStruct()->spell_effects[i].total_time = 0.0f; + GetInfoStruct()->spell_effects[i].expire_timestamp = 0; GetInfoStruct()->spell_effects[i].spell = nullptr; } } - MMaintainedSpells.releasewritelock(__FUNCTION__, __LINE__); MSpellEffects.releasewritelock(__FUNCTION__, __LINE__); + MMaintainedSpells.releasewritelock(__FUNCTION__, __LINE__); map::iterator deletedPtrItrs; for(deletedPtrItrs = deletedPtrs.begin(); deletedPtrItrs != deletedPtrs.end(); deletedPtrItrs++) { @@ -291,6 +301,7 @@ void Entity::MapInfoStruct() get_float_funcs["spell_reuse_speed"] = l::bind(&InfoStruct::get_spell_reuse_speed, &info_struct); get_float_funcs["spell_multi_attack"] = l::bind(&InfoStruct::get_spell_multi_attack, &info_struct); get_float_funcs["size_mod"] = l::bind(&InfoStruct::get_size_mod, &info_struct); + get_int8_funcs["ignore_size_mod_calc"] = l::bind(&InfoStruct::get_ignore_size_mod_calc, &info_struct); get_float_funcs["dps"] = l::bind(&InfoStruct::get_dps, &info_struct); get_float_funcs["dps_multiplier"] = l::bind(&InfoStruct::get_dps_multiplier, &info_struct); get_float_funcs["attackspeed"] = l::bind(&InfoStruct::get_attackspeed, &info_struct); @@ -311,6 +322,11 @@ void Entity::MapInfoStruct() get_int8_funcs["pet_movement"] = l::bind(&InfoStruct::get_pet_movement, &info_struct); get_int8_funcs["pet_behavior"] = l::bind(&InfoStruct::get_pet_behavior, &info_struct); get_int32_funcs["vision"] = l::bind(&InfoStruct::get_vision, &info_struct); + + get_int32_funcs["redlight"] = l::bind(&InfoStruct::get_redlight, &info_struct); + get_int32_funcs["greenlight"] = l::bind(&InfoStruct::get_greenlight, &info_struct); + get_int32_funcs["bluelight"] = l::bind(&InfoStruct::get_bluelight, &info_struct); + get_int8_funcs["breathe_underwater"] = l::bind(&InfoStruct::get_breathe_underwater, &info_struct); get_string_funcs["biography"] = l::bind(&InfoStruct::get_biography, &info_struct); get_float_funcs["drunk"] = l::bind(&InfoStruct::get_drunk, &info_struct); @@ -500,6 +516,7 @@ void Entity::MapInfoStruct() set_float_funcs["spell_reuse_speed"] = l::bind(&InfoStruct::set_spell_reuse_speed, &info_struct, l::_1); set_float_funcs["spell_multi_attack"] = l::bind(&InfoStruct::set_spell_multi_attack, &info_struct, l::_1); set_float_funcs["size_mod"] = l::bind(&InfoStruct::set_size_mod, &info_struct, l::_1); + set_int8_funcs["ignore_size_mod_calc"] = l::bind(&InfoStruct::set_ignore_size_mod_calc, &info_struct, l::_1); set_float_funcs["dps"] = l::bind(&InfoStruct::set_dps, &info_struct, l::_1); set_float_funcs["dps_multiplier"] = l::bind(&InfoStruct::set_dps_multiplier, &info_struct, l::_1); set_float_funcs["attackspeed"] = l::bind(&InfoStruct::set_attackspeed, &info_struct, l::_1); @@ -520,6 +537,11 @@ void Entity::MapInfoStruct() set_int8_funcs["pet_movement"] = l::bind(&InfoStruct::set_pet_movement, &info_struct, l::_1); set_int8_funcs["pet_behavior"] = l::bind(&InfoStruct::set_pet_behavior, &info_struct, l::_1); set_int32_funcs["vision"] = l::bind(&InfoStruct::set_vision, &info_struct, l::_1); + + set_int32_funcs["redlight"] = l::bind(&InfoStruct::set_redlight, &info_struct, l::_1); + set_int32_funcs["greenlight"] = l::bind(&InfoStruct::set_greenlight, &info_struct, l::_1); + set_int32_funcs["bluelight"] = l::bind(&InfoStruct::set_bluelight, &info_struct, l::_1); + set_int8_funcs["breathe_underwater"] = l::bind(&InfoStruct::set_breathe_underwater, &info_struct, l::_1); set_string_funcs["biography"] = l::bind(&InfoStruct::set_biography, &info_struct, l::_1); set_float_funcs["drunk"] = l::bind(&InfoStruct::set_drunk, &info_struct, l::_1); @@ -596,6 +618,65 @@ void Entity::MapInfoStruct() set_float_funcs["max_chase_distance"] = l::bind(&InfoStruct::set_max_chase_distance, &info_struct, l::_1); } +void Entity::RegisterProperty(const std::string& name) { + int32 charID = 0; + + if(IsPlayer()) + charID = ((Player*)this)->GetCharacterID(); + + { + std::shared_lock rlock(propertiesMutex); + if (set_string_funcs.find(name) != set_string_funcs.end() || + get_string_funcs.find(name) != get_string_funcs.end()) + return; + } + + { + std::unique_lock wlock(propertiesMutex); + if (set_string_funcs.find(name) != set_string_funcs.end() || + get_string_funcs.find(name) != get_string_funcs.end()) { + return; + } + + set_string_funcs.emplace(name, [this, name, database, charID](std::string v) { + { + std::lock_guard lk(GetInfoStruct()->classMutex); + GetInfoStruct()->props[name] = v; + } + if(charID) + database.insertCharacterProperty(charID, const_cast(name.c_str()), const_cast(v.c_str())); + }); + + get_string_funcs.emplace(name, [this, name]() -> std::string { + std::lock_guard lk(GetInfoStruct()->classMutex); + auto it = GetInfoStruct()->props.find(name); + return (it == GetInfoStruct()->props.end()) ? std::string{} : it->second; + }); + } +} + +void Entity::SetProperty(const std::string& name, const std::string& value) { + auto it = set_string_funcs.find(name); + if (it == set_string_funcs.end()) + return; + + int32 charID = 0; + + if(IsPlayer()) + charID = ((Player*)this)->GetCharacterID(); + + if(charID) + database.insertCharacterProperty(charID, const_cast(name.c_str()), const_cast(value.c_str())); + + return it->second(value); +} + +std::optional Entity::GetProperty(const std::string& name) const { + auto it = get_string_funcs.find(name); + if (it == get_string_funcs.end()) return std::nullopt; + return it->second(); +} + bool Entity::HasMoved(bool include_heading){ if(GetX() == last_x && GetY() == last_y && GetZ() == last_z && ((!include_heading) || (include_heading && GetHeading() == last_heading))) return false; @@ -1045,7 +1126,7 @@ void Entity::InCombat(bool val){ bool update_regen = false; if(GetInfoStruct()->get_engaged_encounter()) { - if(!IsAggroed() || !IsEngagedInEncounter()) { + if(!IsEngagedInEncounter()) { GetInfoStruct()->set_engaged_encounter(0); update_regen = true; } @@ -1082,6 +1163,11 @@ void Entity::DoRegenUpdate(){ void Entity::AddMaintainedSpell(LuaSpell* luaspell){ if (!luaspell) return; + + if(luaspell->spell->GetSpellData()->not_maintained || luaspell->spell->GetSpellData()->duration1 == 0) { + LogWrite(NPC__SPELLS, 5, "NPC", "AddMaintainedSpell Spell ID: %u, Concentration: %u disallowed, not_maintained true (%u) or duration is 0 (%u).", luaspell->spell->GetSpellData()->id, luaspell->spell->GetSpellData()->req_concentration, luaspell->spell->GetSpellData()->not_maintained, luaspell->spell->GetSpellData()->duration1); + return; + } Spell* spell = luaspell->spell; MaintainedEffects* effect = GetFreeMaintainedSpellSlot(); @@ -1107,6 +1193,9 @@ void Entity::AddSpellEffect(LuaSpell* luaspell, int32 override_expire_time){ return; Spell* spell = luaspell->spell; + if(spell->GetSpellData() && spell->GetSpellData()->icon == 0 && spell->GetSpellData()->duration1 == 0 && spell->GetSpellData()->duration2 == 0) + return; + SpellEffects* old_effect = GetSpellEffect(spell->GetSpellID(), luaspell->caster); SpellEffects* effect = 0; if (old_effect){ @@ -1167,6 +1256,7 @@ void Entity::RemoveMaintainedSpell(LuaSpell* luaspell){ if (found) { memset(&GetInfoStruct()->maintained_effects[29], 0, sizeof(MaintainedEffects)); GetInfoStruct()->maintained_effects[29].spell_id = 0xFFFFFFFF; + GetInfoStruct()->maintained_effects[29].inherited_spell_id = 0; GetInfoStruct()->maintained_effects[29].icon = 0xFFFF; GetInfoStruct()->maintained_effects[29].spell = nullptr; } @@ -1188,6 +1278,7 @@ void Entity::RemoveSpellEffect(LuaSpell* spell) { GetZone()->GetSpellProcess()->RemoveTargetFromSpell(spell, this); memset(&GetInfoStruct()->spell_effects[44], 0, sizeof(SpellEffects)); GetInfoStruct()->spell_effects[44].spell_id = 0xFFFFFFFF; + GetInfoStruct()->spell_effects[44].inherited_spell_id = 0; GetInfoStruct()->spell_effects[44].spell = nullptr; changed = true; info_changed = true; @@ -1217,12 +1308,12 @@ MaintainedEffects* Entity::GetFreeMaintainedSpellSlot(){ return ret; } -MaintainedEffects* Entity::GetMaintainedSpell(int32 spell_id){ +MaintainedEffects* Entity::GetMaintainedSpell(int32 spell_id, bool on_char_load){ MaintainedEffects* ret = 0; InfoStruct* info = GetInfoStruct(); MMaintainedSpells.readlock(__FUNCTION__, __LINE__); for (int i = 0; imaintained_effects[i].spell_id == spell_id){ + if (info->maintained_effects[i].spell_id == spell_id || (on_char_load && info->maintained_effects[i].inherited_spell_id == id)){ ret = &info->maintained_effects[i]; break; } @@ -1246,12 +1337,12 @@ SpellEffects* Entity::GetFreeSpellEffectSlot(){ return ret; } -SpellEffects* Entity::GetSpellEffect(int32 id, Entity* caster) { +SpellEffects* Entity::GetSpellEffect(int32 id, Entity* caster, bool on_char_load) { SpellEffects* ret = 0; InfoStruct* info = GetInfoStruct(); MSpellEffects.readlock(__FUNCTION__, __LINE__); for(int i = 0; i < 45; i++) { - if(info->spell_effects[i].spell_id == id) { + if(info->spell_effects[i].spell_id == id || (on_char_load && info->maintained_effects[i].inherited_spell_id == id)) { if (!caster || info->spell_effects[i].caster == caster){ ret = &info->spell_effects[i]; break; @@ -1276,7 +1367,7 @@ SpellEffects* Entity::GetSpellEffectBySpellType(int8 spell_type) { return ret; } -SpellEffects* Entity::GetSpellEffectWithLinkedTimer(int32 id, int32 linked_timer, sint32 type_group_spell_id, Entity* caster) { +SpellEffects* Entity::GetSpellEffectWithLinkedTimer(int32 id, int32 linked_timer, sint32 type_group_spell_id, Entity* caster, bool notCaster) { SpellEffects* ret = 0; InfoStruct* info = GetInfoStruct(); MSpellEffects.readlock(__FUNCTION__, __LINE__); @@ -1287,7 +1378,7 @@ SpellEffects* Entity::GetSpellEffectWithLinkedTimer(int32 id, int32 linked_timer (linked_timer > 0 && info->spell_effects[i].spell->spell->GetSpellData()->linked_timer == linked_timer) || (type_group_spell_id > 0 && info->spell_effects[i].spell->spell->GetSpellData()->type_group_spell_id == type_group_spell_id)) { - if (type_group_spell_id >= -1 && (!caster || info->spell_effects[i].caster == caster)){ + if (type_group_spell_id >= -1 && (!caster || (!notCaster && info->spell_effects[i].caster == caster) || (notCaster && info->spell_effects[i].caster != caster))){ ret = &info->spell_effects[i]; break; } @@ -1298,12 +1389,14 @@ SpellEffects* Entity::GetSpellEffectWithLinkedTimer(int32 id, int32 linked_timer return ret; } -LuaSpell* Entity::HasLinkedTimerID(LuaSpell* spell, Spawn* target, bool stackWithOtherPlayers) { +LuaSpell* Entity::HasLinkedTimerID(LuaSpell* spell, Spawn* target, bool stackWithOtherPlayers, bool checkNotCaster) { if(!spell->spell->GetSpellData()->linked_timer && !spell->spell->GetSpellData()->type_group_spell_id) return nullptr; LuaSpell* ret = nullptr; InfoStruct* info = GetInfoStruct(); - MSpellEffects.readlock(__FUNCTION__, __LINE__); + std::vector targets = spell->GetTargets(); + + MMaintainedSpells.readlock(__FUNCTION__, __LINE__); //this for loop primarily handles self checks and 'friendly' checks for(int i = 0; i < NUM_MAINTAINED_EFFECTS; i++) { if(info->maintained_effects[i].spell_id != 0xFFFFFFFF) @@ -1313,21 +1406,34 @@ LuaSpell* Entity::HasLinkedTimerID(LuaSpell* spell, Spawn* target, bool stackWit (spell->spell->GetSpellData()->type_group_spell_id > 0 && spell->spell->GetSpellData()->type_group_spell_id == info->maintained_effects[i].spell->spell->GetSpellData()->type_group_spell_id)) && ((spell->spell->GetSpellData()->friendly_spell) || (!spell->spell->GetSpellData()->friendly_spell && spell->spell->GetSpellData()->type_group_spell_id >= -1 && spell->caster == info->maintained_effects[i].spell->caster) ) && - (target == nullptr || info->maintained_effects[i].spell->initial_target == target->GetID())) { + (target == nullptr || info->maintained_effects[i].spell->HasTarget(target->GetID()) || info->maintained_effects[i].spell->HasAnyTarget(targets))) { ret = info->maintained_effects[i].spell; break; } } } - MSpellEffects.releasereadlock(__FUNCTION__, __LINE__); - - if(!ret && !stackWithOtherPlayers && target && target->IsEntity()) - { - SpellEffects* effect = ((Entity*)target)->GetSpellEffectWithLinkedTimer(spell->spell->GetSpellID(), spell->spell->GetSpellData()->linked_timer, spell->spell->GetSpellData()->type_group_spell_id, nullptr); - if(effect) - ret = effect->spell; + MMaintainedSpells.releasereadlock(__FUNCTION__, __LINE__); + + if(checkNotCaster && ret && ret->caster != spell->caster) + return ret; + else if(checkNotCaster && ret) + ret = nullptr; + + for (int32 id : spell->GetTargets()) { + Spawn* tmpTarget = spell->zone->GetSpawnByID(id); + if(!ret && !stackWithOtherPlayers && tmpTarget && tmpTarget->IsEntity()) + { + SpellEffects* effect = ((Entity*)tmpTarget)->GetSpellEffectWithLinkedTimer(spell->spell->GetSpellID(), spell->spell->GetSpellData()->linked_timer, spell->spell->GetSpellData()->type_group_spell_id, checkNotCaster ? spell->caster : nullptr, checkNotCaster); + if(effect) { + ret = effect->spell; + break; + } + } } + if(checkNotCaster && ret && ret->caster == spell->caster) + ret = nullptr; + return ret; } @@ -1462,7 +1568,9 @@ void Entity::CalculateBonuses(){ info->set_spell_multi_attack(0); float previous_size_mod = info->get_size_mod(); - info->set_size_mod(0.0f); + + if(!info->get_ignore_size_mod_calc()) + info->set_size_mod(0.0f); info->set_dps(0); info->set_dps_multiplier(0); info->set_haste(0); @@ -1548,7 +1656,8 @@ void Entity::CalculateBonuses(){ info->add_recovery_speed(values->abilityrecoveryspeed); info->add_spell_reuse_speed(values->spellreusespeed); info->add_spell_multi_attack(values->spellmultiattackchance); - info->add_size_mod(values->size_mod); + if(!info->get_ignore_size_mod_calc()) + info->add_size_mod(values->size_mod); info->add_dps(values->dps); info->add_dps_multiplier(CalculateDPSMultiplier()); info->add_haste(values->attackspeed); @@ -1807,15 +1916,15 @@ EquipmentItemList* Entity::GetAppearanceEquipmentList(){ } void Entity::SetEquipment(Item* item, int8 slot){ - std::lock_guard lk(MEquipment); + MEquipment.lock(); if(!item && slot < NUM_SLOTS){ - SetInfo(&equipment.equip_id[slot], 0); - SetInfo(&equipment.color[slot].red, 0); - SetInfo(&equipment.color[slot].green, 0); - SetInfo(&equipment.color[slot].blue, 0); - SetInfo(&equipment.highlight[slot].red, 0); - SetInfo(&equipment.highlight[slot].green, 0); - SetInfo(&equipment.highlight[slot].blue, 0); + SetInfo(&equipment.equip_id[slot], 0, false); + SetInfo(&equipment.color[slot].red, 0, false); + SetInfo(&equipment.color[slot].green, 0, false); + SetInfo(&equipment.color[slot].blue, 0, false); + SetInfo(&equipment.highlight[slot].red, 0, false); + SetInfo(&equipment.highlight[slot].green, 0, false); + SetInfo(&equipment.highlight[slot].blue, 0, false); } else{ if ( slot >= NUM_SLOTS ) @@ -1824,14 +1933,17 @@ void Entity::SetEquipment(Item* item, int8 slot){ if( slot >= NUM_SLOTS ) return; - SetInfo(&equipment.equip_id[slot], item->generic_info.appearance_id); - SetInfo(&equipment.color[slot].red, item->generic_info.appearance_red); - SetInfo(&equipment.color[slot].green, item->generic_info.appearance_green); - SetInfo(&equipment.color[slot].blue, item->generic_info.appearance_blue); - SetInfo(&equipment.highlight[slot].red, item->generic_info.appearance_highlight_red); - SetInfo(&equipment.highlight[slot].green, item->generic_info.appearance_highlight_green); - SetInfo(&equipment.highlight[slot].blue, item->generic_info.appearance_highlight_blue); + SetInfo(&equipment.equip_id[slot], item->generic_info.appearance_id, false); + SetInfo(&equipment.color[slot].red, item->generic_info.appearance_red, false); + SetInfo(&equipment.color[slot].green, item->generic_info.appearance_green, false); + SetInfo(&equipment.color[slot].blue, item->generic_info.appearance_blue, false); + SetInfo(&equipment.highlight[slot].red, item->generic_info.appearance_highlight_red, false); + SetInfo(&equipment.highlight[slot].green, item->generic_info.appearance_highlight_green, false); + SetInfo(&equipment.highlight[slot].blue, item->generic_info.appearance_highlight_blue, false); } + MEquipment.unlock(); + info_changed = true; + AddChangedZoneSpawn(); } bool Entity::CheckSpellBonusRemoval(LuaSpell* spell, int16 type){ @@ -2259,161 +2371,191 @@ void Entity::RemoveWard(int32 spellID) { } } -int32 Entity::CheckWards(Entity* attacker, int32 damage, int8 damage_type) { +void Entity::RemoveWard(LuaSpell* spell) { std::unique_lock lock(MWardList); map::iterator itr; - WardInfo* ward = 0; - LuaSpell* spell = 0; - - while (m_wardList.size() > 0 && damage > 0) { - // Get the ward with the lowest base damage - for (itr = m_wardList.begin(); itr != m_wardList.end(); itr++) { - if(itr->second->RoundTriggered || itr->second->DeleteWard) - continue; - - if (!ward || itr->second->BaseDamage < ward->BaseDamage) { - if ((itr->second->AbsorbAllDamage || itr->second->DamageLeft > 0) && - (itr->second->WardType == WARD_TYPE_ALL || - (itr->second->WardType == WARD_TYPE_PHYSICAL && damage_type >= DAMAGE_PACKET_DAMAGE_TYPE_SLASH && damage_type <= DAMAGE_PACKET_DAMAGE_TYPE_PIERCE) || - (itr->second->WardType == WARD_TYPE_MAGICAL && ((itr->second->DamageType == 0 && damage_type >= DAMAGE_PACKET_DAMAGE_TYPE_PIERCE) || (damage_type >= DAMAGE_PACKET_DAMAGE_TYPE_PIERCE && itr->second->DamageType == damage_type))))) - ward = itr->second; - } - } - - if (!ward) - break; - - spell = ward->Spell; - - // damage to redirect at the source (like intercept) - int32 redirectDamage = 0; - if (ward->RedirectDamagePercent) - redirectDamage = (int32)(double)damage * ((double)ward->RedirectDamagePercent / 100.0); - - // percentage the spell absorbs of all possible damage - int32 damageToAbsorb = 0; - if (ward->DamageAbsorptionPercentage > 0) - damageToAbsorb = (int32)(double)damage * ((double)ward->DamageAbsorptionPercentage/100.0); - else - damageToAbsorb = damage; - - int32 maxDamageAbsorptionAllowed = 0; - - // spells like Divine Aura have caps on health, eg. anything more than 50% damage is not absorbed - if (ward->DamageAbsorptionMaxHealthPercent > 0) - maxDamageAbsorptionAllowed = (int32)(double)GetTotalHP() * ((double)ward->DamageAbsorptionMaxHealthPercent / 100.0); - - if (maxDamageAbsorptionAllowed > 0 && damageToAbsorb >= maxDamageAbsorptionAllowed) - damageToAbsorb = 0; // its over or equal to 50% of the total hp allowed, thus this damage is not absorbed - - int32 baseDamageRemaining = damage - damageToAbsorb; - - bool hasSpellBeenRemoved = false; - if (ward->AbsorbAllDamage) - { - ward->LastAbsorbedDamage = ward->DamageLeft; - - if (!redirectDamage) - GetZone()->SendHealPacket(ward->Spell->caster, this, HEAL_PACKET_TYPE_ABSORB, damage, spell->spell->GetName()); - - damage = 0; - } - else if (damageToAbsorb >= ward->DamageLeft) { - // Damage is greater than or equal to the amount left on the ward - - ward->LastAbsorbedDamage = ward->DamageLeft; - // remove what damage we can absorb - damageToAbsorb -= ward->DamageLeft; - - // move back what couldn't be absorbed to the base dmg and apply to the overall damage - baseDamageRemaining += damageToAbsorb; - damage = baseDamageRemaining; - ward->DamageLeft = 0; - spell->damage_remaining = 0; - - if(!redirectDamage) - GetZone()->SendHealPacket(spell->caster, this, HEAL_PACKET_TYPE_ABSORB, ward->DamageLeft, spell->spell->GetName()); - - if (!ward->keepWard) { - ward->DeleteWard = true; - hasSpellBeenRemoved = true; - } - } - else { - ward->LastAbsorbedDamage = damageToAbsorb; - // Damage is less then the amount left on the ward - ward->DamageLeft -= damageToAbsorb; - - spell->damage_remaining = ward->DamageLeft; - if (spell->caster->IsPlayer()) - ClientPacketFunctions::SendMaintainedExamineUpdate(((Player*)spell->caster)->GetClient(), spell->slot_pos, ward->DamageLeft, 1); - - if (!redirectDamage) - GetZone()->SendHealPacket(ward->Spell->caster, this, HEAL_PACKET_TYPE_ABSORB, damage, spell->spell->GetName()); - - // remaining damage not absorbed by percentage must be set - damage = baseDamageRemaining; - } - - if (redirectDamage) - { - ward->LastRedirectDamage = redirectDamage; - if (this->IsPlayer()) - { - Client* client = this->GetClient(); - if(client) { - client->Message(CHANNEL_COMBAT, "%s intercepted some of the damage intended for you!", spell->caster->GetName()); - } - } - if (spell->caster && spell->caster->IsPlayer()) - { - Client* client = ((Player*)spell->caster)->GetClient(); - if(client) { - client->Message(CHANNEL_COMBAT, "YOU intercept some of the damage intended for %s!", this->GetName()); - } - } - - if (attacker && spell->caster) - attacker->DamageSpawn(spell->caster, DAMAGE_PACKET_TYPE_SPELL_DAMAGE, damage_type, redirectDamage, redirectDamage, 0, 0, false, false, false, false, spell); - } - - bool shouldRemoveSpell = false; - ward->HitCount++; // increment hit count - ward->RoundTriggered = true; + for (itr = m_wardList.begin(); itr != m_wardList.end(); itr++) { + if(itr->second->DeleteWard) + continue; - if (ward->MaxHitCount && spell->num_triggers && spell->caster->GetZone()) - { - spell->num_triggers--; - if(spell->caster->IsPlayer()) { - ClientPacketFunctions::SendMaintainedExamineUpdate(((Player*)spell->caster)->GetClient(), spell->slot_pos, spell->num_triggers, 0); - } + if(itr->second->Spell == spell) { + itr->second->DeleteWard = true; } - - if (ward->MaxHitCount && ward->HitCount >= ward->MaxHitCount) // there isn't a max hit requirement with the hit count, so just go based on hit count - shouldRemoveSpell = true; - - if (shouldRemoveSpell && !hasSpellBeenRemoved) - { - ward->DeleteWard = true; - } - - // Reset ward pointer - ward = 0; } - + for (itr = m_wardList.begin(); itr != m_wardList.end();) { if(itr->second->DeleteWard) { WardInfo* info = itr->second; itr = m_wardList.erase(itr); - GetZone()->GetSpellProcess()->DeleteCasterSpell(info->Spell, "purged"); safe_delete(info); } else { - itr->second->RoundTriggered = false; itr++; } } +} +int32 Entity::CheckWards(Entity* attacker, int32 damage, int8 damage_type) { + map::iterator itr; + WardInfo* ward = 0; + LuaSpell* spell = 0; + + vector tmp_deletes; + { + std::unique_lock lock(MWardList); + while (m_wardList.size() > 0 && damage > 0) { + // Get the ward with the lowest base damage + for (itr = m_wardList.begin(); itr != m_wardList.end(); itr++) { + if(itr->second->RoundTriggered || itr->second->DeleteWard) + continue; + + if (!ward || itr->second->BaseDamage < ward->BaseDamage) { + if ((itr->second->AbsorbAllDamage || itr->second->DamageLeft > 0) && + (itr->second->WardType == WARD_TYPE_ALL || + (itr->second->WardType == WARD_TYPE_PHYSICAL && damage_type >= DAMAGE_PACKET_DAMAGE_TYPE_SLASH && damage_type <= DAMAGE_PACKET_DAMAGE_TYPE_PIERCE) || + (itr->second->WardType == WARD_TYPE_MAGICAL && ((itr->second->DamageType == 0 && damage_type >= DAMAGE_PACKET_DAMAGE_TYPE_PIERCE) || (damage_type >= DAMAGE_PACKET_DAMAGE_TYPE_PIERCE && itr->second->DamageType == damage_type))))) + ward = itr->second; + } + } + + if (!ward) + break; + + spell = ward->Spell; + + // damage to redirect at the source (like intercept) + int32 redirectDamage = 0; + if (ward->RedirectDamagePercent) + redirectDamage = (int32)(double)damage * ((double)ward->RedirectDamagePercent / 100.0); + + // percentage the spell absorbs of all possible damage + int32 damageToAbsorb = 0; + if (ward->DamageAbsorptionPercentage > 0) + damageToAbsorb = (int32)(double)damage * ((double)ward->DamageAbsorptionPercentage/100.0); + else + damageToAbsorb = damage; + + int32 maxDamageAbsorptionAllowed = 0; + + // spells like Divine Aura have caps on health, eg. anything more than 50% damage is not absorbed + if (ward->DamageAbsorptionMaxHealthPercent > 0) + maxDamageAbsorptionAllowed = (int32)(double)GetTotalHP() * ((double)ward->DamageAbsorptionMaxHealthPercent / 100.0); + + if (maxDamageAbsorptionAllowed > 0 && damageToAbsorb >= maxDamageAbsorptionAllowed) + damageToAbsorb = 0; // its over or equal to 50% of the total hp allowed, thus this damage is not absorbed + + int32 baseDamageRemaining = damage - damageToAbsorb; + + bool hasSpellBeenRemoved = false; + if (ward->AbsorbAllDamage) + { + ward->LastAbsorbedDamage = ward->DamageLeft; + + if (!redirectDamage) + GetZone()->SendHealPacket(ward->Spell->caster, this, HEAL_PACKET_TYPE_ABSORB, damage, spell->spell->GetName()); + + damage = 0; + } + else if (damageToAbsorb >= ward->DamageLeft) { + // Damage is greater than or equal to the amount left on the ward + + ward->LastAbsorbedDamage = ward->DamageLeft; + // remove what damage we can absorb + damageToAbsorb -= ward->DamageLeft; + + // move back what couldn't be absorbed to the base dmg and apply to the overall damage + baseDamageRemaining += damageToAbsorb; + damage = baseDamageRemaining; + ward->DamageLeft = 0; + spell->damage_remaining = 0; + + if(!redirectDamage) + GetZone()->SendHealPacket(spell->caster, this, HEAL_PACKET_TYPE_ABSORB, ward->DamageLeft, spell->spell->GetName()); + + if (!ward->keepWard) { + ward->DeleteWard = true; + hasSpellBeenRemoved = true; + } + } + else { + ward->LastAbsorbedDamage = damageToAbsorb; + // Damage is less then the amount left on the ward + ward->DamageLeft -= damageToAbsorb; + + spell->damage_remaining = ward->DamageLeft; + if (spell->caster->IsPlayer()) + ClientPacketFunctions::SendMaintainedExamineUpdate(((Player*)spell->caster)->GetClient(), spell->slot_pos, ward->DamageLeft, 1); + + if (!redirectDamage) + GetZone()->SendHealPacket(ward->Spell->caster, this, HEAL_PACKET_TYPE_ABSORB, damage, spell->spell->GetName()); + + // remaining damage not absorbed by percentage must be set + damage = baseDamageRemaining; + } + + if (redirectDamage) + { + ward->LastRedirectDamage = redirectDamage; + if (this->IsPlayer()) + { + Client* client = this->GetClient(); + if(client) { + client->Message(CHANNEL_COMBAT, "%s intercepted some of the damage intended for you!", spell->caster->GetName()); + } + } + if (spell->caster && spell->caster->IsPlayer()) + { + Client* client = ((Player*)spell->caster)->GetClient(); + if(client) { + client->Message(CHANNEL_COMBAT, "YOU intercept some of the damage intended for %s!", this->GetName()); + } + } + + if (attacker && spell->caster) + attacker->DamageSpawn(spell->caster, DAMAGE_PACKET_TYPE_SPELL_DAMAGE, damage_type, redirectDamage, redirectDamage, 0, 0, false, false, false, false, spell, true); + } + + bool shouldRemoveSpell = false; + ward->HitCount++; // increment hit count + ward->RoundTriggered = true; + + if (ward->MaxHitCount && spell->num_triggers && spell->caster->GetZone()) + { + spell->num_triggers--; + if(spell->caster->IsPlayer()) { + ClientPacketFunctions::SendMaintainedExamineUpdate(((Player*)spell->caster)->GetClient(), spell->slot_pos, spell->num_triggers, 0); + } + } + + if (ward->MaxHitCount && ward->HitCount >= ward->MaxHitCount) // there isn't a max hit requirement with the hit count, so just go based on hit count + shouldRemoveSpell = true; + + if (shouldRemoveSpell && !hasSpellBeenRemoved) + { + ward->DeleteWard = true; + } + + // Reset ward pointer + ward = 0; + } + + for (itr = m_wardList.begin(); itr != m_wardList.end();) { + if(itr->second->DeleteWard) { + WardInfo* info = itr->second; + itr = m_wardList.erase(itr); + tmp_deletes.push_back(info->Spell); + safe_delete(info); + } + else { + itr->second->RoundTriggered = false; + itr++; + } + } + } + + for (auto it = tmp_deletes.begin(); it != tmp_deletes.end(); ++it) { + GetZone()->GetSpellProcess()->DeleteCasterSpell(*it, "purged"); + } return damage; } @@ -2458,7 +2600,18 @@ float Entity::GetSpeed() { ret += stats[ITEM_STAT_OFFENSIVESPEED]; } } - + + if (stats.count(ITEM_STAT_SLOW)) { + float slowPct = stats[ITEM_STAT_SLOW]; + if(slowPct > 99.9f) { + ret = 0.0f; + } + else { + float slow_multiplier = 1.0f - (slowPct / 100.0f); + ret = ret * slow_multiplier; + } + } + MStats.unlock(); ret *= speed_multiplier; return ret; @@ -2703,6 +2856,7 @@ void Entity::AddDetrimentalSpell(LuaSpell* luaspell, int32 override_expire_times new_det.det_type = data->det_type; new_det.incurable = data->incurable; new_det.spell_id = spell->GetSpellID(); + new_det.inherited_spell_id = data->inherited_spell_id; new_det.control_effect = data->control_effect_type; new_det.total_time = spell->GetSpellDuration()/10; @@ -3498,7 +3652,7 @@ void Entity::UpdateGroupMemberInfo(bool inGroupMgrLock, bool groupMembersLocked) world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__); } -#include "WorldDatabase.hpp" +#include "WorldDatabase.h" extern WorldDatabase database; void Entity::CustomizeAppearance(PacketStruct* packet) { @@ -3687,251 +3841,272 @@ void Entity::HaltMovement() std::string Entity::GetInfoStructString(std::string field) { - map>::const_iterator itr = get_string_funcs.find(field); - if(itr != get_string_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_string_funcs.find(field); + if(itr != get_string_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return std::string(""); } int8 Entity::GetInfoStructInt8(std::string field) { - map>::const_iterator itr = get_int8_funcs.find(field); - if(itr != get_int8_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_int8_funcs.find(field); + if(itr != get_int8_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0; } int16 Entity::GetInfoStructInt16(std::string field) { - map>::const_iterator itr = get_int16_funcs.find(field); - if(itr != get_int16_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_int16_funcs.find(field); + if(itr != get_int16_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0; } int32 Entity::GetInfoStructInt32(std::string field) { - map>::const_iterator itr = get_int32_funcs.find(field); - if(itr != get_int32_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_int32_funcs.find(field); + if(itr != get_int32_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0; } int64 Entity::GetInfoStructInt64(std::string field) { - map>::const_iterator itr = get_int64_funcs.find(field); - if(itr != get_int64_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_int64_funcs.find(field); + if(itr != get_int64_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0; } sint8 Entity::GetInfoStructSInt8(std::string field) { - map>::const_iterator itr = get_sint8_funcs.find(field); - if(itr != get_sint8_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_sint8_funcs.find(field); + if(itr != get_sint8_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0; } sint16 Entity::GetInfoStructSInt16(std::string field) { - map>::const_iterator itr = get_sint16_funcs.find(field); - if(itr != get_sint16_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_sint16_funcs.find(field); + if(itr != get_sint16_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0; } sint32 Entity::GetInfoStructSInt32(std::string field) { - map>::const_iterator itr = get_sint32_funcs.find(field); - if(itr != get_sint32_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_sint32_funcs.find(field); + if(itr != get_sint32_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0; } sint64 Entity::GetInfoStructSInt64(std::string field) { - map>::const_iterator itr = get_sint64_funcs.find(field); - if(itr != get_sint64_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_sint64_funcs.find(field); + if(itr != get_sint64_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0; } float Entity::GetInfoStructFloat(std::string field) { - map>::const_iterator itr = get_float_funcs.find(field); - if(itr != get_float_funcs.end()) - { - auto func = (itr->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_float_funcs.find(field); + if(itr != get_float_funcs.end()) + { + auto func = (itr->second)(); + return func; + } return 0.0f; } int64 Entity::GetInfoStructUInt(std::string field) { - map>::const_iterator itr = get_int8_funcs.find(field); - if(itr != get_int8_funcs.end()) - { - auto func = (itr->second)(); - return func; - } - map>::const_iterator itr2 = get_int16_funcs.find(field); - if(itr2 != get_int16_funcs.end()) - { - auto func = (itr2->second)(); - return func; - } - map>::const_iterator itr3 = get_int32_funcs.find(field); - if(itr3 != get_int32_funcs.end()) - { - auto func = (itr3->second)(); - return func; - } - map>::const_iterator itr4 = get_int64_funcs.find(field); - if(itr4 != get_int64_funcs.end()) - { - auto func = (itr4->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_int8_funcs.find(field); + if(itr != get_int8_funcs.end()) + { + auto func = (itr->second)(); + return func; + } + map>::const_iterator itr2 = get_int16_funcs.find(field); + if(itr2 != get_int16_funcs.end()) + { + auto func = (itr2->second)(); + return func; + } + map>::const_iterator itr3 = get_int32_funcs.find(field); + if(itr3 != get_int32_funcs.end()) + { + auto func = (itr3->second)(); + return func; + } + map>::const_iterator itr4 = get_int64_funcs.find(field); + if(itr4 != get_int64_funcs.end()) + { + auto func = (itr4->second)(); + return func; + } return 0; } sint64 Entity::GetInfoStructSInt(std::string field) { - map>::const_iterator itr = get_sint8_funcs.find(field); - if(itr != get_sint8_funcs.end()) - { - auto func = (itr->second)(); - return func; - } - map>::const_iterator itr2 = get_sint16_funcs.find(field); - if(itr2 != get_sint16_funcs.end()) - { - auto func = (itr2->second)(); - return func; - } - map>::const_iterator itr3 = get_sint32_funcs.find(field); - if(itr3 != get_sint32_funcs.end()) - { - auto func = (itr3->second)(); - return func; - } - map>::const_iterator itr4 = get_sint64_funcs.find(field); - if(itr4 != get_sint64_funcs.end()) - { - auto func = (itr4->second)(); - return func; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = get_sint8_funcs.find(field); + if(itr != get_sint8_funcs.end()) + { + auto func = (itr->second)(); + return func; + } + map>::const_iterator itr2 = get_sint16_funcs.find(field); + if(itr2 != get_sint16_funcs.end()) + { + auto func = (itr2->second)(); + return func; + } + map>::const_iterator itr3 = get_sint32_funcs.find(field); + if(itr3 != get_sint32_funcs.end()) + { + auto func = (itr3->second)(); + return func; + } + map>::const_iterator itr4 = get_sint64_funcs.find(field); + if(itr4 != get_sint64_funcs.end()) + { + auto func = (itr4->second)(); + return func; + } return 0; } bool Entity::SetInfoStructString(std::string field, std::string value) { + { + std::shared_lock rlock(propertiesMutex); map>::const_iterator itr = set_string_funcs.find(field); if(itr != set_string_funcs.end()) { (itr->second)(value); return true; } - return false; + } + + RegisterProperty(field); + SetProperty(field, value); + return true; } bool Entity::SetInfoStructUInt(std::string field, int64 value) { - map>::const_iterator itr = set_int8_funcs.find(field); - if(itr != set_int8_funcs.end()) - { - (itr->second)((int8)value); - return true; - } - map>::const_iterator itr2 = set_int16_funcs.find(field); - if(itr2 != set_int16_funcs.end()) - { - (itr2->second)((int16)value); - return true; - } - map>::const_iterator itr3 = set_int32_funcs.find(field); - if(itr3 != set_int32_funcs.end()) - { - (itr3->second)((int32)value); - return true; - } - map>::const_iterator itr4 = set_int64_funcs.find(field); - if(itr4 != set_int64_funcs.end()) - { - (itr4->second)(value); - return true; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = set_int8_funcs.find(field); + if(itr != set_int8_funcs.end()) + { + (itr->second)((int8)value); + return true; + } + map>::const_iterator itr2 = set_int16_funcs.find(field); + if(itr2 != set_int16_funcs.end()) + { + (itr2->second)((int16)value); + return true; + } + map>::const_iterator itr3 = set_int32_funcs.find(field); + if(itr3 != set_int32_funcs.end()) + { + (itr3->second)((int32)value); + return true; + } + map>::const_iterator itr4 = set_int64_funcs.find(field); + if(itr4 != set_int64_funcs.end()) + { + (itr4->second)(value); + return true; + } return false; } bool Entity::SetInfoStructSInt(std::string field, sint64 value) { - map>::const_iterator itr = set_sint8_funcs.find(field); - if(itr != set_sint8_funcs.end()) - { - (itr->second)((sint8)value); - return true; - } - map>::const_iterator itr2 = set_sint16_funcs.find(field); - if(itr2 != set_sint16_funcs.end()) - { - (itr2->second)((sint16)value); - return true; - } - map>::const_iterator itr3 = set_sint32_funcs.find(field); - if(itr3 != set_sint32_funcs.end()) - { - (itr3->second)((sint32)value); - return true; - } - map>::const_iterator itr4 = set_sint64_funcs.find(field); - if(itr4 != set_sint64_funcs.end()) - { - (itr4->second)(value); - return true; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = set_sint8_funcs.find(field); + if(itr != set_sint8_funcs.end()) + { + (itr->second)((sint8)value); + return true; + } + map>::const_iterator itr2 = set_sint16_funcs.find(field); + if(itr2 != set_sint16_funcs.end()) + { + (itr2->second)((sint16)value); + return true; + } + map>::const_iterator itr3 = set_sint32_funcs.find(field); + if(itr3 != set_sint32_funcs.end()) + { + (itr3->second)((sint32)value); + return true; + } + map>::const_iterator itr4 = set_sint64_funcs.find(field); + if(itr4 != set_sint64_funcs.end()) + { + (itr4->second)(value); + return true; + } return false; } bool Entity::SetInfoStructFloat(std::string field, float value) { - map>::const_iterator itr = set_float_funcs.find(field); - if(itr != set_float_funcs.end()) - { - (itr->second)(value); - return true; - } + std::shared_lock rlock(propertiesMutex); + map>::const_iterator itr = set_float_funcs.find(field); + if(itr != set_float_funcs.end()) + { + (itr->second)(value); + return true; + } return false; } @@ -4045,4 +4220,20 @@ void Entity::CalculateMaxReduction() { } GetInfoStruct()->set_max_spell_reduction(maxReduction); +} + +bool Entity::IsAggroed() { + return (bool)GetInfoStruct()->get_engaged_encounter(); +} + +void Entity::SendHatedByList(Client* client) { + set::iterator itr; + MHatedBy.lock(); + set hatedByCopy(HatedBy); + MHatedBy.unlock(); + client->Message(CHANNEL_COLOR_RED, "HatedBy List for %s, size: %u", GetName(), hatedByCopy.size()); + for (itr = hatedByCopy.begin(); itr != hatedByCopy.end(); itr++) { + Spawn* spawn = GetZone()->GetSpawnByID(*itr); + client->Message(CHANNEL_COLOR_YELLOW, "ID: %u, Name: %s", *itr, spawn ? spawn->GetName() : "N/A"); + } } \ No newline at end of file diff --git a/old/WorldServer/Entity.h b/old/WorldServer/Entity.h index eb9f3e9..c0dccf9 100644 --- a/old/WorldServer/Entity.h +++ b/old/WorldServer/Entity.h @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -20,6 +20,7 @@ #ifndef __EQ2_ENTITY__ #define __EQ2_ENTITY__ #include "Spawn.h" +#include "../common/Mutex.h" #include "Skills.h" #include "MutexList.h" #include "MutexVector.h" @@ -27,6 +28,8 @@ #include #include #include +#include +#include #include #include @@ -54,6 +57,7 @@ struct MaintainedEffects{ int32 target; int8 target_type; int32 spell_id; + int32 inherited_spell_id; int32 slot_pos; int16 icon; int16 icon_backdrop; @@ -66,6 +70,7 @@ struct MaintainedEffects{ struct SpellEffects{ int32 spell_id; + int32 inherited_spell_id; Entity* caster; float total_time; int32 expire_timestamp; @@ -77,6 +82,7 @@ struct SpellEffects{ struct DetrimentalEffects { int32 spell_id; + int32 inherited_spell_id; Entity* caster; int32 expire_timestamp; int16 icon; @@ -209,6 +215,8 @@ struct InfoStruct{ recovery_speed_ = 0; spell_reuse_speed_ = 0; spell_multi_attack_ = 0; + size_mod_ = 0.0f; + ignore_size_mod_calc_ = 0; dps_ = 0; dps_multiplier_ = 0; attackspeed_ = 0; @@ -229,6 +237,11 @@ struct InfoStruct{ pet_movement_ = 0; pet_behavior_ = 0; vision_ = 0; + + redlight_ = 0; + greenlight_ = 0; + bluelight_ = 0; + breathe_underwater_ = 0; biography_ = std::string(""); drunk_ = 0; @@ -419,6 +432,10 @@ struct InfoStruct{ spell_reuse_speed_ = oldStruct->get_spell_reuse_speed(); spell_multi_attack_ = oldStruct->get_spell_multi_attack(); dps_ = oldStruct->get_dps(); + + size_mod_ = oldStruct->get_size_mod(); + ignore_size_mod_calc_ = oldStruct->get_ignore_size_mod_calc(); + dps_multiplier_ = oldStruct->get_dps_multiplier(); attackspeed_ = oldStruct->get_attackspeed(); haste_ = oldStruct->get_haste(); @@ -438,6 +455,11 @@ struct InfoStruct{ pet_movement_ = oldStruct->get_pet_movement(); pet_behavior_ = oldStruct->get_pet_behavior(); vision_ = oldStruct->get_vision(); + + redlight_ = oldStruct->get_redlight(); + greenlight_ = oldStruct->get_greenlight(); + bluelight_ = oldStruct->get_bluelight(); + breathe_underwater_ = oldStruct->get_breathe_underwater(); biography_ = std::string(oldStruct->get_biography()); drunk_ = oldStruct->get_drunk(); @@ -636,6 +658,7 @@ struct InfoStruct{ float get_spell_reuse_speed() { std::lock_guard lk(classMutex); return spell_reuse_speed_; } float get_spell_multi_attack() { std::lock_guard lk(classMutex); return spell_multi_attack_; } float get_size_mod() { std::lock_guard lk(classMutex); return size_mod_; } + int8 get_ignore_size_mod_calc() { std::lock_guard lk(classMutex); return ignore_size_mod_calc_; } float get_dps() { std::lock_guard lk(classMutex); return dps_; } float get_dps_multiplier() { std::lock_guard lk(classMutex); return dps_multiplier_; } float get_attackspeed() { std::lock_guard lk(classMutex); return attackspeed_; } @@ -657,6 +680,11 @@ struct InfoStruct{ int8 get_pet_movement() { std::lock_guard lk(classMutex); return pet_movement_; } int8 get_pet_behavior() { std::lock_guard lk(classMutex); return pet_behavior_; } int32 get_vision() { std::lock_guard lk(classMutex); return vision_; } + + int32 get_redlight() { std::lock_guard lk(classMutex); return redlight_; } + int32 get_greenlight() { std::lock_guard lk(classMutex); return greenlight_; } + int32 get_bluelight() { std::lock_guard lk(classMutex); return bluelight_; } + int8 get_breathe_underwater() { std::lock_guard lk(classMutex); return breathe_underwater_; } std::string get_biography() { std::lock_guard lk(classMutex); return biography_; } float get_drunk() { std::lock_guard lk(classMutex); return drunk_; } @@ -916,6 +944,7 @@ struct InfoStruct{ void set_spell_reuse_speed(float value) { std::lock_guard lk(classMutex); spell_reuse_speed_ = value; } void set_spell_multi_attack(float value) { std::lock_guard lk(classMutex); spell_multi_attack_ = value; } void set_size_mod(float value) { std::lock_guard lk(classMutex); size_mod_ = value; } + void set_ignore_size_mod_calc(int8 value) { std::lock_guard lk(classMutex); ignore_size_mod_calc_ = value; } void set_dps(float value) { std::lock_guard lk(classMutex); dps_ = value; } void set_dps_multiplier(float value) { std::lock_guard lk(classMutex); dps_multiplier_ = value; } void set_attackspeed(float value) { std::lock_guard lk(classMutex); attackspeed_ = value; } @@ -975,6 +1004,11 @@ struct InfoStruct{ void set_max_weight(int32 value) { std::lock_guard lk(classMutex); max_weight_ = value; } void set_vision(int32 value) { std::lock_guard lk(classMutex); vision_ = value; } + + void set_redlight(int32 value) { std::lock_guard lk(classMutex); redlight_ = value; } + void set_greenlight(int32 value) { std::lock_guard lk(classMutex); greenlight_ = value; } + void set_bluelight(int32 value) { std::lock_guard lk(classMutex); bluelight_ = value; } + void set_breathe_underwater(int8 value) { std::lock_guard lk(classMutex); breathe_underwater_ = value; } void set_drunk(float value) { std::lock_guard lk(classMutex); drunk_ = value; } @@ -1058,12 +1092,19 @@ struct InfoStruct{ for(int i=0;i<45;i++){ if(i<30){ maintained_effects[i].spell_id = 0xFFFFFFFF; + maintained_effects[i].inherited_spell_id = 0; if (spawn->IsPlayer()) maintained_effects[i].icon = 0xFFFF; maintained_effects[i].spell = nullptr; } + spell_effects[i].icon = 0; spell_effects[i].spell_id = 0xFFFFFFFF; + spell_effects[i].inherited_spell_id = 0; + spell_effects[i].icon_backdrop = 0; + spell_effects[i].tier = 0; + spell_effects[i].total_time = 0.0f; + spell_effects[i].expire_timestamp = 0; spell_effects[i].spell = nullptr; } } @@ -1071,6 +1112,9 @@ struct InfoStruct{ // maintained via their own mutex SpellEffects spell_effects[45]; MaintainedEffects maintained_effects[30]; + // when PacketStruct is fixed for C++17 this should become a shared_mutex and handle read/write lock + std::mutex classMutex; + std::unordered_map props; private: std::string name_; int8 class1_; @@ -1190,6 +1234,7 @@ private: float spell_reuse_speed_; float spell_multi_attack_; float size_mod_; + int8 ignore_size_mod_calc_; float dps_; float dps_multiplier_; float attackspeed_; @@ -1212,6 +1257,11 @@ private: int8 pet_behavior_; int32 vision_; + + int32 redlight_; + int32 greenlight_; + int32 bluelight_; + int8 breathe_underwater_; std::string biography_; float drunk_; @@ -1283,8 +1333,6 @@ private: int8 max_spell_reduction_override_; float max_chase_distance_; - // when PacketStruct is fixed for C++17 this should become a shared_mutex and handle read/write lock - std::mutex classMutex; }; struct WardInfo { @@ -1347,6 +1395,8 @@ struct Proc { #define PROC_TYPE_DAMAGED_MAGIC 17 #define PROC_TYPE_RANGED_ATTACK 18 #define PROC_TYPE_RANGED_DEFENSE 19 +#define PROC_TYPE_PHYSICAL_ATTEMPT 20 +#define PROC_TYPE_MAGICAL_ATTEMPT 21 struct ThreatTransfer { int32 Target; @@ -1402,6 +1452,9 @@ public: void DeleteSpellEffects(bool removeClient = false); void RemoveSpells(bool unfriendlyOnly = false); void MapInfoStruct(); + void RegisterProperty(const std::string& name); + void SetProperty(const std::string& name, const std::string& value); + std::optional GetProperty(const std::string& name) const; virtual float GetDodgeChance(); virtual void AddMaintainedSpell(LuaSpell* spell); virtual void AddSpellEffect(LuaSpell* spell, int32 override_expire_time = 0); @@ -1410,7 +1463,7 @@ public: virtual void AddSkillBonus(int32 spell_id, int32 skill_id, float value); void AddDetrimentalSpell(LuaSpell* spell, int32 override_expire_timestamp = 0); DetrimentalEffects* GetDetrimentalEffect(int32 spell_id, Entity* caster); - virtual MaintainedEffects* GetMaintainedSpell(int32 spell_id); + virtual MaintainedEffects* GetMaintainedSpell(int32 spell_id, bool on_char_load = false); void RemoveDetrimentalSpell(LuaSpell* spell); void SetDeity(int8 new_deity){ deity = new_deity; @@ -1474,10 +1527,10 @@ public: void DoRegenUpdate(); MaintainedEffects* GetFreeMaintainedSpellSlot(); SpellEffects* GetFreeSpellEffectSlot(); - SpellEffects* GetSpellEffect(int32 id, Entity* caster = 0); + SpellEffects* GetSpellEffect(int32 id, Entity* caster = 0, bool on_char_load = false); SpellEffects* GetSpellEffectBySpellType(int8 spell_type); - SpellEffects* GetSpellEffectWithLinkedTimer(int32 id, int32 linked_timer = 0, sint32 type_group_spell_id = 0, Entity* caster = 0); - LuaSpell* HasLinkedTimerID(LuaSpell* spell, Spawn* target = nullptr, bool stackWithOtherPlayers = true); + SpellEffects* GetSpellEffectWithLinkedTimer(int32 id, int32 linked_timer = 0, sint32 type_group_spell_id = 0, Entity* caster = 0, bool notCaster = false); + LuaSpell* HasLinkedTimerID(LuaSpell* spell, Spawn* target = nullptr, bool stackWithOtherPlayers = true, bool checkNotCaster = false); //flags int32 GetFlags() { return info_struct.get_flags(); } @@ -1536,19 +1589,19 @@ public: bool SecondaryWeaponReady(); bool RangeWeaponReady(); void MeleeAttack(Spawn* victim, float distance, bool primary, bool multi_attack = false); - void RangeAttack(Spawn* victim, float distance, Item* weapon, Item* ammo, bool multi_attack = false); + bool RangeAttack(Spawn* victim, float distance, Item* weapon, Item* ammo, bool multi_attack = false); bool SpellAttack(Spawn* victim, float distance, LuaSpell* luaspell, int8 damage_type, int32 low_damage, int32 high_damage, int8 crit_mod = 0, bool no_calcs = false, int8 override_packet_type = 0, bool take_power = false); bool ProcAttack(Spawn* victim, int8 damage_type, int32 low_damage, int32 high_damage, string name, string success_msg, string effect_msg); bool SpellHeal(Spawn* target, float distance, LuaSpell* luaspell, string heal_type, int32 low_heal, int32 high_heal, int8 crit_mod = 0, bool no_calcs = false, string custom_spell_name=""); int8 DetermineHit(Spawn* victim, int8 type, int8 damage_type, float ToHitBonus, bool is_caster_spell, LuaSpell* lua_spell = nullptr); float GetDamageTypeResistPercentage(int8 damage_type); Skill* GetSkillByWeaponType(int8 type, int8 damage_type, bool update); - bool DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_damage, int32 high_damage, const char* spell_name, int8 crit_mod = 0, bool is_tick = false, bool no_damage_calcs = false, bool ignore_attacker = false, bool take_power = false, LuaSpell* spell = 0); + bool DamageSpawn(Entity* victim, int8 type, int8 damage_type, int32 low_damage, int32 high_damage, const char* spell_name, int8 crit_mod = 0, bool is_tick = false, bool no_damage_calcs = false, bool ignore_attacker = false, bool take_power = false, LuaSpell* spell = 0, bool skip_check_wards = false, bool is_melee_spawn = false); float CalculateMitigation(int8 type = DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE, int8 damage_type = 0, int16 attacker_level = 0, bool for_pvp = false); void AddHate(Entity* attacker, sint32 hate, bool ignore_pet_behavior = false); bool CheckInterruptSpell(Entity* attacker); bool CheckFizzleSpell(LuaSpell* spell); - void KillSpawn(Spawn* dead, int8 type = 0, int8 damage_type = 0, int16 kill_blow_type = 0); + void KillSpawn(Spawn* dead, int8 type = 0, int8 damage_type = 0, int16 kill_blow_type = 0, bool is_melee_spawn = false); void HandleDeathExperienceDebt(Spawn* killer); void SetAttackDelay(bool primary = false, bool ranged = false); float CalculateAttackSpeedMod(); @@ -1835,6 +1888,7 @@ public: /// Removes the ward with the given spell id /// The spell id of the ward to remove void RemoveWard(int32 spellID); + void RemoveWard(LuaSpell* spell); /// Subtracts the given damage from the wards /// The damage to subtract from the wards @@ -1944,15 +1998,8 @@ public: set HatedBy; std::mutex MHatedBy; - bool IsAggroed() { - int32 size = 0; - - MHatedBy.lock(); - size = HatedBy.size(); - MHatedBy.unlock(); - - return size > 0; - } + bool IsAggroed(); + void SendHatedByList(Client* client); Mutex MCommandMutex; @@ -2164,6 +2211,8 @@ private: map > set_sint8_funcs; map > set_string_funcs; + + mutable std::shared_mutex propertiesMutex; }; #endif diff --git a/old/WorldServer/Factions.h b/old/WorldServer/Factions.h index 21fe97d..ae88107 100644 --- a/old/WorldServer/Factions.h +++ b/old/WorldServer/Factions.h @@ -20,7 +20,8 @@ #ifndef EQ2_FACTIONS #define EQ2_FACTIONS -#include "../common/config_reader.hpp" +#include "../common/ConfigReader.h" +#include "../common/Mutex.h" struct Faction { int32 id; diff --git a/old/WorldServer/GroundSpawn.cpp b/old/WorldServer/GroundSpawn.cpp index f6978fb..86d243a 100644 --- a/old/WorldServer/GroundSpawn.cpp +++ b/old/WorldServer/GroundSpawn.cpp @@ -21,8 +21,8 @@ #include "World.h" #include "Spells.h" #include "Rules/Rules.h" -#include "../common/misc_functions.hpp" -#include "../common/log.hpp" +#include "../common/MiscFunctions.h" +#include "../common/Log.h" extern ConfigReader configReader; extern MasterSpellList master_spell_list; diff --git a/old/WorldServer/GroundSpawn.h b/old/WorldServer/GroundSpawn.h index e94ba85..f830686 100644 --- a/old/WorldServer/GroundSpawn.h +++ b/old/WorldServer/GroundSpawn.h @@ -22,7 +22,7 @@ #include "Spawn.h" #include "client.h" - +#include "../common/Mutex.h" class GroundSpawn : public Spawn { public: diff --git a/old/WorldServer/Guilds/Guild.cpp b/old/WorldServer/Guilds/Guild.cpp index e181c17..9a80672 100644 --- a/old/WorldServer/Guilds/Guild.cpp +++ b/old/WorldServer/Guilds/Guild.cpp @@ -26,7 +26,7 @@ #include "../client.h" #include "../World.h" #include "../zoneserver.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "../../common/Log.h" #include "../Rules/Rules.h" #include "../Web/PeerManager.h" @@ -187,7 +187,7 @@ void Guild::AddEXPCurrent(sint64 exp, bool send_packet) { else strncpy(adjective, "too uber for cheerios", sizeof(adjective) - 1); sprintf(message, "The %s guild <%s> has attained level %u", adjective, name, level); - zone_list.HandleGlobalAnnouncement(message); + zone_list.TransmitGlobalAnnouncement(message); } } save_needed = true; diff --git a/old/WorldServer/Guilds/Guild.h b/old/WorldServer/Guilds/Guild.h index 0f23d07..cad0f57 100644 --- a/old/WorldServer/Guilds/Guild.h +++ b/old/WorldServer/Guilds/Guild.h @@ -24,6 +24,7 @@ #include #include #include +#include "../../common/Mutex.h" #include "../MutexMap.h" using namespace std; diff --git a/old/WorldServer/Guilds/GuildDB.cpp b/old/WorldServer/Guilds/GuildDB.cpp index 8d7e6ea..eca4533 100644 --- a/old/WorldServer/Guilds/GuildDB.cpp +++ b/old/WorldServer/Guilds/GuildDB.cpp @@ -29,7 +29,7 @@ #include #include #include "../../common/Log.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "Guild.h" extern GuildList guild_list; diff --git a/old/WorldServer/HeroicOp/HeroicOp.h b/old/WorldServer/HeroicOp/HeroicOp.h index af7326a..a5c9fcf 100644 --- a/old/WorldServer/HeroicOp/HeroicOp.h +++ b/old/WorldServer/HeroicOp/HeroicOp.h @@ -24,7 +24,7 @@ #include #include -#include "../../common/types.hpp" +#include "../../common/types.h" using namespace std; diff --git a/old/WorldServer/HeroicOp/HeroicOpDB.cpp b/old/WorldServer/HeroicOp/HeroicOpDB.cpp index 49f6e71..3701080 100644 --- a/old/WorldServer/HeroicOp/HeroicOpDB.cpp +++ b/old/WorldServer/HeroicOp/HeroicOpDB.cpp @@ -18,7 +18,7 @@ along with EQ2Emulator. If not, see . */ -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "../../common/Log.h" #include "HeroicOp.h" diff --git a/old/WorldServer/Housing/HousingDB.cpp b/old/WorldServer/Housing/HousingDB.cpp index edc6a49..c90c9b5 100644 --- a/old/WorldServer/Housing/HousingDB.cpp +++ b/old/WorldServer/Housing/HousingDB.cpp @@ -1,4 +1,4 @@ -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "../World.h" extern World world; diff --git a/old/WorldServer/Housing/HousingPackets.cpp b/old/WorldServer/Housing/HousingPackets.cpp index b889868..3f817eb 100644 --- a/old/WorldServer/Housing/HousingPackets.cpp +++ b/old/WorldServer/Housing/HousingPackets.cpp @@ -1,7 +1,7 @@ #include "../ClientPacketFunctions.h" #include "../World.h" #include "../client.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "../Rules/Rules.h" extern ConfigReader configReader; diff --git a/old/WorldServer/Items/Items.cpp b/old/WorldServer/Items/Items.cpp index 37f3d33..9f35245 100644 --- a/old/WorldServer/Items/Items.cpp +++ b/old/WorldServer/Items/Items.cpp @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #include "Items.h" #include "../Spells.h" #include "../Quests.h" @@ -32,6 +33,8 @@ #include #include #include "../Rules/Rules.h" +#include "../WorldDatabase.h" +#include "../Broker/BrokerManager.h" extern World world; extern MasterSpellList master_spell_list; @@ -41,6 +44,9 @@ extern ConfigReader configReader; extern LuaInterface* lua_interface; extern RuleManager rule_manager; extern Classes classes; +extern MasterItemList master_item_list; +extern WorldDatabase database; +extern BrokerManager broker; MasterItemList::MasterItemList(){ AddMappedItemStat(ITEM_STAT_ADORNING, std::string("adorning")); @@ -151,10 +157,519 @@ map>::iterator MasterItemList::FindBrokerItemMap return enditr; } +bool MasterItemList::ShouldAddItemBrokerType(Item* item, int64 itype) { + bool should_add = false; + switch(itype){ + case ITEM_BROKER_TYPE_ADORNMENT:{ + if(item->IsAdornment()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_AMMO:{ + if(item->IsAmmo()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_ATTUNEABLE:{ + if(item->CheckFlag(ATTUNEABLE)) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_BAG:{ + if(item->IsBag()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_BAUBLE:{ + if(item->IsBauble()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_BOOK:{ + if(item->IsBook()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_CHAINARMOR:{ + if(item->IsChainArmor()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_CLOAK:{ + if(item->IsCloak()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_CLOTHARMOR:{ + if(item->IsClothArmor()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_COLLECTABLE:{ + if(item->IsCollectable()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_CRUSHWEAPON:{ + if(item->IsCrushWeapon() && (item->weapon_info->wield_type == ITEM_WIELD_TYPE_DUAL || item->weapon_info->wield_type == ITEM_WIELD_TYPE_SINGLE)) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_DRINK:{ + if(item->IsFoodDrink()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_FOOD:{ + if(item->IsFoodFood()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_HOUSEITEM:{ + if(item->IsHouseItem() || item->IsHouseContainer()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_JEWELRY:{ + if(item->IsJewelry()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_LEATHERARMOR:{ + if(item->IsLeatherArmor()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_LORE:{ + if(item->CheckFlag(LORE)) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_MISC:{ + if(item->IsMisc()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_PIERCEWEAPON:{ + if(item->IsPierceWeapon() && (item->weapon_info->wield_type == ITEM_WIELD_TYPE_DUAL || item->weapon_info->wield_type == ITEM_WIELD_TYPE_SINGLE)) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_PLATEARMOR:{ + if(item->IsPlateArmor()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_POISON:{ + if(item->IsPoison()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_POTION:{ + if(item->IsPotion()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_RECIPEBOOK:{ + if(item->IsRecipeBook()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_SALESDISPLAY:{ + if(item->IsSalesDisplay()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_SHIELD:{ + if(item->IsShield()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_SLASHWEAPON:{ + if(item->IsSlashWeapon() && (item->weapon_info->wield_type == ITEM_WIELD_TYPE_DUAL || item->weapon_info->wield_type == ITEM_WIELD_TYPE_SINGLE)) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_SPELLSCROLL:{ + if(item->IsSpellScroll()) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_TINKERED:{ + if(item->tinkered == 1) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_TRADESKILL:{ + if(item->crafted == 1) + should_add = true; + break; + } + case ITEM_BROKER_TYPE_2H_CRUSH:{ + should_add = item->IsWeapon() && item->weapon_info->wield_type == ITEM_WIELD_TYPE_TWO_HAND && item->generic_info.skill_req1 == SKILL_ID_STAFF; + break; + } + case ITEM_BROKER_TYPE_2H_PIERCE:{ + should_add = item->IsWeapon() && item->weapon_info->wield_type == ITEM_WIELD_TYPE_TWO_HAND && item->generic_info.skill_req1 == SKILL_ID_GREATSPEAR; + break; + } + case ITEM_BROKER_TYPE_2H_SLASH:{ + should_add = item->IsWeapon() && item->weapon_info->wield_type == ITEM_WIELD_TYPE_TWO_HAND && item->generic_info.skill_req1 == SKILL_ID_GREATSWORD; + break; + } + } + return should_add; +} +bool MasterItemList::ShouldAddItemBrokerSlot(Item* item, int64 ltype) { + bool should_add = false; + + switch(ltype){ + case ITEM_BROKER_SLOT_AMMO:{ + should_add = item->HasSlot(EQ2_AMMO_SLOT); + break; + } + case ITEM_BROKER_SLOT_CHARM:{ + should_add = item->HasSlot(EQ2_CHARM_SLOT_1, EQ2_CHARM_SLOT_2); + break; + } + case ITEM_BROKER_SLOT_CHEST:{ + should_add = item->HasSlot(EQ2_CHEST_SLOT); + break; + } + case ITEM_BROKER_SLOT_CLOAK:{ + should_add = item->HasSlot(EQ2_CLOAK_SLOT); + break; + } + case ITEM_BROKER_SLOT_DRINK:{ + should_add = item->HasSlot(EQ2_DRINK_SLOT); + break; + } + case ITEM_BROKER_SLOT_EARS:{ + should_add = item->HasSlot(EQ2_EARS_SLOT_1, EQ2_EARS_SLOT_2); + break; + } + case ITEM_BROKER_SLOT_FEET:{ + should_add = item->HasSlot(EQ2_FEET_SLOT); + break; + } + case ITEM_BROKER_SLOT_FOOD:{ + should_add = item->HasSlot(EQ2_FOOD_SLOT); + break; + } + case ITEM_BROKER_SLOT_FOREARMS:{ + should_add = item->HasSlot(EQ2_FOREARMS_SLOT); + break; + } + case ITEM_BROKER_SLOT_HANDS:{ + should_add = item->HasSlot(EQ2_HANDS_SLOT); + break; + } + case ITEM_BROKER_SLOT_HEAD:{ + should_add = item->HasSlot(EQ2_HEAD_SLOT); + break; + } + case ITEM_BROKER_SLOT_LEGS:{ + should_add = item->HasSlot(EQ2_LEGS_SLOT); + break; + } + case ITEM_BROKER_SLOT_NECK:{ + should_add = item->HasSlot(EQ2_NECK_SLOT); + break; + } + case ITEM_BROKER_SLOT_PRIMARY:{ + should_add = item->HasSlot(EQ2_PRIMARY_SLOT); + break; + } + case ITEM_BROKER_SLOT_PRIMARY_2H:{ + should_add = item->HasSlot(EQ2_PRIMARY_SLOT) && item->IsWeapon() && item->weapon_info->wield_type == ITEM_WIELD_TYPE_TWO_HAND; + break; + } + case ITEM_BROKER_SLOT_RANGE_WEAPON:{ + should_add = item->HasSlot(EQ2_RANGE_SLOT); + break; + } + case ITEM_BROKER_SLOT_RING:{ + should_add = item->HasSlot(EQ2_LRING_SLOT, EQ2_RRING_SLOT); + break; + } + case ITEM_BROKER_SLOT_SECONDARY:{ + should_add = item->HasSlot(EQ2_SECONDARY_SLOT); + break; + } + case ITEM_BROKER_SLOT_SHOULDERS:{ + should_add = item->HasSlot(EQ2_SHOULDERS_SLOT); + break; + } + case ITEM_BROKER_SLOT_WAIST:{ + should_add = item->HasSlot(EQ2_WAIST_SLOT); + break; + } + case ITEM_BROKER_SLOT_WRIST:{ + should_add = item->HasSlot(EQ2_LWRIST_SLOT, EQ2_RWRIST_SLOT); + break; + } + } + + return should_add; +} + +bool MasterItemList::ShouldAddItemBrokerStat(Item* item, int64 btype) { + bool should_add = false; + bool stat_found = false; + switch(btype){ + case ITEM_BROKER_STAT_TYPE_NONE:{ + if (item->item_stats.size() == 0) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_DEF:{ + stat_found = item->HasStat(ITEM_STAT_DEFENSE, GetItemStatNameByID(ITEM_STAT_DEFENSE)); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_STR:{ + stat_found = item->HasStat(ITEM_STAT_STR); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_STA:{ + stat_found = item->HasStat(ITEM_STAT_STA); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_AGI:{ + stat_found = item->HasStat(ITEM_STAT_AGI); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_WIS:{ + stat_found = item->HasStat(ITEM_STAT_WIS); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_INT:{ + stat_found = item->HasStat(ITEM_STAT_INT); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_HEALTH:{ + stat_found = item->HasStat(ITEM_STAT_HEALTH); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_POWER:{ + stat_found = item->HasStat(ITEM_STAT_POWER); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_HEAT:{ + stat_found = item->HasStat(ITEM_STAT_VS_HEAT); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_COLD:{ + stat_found = item->HasStat(ITEM_STAT_VS_COLD); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_MAGIC:{ + stat_found = item->HasStat(ITEM_STAT_VS_MAGIC); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_MENTAL:{ + stat_found = item->HasStat(ITEM_STAT_VS_MENTAL); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_DIVINE:{ + stat_found = item->HasStat(ITEM_STAT_VS_DIVINE); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_POISON:{ + stat_found = item->HasStat(ITEM_STAT_VS_POISON); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_DISEASE:{ + stat_found = item->HasStat(ITEM_STAT_VS_DISEASE); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_CRUSH:{ + stat_found = item->HasStat(ITEM_STAT_DMG_CRUSH); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_SLASH:{ + stat_found = item->HasStat(ITEM_STAT_DMG_SLASH); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_PIERCE:{ + stat_found = item->HasStat(ITEM_STAT_DMG_PIERCE); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_CRITICAL: { + stat_found = item->HasStat(ITEM_STAT_CRITICALMITIGATION); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_DBL_ATTACK:{ + stat_found = item->HasStat(ITEM_STAT_MULTIATTACKCHANCE); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_ABILITY_MOD:{ + stat_found = item->HasStat(ITEM_STAT_ABILITY_MODIFIER); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_POTENCY:{ + stat_found = item->HasStat(ITEM_STAT_POTENCY); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_AEAUTOATTACK:{ + stat_found = item->HasStat(ITEM_STAT_AEAUTOATTACKCHANCE); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_ATTACKSPEED:{ + stat_found = item->HasStat(ITEM_STAT_ATTACKSPEED); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_BLOCKCHANCE:{ + stat_found = item->HasStat(ITEM_STAT_EXTRASHIELDBLOCKCHANCE); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_CASTINGSPEED:{ + stat_found = item->HasStat(ITEM_STAT_ABILITYCASTINGSPEED); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_CRITBONUS:{ + stat_found = item->HasStat(ITEM_STAT_CRITBONUS); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_CRITCHANCE:{ + stat_found = item->HasStat(ITEM_STAT_MELEECRITCHANCE); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_DPS:{ + stat_found = item->HasStat(ITEM_STAT_DPS); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_FLURRYCHANCE:{ + stat_found = item->HasStat(ITEM_STAT_FLURRY); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_HATEGAIN:{ + stat_found = item->HasStat(ITEM_STAT_HATEGAINMOD); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_MITIGATION:{ + stat_found = item->HasStat(ITEM_STAT_ARMORMITIGATIONINCREASE); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_MULTI_ATTACK:{ + stat_found = item->HasStat(ITEM_STAT_MULTIATTACKCHANCE); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_RECOVERY:{ + stat_found = item->HasStat(ITEM_STAT_ABILITYRECOVERYSPEED); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_REUSE_SPEED:{ + stat_found = item->HasStat(ITEM_STAT_ABILITYREUSESPEED); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_SPELL_WPNDMG:{ + stat_found = item->HasStat(ITEM_STAT_SPELLWEAPONDAMAGEBONUS); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_STRIKETHROUGH:{ + stat_found = item->HasStat(ITEM_STAT_STRIKETHROUGH); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_TOUGHNESS:{ + stat_found = item->HasStat(ITEM_STAT_PVPTOUGHNESS); + if (stat_found) + should_add = true; + break; + } + case ITEM_BROKER_STAT_TYPE_WEAPONDMG:{ + stat_found = item->HasStat(ITEM_STAT_WEAPONDAMAGEBONUS); + if (stat_found) + should_add = true; + break; + } + default: { + LogWrite(ITEM__DEBUG, 0, "Item", "Unknown item broker stat type %u", btype); + LogWrite(ITEM__DEBUG, 0, "Item", "If you have a client before the new expansion this may be the reason. Please be patient while we update items to support the new client.", btype); + break; + } + } + + return should_add; +} + vector* MasterItemList::GetItems(string name, int64 itype, int64 ltype, int64 btype, int64 minprice, int64 maxprice, int8 minskill, int8 maxskill, string seller, string adornment, int8 mintier, int8 maxtier, int16 minlevel, int16 maxlevel, sint8 itemclass){ vector* ret = new vector; map::iterator iter; - Item* item = 0; + Item* item = 0; const char* chkname = 0; //const char* chkseller = 0; //const char* chkadornment = 0; @@ -170,511 +685,19 @@ vector* MasterItemList::GetItems(string name, int64 itype, int64 ltype, i item = iter->second; if(item){ if(itype != ITEM_BROKER_TYPE_ANY && itype != ITEM_BROKER_TYPE_ANY64BIT){ - should_add = false; - switch(itype){ - case ITEM_BROKER_TYPE_ADORNMENT:{ - if(item->IsAdornment()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_AMMO:{ - if(item->IsAmmo()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_ATTUNEABLE:{ - if(item->CheckFlag(ATTUNEABLE)) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_BAG:{ - if(item->IsBag()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_BAUBLE:{ - if(item->IsBauble()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_BOOK:{ - if(item->IsBook()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_CHAINARMOR:{ - if(item->IsChainArmor()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_CLOAK:{ - if(item->IsCloak()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_CLOTHARMOR:{ - if(item->IsClothArmor()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_COLLECTABLE:{ - if(item->IsCollectable()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_CRUSHWEAPON:{ - if(item->IsCrushWeapon() && (item->weapon_info->wield_type == ITEM_WIELD_TYPE_DUAL || item->weapon_info->wield_type == ITEM_WIELD_TYPE_SINGLE)) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_DRINK:{ - if(item->IsFoodDrink()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_FOOD:{ - if(item->IsFoodFood()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_HOUSEITEM:{ - if(item->IsHouseItem()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_JEWELRY:{ - if(item->IsJewelry()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_LEATHERARMOR:{ - if(item->IsLeatherArmor()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_LORE:{ - if(item->CheckFlag(LORE)) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_MISC:{ - if(item->IsMisc()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_PIERCEWEAPON:{ - if(item->IsPierceWeapon() && (item->weapon_info->wield_type == ITEM_WIELD_TYPE_DUAL || item->weapon_info->wield_type == ITEM_WIELD_TYPE_SINGLE)) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_PLATEARMOR:{ - if(item->IsPlateArmor()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_POISON:{ - if(item->IsPoison()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_POTION:{ - if(item->IsPotion()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_RECIPEBOOK:{ - if(item->IsRecipeBook()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_SALESDISPLAY:{ - if(item->IsSalesDisplay()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_SHIELD:{ - if(item->IsShield()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_SLASHWEAPON:{ - if(item->IsSlashWeapon() && (item->weapon_info->wield_type == ITEM_WIELD_TYPE_DUAL || item->weapon_info->wield_type == ITEM_WIELD_TYPE_SINGLE)) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_SPELLSCROLL:{ - if(item->IsSpellScroll()) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_TINKERED:{ - if(item->tinkered == 1) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_TRADESKILL:{ - if(item->crafted == 1) - should_add = true; - break; - } - case ITEM_BROKER_TYPE_2H_CRUSH:{ - should_add = item->IsWeapon() && item->weapon_info->wield_type == ITEM_WIELD_TYPE_TWO_HAND && item->generic_info.skill_req1 == SKILL_ID_STAFF; - break; - } - case ITEM_BROKER_TYPE_2H_PIERCE:{ - should_add = item->IsWeapon() && item->weapon_info->wield_type == ITEM_WIELD_TYPE_TWO_HAND && item->generic_info.skill_req1 == SKILL_ID_GREATSPEAR; - break; - } - case ITEM_BROKER_TYPE_2H_SLASH:{ - should_add = item->IsWeapon() && item->weapon_info->wield_type == ITEM_WIELD_TYPE_TWO_HAND && item->generic_info.skill_req1 == SKILL_ID_GREATSWORD; - break; - } - } + should_add = ShouldAddItemBrokerType(item, itype); if(!should_add) continue; } if(ltype != ITEM_BROKER_SLOT_ANY){ - should_add = false; - switch(ltype){ - case ITEM_BROKER_SLOT_AMMO:{ - should_add = item->HasSlot(EQ2_AMMO_SLOT); - break; - } - case ITEM_BROKER_SLOT_CHARM:{ - should_add = item->HasSlot(EQ2_CHARM_SLOT_1, EQ2_CHARM_SLOT_2); - break; - } - case ITEM_BROKER_SLOT_CHEST:{ - should_add = item->HasSlot(EQ2_CHEST_SLOT); - break; - } - case ITEM_BROKER_SLOT_CLOAK:{ - should_add = item->HasSlot(EQ2_CLOAK_SLOT); - break; - } - case ITEM_BROKER_SLOT_DRINK:{ - should_add = item->HasSlot(EQ2_DRINK_SLOT); - break; - } - case ITEM_BROKER_SLOT_EARS:{ - should_add = item->HasSlot(EQ2_EARS_SLOT_1, EQ2_EARS_SLOT_2); - break; - } - case ITEM_BROKER_SLOT_FEET:{ - should_add = item->HasSlot(EQ2_FEET_SLOT); - break; - } - case ITEM_BROKER_SLOT_FOOD:{ - should_add = item->HasSlot(EQ2_FOOD_SLOT); - break; - } - case ITEM_BROKER_SLOT_FOREARMS:{ - should_add = item->HasSlot(EQ2_FOREARMS_SLOT); - break; - } - case ITEM_BROKER_SLOT_HANDS:{ - should_add = item->HasSlot(EQ2_HANDS_SLOT); - break; - } - case ITEM_BROKER_SLOT_HEAD:{ - should_add = item->HasSlot(EQ2_HEAD_SLOT); - break; - } - case ITEM_BROKER_SLOT_LEGS:{ - should_add = item->HasSlot(EQ2_LEGS_SLOT); - break; - } - case ITEM_BROKER_SLOT_NECK:{ - should_add = item->HasSlot(EQ2_NECK_SLOT); - break; - } - case ITEM_BROKER_SLOT_PRIMARY:{ - should_add = item->HasSlot(EQ2_PRIMARY_SLOT); - break; - } - case ITEM_BROKER_SLOT_PRIMARY_2H:{ - should_add = item->HasSlot(EQ2_PRIMARY_SLOT) && item->IsWeapon() && item->weapon_info->wield_type == ITEM_WIELD_TYPE_TWO_HAND; - break; - } - case ITEM_BROKER_SLOT_RANGE_WEAPON:{ - should_add = item->HasSlot(EQ2_RANGE_SLOT); - break; - } - case ITEM_BROKER_SLOT_RING:{ - should_add = item->HasSlot(EQ2_LRING_SLOT, EQ2_RRING_SLOT); - break; - } - case ITEM_BROKER_SLOT_SECONDARY:{ - should_add = item->HasSlot(EQ2_SECONDARY_SLOT); - break; - } - case ITEM_BROKER_SLOT_SHOULDERS:{ - should_add = item->HasSlot(EQ2_SHOULDERS_SLOT); - break; - } - case ITEM_BROKER_SLOT_WAIST:{ - should_add = item->HasSlot(EQ2_WAIST_SLOT); - break; - } - case ITEM_BROKER_SLOT_WRIST:{ - should_add = item->HasSlot(EQ2_LWRIST_SLOT, EQ2_RWRIST_SLOT); - break; - } - } + should_add = ShouldAddItemBrokerSlot(item, ltype); if(!should_add) continue; } if(btype != 0xFFFFFFFF){ vector::iterator itr; - bool stat_found = false; - should_add = false; - switch(btype){ - case ITEM_BROKER_STAT_TYPE_NONE:{ - if (item->item_stats.size() == 0) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_DEF:{ - stat_found = item->HasStat(ITEM_STAT_DEFENSE, GetItemStatNameByID(ITEM_STAT_DEFENSE)); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_STR:{ - stat_found = item->HasStat(ITEM_STAT_STR); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_STA:{ - stat_found = item->HasStat(ITEM_STAT_STA); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_AGI:{ - stat_found = item->HasStat(ITEM_STAT_AGI); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_WIS:{ - stat_found = item->HasStat(ITEM_STAT_WIS); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_INT:{ - stat_found = item->HasStat(ITEM_STAT_INT); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_HEALTH:{ - stat_found = item->HasStat(ITEM_STAT_HEALTH); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_POWER:{ - stat_found = item->HasStat(ITEM_STAT_POWER); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_HEAT:{ - stat_found = item->HasStat(ITEM_STAT_VS_HEAT); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_COLD:{ - stat_found = item->HasStat(ITEM_STAT_VS_COLD); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_MAGIC:{ - stat_found = item->HasStat(ITEM_STAT_VS_MAGIC); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_MENTAL:{ - stat_found = item->HasStat(ITEM_STAT_VS_MENTAL); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_DIVINE:{ - stat_found = item->HasStat(ITEM_STAT_VS_DIVINE); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_POISON:{ - stat_found = item->HasStat(ITEM_STAT_VS_POISON); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_DISEASE:{ - stat_found = item->HasStat(ITEM_STAT_VS_DISEASE); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_CRUSH:{ - stat_found = item->HasStat(ITEM_STAT_DMG_CRUSH); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_SLASH:{ - stat_found = item->HasStat(ITEM_STAT_DMG_SLASH); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_PIERCE:{ - stat_found = item->HasStat(ITEM_STAT_DMG_PIERCE); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_CRITICAL: { - stat_found = item->HasStat(ITEM_STAT_CRITICALMITIGATION); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_DBL_ATTACK:{ - stat_found = item->HasStat(ITEM_STAT_MULTIATTACKCHANCE); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_ABILITY_MOD:{ - stat_found = item->HasStat(ITEM_STAT_ABILITY_MODIFIER); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_POTENCY:{ - stat_found = item->HasStat(ITEM_STAT_POTENCY); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_AEAUTOATTACK:{ - stat_found = item->HasStat(ITEM_STAT_AEAUTOATTACKCHANCE); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_ATTACKSPEED:{ - stat_found = item->HasStat(ITEM_STAT_ATTACKSPEED); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_BLOCKCHANCE:{ - stat_found = item->HasStat(ITEM_STAT_EXTRASHIELDBLOCKCHANCE); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_CASTINGSPEED:{ - stat_found = item->HasStat(ITEM_STAT_ABILITYCASTINGSPEED); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_CRITBONUS:{ - stat_found = item->HasStat(ITEM_STAT_CRITBONUS); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_CRITCHANCE:{ - stat_found = item->HasStat(ITEM_STAT_MELEECRITCHANCE); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_DPS:{ - stat_found = item->HasStat(ITEM_STAT_DPS); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_FLURRYCHANCE:{ - stat_found = item->HasStat(ITEM_STAT_FLURRY); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_HATEGAIN:{ - stat_found = item->HasStat(ITEM_STAT_HATEGAINMOD); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_MITIGATION:{ - stat_found = item->HasStat(ITEM_STAT_ARMORMITIGATIONINCREASE); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_MULTI_ATTACK:{ - stat_found = item->HasStat(ITEM_STAT_MULTIATTACKCHANCE); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_RECOVERY:{ - stat_found = item->HasStat(ITEM_STAT_ABILITYRECOVERYSPEED); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_REUSE_SPEED:{ - stat_found = item->HasStat(ITEM_STAT_ABILITYREUSESPEED); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_SPELL_WPNDMG:{ - stat_found = item->HasStat(ITEM_STAT_SPELLWEAPONDAMAGEBONUS); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_STRIKETHROUGH:{ - stat_found = item->HasStat(ITEM_STAT_STRIKETHROUGH); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_TOUGHNESS:{ - stat_found = item->HasStat(ITEM_STAT_PVPTOUGHNESS); - if (stat_found) - should_add = true; - break; - } - case ITEM_BROKER_STAT_TYPE_WEAPONDMG:{ - stat_found = item->HasStat(ITEM_STAT_WEAPONDAMAGEBONUS); - if (stat_found) - should_add = true; - break; - } - default: { - LogWrite(ITEM__DEBUG, 0, "Item", "Unknown item broker stat type %u", btype); - LogWrite(ITEM__DEBUG, 0, "Item", "If you have a client before the new expansion this may be the reason. Please be patient while we update items to support the new client.", btype); - break; - } - } + should_add = ShouldAddItemBrokerStat(item, btype); if (!should_add) continue; } @@ -775,18 +798,16 @@ vector* MasterItemList::GetItems(map criteria, Client* cl btype = itr->second[btype]; } } - return GetItems(name, itype, ltype, btype, minprice, maxprice, minskill, maxskill, seller, adornment, mintier, maxtier, minlevel, maxlevel, itemclass); + if(client_to_map && client_to_map->IsGMStoreSearch()) { + return GetItems(name, itype, ltype, btype, minprice, maxprice, minskill, maxskill, seller, adornment, mintier, maxtier, minlevel, maxlevel, itemclass); + } + else { + return broker.GetItems(name, itype, ltype, btype, minprice, maxprice, minskill, maxskill, seller, adornment, mintier, maxtier, minlevel, maxlevel, itemclass); + } } -void MasterItemList::ResetUniqueID(int32 new_id){ - next_unique_id = new_id; -} - -int32 MasterItemList::NextUniqueID(){ - next_unique_id++; - if(next_unique_id >= 0xFFFFFFF0) - next_unique_id = 1; - return next_unique_id; +int64 MasterItemList::NextUniqueID(){ + return database.LoadNextUniqueItemID(); } bool MasterItemList::IsBag(int32 item_id){ @@ -888,7 +909,12 @@ void MasterItemList::AddItem(Item* item){ } Item::Item(){ + seller_char_id = 0; + seller_house_id = 0; + is_search_store_item = false; + is_search_in_inventory = false; item_script = ""; + broker_price = 0; sell_price = 0; sell_status = 0; max_sell_value = 0; @@ -920,7 +946,12 @@ Item::Item(){ } Item::Item(Item* in_item){ + seller_char_id = 0; + seller_house_id = 0; + is_search_store_item = false; + is_search_in_inventory = false; needs_deletion = false; + broker_price = 0; sell_price = in_item->sell_price; sell_status = in_item->sell_status; max_sell_value = in_item->max_sell_value; @@ -938,6 +969,39 @@ Item::Item(Item* in_item){ grouped_char_ids.insert(in_item->grouped_char_ids.begin(), in_item->grouped_char_ids.end()); effect_type = in_item->effect_type; book_language = in_item->book_language; + details.lock_flags = 0; + details.item_locked = false; +} + +Item::Item(Item* in_item, int64 unique_id, std::string in_creator, std::string in_seller_name, int32 in_seller_char_id, int64 in_broker_price, int16 count, int64 in_seller_house_id, bool search_in_inventory){ + is_search_store_item = true; + broker_price = in_broker_price; + needs_deletion = false; + sell_price = in_item->sell_price; + sell_status = in_item->sell_status; + max_sell_value = in_item->max_sell_value; + save_needed = false; + SetItem(in_item); + details.unique_id = unique_id; + if (IsBag()) + details.bag_id = details.unique_id; + generic_info.condition = 100; + spell_id = in_item->spell_id; + spell_tier = in_item->spell_tier; + no_buy_back = in_item->no_buy_back; + no_sale = in_item->no_sale; + created = in_item->created; + grouped_char_ids.insert(in_item->grouped_char_ids.begin(), in_item->grouped_char_ids.end()); + effect_type = in_item->effect_type; + book_language = in_item->book_language; + creator = in_creator; + seller_name = in_seller_name; + seller_char_id = in_seller_char_id; + details.count = count; + seller_house_id = in_seller_house_id; + details.lock_flags = 0; + details.item_locked = false; + is_search_in_inventory = search_in_inventory; } Item::~Item(){ @@ -1029,7 +1093,7 @@ void Item::SetItem(Item* old_item){ memcpy(bauble_info, old_item->bauble_info, sizeof(Bauble_Info)); break; } - case ITEM_TYPE_SKILL:{ + case ITEM_TYPE_SKILL:{ skill_info = new Skill_Info; memcpy(skill_info, old_item->skill_info, sizeof(Skill_Info)); break; @@ -1072,6 +1136,18 @@ void Item::SetItem(Item* old_item){ break; } case ITEM_TYPE_HOUSE_CONTAINER:{ + houseitem_info = new HouseItem_Info; + memset(houseitem_info, 0, sizeof(HouseItem_Info)); + bag_info = new Bag_Info; + memset(bag_info, 0, sizeof(Bag_Info)); + + if(old_item->bag_info) + memcpy(bag_info, old_item->bag_info, sizeof(Bag_Info)); + + if(old_item->houseitem_info) { + memcpy(houseitem_info, old_item->houseitem_info, sizeof(HouseItem_Info)); + } + // House Containers housecontainer_info = new HouseContainer_Info; if (old_item->housecontainer_info) { @@ -1340,7 +1416,7 @@ bool Item::IsRanged(){ } bool Item::IsBag(){ - return generic_info.item_type == ITEM_TYPE_BAG; + return generic_info.item_type == ITEM_TYPE_BAG || generic_info.item_type == ITEM_TYPE_HOUSE_CONTAINER; } bool Item::IsFood(){ @@ -1541,7 +1617,7 @@ void Item::SetItemType(int8 in_type){ ranged_info = new Ranged_Info; memset(ranged_info, 0, sizeof(Ranged_Info)); } - else if(IsBag() && !bag_info){ + else if(IsBag() && !IsHouseContainer() && !bag_info){ bag_info = new Bag_Info; memset(bag_info, 0, sizeof(Bag_Info)); } @@ -1572,11 +1648,18 @@ void Item::SetItemType(int8 in_type){ book_info->author.size = 0; book_info->title.size = 0; } - else if(IsHouseItem() && !houseitem_info){ + else if(IsHouseItem() && !IsHouseContainer() && !houseitem_info){ houseitem_info = new HouseItem_Info; memset(houseitem_info, 0, sizeof(HouseItem_Info)); } else if(IsHouseContainer() && !housecontainer_info){ + bag_info = new Bag_Info; + memset(bag_info, 0, sizeof(Bag_Info)); + + if(!houseitem_info) { + houseitem_info = new HouseItem_Info; + memset(houseitem_info, 0, sizeof(HouseItem_Info)); + } housecontainer_info = new HouseContainer_Info; housecontainer_info->allowed_types = 0; housecontainer_info->broker_commission = 0; @@ -2493,6 +2576,11 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16 } case ITEM_TYPE_HOUSE_CONTAINER:{ + if(houseitem_info && client->GetVersion() >= 374){ + packet->setDataByName("status_rent_reduction", houseitem_info->status_rent_reduction); + packet->setDataByName("coin_rent_reduction", houseitem_info->coin_rent_reduction); + packet->setDataByName("house_only", houseitem_info->house_only); + } //House Containers if(housecontainer_info && client->GetVersion() >= 374){ packet->setDataByName("allowed_types", housecontainer_info->allowed_types); @@ -2500,7 +2588,58 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16 packet->setDataByName("broker_commission", housecontainer_info->broker_commission); packet->setDataByName("fence_commission", housecontainer_info->fence_commission); } - } + if(bag_info){ + int8 max_slots = player->GetMaxBagSlots(client->GetVersion()); + if (bag_info->num_slots > max_slots) + bag_info->num_slots = max_slots; + + int16 free_slots = bag_info->num_slots; + if (player) { + Item* bag = player->GetPlayerItemList()->GetItemFromUniqueID(details.unique_id, true); + if (bag && bag->IsBag()) { + vector* bag_items = player->GetPlayerItemList()->GetItemsInBag(bag); + if (bag_items->size() > bag->bag_info->num_slots) { + free_slots = 0; + packet->setArrayLengthByName("num_names", bag->bag_info->num_slots); + } + else { + free_slots = bag->bag_info->num_slots - bag_items->size(); + packet->setArrayLengthByName("num_names", bag_items->size()); + } + vector::iterator itr; + int16 i = 0; + Item* tmp_bag_item = 0; + for (itr = bag_items->begin(); itr != bag_items->end(); itr++) { + tmp_bag_item = *itr; + if (tmp_bag_item && tmp_bag_item->details.slot_id < bag->bag_info->num_slots) { + packet->setArrayDataByName("item_name", tmp_bag_item->name.c_str(), i); + i++; + } + } + safe_delete(bag_items); + } + } + packet->setDataByName("num_slots", bag_info->num_slots); + packet->setDataByName("num_empty", free_slots); + packet->setDataByName("weight_reduction", bag_info->weight_reduction); + packet->setDataByName("item_score", 2); + //packet->setDataByName("unknown5", 0x1e50a86f); + //packet->setDataByName("unknown6", 0x2c17f61d); + //1 armorer + //2 weaponsmith + //4 tailor + //16 jeweler + //32 sage + //64 alchemist + //120 all scholars + //250 all craftsman + //int8 blah[] = {0x00,0x00,0x01,0x01,0xb6,0x01,0x01}; + //int8 blah[] = {0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + int8 blah[] = { 0xd8,0x66,0x9b,0x6d,0xb6,0xfb,0x7f }; + for (int8 i = 0; i < sizeof(blah); i++) + packet->setSubstructDataByName("footer", "footer_unknown_0", blah[i], 0, i); + } + } } } @@ -3025,8 +3164,8 @@ Item* PlayerItemList::GetBankBag(int8 inventory_slot, bool lock){ Item* bag = 0; if(lock) MPlayerItems.readlock(__FUNCTION__, __LINE__); - if(items.count(-3) > 0 && items[-3][BASE_EQUIPMENT].count(inventory_slot) > 0 && items[-3][BASE_EQUIPMENT][inventory_slot]->IsBag()) - bag = items[-3][BASE_EQUIPMENT][inventory_slot]; + if(items.count(InventorySlotType::BANK) > 0 && items[InventorySlotType::BANK][BASE_EQUIPMENT].count(inventory_slot) > 0 && items[InventorySlotType::BANK][BASE_EQUIPMENT][inventory_slot]->IsBag()) + bag = items[InventorySlotType::BANK][BASE_EQUIPMENT][inventory_slot]; if(lock) MPlayerItems.releasereadlock(__FUNCTION__, __LINE__); return bag; @@ -3114,10 +3253,10 @@ bool PlayerItemList::HasFreeSlot(){ bool PlayerItemList::GetFirstFreeBankSlot(sint32* bag_id, sint16* slot) { bool ret = false; MPlayerItems.readlock(__FUNCTION__, __LINE__); - if (items.count(-3) > 0) { + if (items.count(InventorySlotType::BANK) > 0) { for (int8 i = 0; i < NUM_BANK_SLOTS; i++) { - if (items[-3][BASE_EQUIPMENT].count(i) == 0) { - *bag_id = -3; + if (items[InventorySlotType::BANK][BASE_EQUIPMENT].count(i) == 0) { + *bag_id = InventorySlotType::BANK; *slot = i; ret = true; break; @@ -3125,7 +3264,7 @@ bool PlayerItemList::GetFirstFreeBankSlot(sint32* bag_id, sint16* slot) { } } else { - *bag_id = -3; + *bag_id = InventorySlotType::BANK; *slot = 0; ret = true; } @@ -3284,9 +3423,13 @@ void PlayerItemList::Stack(Item* orig_item, Item* item){ orig_item->save_needed = true; } -bool PlayerItemList::AssignItemToFreeSlot(Item* item){ +bool PlayerItemList::AssignItemToFreeSlot(Item* item, bool inventory_only){ if(item){ - Item* orig_item = CanStack(item); + Item* orig_item = CanStack(item, !inventory_only); + + if(inventory_only && !IsItemInSlotType(orig_item, InventorySlotType::BASE_INVENTORY)){ + orig_item = nullptr; + } if(orig_item){ Stack(orig_item, item); return true; @@ -3332,8 +3475,9 @@ bool PlayerItemList::AssignItemToFreeSlot(Item* item){ } -void PlayerItemList::RemoveItem(Item* item, bool delete_item){ - MPlayerItems.writelock(__FUNCTION__, __LINE__); +void PlayerItemList::RemoveItem(Item* item, bool delete_item, bool lock){ + if(lock) + MPlayerItems.writelock(__FUNCTION__, __LINE__); if(items.count(item->details.inv_slot_id) > 0 && items[item->details.inv_slot_id][item->details.appearance_type].count(item->details.slot_id) > 0){ items[item->details.inv_slot_id][item->details.appearance_type].erase(item->details.slot_id); indexed_items[item->details.index] = 0; @@ -3360,7 +3504,8 @@ void PlayerItemList::RemoveItem(Item* item, bool delete_item){ lua_interface->SetLuaUserDataStale(item); safe_delete(item); } - MPlayerItems.releasewritelock(__FUNCTION__, __LINE__); + if(lock) + MPlayerItems.releasewritelock(__FUNCTION__, __LINE__); } void PlayerItemList::DestroyItem(int16 index){ @@ -3431,7 +3576,9 @@ int32 PlayerItemList::GetWeight(){ for(int16 i = 0; i < indexed_items.size(); i++){ Item* item = indexed_items[i]; if (item) { - if(item->details.inv_slot_id != -3 && item->details.inv_slot_id != -4) + if(!IsItemInSlotType(item, InventorySlotType::BANK, false) && + !IsItemInSlotType(item, InventorySlotType::SHARED_BANK, false) && + !IsItemInSlotType(item, InventorySlotType::HOUSE_VAULT, false)) ret += item->generic_info.weight; } } @@ -3439,15 +3586,27 @@ int32 PlayerItemList::GetWeight(){ return ret; } +bool PlayerItemList::IsItemInSlotType(Item* item, InventorySlotType type, bool lockItems) { + if(!item) + return false; + + bool matchType = (item->details.inv_slot_id == type); + if(item->details.inv_slot_id > 0) { + Item* bagItem = GetItemFromUniqueID(item->details.inv_slot_id, true, lockItems); + if(bagItem && bagItem->details.inv_slot_id == type) + matchType = true; + } + return matchType; +} bool PlayerItemList::MoveItem(sint32 to_bag_id, int16 from_index, sint8 to, int8 appearance_type, int8 charges){ MPlayerItems.writelock(__FUNCTION__, __LINE__); Item* item_from = indexed_items[from_index]; Item* item_to = 0; - if(item_from){ + if(item_from && !item_from->IsItemLocked()){ if(to_bag_id > 0){ //bag item Item* bag = GetItemFromUniqueID(to_bag_id, true, false); - if(bag && bag->details.num_slots > to && (!item_from || !item_from->IsBag())) + if(bag && !bag->IsItemLocked() && bag->details.num_slots > to && (!item_from || !item_from->IsBag())) item_to = items[to_bag_id][BASE_EQUIPMENT][to]; else{ MPlayerItems.releasewritelock(__FUNCTION__, __LINE__); @@ -3461,6 +3620,11 @@ bool PlayerItemList::MoveItem(sint32 to_bag_id, int16 from_index, sint8 to, int8 return false; } } + + if(item_to && item_to->IsItemLocked()) { + MPlayerItems.releasewritelock(__FUNCTION__, __LINE__); + return false; + } if(charges > 0) { if (item_to && item_from->details.item_id == item_to->details.item_id){ if(item_to->details.count > 0 && item_to->details.count < item_to->stack_count){ @@ -3539,14 +3703,20 @@ bool PlayerItemList::MoveItem(sint32 to_bag_id, int16 from_index, sint8 to, int8 return true; } } - MPlayerItems.releasewritelock(__FUNCTION__, __LINE__); - if (item_to) + bool canMove = true; + if(item_to && item_to->IsItemLocked()) + canMove = false; + + MPlayerItems.releasewritelock(__FUNCTION__, __LINE__); + + if (item_to && canMove) MoveItem(item_to, item_from->details.inv_slot_id, item_from->details.slot_id, BASE_EQUIPMENT, true); - MoveItem(item_from, to_bag_id, to, BASE_EQUIPMENT, item_to ? false:true); + if(canMove) + MoveItem(item_from, to_bag_id, to, BASE_EQUIPMENT, item_to ? false:true); - return true; + return canMove; } MPlayerItems.releasewritelock(__FUNCTION__, __LINE__); return false; @@ -3736,7 +3906,7 @@ void PlayerItemList::AddItemToPacket(PacketStruct* packet, Player* player, Item* //TODO: Add check to allow scribe menu_data += ITEM_MENU_TYPE_SCRIBE; } - if (item->generic_info.item_type == 10){ + if (item->generic_info.item_type == ITEM_TYPE_HOUSE || (item->generic_info.item_type == ITEM_TYPE_HOUSE_CONTAINER && item->details.inv_slot_id == InventorySlotType::HOUSE_VAULT)){ // containers must be in base house slot for placement menu_data += ITEM_MENU_TYPE_TEST1; menu_data += ITEM_MENU_TYPE_HOUSE; } @@ -3785,7 +3955,7 @@ void PlayerItemList::AddItemToPacket(PacketStruct* packet, Player* player, Item* menu_data += ORIG_ITEM_MENU_TYPE_FOOD; } } - if(item->details.item_locked) { + if(item->IsItemLocked()) { menu_data += ITEM_MENU_TYPE_BROKEN; // broken is also used to lock item during crafting } // Added the if (overflow) so mouseover examines work properly @@ -3847,7 +4017,7 @@ bool PlayerItemList::AddOverflowItem(Item* item) { MPlayerItems.writelock(__FUNCTION__, __LINE__); if (item && item->details.item_id > 0 && overflowItems.size() < 255) { item->details.slot_id = 6; - item->details.inv_slot_id = -2; + item->details.inv_slot_id = InventorySlotType::OVERFLOW; overflowItems.push_back(item); ret = true; } @@ -3882,11 +4052,18 @@ vector* PlayerItemList::GetOverflowItemList() { } bool PlayerItemList::HasItem(int32 id, bool include_bank){ + if(include_bank) { + Item* item = GetItemFromID(id, 1, true, true); + if(item) + return true; + else + return false; + } map> >::iterator itr; map::iterator slot_itr; MPlayerItems.readlock(__FUNCTION__, __LINE__); for(itr = items.begin(); itr != items.end(); itr++){ - if(include_bank || (!include_bank && itr->first >= 0)){ + if(itr->first >= 0){ for(slot_itr=itr->second[BASE_EQUIPMENT].begin();slot_itr!=itr->second[BASE_EQUIPMENT].end(); slot_itr++){ if(slot_itr->second && slot_itr->second->details.item_id == id){ MPlayerItems.releasereadlock(__FUNCTION__, __LINE__); @@ -4067,10 +4244,245 @@ Item* PlayerItemList::GetItemFromUniqueID(int32 id, bool include_bank, bool lock return 0; } +void PlayerItemList::SetVaultItemLockUniqueID(Client* client, int64 id, bool state, bool lock) { + if (lock) { + MPlayerItems.readlock(__FUNCTION__, __LINE__); + } + sint32 inv_slot_id = 0; + Item* item = client->GetPlayer()->item_list.GetVaultItemFromUniqueID(id, false); + if(item) { + bool bag_remains_locked = true; + if(state && !item->TryLockItem(LockReason::LockReason_Shop)) { + // this shouldn't happen, but if we have a conflict it might + client->Message(CHANNEL_COLOR_RED, "Failed to lock item %u for vault.", id); + return; + } + else if(!state && !item->TryUnlockItem(LockReason::LockReason_Shop)) { + // still in use for another reason, we don't need to report to the user it will spam them + } + if(item->details.inv_slot_id) { + Item* bagItem = client->GetPlayer()->item_list.GetVaultItemFromUniqueID(item->details.inv_slot_id, false); + if(bagItem) { + inv_slot_id = item->details.inv_slot_id; + if(!state) { + bag_remains_locked = false; + if (auto bagIt = items.find(item->details.inv_slot_id); + bagIt != items.end()) + { + const auto& bagSlots = bagIt->second.at(BASE_EQUIPMENT); + for (auto& [bagSlot, bagItem] : bagSlots) { + if (bagItem && bagItem->IsItemLocked()) { + bag_remains_locked = true; + break; + } + } + } + } + if(!bag_remains_locked) { + bagItem->TryUnlockItem(LockReason::LockReason_Shop); + } + else { + bagItem->TryLockItem(LockReason::LockReason_Shop); + } + } + } + } + if (lock) { + MPlayerItems.releasereadlock(__FUNCTION__, __LINE__); + } + if(inv_slot_id) { + client->GetPlayer()->UpdateInventory(item->details.inv_slot_id); + } + else { + EQ2Packet* outapp = client->GetPlayer()->SendInventoryUpdate(client->GetVersion()); + client->QueuePacket(outapp); + } +} + +bool PlayerItemList::CanStoreSellItem(int64 unique_id, bool lock) { + if (lock) { + MPlayerItems.readlock(__FUNCTION__, __LINE__); + } + Item* item = GetVaultItemFromUniqueID(unique_id, false); + if(!item || (item->CheckFlag(NO_TRADE) && (item->CheckFlag2(HEIRLOOM) == 0))) { + if(lock) + MPlayerItems.releasereadlock(__FUNCTION__, __LINE__); + return false; + } + if(item->CheckFlag(ATTUNED)) { + if(lock) + MPlayerItems.releasereadlock(__FUNCTION__, __LINE__); + return false; + } + + if(item->IsBag() && items.count(item->details.bag_id) > 0){ + map::iterator itr; + for(itr = items[item->details.bag_id][BASE_EQUIPMENT].begin(); itr != items[item->details.bag_id][BASE_EQUIPMENT].end(); itr++){ + if(itr->second->CheckFlag(NO_TRADE) && itr->second->CheckFlag2(HEIRLOOM) == 0){ + if(lock) + MPlayerItems.releasereadlock(__FUNCTION__, __LINE__); + return false; + } + if(item->CheckFlag(ATTUNED)) { + if(lock) + MPlayerItems.releasereadlock(__FUNCTION__, __LINE__); + return false; + } + } + } + if(lock) { + MPlayerItems.releasereadlock(__FUNCTION__, __LINE__); + } + return true; +} + +void PlayerItemList::SetVaultItemUniqueIDCount(Client* client, int64 unique_id, int16 count, bool lock) { + if (lock) { + MPlayerItems.readlock(__FUNCTION__, __LINE__); + } + + sint32 inv_slot_id = 0; + bool countUpdated = false; + Item* item = GetVaultItemFromUniqueID(unique_id, false); + if(item) { + item->details.count = count; + item->save_needed = true; + inv_slot_id = item->details.inv_slot_id; + countUpdated = true; + } + if (lock) { + MPlayerItems.releasereadlock(__FUNCTION__, __LINE__); + } + + if(client && client->GetPlayer() && countUpdated) { + if(inv_slot_id) { + client->GetPlayer()->UpdateInventory(inv_slot_id); + } + else { + EQ2Packet* outapp = client->GetPlayer()->SendInventoryUpdate(client->GetVersion()); + client->QueuePacket(outapp); + } + } +} + + +void PlayerItemList::RemoveVaultItemFromUniqueID(Client* client, int64 unique_id, bool lock) { + if (lock) { + MPlayerItems.writelock(__FUNCTION__, __LINE__); + } + + sint32 inv_slot_id = 0; + bool foundItem = false; + Item* item = GetVaultItemFromUniqueID(unique_id, false); + if(item) { + inv_slot_id = item->details.inv_slot_id; + RemoveItem(item, true, false); + foundItem = true; + } + if (lock) { + MPlayerItems.releasewritelock(__FUNCTION__, __LINE__); + } + + if(client && client->GetPlayer() && foundItem) { + if(inv_slot_id) { + client->GetPlayer()->UpdateInventory(inv_slot_id); + } + else { + EQ2Packet* outapp = client->GetPlayer()->SendInventoryUpdate(client->GetVersion()); + client->QueuePacket(outapp); + } + } +} + +Item* PlayerItemList::GetVaultItemFromUniqueID(int64 id, bool lock) { + if (lock) { + MPlayerItems.readlock(__FUNCTION__, __LINE__); + } + + // 1) Check the house vault + if (auto vaultIt = items.find(InventorySlotType::HOUSE_VAULT); + vaultIt != items.end()) + { + for (auto& [containerIdx, slotMap] : vaultIt->second) { + for (auto& [slotID, itemPtr] : slotMap) { + if(itemPtr) { + LogWrite(PLAYER__ERROR, 0, "Vault", + "--GetVaultItem: %u (%s - %u) needs to match %u", slotID, itemPtr->name.c_str(), itemPtr->details.unique_id, id + ); + } + else { + LogWrite(PLAYER__ERROR, 0, "Vault", + "--GetVaultItem: %u (??) needs to match %u", slotID, id + ); + } + if (itemPtr && itemPtr->details.unique_id == id) { + if (lock) MPlayerItems.releasereadlock(__FUNCTION__, __LINE__); + return itemPtr; + } + + // If it's a bag, search its contents + if (!itemPtr->IsBag()) + continue; + + if (auto bagIt = items.find(itemPtr->details.bag_id); + bagIt != items.end()) + { + const auto& bagSlots = bagIt->second.at(BASE_EQUIPMENT); + for (auto& [bagSlot, bagItem] : bagSlots) { + if (bagItem && bagItem->details.unique_id == id) { + if (lock) MPlayerItems.releasereadlock(__FUNCTION__, __LINE__); + return bagItem; + } + } + } + } + } + } + + // 2) Check base inventory slots and any bags inside them + const auto& baseEquip = items + .at(InventorySlotType::BASE_INVENTORY) + .at(BASE_EQUIPMENT); + + for (int8 slotIdx = 0; slotIdx < NUM_INV_SLOTS; ++slotIdx) { + auto it = baseEquip.find(slotIdx); + if (it == baseEquip.end() || it->second == nullptr) + continue; + + Item* curr = it->second; + // Direct match in base slot + if (curr->details.unique_id == id) { + if (lock) MPlayerItems.releasereadlock(__FUNCTION__, __LINE__); + return curr; + } + + // If it's a bag, search its contents + if (!curr->IsBag()) + continue; + + if (auto bagIt = items.find(curr->details.bag_id); + bagIt != items.end()) + { + const auto& bagSlots = bagIt->second.at(BASE_EQUIPMENT); + for (auto& [bagSlot, bagItem] : bagSlots) { + if (bagItem && bagItem->details.unique_id == id) { + if (lock) MPlayerItems.releasereadlock(__FUNCTION__, __LINE__); + return bagItem; + } + } + } + } + + if (lock) { + MPlayerItems.releasereadlock(__FUNCTION__, __LINE__); + } + return nullptr; +} + bool PlayerItemList::HasFreeBankSlot() { bool ret = false; MPlayerItems.readlock(__FUNCTION__, __LINE__); - if (items[-3][BASE_EQUIPMENT].size() < 12) //12 slots in the bank + if (items[InventorySlotType::BANK][BASE_EQUIPMENT].size() < 12) //12 slots in the bank ret = true; MPlayerItems.releasereadlock(__FUNCTION__, __LINE__); return ret; @@ -4080,7 +4492,7 @@ int8 PlayerItemList::FindFreeBankSlot() { int8 ret = 0; MPlayerItems.readlock(__FUNCTION__, __LINE__); for (int8 i = 0; i < 12; i++) { //12 slots in the bank - if (items[-3][BASE_EQUIPMENT].count(i) == 0) { + if (items[InventorySlotType::BANK][BASE_EQUIPMENT].count(i) == 0) { ret = i; break; } @@ -4089,6 +4501,154 @@ int8 PlayerItemList::FindFreeBankSlot() { return ret; } +void PlayerItemList::PopulateHouseStoragePacket(Client* client, PacketStruct* packet, Item* item, int16 itemIdx, int8 storage_flags) { + int64 cost = broker.GetSalePrice(client->GetPlayer()->GetCharacterID(), item->details.unique_id); + bool sale = broker.IsItemForSale(client->GetPlayer()->GetCharacterID(), item->details.unique_id); + bool isInv = !IsItemInSlotType(item, InventorySlotType::HOUSE_VAULT, false); + client->AddItemSale(item->details.unique_id, item->details.item_id, cost, item->details.inv_slot_id, item->details.slot_id, item->details.count, isInv, sale, item->creator); + + if(broker.IsItemForSale(client->GetPlayer()->GetCharacterID(), item->details.unique_id)) + storage_flags += HouseStoreItemFlags::HOUSE_STORE_FOR_SALE; + + LogWrite(PLAYER__ERROR, 5, "Broker", + "--Sell broker item: %u (%s - %u), cost=%u", + client->GetPlayer()->GetCharacterID(), item->name.c_str(), item->details.unique_id, cost + ); + + packet->setArrayDataByName("your_item_name", item->name.c_str(), itemIdx); + packet->setArrayDataByName("unique_id", item->details.item_id, itemIdx); + packet->setArrayDataByName("unique_id2", item->details.unique_id, itemIdx); + packet->setArrayDataByName("cost", cost, itemIdx); + packet->setArrayDataByName("your_item_quantity", item->details.count, itemIdx); + packet->setArrayDataByName("your_item_icon", item->GetIcon(packet->GetVersion()), itemIdx); + packet->setArrayDataByName("storage_flags", storage_flags, itemIdx); +} + +void PlayerItemList::GetVaultItems(Client* client, int32 spawn_id, int8 maxSlots, bool isSelling) { + int8 ret = 0; + int8 numItems = 0; + MPlayerItems.readlock(__FUNCTION__, __LINE__); + for (int8 i = 0; i < maxSlots; i++) { + if (items[InventorySlotType::HOUSE_VAULT][BASE_EQUIPMENT].count(i) != 0) { + Item* item = items[InventorySlotType::HOUSE_VAULT][BASE_EQUIPMENT][i]; + if(item) { + if(!item->IsBag()) { + numItems++; + } + else { + bool bagHasItem = false; + if(items.count(item->details.bag_id) > 0){ + map::iterator itr; + for(itr = items[item->details.bag_id][BASE_EQUIPMENT].begin(); itr != items[item->details.bag_id][BASE_EQUIPMENT].end(); itr++){ + if(itr->second) { + numItems++; + bagHasItem = true; + } + } + } + if(!bagHasItem) { + numItems++; + } + } + } + } + } + for (int8 i = 0; i < NUM_INV_SLOTS; i++) { + if (items[InventorySlotType::BASE_INVENTORY][BASE_EQUIPMENT].count(i) != 0) { + Item* item = items[InventorySlotType::BASE_INVENTORY][BASE_EQUIPMENT][i]; + if(item) { + if(!item->IsBag()) { + numItems++; + } + else { + bool bagHasItem = false; + if(items.count(item->details.bag_id) > 0){ + map::iterator itr; + for(itr = items[item->details.bag_id][BASE_EQUIPMENT].begin(); itr != items[item->details.bag_id][BASE_EQUIPMENT].end(); itr++){ + if(itr->second) { + numItems++; + bagHasItem = true; + } + } + } + if(!bagHasItem) { + numItems++; + } + } + } + } + } + + PacketStruct* packet = configReader.getStruct("WS_HouseStorage", client->GetVersion()); + if (packet) { + packet->setDataByName("spawn_id", spawn_id); + packet->setDataByName("type", isSelling ? 6 : 4); + packet->setArrayLengthByName("your_item_count", numItems); + int16 itemIdx = 0; + for (int8 i = 0; i < maxSlots; i++) { + if (items[InventorySlotType::HOUSE_VAULT][BASE_EQUIPMENT].count(i) != 0) { + Item* item = items[InventorySlotType::HOUSE_VAULT][BASE_EQUIPMENT][i]; + if(item) { + if(!item->IsBag()) { + PopulateHouseStoragePacket(client, packet, item, itemIdx, HouseStoreItemFlags::HOUSE_STORE_VAULT_TAB); + itemIdx++; + } + else { + bool bagHasItem = false; + if(items.count(item->details.bag_id) > 0){ + map::iterator itr; + for(itr = items[item->details.bag_id][BASE_EQUIPMENT].begin(); itr != items[item->details.bag_id][BASE_EQUIPMENT].end(); itr++){ + if(itr->second) { + PopulateHouseStoragePacket(client, packet, itr->second, itemIdx, HouseStoreItemFlags::HOUSE_STORE_VAULT_TAB); + bagHasItem = true; + itemIdx++; + } + } + } + if(!bagHasItem) { + PopulateHouseStoragePacket(client, packet, item, itemIdx, HouseStoreItemFlags::HOUSE_STORE_VAULT_TAB); + itemIdx++; + } + } + } + } + } + + for (int8 i = 0; i < NUM_INV_SLOTS; i++) { + if (items[InventorySlotType::BASE_INVENTORY][BASE_EQUIPMENT].count(i) != 0) { + Item* item = items[InventorySlotType::BASE_INVENTORY][BASE_EQUIPMENT][i]; + if(item) { + if(!item->IsBag()) { + PopulateHouseStoragePacket(client, packet, item, itemIdx, 0); + itemIdx++; + } + else { + bool bagHasItem = false; + if(items.count(item->details.bag_id) > 0){ + map::iterator itr; + for(itr = items[item->details.bag_id][BASE_EQUIPMENT].begin(); itr != items[item->details.bag_id][BASE_EQUIPMENT].end(); itr++){ + if(itr->second) { + PopulateHouseStoragePacket(client, packet, itr->second, itemIdx, 0); + bagHasItem = true; + itemIdx++; + } + } + } + if(!bagHasItem) { + PopulateHouseStoragePacket(client, packet, item, itemIdx, 0); + itemIdx++; + } + } + } + } + } + EQ2Packet* outapp = packet->serialize(); + client->QueuePacket(outapp); + safe_delete(packet); + } + MPlayerItems.releasereadlock(__FUNCTION__, __LINE__); +} + void PlayerItemList::ResetPackets() { MPlayerItems.writelock(__FUNCTION__, __LINE__); safe_delete_array(orig_packet); @@ -4222,7 +4782,7 @@ bool EquipmentItemList::AddItem(int8 slot, Item* item){ SetItem(slot, item, true); if (item->details.unique_id == 0) { - GetItem(slot)->details.unique_id = MasterItemList::NextUniqueID(); + GetItem(slot)->details.unique_id = master_item_list.NextUniqueID(); if (item->IsBag()) item->details.bag_id = item->details.unique_id; } @@ -4624,6 +5184,57 @@ int16 Item::GetIcon(int16 version) { return details.icon; } +bool Item::TryLockItem(LockReason reason) { + std::unique_lock lock(item_lock_mtx_); + // current flags + auto cur = static_cast(details.lock_flags); + + // 0) If this reason is already applied, succeed immediately + if ((cur & reason) == reason) { + return true; + } + + // 1) No lock held? allow any first‐lock + if (cur == LockReason::LockReason_None) { + details.lock_flags = static_cast(reason); + details.item_locked = true; + return true; + } + + // 2) Only House‐lock held, and we're adding Shop‐lock? allow + if ((cur == LockReason::LockReason_House && reason == LockReason::LockReason_Shop) || + (cur == LockReason::LockReason_Shop && reason == LockReason::LockReason_House)) { + details.lock_flags = static_cast(cur | reason); + // item_locked already true + return true; + } + + // 3) Anything else: reject + return false; +} + +bool Item::TryUnlockItem(LockReason reason) { + std::unique_lock lock(item_lock_mtx_); + LockReason cur = static_cast(details.lock_flags); + if ((cur & reason) == reason) { + details.lock_flags = int32(cur & ~reason); + if (details.lock_flags == 0) + details.item_locked = false; + return true; + } + return false; +} + +bool Item::IsItemLocked() { + std::shared_lock lock(item_lock_mtx_); + return details.lock_flags != 0; +} + +bool Item::IsItemLockedFor(LockReason reason) { + std::shared_lock lock(item_lock_mtx_); + return (static_cast(details.lock_flags) & reason) == reason; +} + int32 MasterItemList::GetItemStatIDByName(std::string name) { boost::to_lower(name); diff --git a/old/WorldServer/Items/Items.h b/old/WorldServer/Items/Items.h index 6a6a019..ed64701 100644 --- a/old/WorldServer/Items/Items.h +++ b/old/WorldServer/Items/Items.h @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,15 +17,17 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #ifndef __EQ2_ITEMS__ #define __EQ2_ITEMS__ #include #include #include -#include "../../common/types.hpp" -#include "../../common/data_buffer.hpp" +#include +#include "../../common/types.h" +#include "../../common/DataBuffer.h" #include "../Commands/Commands.h" -#include "../../common/config_reader.hpp" +#include "../../common/ConfigReader.h" using namespace std; class MasterItemList; @@ -647,6 +649,47 @@ enum ItemEffectType { EFFECT_CURE_TYPE_MAGIC=6, EFFECT_CURE_TYPE_ALL=7 }; + +enum InventorySlotType { + HOUSE_VAULT=-5, + SHARED_BANK=-4, + BANK=-3, + OVERFLOW=-2, + UNKNOWN_INV_SLOT_TYPE=-1, + BASE_INVENTORY=0 +}; + +enum class LockReason : int32 { + LockReason_None = 0, + LockReason_House = 1u << 0, + LockReason_Crafting = 1u << 1, + LockReason_Shop = 1u << 2, +}; + +inline LockReason operator|(LockReason a, LockReason b) { + return static_cast( + static_cast(a) | static_cast(b) + ); +} +inline LockReason operator&(LockReason a, LockReason b) { + return static_cast( + static_cast(a) & static_cast(b) + ); +} +inline LockReason operator~(LockReason a) { + return static_cast(~static_cast(a)); +} + +enum HouseStoreItemFlags { + HOUSE_STORE_ITEM_TEXT_RED=1, + HOUSE_STORE_UNKNOWN_BIT2=2, + HOUSE_STORE_UNKNOWN_BIT4=4, + HOUSE_STORE_FOR_SALE=8, + HOUSE_STORE_UNKNOWN_BIT16=16, + HOUSE_STORE_VAULT_TAB=32 + // rest are also unknown +}; + #pragma pack(1) struct ItemStatsValues{ sint16 str; @@ -710,10 +753,11 @@ struct ItemCore{ int16 count; int8 tier; int8 num_slots; - int32 unique_id; + int64 unique_id; int8 num_free_slots; int16 recommended_level; bool item_locked; + int32 lock_flags; bool new_item; int16 new_index; }; @@ -934,6 +978,8 @@ public: #pragma pack() Item(); Item(Item* in_item); + Item(Item* in_item, int64 unique_id, std::string in_creator, std::string in_seller_name, int32 in_seller_char_id, int64 in_broker_price, int16 count, int64 in_seller_house_id, bool search_in_inventory); + ~Item(); string lowername; string name; @@ -942,10 +988,16 @@ public: int32 sell_price; int32 sell_status; int32 max_sell_value; + int64 broker_price; + bool is_search_store_item; + bool is_search_in_inventory; bool save_needed; int8 weapon_type; string adornment; string creator; + string seller_name; + int32 seller_char_id; + int64 seller_house_id; int32 adorn0; int32 adorn1; int32 adorn2; @@ -986,6 +1038,7 @@ public: bool crafted; bool tinkered; int8 book_language; + mutable std::shared_mutex item_lock_mtx_; void AddEffect(string effect, int8 percentage, int8 subbulletflag); void AddBookPage(int8 page, string page_text,int8 valign, int8 halign); @@ -1067,6 +1120,10 @@ public: void AddSlot(int8 slot_id); void SetSlots(int32 slots); int16 GetIcon(int16 version); + bool TryLockItem(LockReason reason); + bool TryUnlockItem(LockReason reason); + bool IsItemLocked(); + bool IsItemLockedFor(LockReason reason); }; class MasterItemList{ public: @@ -1079,14 +1136,16 @@ public: Item* GetAllItemsByClassification(const char* name); ItemStatsValues* CalculateItemBonuses(int32 item_id, Entity* entity = 0); ItemStatsValues* CalculateItemBonuses(Item* desc, Entity* entity = 0, ItemStatsValues* values = 0); + + bool ShouldAddItemBrokerType(Item* item, int64 itype); + bool ShouldAddItemBrokerSlot(Item* item, int64 ltype); + bool ShouldAddItemBrokerStat(Item* item, int64 btype); vector* GetItems(string name, int64 itype, int64 ltype, int64 btype, int64 minprice, int64 maxprice, int8 minskill, int8 maxskill, string seller, string adornment, int8 mintier, int8 maxtier, int16 minlevel, int16 maxlevel, sint8 itemclass); vector* GetItems(map criteria, Client* client_to_map); void AddItem(Item* item); bool IsBag(int32 item_id); void RemoveAll(); - static int32 NextUniqueID(); - static void ResetUniqueID(int32 new_id); - static int32 next_unique_id; + static int64 NextUniqueID(); int32 GetItemStatIDByName(std::string name); std::string GetItemStatNameByID(int32 id); void AddMappedItemStat(int32 id, std::string lower_case_name); @@ -1120,10 +1179,18 @@ public: void MoveItem(Item* item, sint32 inv_slot, int16 slot, int8 appearance_type, bool erase_old); // erase old was true bool MoveItem(sint32 to_bag_id, int16 from_index, sint8 to, int8 appearance_type, int8 charges); void EraseItem(Item* item); + Item* GetItemFromUniqueID(int32 item_id, bool include_bank = false, bool lock = true); + void SetVaultItemLockUniqueID(Client* client, int64 id, bool state, bool lock); + bool CanStoreSellItem(int64 unique_id, bool lock); + bool IsItemInSlotType(Item* item, InventorySlotType type, bool lockItems=true); + + void SetVaultItemUniqueIDCount(Client* client, int64 unique_id, int16 count, bool lock = true); + void RemoveVaultItemFromUniqueID(Client* client, int64 item_id, bool lock = true); + Item* GetVaultItemFromUniqueID(int64 item_id, bool lock = true); Item* GetItemFromID(int32 item_id, int8 count = 0, bool include_bank = false, bool lock = true); sint32 GetAllStackCountItemFromID(int32 item_id, int8 count = 0, bool include_bank = false, bool lock = true); - bool AssignItemToFreeSlot(Item* item); + bool AssignItemToFreeSlot(Item* item, bool inventory_only = true); int16 GetNumberOfFreeSlots(); int16 GetNumberOfItems(); int32 GetWeight(); @@ -1132,7 +1199,7 @@ public: void DestroyItem(int16 index); Item* CanStack(Item* item, bool include_bank = false); vector GetAllItemsFromID(int32 item, bool include_bank = false, bool lock = false); - void RemoveItem(Item* item, bool delete_item = false); + void RemoveItem(Item* item, bool delete_item = false, bool lock = true); bool AddItem(Item* item); Item* GetItem(sint32 bag_slot, int16 slot, int8 appearance_type = 0); @@ -1143,7 +1210,10 @@ public: map* GetAllItems(); bool HasFreeBankSlot(); int8 FindFreeBankSlot(); - + + void GetVaultItems(Client* client, int32 spawn_id, int8 maxSlots, bool isSelling = false); + void PopulateHouseStoragePacket(Client* client, PacketStruct* packet, Item* item, int16 itemIdx, int8 storage_flags); + ///Get the first free slot and store them in the provided variables ///Will contain the bag id of the first free spot ///Will contain the slot id of the first free slot diff --git a/old/WorldServer/Items/ItemsDB.cpp b/old/WorldServer/Items/ItemsDB.cpp index eafbe91..76c8b99 100644 --- a/old/WorldServer/Items/ItemsDB.cpp +++ b/old/WorldServer/Items/ItemsDB.cpp @@ -1,6 +1,6 @@ -/* +/* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #ifdef WIN32 #include #include @@ -24,7 +25,7 @@ #include #include #include "../../common/Log.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "Items.h" #include "../World.h" #include "../Rules/Rules.h" @@ -172,6 +173,7 @@ void WorldDatabase::LoadDataFromRow(DatabaseResult* result, Item* item) item->generic_info.part_of_quest_id = result->GetInt32Str("part_of_quest_id"); item->details.recommended_level = result->GetInt16Str("recommended_level"); item->details.item_locked = false; + item->details.lock_flags = 0; item->generic_info.adventure_default_level = result->GetInt16Str("adventure_default_level"); item->generic_info.max_charges = result->GetInt16Str("max_charges"); item->generic_info.display_charges = result->GetInt8Str("display_charges"); @@ -473,7 +475,7 @@ int32 WorldDatabase::LoadHouseItem(int32 item_id) { LogWrite(ITEM__DEBUG, 5, "Items", "\tItem HouseItem for item_id %u", id); LogWrite(ITEM__DEBUG, 5, "Items", "\ttype: %i, %i, %u, %.2f, %u", ITEM_TYPE_HOUSE, atoul(row[1]), atoi(row[2]), atof(row[3]), atoul(row[4])); - item->SetItemType(ITEM_TYPE_HOUSE); + item->SetItemType(ITEM_TYPE_HOUSE); // container will be overwritten by LoadHouseContainers which is ran after 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]); @@ -541,7 +543,7 @@ int32 WorldDatabase::LoadHouseContainers(int32 item_id){ if (item) { LogWrite(ITEM__DEBUG, 5, "Items", "\tHouse Container for item_id %u", id); - LogWrite(ITEM__DEBUG, 5, "Items", "\tType: %i, '%i', '%u', '%i', '%i'", ITEM_TYPE_RECIPE, result.GetInt8Str("num_slots"), result.GetInt64Str("allowed_types"), result.GetInt8Str("broker_commission"), result.GetInt8Str("fence_commission")); + LogWrite(ITEM__DEBUG, 5, "Items", "\tType: %i, '%i', '%u', '%i', '%i'", ITEM_TYPE_HOUSE_CONTAINER, result.GetInt8Str("num_slots"), result.GetInt64Str("allowed_types"), result.GetInt8Str("broker_commission"), result.GetInt8Str("fence_commission")); item->SetItemType(ITEM_TYPE_HOUSE_CONTAINER); item->housecontainer_info->num_slots = result.GetInt8Str("num_slots"); @@ -549,6 +551,11 @@ int32 WorldDatabase::LoadHouseContainers(int32 item_id){ item->housecontainer_info->broker_commission = result.GetInt8Str("broker_commission"); item->housecontainer_info->fence_commission = result.GetInt8Str("fence_commission"); + item->details.num_slots = item->housecontainer_info->num_slots; + item->details.num_free_slots = item->housecontainer_info->num_slots; + item->bag_info->num_slots = item->housecontainer_info->num_slots; + item->bag_info->weight_reduction = 0; + total++; } else @@ -1069,7 +1076,7 @@ void WorldDatabase::LoadItemList(int32 item_id) LogWrite(ITEM__DEBUG, 0, "Items", "\tLoaded %u Skill Items", LoadSkillItems(item_id)); LogWrite(ITEM__DEBUG, 0, "Items", "\tLoaded %u Adornment Items", LoadAdornments(item_id)); LogWrite(ITEM__DEBUG, 0, "Items", "\tLoaded %u Recipe Book Items", LoadRecipeBookItems(item_id)); - LogWrite(ITEM__DEBUG, 0, "Items", "\tLoaded %u House Containers", LoadHouseContainers(item_id)); + LogWrite(ITEM__DEBUG, 0, "Items", "\tLoaded %u House Containers", LoadHouseContainers(item_id)); // must be called after LoadHouseItem LogWrite(ITEM__DEBUG, 0, "Items", "Loading Item Appearances..."); LogWrite(ITEM__DEBUG, 0, "Items", "\tLoaded %u Item Appearances", LoadItemAppearances(item_id)); @@ -1097,18 +1104,19 @@ void WorldDatabase::LoadItemList(int32 item_id) LogWrite(ITEM__INFO, 0, "Items", "Loaded %u Total Item%s (took %u seconds)", total, ( total == 1 ) ? "" : "s", Timer::GetUnixTimeStamp() - t_now); } -int32 WorldDatabase::LoadNextUniqueItemID() +int64 WorldDatabase::LoadNextUniqueItemID() { Query query; MYSQL_ROW row; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT max(id) FROM character_items"); + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT NEXT VALUE FOR seq_character_items AS next_id"); if(result && (row = mysql_fetch_row(result))) { if(row[0]) { - LogWrite(ITEM__DEBUG, 0, "Items", "%s: max(id): %u", __FUNCTION__, atoul(row[0])); - return strtoul(row[0], NULL, 0); + int64 max_ = strtoull(row[0], NULL, 0); + LogWrite(ITEM__DEBUG, 0, "Items", "%s: max(id): %u", __FUNCTION__, max_); + return max_; } else return 0; @@ -1119,6 +1127,31 @@ int32 WorldDatabase::LoadNextUniqueItemID() return 0; } +void WorldDatabase::ResetNextUniqueItemID() +{ + Query query; + Query query2; + MYSQL_ROW row; + MYSQL_ROW row2; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT next_not_cached_value FROM seq_character_items"); + MYSQL_RES* result2 = query2.RunQuery2(Q_SELECT, "SELECT COALESCE(MAX(id),0) + 1 FROM character_items"); + if(result && (row = mysql_fetch_row(result)) && result2 && (row2 = mysql_fetch_row(result2))) + { + if(row[0] && row2[0]) + { + int64 max_cur = strtoull(row[0], NULL, 0); + int64 max_expected = strtoull(row2[0], NULL, 0); + string update_item = string("ALTER SEQUENCE seq_character_items RESTART WITH %llu"); + if(max_cur < max_expected) + query.AddQueryAsync(0, this, Q_UPDATE, update_item.c_str(), max_expected); + + LogWrite(ITEM__DEBUG, 0, "Items", "%s: max(current): %u max(expected): %u", __FUNCTION__, max_cur, max_expected); + } + } + else if(!result) + LogWrite(ITEM__ERROR, 0, "Items", "%s: Unable to reset next unique item ID.", __FUNCTION__); +} + void WorldDatabase::SaveItems(Client* client) { LogWrite(ITEM__DEBUG, 3, "Items", "Save Items for Player %i", client->GetCharacterID()); @@ -1382,7 +1415,7 @@ void WorldDatabase::LoadCharacterItemList(int32 account_id, int32 char_id, Playe int8 remainder = item->details.count % 255; item->details.count = remainder; - if (item->details.inv_slot_id == -2) + if (item->details.inv_slot_id == InventorySlotType::OVERFLOW) player->item_list.AddOverflowItem(item); else { if(!player->item_list.AddItem(item)) @@ -1398,7 +1431,7 @@ void WorldDatabase::LoadCharacterItemList(int32 account_id, int32 char_id, Playe } } else { - if (item->details.inv_slot_id == -2) + if (item->details.inv_slot_id == InventorySlotType::OVERFLOW) player->item_list.AddOverflowItem(item); else player->item_list.AddItem(item); diff --git a/old/WorldServer/Items/Items_CoE.h b/old/WorldServer/Items/Items_CoE.h index 0e9aa7c..457c0f1 100644 --- a/old/WorldServer/Items/Items_CoE.h +++ b/old/WorldServer/Items/Items_CoE.h @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,15 +17,16 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #ifndef __EQ2_ITEMS__ #define __EQ2_ITEMS__ #include #include -#include "../../common/types.hpp" -#include "../../common/data_buffer.hpp" -#include "../../common/misc_functions.hpp" +#include "../../common/types.h" +#include "../../common/DataBuffer.h" +#include "../../common/MiscFunctions.h" #include "../Commands/Commands.h" -#include "../../common/config_reader.hpp" +#include "../../common/ConfigReader.h" using namespace std; class MasterItemList; @@ -730,9 +731,7 @@ public: void AddItem(Item* item); bool IsBag(int32 item_id); void RemoveAll(); - static int32 NextUniqueID(); - static void ResetUniqueID(int32 new_id); - static int32 next_unique_id; + static int64 NextUniqueID(); }; class PlayerItemList { public: diff --git a/old/WorldServer/Items/Items_DoV.h b/old/WorldServer/Items/Items_DoV.h index 444fc41..7ce9a88 100644 --- a/old/WorldServer/Items/Items_DoV.h +++ b/old/WorldServer/Items/Items_DoV.h @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,15 +17,16 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #ifndef __EQ2_ITEMS__ #define __EQ2_ITEMS__ #include #include -#include "../../common/types.hpp" -#include "../../common/data_buffer.hpp" -#include "../../common/misc_functions.hpp" +#include "../../common/types.h" +#include "../../common/DataBuffer.h" +#include "../../common/MiscFunctions.h" #include "../Commands/Commands.h" -#include "../../common/config_reader.hpp" +#include "../../common/ConfigReader.h" using namespace std; class MasterItemList; @@ -824,9 +825,7 @@ public: void AddItem(Item* item); bool IsBag(int32 item_id); void RemoveAll(); - static int32 NextUniqueID(); - static void ResetUniqueID(int32 new_id); - static int32 next_unique_id; + static int64 NextUniqueID(); }; class PlayerItemList { public: diff --git a/old/WorldServer/Items/Loot.cpp b/old/WorldServer/Items/Loot.cpp index ffb0fc5..ecc92fe 100644 --- a/old/WorldServer/Items/Loot.cpp +++ b/old/WorldServer/Items/Loot.cpp @@ -19,9 +19,9 @@ */ #include "Loot.h" #include "../client.h" -#include "../../common/config_reader.hpp" +#include "../../common/ConfigReader.h" #include "../classes.h" -#include "../../common/debug.hpp" +#include "../../common/debug.h" #include "../zoneserver.h" #include "../Skills.h" #include "../classes.h" diff --git a/old/WorldServer/Items/LootDB.cpp b/old/WorldServer/Items/LootDB.cpp index 879ac5b..b448fd1 100644 --- a/old/WorldServer/Items/LootDB.cpp +++ b/old/WorldServer/Items/LootDB.cpp @@ -19,7 +19,7 @@ */ #include "../../common/Log.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "../World.h" extern World world; diff --git a/old/WorldServer/Languages.h b/old/WorldServer/Languages.h index dae6e37..4d8a1e2 100644 --- a/old/WorldServer/Languages.h +++ b/old/WorldServer/Languages.h @@ -23,7 +23,7 @@ #include #include -#include "../common/types.hpp" +#include "../common/types.h" using namespace std; diff --git a/old/WorldServer/LoginServer.cpp b/old/WorldServer/LoginServer.cpp index 128d2bc..cbaec9d 100644 --- a/old/WorldServer/LoginServer.cpp +++ b/old/WorldServer/LoginServer.cpp @@ -17,8 +17,8 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ -#include "../common/debug.hpp" -#include "../common/log.hpp" +#include "../common/debug.h" +#include "../common/Log.h" #include using namespace std; #include @@ -58,13 +58,13 @@ extern int errno; #include "../common/servertalk.h" #include "LoginServer.h" -#include "../common/packet/packet_dump.hpp" +#include "../common/packet_dump.h" #include "net.h" #include "zoneserver.h" -#include "WorldDatabase.hpp" +#include "WorldDatabase.h" #include "Variables.h" #include "World.h" -#include "../common/config_reader.hpp" +#include "../common/ConfigReader.h" #include "Rules/Rules.h" #include "Web/PeerManager.h" #include "Web/HTTPSClientPool.h" @@ -583,7 +583,7 @@ int32 LoginServer::DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* u else status = database.GetCharacterAdminStatus ( utwr->lsaccountid , utwr->char_id ); - if(status < 100 && zone_list.ClientConnected(utwr->lsaccountid)) + if(status < 100 && zone_list.ClientConnected(utwr->lsaccountid, utwr->char_id)) status = -9; if(status < 0){ LogWrite(WORLD__ERROR, 0, "World", "Login Rejected based on PLAY_ERROR (UserStatus) (MinStatus: %i), UserStatus: %i, CharID: %i",loginserver.minLockedStatus,status,utwr->char_id ); @@ -592,7 +592,7 @@ int32 LoginServer::DetermineCharacterLoginRequest ( UsertoWorldRequest_Struct* u response = PLAY_ERROR_CHAR_NOT_LOADED; break; case -9: - response = 0;//PLAY_ERROR_ACCOUNT_IN_USE; + response = PLAY_ERROR_ACCOUNT_IN_USE; break; case -8: response = PLAY_ERROR_LOADING_ERROR; diff --git a/old/WorldServer/LoginServer.h b/old/WorldServer/LoginServer.h index 8dd84c5..e888c8b 100644 --- a/old/WorldServer/LoginServer.h +++ b/old/WorldServer/LoginServer.h @@ -24,7 +24,7 @@ #include "../common/linked_list.h" #include "../common/timer.h" #include "../common/queue.h" - +#include "../common/Mutex.h" #include "../common/TCPConnection.h" #include #include "MutexMap.h" diff --git a/old/WorldServer/LuaFunctions.cpp b/old/WorldServer/LuaFunctions.cpp index 8188b1c..abf38f6 100644 --- a/old/WorldServer/LuaFunctions.cpp +++ b/old/WorldServer/LuaFunctions.cpp @@ -1,27 +1,28 @@ -/* - EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) - This file is part of EQ2Emulator. + This file is part of EQ2Emulator. - EQ2Emulator 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. + EQ2Emulator 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. - EQ2Emulator 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. + EQ2Emulator 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 EQ2Emulator. If not, see . + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . */ + #include "LuaFunctions.h" #include "Spawn.h" -#include "WorldDatabase.hpp" +#include "WorldDatabase.h" #include "LuaInterface.h" -#include "../common/config_reader.hpp" +#include "../common/ConfigReader.h" #include "client.h" #include "World.h" #include "Commands/Commands.h" @@ -30,13 +31,14 @@ #include "Variables.h" #include "SpellProcess.h" #include "Rules/Rules.h" -#include "../common/log.hpp" +#include "../common/Log.h" #include #include "HeroicOp/HeroicOp.h" #include "RaceTypes/RaceTypes.h" #include "ClientPacketFunctions.h" #include "Transmute.h" #include "Titles.h" +#include "./Broker/BrokerManager.h" #include #include #include @@ -60,6 +62,7 @@ extern MasterRaceTypeList race_types_list; extern MasterLanguagesList master_languages_list; extern MasterTitlesList master_titles_list; extern RuleManager rule_manager; +extern BrokerManager broker; vector ParseString(string strVal, char delim) { stringstream ss(strVal); @@ -1401,17 +1404,15 @@ int EQ2Emu_lua_SpellHeal(lua_State* state) { if (((Entity*)caster)->SpellHeal(target, distance, luaspell, heal_type, min_heal, max_heal, crit_mod, no_calcs, custom_spell_name)) success = true; } - if ((!success || luaspell->spell->GetSpellData()->group_spell) && luaspell->targets.size() > 0) { + if ((!success || luaspell->spell->GetSpellData()->group_spell) && luaspell->GetTargetCount() > 0) { Spawn* target = 0; - ZoneServer* zone = luaspell->caster->GetZone(); - luaspell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int32 i = 0; i < luaspell->targets.size(); i++) { - if ((target = zone->GetSpawnByID(luaspell->targets[i]))) { + ZoneServer* zone = luaspell->zone; + for (int32 id : luaspell->GetTargets()) { + if ((target = zone->GetSpawnByID(id))) { float distance = caster->GetDistance(target, true); ((Entity*)caster)->SpellHeal(target, distance, luaspell, heal_type, min_heal, max_heal, crit_mod, no_calcs, custom_spell_name); } } - luaspell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); success = true; } if (success) { @@ -1494,17 +1495,15 @@ int EQ2Emu_lua_SpellHealPct(lua_State* state) { if (((Entity*)caster)->SpellHeal(target, distance, luaspell, heal_type, min_heal, max_heal, crit_mod, no_calcs, custom_spell_name)) success = true; } - if ((!success || luaspell->spell->GetSpellData()->group_spell) && luaspell->targets.size() > 0) { + if ((!success || luaspell->spell->GetSpellData()->group_spell) && luaspell->GetTargetCount() > 0) { Spawn* target = 0; - ZoneServer* zone = luaspell->caster->GetZone(); - luaspell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int32 i = 0; i < luaspell->targets.size(); i++) { - if ((target = zone->GetSpawnByID(luaspell->targets[i]))) { + ZoneServer* zone = luaspell->zone; + for (int32 id : luaspell->GetTargets()) { + if ((target = zone->GetSpawnByID(id))) { float distance = caster->GetDistance(target, true); ((Entity*)caster)->SpellHeal(target, distance, luaspell, heal_type, min_heal, max_heal, crit_mod, no_calcs, custom_spell_name); } } - luaspell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); success = true; } if (success) { @@ -1751,10 +1750,9 @@ int EQ2Emu_lua_AddHate(lua_State* state) { if (entity && entity->IsEntity() && amount != 0) { if (luaspell && luaspell->caster) { - ZoneServer* zone = luaspell->caster->GetZone(); - luaspell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int32 i = 0; i < luaspell->targets.size(); i++) { - Spawn* spawn = zone->GetSpawnByID(luaspell->targets.at(i)); + ZoneServer* zone = luaspell->zone; + for (int32 id : luaspell->GetTargets()) { + Spawn* spawn = zone->GetSpawnByID(id); if (spawn && spawn->IsNPC() && spawn->Alive() && spawn->GetZone()) { entity->CheckEncounterState((Entity*)spawn); ((NPC*)spawn)->AddHate((Entity*)entity, amount); @@ -1762,7 +1760,6 @@ int EQ2Emu_lua_AddHate(lua_State* state) { entity->GetZone()->SendThreatPacket(entity, npc, amount, luaspell->spell->GetName()); } } - luaspell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } else if (npc && npc->IsNPC() && npc->GetZone()) { entity->CheckEncounterState((Entity*)npc); @@ -1910,12 +1907,11 @@ int EQ2Emu_lua_SpellDamage(lua_State* state) { bool race_match = false; bool success = false; luaspell->resisted = false; - if (luaspell->targets.size() > 0) { - ZoneServer* zone = luaspell->caster->GetZone(); + if (luaspell->GetTargetCount() > 0) { + ZoneServer* zone = luaspell->zone; Spawn* target = 0; - luaspell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int32 i = 0; i < luaspell->targets.size(); i++) { - if ((target = zone->GetSpawnByID(luaspell->targets[i]))) { + for (int32 id : luaspell->GetTargets()) { + if ((target = zone->GetSpawnByID(id))) { if (race_req.size() > 0) { for (int8 i = 0; i < race_req.size(); i++) { @@ -1935,7 +1931,6 @@ int EQ2Emu_lua_SpellDamage(lua_State* state) { } } success = true; - luaspell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } else if (target) { @@ -2018,12 +2013,11 @@ int EQ2Emu_lua_SpellDamageExt(lua_State* state) { bool race_match = false; bool success = false; luaspell->resisted = false; - if (luaspell->targets.size() > 0) { - ZoneServer* zone = luaspell->caster->GetZone(); + if (luaspell->GetTargetCount() > 0) { + ZoneServer* zone = luaspell->zone; Spawn* target = 0; - luaspell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int32 i = 0; i < luaspell->targets.size(); i++) { - if ((target = zone->GetSpawnByID(luaspell->targets[i]))) { + for (int32 id : luaspell->GetTargets()) { + if ((target = zone->GetSpawnByID(id))) { if (race_req.size() > 0) { for (int8 i = 0; i < race_req.size(); i++) { @@ -2043,7 +2037,6 @@ int EQ2Emu_lua_SpellDamageExt(lua_State* state) { } } success = true; - luaspell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } else if (target) { @@ -2455,10 +2448,9 @@ int EQ2Emu_lua_AddSpellBonus(lua_State* state) { if (value != 0 && type >= 0) { if (luaspell && luaspell->spell && luaspell->caster) { - ZoneServer* zone = luaspell->caster->GetZone(); - luaspell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int32 i = 0; i < luaspell->targets.size(); i++) { - Spawn* target = zone->GetSpawnByID(luaspell->targets[i]); + ZoneServer* zone = luaspell->zone; + for (int32 id : luaspell->GetTargets()) { + Spawn* target = zone->GetSpawnByID(id); if (target) { if (target->IsPlayer()) { ((Player*)target)->AddSpellBonus(luaspell, type, value, class_req, race_req, faction_req); @@ -2473,7 +2465,6 @@ int EQ2Emu_lua_AddSpellBonus(lua_State* state) { lua_interface->LogError("%s: Error applying spell bonus on non entity.", lua_interface->GetScriptName(state)); } } - luaspell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); if (!(luaspell->effect_bitmask & EFFECT_FLAG_SPELLBONUS)) luaspell->effect_bitmask += EFFECT_FLAG_SPELLBONUS; } @@ -2599,22 +2590,20 @@ int EQ2Emu_lua_RemoveSpellBonus(lua_State* state) { else if (luaspell && luaspell->spell) { ZoneServer* zone = nullptr; if (luaspell->caster != nullptr) - zone = luaspell->caster->GetZone(); + zone = luaspell->zone; if(!zone && spawn) { zone = spawn->GetZone(); // workaround to try to establish a zone to find the targets and remove the spells } Spawn* target = 0; if(zone) { - luaspell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int32 i = 0; i < luaspell->targets.size(); i++) { - target = zone->GetSpawnByID(luaspell->targets[i]); + for (int32 id : luaspell->GetTargets()) { + target = zone->GetSpawnByID(id); if (target && target->IsEntity()) { ((Entity*)target)->RemoveSpellBonus(luaspell); if (target->IsPlayer()) ((Player*)target)->SetCharSheetChanged(true); } } - 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 ? spawn->GetName() : "N/A"); @@ -2638,11 +2627,10 @@ int EQ2Emu_lua_AddSkillBonus(lua_State* state) { return 0; } spell_id = luaspell->spell->GetSpellID(); - ZoneServer* zone = luaspell->caster->GetZone(); + ZoneServer* zone = luaspell->zone; Spawn* target = 0; - luaspell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int32 i = 0; i < luaspell->targets.size(); i++) { - target = zone->GetSpawnByID(luaspell->targets[i]); + for (int32 id : luaspell->GetTargets()) { + target = zone->GetSpawnByID(id); if (target && target->Alive()) { if (target->IsPlayer()) { ((Player*)target)->AddSkillBonus(spell_id, skill_id, value); @@ -2664,7 +2652,6 @@ int EQ2Emu_lua_AddSkillBonus(lua_State* state) { LogWrite(LUA__ERROR, 0, "LUA", "Error applying bonus buff on '%s'. Not a NPC or player.", target->GetName()); } } - luaspell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } else if (spawn) { if (spawn->IsPlayer()) { @@ -2699,11 +2686,10 @@ int EQ2Emu_lua_RemoveSkillBonus(lua_State* state) { return 0; } spell_id = luaspell->spell->GetSpellID(); - ZoneServer* zone = luaspell->caster->GetZone(); + ZoneServer* zone = luaspell->zone; Spawn* target = 0; - luaspell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int32 i = 0; i < luaspell->targets.size(); i++) { - target = zone->GetSpawnByID(luaspell->targets[i]); + for (int32 id : luaspell->GetTargets()) { + target = zone->GetSpawnByID(id); if (target) { if (target->IsPlayer()) { ((Player*)target)->RemoveSkillBonus(spell_id); @@ -2720,7 +2706,6 @@ int EQ2Emu_lua_RemoveSkillBonus(lua_State* state) { LogWrite(LUA__ERROR, 0, "LUA", "Error removing skill bonus on '%s'. Not a NPC or player.", spawn->GetName()); } } - luaspell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } else if (spawn) { if (spawn->IsPlayer()) { @@ -2754,11 +2739,10 @@ int EQ2Emu_lua_AddControlEffect(lua_State* state) { } if (!only_add_spawn && luaspell && luaspell->spell && luaspell->caster && type != 0) { - ZoneServer* zone = luaspell->caster->GetZone(); + ZoneServer* zone = luaspell->zone; Spawn* target = 0; - luaspell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int32 i = 0; i < luaspell->targets.size(); i++) { - target = zone->GetSpawnByID(luaspell->targets[i]); + for (int32 id : luaspell->GetTargets()) { + target = zone->GetSpawnByID(id); if (target && target->IsEntity()) { if (type == CONTROL_EFFECT_TYPE_MEZ) { ((Entity*)target)->AddMezSpell(luaspell); @@ -2840,7 +2824,6 @@ int EQ2Emu_lua_AddControlEffect(lua_State* state) { else lua_interface->LogError("%s: Error applying control effect on non entity '%s'.", lua_interface->GetScriptName(state), (target != nullptr) ? target->GetName() : "NO_TARGET"); } - luaspell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } else if (only_add_spawn && spawn && spawn->IsEntity()) { if (type == CONTROL_EFFECT_TYPE_MEZ) { @@ -2921,11 +2904,10 @@ int EQ2Emu_lua_RemoveControlEffect(lua_State* state) { lua_interface->ResetFunctionStack(state); if (spawn && spawn->IsEntity()) { if (!only_remove_spawn && luaspell && luaspell->spell && luaspell->caster) { - ZoneServer* zone = luaspell->caster->GetZone(); + ZoneServer* zone = luaspell->zone; Spawn* target = 0; - luaspell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int32 i = 0; i < luaspell->targets.size(); i++) { - target = zone->GetSpawnByID(luaspell->targets[i]); + for (int32 id : luaspell->GetTargets()) { + target = zone->GetSpawnByID(id); if (target) { if (type == CONTROL_EFFECT_TYPE_MEZ) ((Entity*)target)->RemoveMezSpell(luaspell); @@ -2955,7 +2937,6 @@ int EQ2Emu_lua_RemoveControlEffect(lua_State* state) { lua_interface->LogError("%s: Unhandled control effect type of %u.", lua_interface->GetScriptName(state), type); } } - luaspell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } else if (only_remove_spawn) { if (type == CONTROL_EFFECT_TYPE_MEZ) @@ -3901,7 +3882,6 @@ int EQ2Emu_lua_OfferQuest(lua_State* state) { bool forced = lua_interface->GetBooleanValue(state, 4); lua_interface->ResetFunctionStack(state); - /* NPC is allowed to be null */ if (player && player->IsPlayer() && quest_id > 0) { Quest* master_quest = master_quest_list.GetQuest(quest_id, false); if (master_quest) { @@ -3931,6 +3911,52 @@ int EQ2Emu_lua_OfferQuest(lua_State* state) { return 0; } +int EQ2Emu_lua_DeleteQuest(lua_State* state) { + if (!lua_interface) + return 0; + Spawn* player = lua_interface->GetSpawn(state); + int32 quest_id = lua_interface->GetInt32Value(state, 2); + bool override_deny_delete = lua_interface->GetBooleanValue(state, 3); + lua_interface->ResetFunctionStack(state); + + if (player && player->IsPlayer() && quest_id > 0) { + Client* client = ((Player*)player)->GetClient(); + if (!client) { + lua_interface->LogError("%s: LUA DeleteQuest command error: client is not set", lua_interface->GetScriptName(state)); + } + else { + client->DeleteQuest(quest_id, override_deny_delete); + } + } + else { + lua_interface->LogError("%s: LUA DeleteQuest command error: player is not set or bad quest id %d", lua_interface->GetScriptName(state), quest_id); + } + return 0; +} + +int EQ2Emu_lua_DeleteAllQuests(lua_State* state) { + if (!lua_interface) + return 0; + Spawn* player = lua_interface->GetSpawn(state); + bool override_deny_delete = lua_interface->GetBooleanValue(state, 2); + lua_interface->ResetFunctionStack(state); + + /* NPC is allowed to be null */ + if (player && player->IsPlayer()) { + Client* client = ((Player*)player)->GetClient(); + if (!client) { + lua_interface->LogError("%s: LUA DeleteAllQuests command error: client is not set", lua_interface->GetScriptName(state)); + } + else { + client->DeleteAllQuests(override_deny_delete); + } + } + else { + lua_interface->LogError("%s: LUA DeleteAllQuests command error: player is not set", lua_interface->GetScriptName(state)); + } + return 0; +} + int EQ2Emu_lua_AddQuestPrereqClass(lua_State* state) { if (!lua_interface) return 0; @@ -4681,7 +4707,7 @@ int EQ2Emu_lua_Harvest(lua_State* state) { ((GroundSpawn*)node)->ProcessHarvest(client); if (((GroundSpawn*)node)->GetNumberHarvests() == 0) { LuaSpell* spell = lua_interface->GetCurrentSpell(state); - player->GetZone()->RemoveSpawn(node, true, true, true, true, (spell != nullptr) ? false : true); + player->GetZone()->RemoveSpawn(node, true, true, true, false, (spell != nullptr) ? false : true); } } } @@ -6533,13 +6559,15 @@ int EQ2Emu_lua_AddWard(lua_State* state) { bool ward_was_added = false; - ZoneServer* zone = spell->caster->GetZone(); - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int32 i = 0; i < spell->targets.size(); i++) { - Spawn* target = zone->GetSpawnByID(spell->targets.at(i)); + ZoneServer* zone = spell->zone; + for (int32 id : spell->GetTargets()) { + Spawn* target = zone->GetSpawnByID(id); if (!target) continue; if (target->IsEntity()) { + if(spell->is_loaded_recast && ((Entity*)target)->GetWard(spell->spell->GetSpellID())) // recast of spell, skip readding ward if they have it + continue; + // If the ward is already active remove it if (((Entity*)target)->GetWard(spell->spell->GetSpellID())) ((Entity*)target)->RemoveWard(spell->spell->GetSpellID()); @@ -6577,6 +6605,8 @@ int EQ2Emu_lua_AddWard(lua_State* state) { if (wardType == WARD_TYPE_MAGICAL) ward->DamageType = damageTypes; + else + ward->DamageType = 0; // doesn't get used, but default set it anyway ward->DeleteWard = false; // Add the ward to the entity @@ -6584,7 +6614,6 @@ int EQ2Emu_lua_AddWard(lua_State* state) { ward_was_added = true; } } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); if (ward_was_added && spell->caster->IsPlayer()) { spell->had_dmg_remaining = true; @@ -6606,23 +6635,26 @@ int EQ2Emu_lua_AddToWard(lua_State* state) { if(!spell || spell->resisted) { return 0; } - ZoneServer* zone = spell->caster->GetZone(); - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - if (spell->targets.size() > 0 && zone->GetSpawnByID(spell->targets.at(0))->IsEntity()) { - Entity* target = (Entity*)zone->GetSpawnByID(spell->targets.at(0)); + if(!spell->caster || !spell->zone) { + return 0; + } + ZoneServer* zone = spell->zone; + Entity* target = nullptr; + if (auto target_id_opt = spell->GetPrimaryTarget(); + target_id_opt && (target = (Entity*)zone->GetSpawnByID(*target_id_opt))) { ward = target->GetWard(spell->spell->GetSpellID()); if (target && ward) { ward->DamageLeft += amount; if (ward->DamageLeft > ward->BaseDamage) ward->DamageLeft = ward->BaseDamage; - for (int32 i = 0; i < spell->targets.size(); i++) { - if (Spawn* spawn = zone->GetSpawnByID(spell->targets.at(i))) + for (int32 id : spell->GetTargets()) { + if (Spawn* spawn = zone->GetSpawnByID(id)) { zone->SendHealPacket(ward->Spell->caster, spawn, HEAL_PACKET_TYPE_REGEN_ABSORB, amount, ward->Spell->spell->GetName()); + } } } } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); if (ward && spell->caster->IsPlayer()) ClientPacketFunctions::SendMaintainedExamineUpdate(((Player*)spell->caster)->GetClient(), spell->slot_pos, ward->DamageLeft, 1); @@ -6641,17 +6673,15 @@ int EQ2Emu_lua_GetWardAmountLeft(lua_State* state) { return 0; } - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - if (spell->caster && spell->caster->GetZone() && spell->targets.size() > 0 && spell->caster->GetZone()->GetSpawnByID(spell->targets.at(0))->IsEntity()) { - Entity* target = (Entity*)spell->caster->GetZone()->GetSpawnByID(spell->targets.at(0)); + Entity* target = nullptr; + if (auto target_id_opt = spell->GetPrimaryTarget(); + target_id_opt && spell->zone && (target = (Entity*)spell->zone->GetSpawnByID(*target_id_opt))) { WardInfo* ward = target->GetWard(spell->spell->GetSpellID()); if (ward) { lua_interface->SetInt32Value(state, ward->DamageLeft); - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); return 1; } } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); return 0; } @@ -6669,11 +6699,9 @@ int EQ2Emu_lua_GetWardValue(lua_State* state) { string type = lua_interface->GetStringValue(state, 2); lua_interface->ResetFunctionStack(state); - - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - if (spell->caster && spell->caster->GetZone() && spell->targets.size() > 0 && spell->caster->GetZone()->GetSpawnByID(spell->targets.at(0))->IsEntity()) { - - Entity* target = (Entity*)spell->caster->GetZone()->GetSpawnByID(spell->targets.at(0)); + Entity* target = nullptr; + if (auto target_id_opt = spell->GetPrimaryTarget(); + target_id_opt && spell->zone && (target = (Entity*)spell->zone->GetSpawnByID(*target_id_opt))) { WardInfo* ward = target->GetWard(spell->spell->GetSpellID()); if (ward) { if (boost::iequals(type, "damageleft")) @@ -6700,12 +6728,9 @@ int EQ2Emu_lua_GetWardValue(lua_State* state) { lua_interface->SetInt32Value(state, ward->MaxHitCount); else lua_interface->LogError("%s: LUA GetWardValue command argument type '%s' did not match any options", lua_interface->GetScriptName(state), type.c_str()); - - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); return 1; } } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); return 0; } @@ -6725,21 +6750,19 @@ int EQ2Emu_lua_RemoveWard(lua_State* state) { return 0; } - ZoneServer* zone = spell->caster->GetZone(); + ZoneServer* zone = spell->zone; 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++) { - target = zone->GetSpawnByID(spell->targets.at(i)); + for (int32 id : spell->GetTargets()) { + target = zone->GetSpawnByID(id); if (target && target->IsEntity()) { ((Entity*)target)->RemoveWard(spell->spell->GetSpellID()); } } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); return 0; } @@ -6779,15 +6802,13 @@ int EQ2Emu_lua_Interrupt(lua_State* state) } if (!target && spell) { - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int8 i = 0; i < spell->targets.size(); i++) { - target = caster->GetZone()->GetSpawnByID(spell->targets.at(i)); + for (int32 id : spell->GetTargets()) { + target = caster->GetZone()->GetSpawnByID(id); if (!target || !target->IsEntity()) continue; ((Entity*)target)->GetZone()->GetSpellProcess()->Interrupted((Entity*)target, caster, SPELL_ERROR_INTERRUPTED); } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } else caster->GetZone()->GetSpellProcess()->Interrupted((Entity*)target, caster, SPELL_ERROR_INTERRUPTED); @@ -6809,7 +6830,7 @@ int EQ2Emu_lua_Stealth(lua_State* state) { return 0; } - ZoneServer* zone = spell->caster->GetZone(); + ZoneServer* zone = spell->zone; if (spawn) { if (spawn->IsEntity()) { @@ -6831,9 +6852,8 @@ int EQ2Emu_lua_Stealth(lua_State* state) { } } else { - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int32 i = 0; i < spell->targets.size(); i++) { - spawn = zone->GetSpawnByID(spell->targets.at(i)); + for (int32 id : spell->GetTargets()) { + spawn = zone->GetSpawnByID(id); if (!spawn || !spawn->IsEntity()) continue; @@ -6852,7 +6872,6 @@ int EQ2Emu_lua_Stealth(lua_State* state) { break; } } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } return 0; } @@ -7192,16 +7211,14 @@ int EQ2Emu_lua_PlayAnimationString(lua_State* state) { return 0; } - if (spell && spell->caster && spell->caster->GetZone() && use_all_spelltargets) { + if (spell && spell->zone && use_all_spelltargets) { Spawn* target; - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int8 i = 0; i < spell->targets.size(); i++) { - target = spell->caster->GetZone()->GetSpawnByID(spell->targets.at(i)); + for (int32 id : spell->GetTargets()) { + target = spell->zone->GetSpawnByID(id); if(target && (!ignore_self || spawn != target)) { - spell->caster->GetZone()->HandleEmote(target, name, opt_target, set_no_target); + spell->zone->HandleEmote(target, name, opt_target, set_no_target); } } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } else { spawn->GetZone()->HandleEmote(spawn, name, opt_target, set_no_target); @@ -8687,17 +8704,15 @@ int EQ2Emu_lua_AddProc(lua_State* state) { return 0; } - if (spell && spell->caster && spell->caster->GetZone() && use_all_spelltargets) { + if (spell && spell->zone && use_all_spelltargets) { Spawn* target; - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int8 i = 0; i < spell->targets.size(); i++) { - target = spell->caster->GetZone()->GetSpawnByID(spell->targets.at(i)); + for (int32 id : spell->GetTargets()) { + target = spell->zone->GetSpawnByID(id); if (!target || !target->IsEntity()) continue; ((Entity*)target)->AddProc(type, chance, item, spell); } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } else ((Entity*)spawn)->AddProc(type, chance, item, spell); @@ -8743,17 +8758,15 @@ int EQ2Emu_lua_AddProcExt(lua_State* state) { return 0; } - if (spell && spell->caster && spell->caster->GetZone() && use_all_spelltargets) { + if (spell && spell->zone && use_all_spelltargets) { Spawn* target; - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int8 i = 0; i < spell->targets.size(); i++) { - target = spell->caster->GetZone()->GetSpawnByID(spell->targets.at(i)); + for (int32 id : spell->GetTargets()) { + target = spell->zone->GetSpawnByID(id); if (!target || !target->IsEntity()) continue; ((Entity*)target)->AddProc(type, chance, item, spell, damage_type, hp_ratio, below_health, target_health, extended_version); } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } else ((Entity*)spawn)->AddProc(type, chance, item, spell); @@ -8782,17 +8795,15 @@ int EQ2Emu_lua_RemoveProc(lua_State* state) { if(spawn && spawn->IsEntity()) { ((Entity*)spawn)->RemoveProc(item, spell); } - else if (spell && spell->caster && spell->caster->GetZone()) { + else if (spell && spell->zone) { Spawn* target; - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int8 i = 0; i < spell->targets.size(); i++) { - target = spell->caster->GetZone()->GetSpawnByID(spell->targets.at(i)); + for (int32 id : spell->GetTargets()) { + target = spell->zone->GetSpawnByID(id); if (!target || !target->IsEntity()) continue; ((Entity*)target)->RemoveProc(item, spell); } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); spell->caster->RemoveProc(item, spell); } else if (!spawn) { @@ -9135,7 +9146,7 @@ int EQ2Emu_lua_AddSpellTimer(lua_State* state) { return 0; } - if (!spell || (!spell->caster || !spell->caster->GetZone())) { + if (!spell || (!spell->caster || !spell->zone)) { lua_interface->LogError("%s: LUA AddSpellTimer command error: spell not found, AddSpellTimer must be used in a spell script", lua_interface->GetScriptName(state)); return 0; } @@ -9161,7 +9172,7 @@ int EQ2Emu_lua_AddSpellTimer(lua_State* state) { if (target) timer->target = target->GetID(); - spell->caster->GetZone()->GetSpellProcess()->AddSpellScriptTimer(timer); + spell->zone->GetSpellProcess()->AddSpellScriptTimer(timer); return 0; } @@ -9187,65 +9198,59 @@ int EQ2Emu_lua_Resurrect(lua_State* state) { } Entity* caster = spell->caster; - if (!caster) { - lua_interface->LogError("%s: LUA command error: could not find caster", lua_interface->GetScriptName(state)); + if (!caster || !caster->GetZone()) { + lua_interface->LogError("%s: LUA command error: could not find caster or caster has no zone", lua_interface->GetScriptName(state)); return 0; } Client* client = 0; PendingResurrection* rez = 0; - ZoneServer* zone = spell->caster->GetZone(); + ZoneServer* zone = spell->zone; if (!target) { - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - if (spell->targets.size() > 0) { - vector spell_targets = spell->targets; - for (int8 i = 0; i < spell_targets.size(); i++) { - target = zone->GetSpawnByID(spell_targets.at(i)); - if (!target) - continue; - if (!target->IsPlayer()) - continue; + for (int32 id : spell->GetTargets()) { + target = zone->GetSpawnByID(id); + if (!target) + continue; + if (!target->IsPlayer()) + continue; - client = ((Player*)target)->GetClient(); + client = ((Player*)target)->GetClient(); - if (!client) - continue; + if (!client) + continue; - rez = client->GetCurrentRez(); - if (rez->active) - continue; - - client->GetResurrectMutex()->writelock(__FUNCTION__, __LINE__); - rez->active = true; - rez->caster = caster->GetID(); - rez->expire_timer = new Timer; - int32 duration = spell->spell->GetSpellDuration(); - rez->expire_timer->Start(duration * 100); - rez->hp_perc = hp_perc; - rez->mp_perc = power_perc; - rez->range = spell->spell->GetSpellData()->range; - rez->spell_name = spell->spell->GetName(); - if (heal_name.length() > 0) - rez->heal_name = heal_name; - else - rez->heal_name = rez->spell_name; - rez->orig_spell_id = spell->spell->GetSpellID(); - rez->orig_spell_tier = spell->spell->GetSpellTier(); - rez->revive_sickness_spell_id = revive_spell_id; - rez->revive_sickness_spell_tier = revive_spell_tier; - rez->no_calcs = no_calcs; - rez->crit_mod = crit_mod; - rez->spell_visual = spell->spell->GetSpellData()->spell_visual; - if (send_window) - client->SendResurrectionWindow(); - else { - target->GetZone()->ResurrectSpawn(target, client); - rez->should_delete = true; - } - client->GetResurrectMutex()->releasewritelock(__FUNCTION__, __LINE__); + rez = client->GetCurrentRez(); + if (rez->active) + continue; + client->GetResurrectMutex()->writelock(__FUNCTION__, __LINE__); + rez->active = true; + rez->caster = caster->GetID(); + rez->expire_timer = new Timer; + int32 duration = spell->spell->GetSpellDuration(); + rez->expire_timer->Start(duration * 100); + rez->hp_perc = hp_perc; + rez->mp_perc = power_perc; + rez->range = spell->spell->GetSpellData()->range; + rez->spell_name = spell->spell->GetName(); + if (heal_name.length() > 0) + rez->heal_name = heal_name; + else + rez->heal_name = rez->spell_name; + rez->orig_spell_id = spell->spell->GetSpellID(); + rez->orig_spell_tier = spell->spell->GetSpellTier(); + rez->revive_sickness_spell_id = revive_spell_id; + rez->revive_sickness_spell_tier = revive_spell_tier; + rez->no_calcs = no_calcs; + rez->crit_mod = crit_mod; + rez->spell_visual = spell->spell->GetSpellData()->spell_visual; + if (send_window) + client->SendResurrectionWindow(); + else { + target->GetZone()->ResurrectSpawn(target, client); + rez->should_delete = true; } + client->GetResurrectMutex()->releasewritelock(__FUNCTION__, __LINE__); } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } else { if(!target->IsPlayer()) @@ -9312,10 +9317,10 @@ int EQ2Emu_lua_SetVision(lua_State* state) { return 0; } - if (spell && spell->targets.size() > 0) { - ZoneServer* zone = spell->caster->GetZone(); - for (int8 i = 0; i < spell->targets.size(); i++) { - Spawn* target = zone->GetSpawnByID(spell->targets.at(i)); + if (spell && spell->GetTargetCount() > 0) { + ZoneServer* zone = spell->zone; + for (int32 id : spell->GetTargets()) { + Spawn* target = zone->GetSpawnByID(id); if (target && target->IsEntity()) { ((Entity*)target)->GetInfoStruct()->set_vision(vision); if (target->IsPlayer()) @@ -9350,10 +9355,10 @@ int EQ2Emu_lua_BlurVision(lua_State* state) { return 0; } - if (spell && spell->targets.size() > 0) { - ZoneServer* zone = spell->caster->GetZone(); - for (int8 i = 0; i < spell->targets.size(); i++) { - Spawn* target = zone->GetSpawnByID(spell->targets.at(i)); + if (spell && spell->GetTargetCount() > 0) { + ZoneServer* zone = spell->zone; + for (int32 id : spell->GetTargets()) { + Spawn* target = zone->GetSpawnByID(id); if (target && target->IsEntity()) { ((Entity*)target)->GetInfoStruct()->set_drunk(intensity); if (target->IsPlayer()) @@ -9388,10 +9393,10 @@ int EQ2Emu_lua_BreatheUnderwater(lua_State* state) { return 0; } - if (spell && spell->targets.size() > 0) { - ZoneServer* zone = spell->caster->GetZone(); - for (int8 i = 0; i < spell->targets.size(); i++) { - Spawn* target = zone->GetSpawnByID(spell->targets.at(i)); + if (spell && spell->GetTargetCount() > 0) { + ZoneServer* zone = spell->zone; + for (int32 id : spell->GetTargets()) { + Spawn* target = zone->GetSpawnByID(id); if (target && target->IsEntity()) { ((Entity*)target)->GetInfoStruct()->set_breathe_underwater(breatheUnderwater); if (target->IsPlayer()) @@ -9443,10 +9448,10 @@ int EQ2Emu_lua_SetSpeedMultiplier(lua_State* state) { if (val > 1.0f) val = 1.0f - (val / 100.0f); - if (spell && spell->spell && spell->targets.size() > 0) { - ZoneServer* zone = spell->caster->GetZone(); - for (int32 i = 0; i != spell->targets.size(); i++) { - Spawn* spawn = zone->GetSpawnByID(spell->targets.at(i)); + if (spell && spell->spell && spell->GetTargetCount() > 0) { + ZoneServer* zone = spell->zone; + for (int32 id : spell->GetTargets()) { + Spawn* spawn = zone->GetSpawnByID(id); if (spawn && spawn->IsEntity()) { ((Entity*)spawn)->SetSpeedMultiplier(val); if (spawn->IsPlayer()) @@ -9474,10 +9479,10 @@ int EQ2Emu_lua_SetIllusion(lua_State* state) { LuaSpell* spell = lua_interface->GetCurrentSpell(state); lua_interface->ResetFunctionStack(state); - if (spell && spell->spell && spell->targets.size() > 0) { - ZoneServer* zone = spell->caster->GetZone(); - for (int32 i = 0; i < spell->targets.size(); i++) { - Spawn* target = zone->GetSpawnByID(spell->targets.at(i)); + if (spell && spell->spell && spell->GetTargetCount() > 0) { + ZoneServer* zone = spell->zone; + for (int32 id : spell->GetTargets()) { + Spawn* target = zone->GetSpawnByID(id); if (target) target->SetIllusionModel(model); } @@ -9502,10 +9507,10 @@ int EQ2Emu_lua_ResetIllusion(lua_State* state) { LuaSpell* spell = lua_interface->GetCurrentSpell(state); lua_interface->ResetFunctionStack(state); - if (spell && spell->spell && spell->targets.size() > 0) { - ZoneServer* zone = spell->caster->GetZone(); - for (int32 i = 0; i < spell->targets.size(); i++) { - Spawn* target = zone->GetSpawnByID(spell->targets.at(i)); + if (spell && spell->spell && spell->GetTargetCount() > 0) { + ZoneServer* zone = spell->zone; + for (int32 id : spell->GetTargets()) { + Spawn* target = zone->GetSpawnByID(id); if (target) target->SetIllusionModel(0); } @@ -9649,13 +9654,10 @@ int EQ2Emu_lua_CureByType(lua_State* state) { } } else { - ZoneServer* zone = spell->caster->GetZone(); - vector targets = spell->targets; + ZoneServer* zone = spell->zone; vector targets_to_cure; - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int8 i = 0; i < targets.size(); i++) { - target = zone->GetSpawnByID(targets.at(i)); - + for (int32 id : spell->GetTargets()) { + target = zone->GetSpawnByID(id); if (!target || !target->IsEntity()) continue; @@ -9663,7 +9665,6 @@ int EQ2Emu_lua_CureByType(lua_State* state) { targets_to_cure.push_back((Entity*)target); } } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); vector::iterator itr; for(itr = targets_to_cure.begin(); itr != targets_to_cure.end(); itr++) { @@ -9701,12 +9702,9 @@ int EQ2Emu_lua_CureByControlEffect(lua_State* state) { ((Entity*)target)->CureDetrimentByControlEffect(cure_count, cure_type, cure_name.length() > 0 ? cure_name : (string)spell->spell->GetName(), spell->caster, cure_level); } else { - ZoneServer* zone = spell->caster->GetZone(); - vector targets = spell->targets; - - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int8 i = 0; i < targets.size(); i++) { - target = zone->GetSpawnByID(targets.at(i)); + ZoneServer* zone = spell->zone; + for (int32 id : spell->GetTargets()) { + target = zone->GetSpawnByID(id); if (!target || !target->IsEntity()) continue; @@ -9714,7 +9712,6 @@ int EQ2Emu_lua_CureByControlEffect(lua_State* state) { if (((Entity*)target)->GetDetCount() > 0) ((Entity*)target)->CureDetrimentByControlEffect(cure_count, cure_type, cure_name.length() > 0 ? cure_name : (string)spell->spell->GetName(), spell->caster, cure_level); } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } return 0; } @@ -9736,12 +9733,12 @@ int EQ2Emu_lua_CancelSpell(lua_State* state) { return 0; } - if (!spell->caster->GetZone()) { + if (!spell->zone) { lua_interface->LogError("%s: LUA CancelSpell command error: unable to get the zone of the caster", lua_interface->GetScriptName(state)); return 0; } - spell->caster->GetZone()->GetSpellProcess()->AddSpellCancel(spell); + spell->zone->GetSpellProcess()->AddSpellCancel(spell); return 0; } @@ -9762,16 +9759,14 @@ int EQ2Emu_lua_RemoveStealth(lua_State* state) { if (spawn && spawn->IsEntity()) ((Entity*)spawn)->RemoveStealthSpell(spell); else { - ZoneServer* zone = spell->caster->GetZone(); - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int32 i = 0; i < spell->targets.size(); i++) { - spawn = zone->GetSpawnByID(spell->targets.at(i)); + ZoneServer* zone = spell->zone; + for (int32 id : spell->GetTargets()) { + spawn = zone->GetSpawnByID(id); if (!spawn || !spawn->IsEntity()) continue; ((Entity*)spawn)->RemoveStealthSpell(spell); } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } return 0; } @@ -9792,16 +9787,14 @@ int EQ2Emu_lua_RemoveInvis(lua_State* state) { if (spawn && spawn->IsEntity()) ((Entity*)spawn)->RemoveInvisSpell(spell); else { - ZoneServer* zone = spell->caster->GetZone(); - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int32 i = 0; i < spell->targets.size(); i++) { - spawn = zone->GetSpawnByID(spell->targets.at(i)); + ZoneServer* zone = spell->zone; + for (int32 id : spell->GetTargets()) { + spawn = zone->GetSpawnByID(id); if (!spawn || !spawn->IsEntity()) continue; ((Entity*)spawn)->RemoveInvisSpell(spell); } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } return 0; } @@ -9812,6 +9805,7 @@ int EQ2Emu_lua_StartHeroicOpportunity(lua_State* state) { Spawn* caster = lua_interface->GetSpawn(state); int8 class_id = lua_interface->GetInt8Value(state, 2); + Spawn* targetOverride = lua_interface->GetSpawn(state, 3); lua_interface->ResetFunctionStack(state); if (!caster) { @@ -9825,10 +9819,13 @@ int EQ2Emu_lua_StartHeroicOpportunity(lua_State* state) { } Spawn* target = caster->GetTarget(); - if (!target) { + if (!target && !targetOverride) { lua_interface->LogError("%s: LUA StartHeroicOpportunity command error: target is not valid", lua_interface->GetScriptName(state)); return 0; } + + if(!target) + target = targetOverride; Client* client = ((Player*)caster)->GetClient(); if (!client) { @@ -9839,8 +9836,6 @@ int EQ2Emu_lua_StartHeroicOpportunity(lua_State* state) { HeroicOP* ho = master_ho_list.GetHeroicOP(class_id); if (ho) { ho->SetTarget(target->GetID()); - LogWrite(SPELL__ERROR, 0, "HO", "caster: %u", caster->GetID()); - LogWrite(SPELL__ERROR, 0, "HO", "target: %u", target->GetID()); if (((Entity*)caster)->GetGroupMemberInfo()) { if (caster->GetZone()->GetSpellProcess()->AddHO(client, ho)) { world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__); @@ -9930,7 +9925,7 @@ int EQ2Emu_lua_RemoveTriggerFromSpell(lua_State* state) { return 0; } - if (!spell->caster || !spell->caster->GetZone()) { + if (!spell->caster || !spell->zone) { lua_interface->LogError("%s: LUA RemoveTriggerFromSpell command error: caster / caster zone must be set!", lua_interface->GetScriptName(state)); lua_interface->ResetFunctionStack(state); return 0; @@ -9946,7 +9941,7 @@ int EQ2Emu_lua_RemoveTriggerFromSpell(lua_State* state) { if (remove_count >= spell->num_triggers) { spell->num_triggers = 0; if (spell->cancel_after_all_triggers) - spell->caster->GetZone()->GetSpellProcess()->AddSpellCancel(spell); + spell->zone->GetSpellProcess()->AddSpellCancel(spell); } else { spell->num_triggers -= remove_count; @@ -10021,16 +10016,14 @@ int EQ2Emu_lua_AddImmunitySpell(lua_State* state) { Entity* entity = ((Entity*)spawn); entity->AddImmunity(spell, type); } - else if(spell->caster && spell->caster->GetZone()) { - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int8 i = 0; i < spell->targets.size(); i++) { - spawn = spell->caster->GetZone()->GetSpawnByID(spell->targets.at(i)); + else if(spell->zone) { + for (int32 id : spell->GetTargets()) { + spawn = spell->zone->GetSpawnByID(id); if (!spawn || !spawn->IsEntity()) continue; Entity* entity = ((Entity*)spawn); entity->AddImmunity(spell, type); } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } return 0; @@ -10058,16 +10051,14 @@ int EQ2Emu_lua_RemoveImmunitySpell(lua_State* state) { Entity* entity = ((Entity*)spawn); entity->RemoveImmunity(spell, type); } - else if(spell->caster && spell->caster->GetZone()) { - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int8 i = 0; i < spell->targets.size(); i++) { - spawn = spell->caster->GetZone()->GetSpawnByID(spell->targets.at(i)); + else if(spell->zone) { + for (int32 id : spell->GetTargets()) { + spawn = spell->zone->GetSpawnByID(id); if (!spawn || !spawn->IsEntity()) continue; Entity* entity = ((Entity*)spawn); entity->RemoveImmunity(spell, type); } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } return 0; @@ -10099,16 +10090,14 @@ int EQ2Emu_lua_SetSpellSnareValue(lua_State* state) { ((Entity*)spawn)->SetSnareValue(spell, val); } - else if(spell && spell->caster && spell->caster->GetZone()) { - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int8 i = 0; i < spell->targets.size(); i++) { - spawn = spell->caster->GetZone()->GetSpawnByID(spell->targets.at(i)); + else if(spell && spell->zone) { + for (int32 id : spell->GetTargets()) { + spawn = spell->zone->GetSpawnByID(id); if (!spawn || !spawn->IsEntity()) continue; ((Entity*)spawn)->SetSnareValue(spell, val); } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } return 0; @@ -11312,18 +11301,18 @@ int EQ2Emu_lua_Evac(lua_State* state) { else { LuaSpell* spell = lua_interface->GetCurrentSpell(state); - - if(!spell || !spell->caster || !spell->caster->GetZone()) { + + if(!spell || !spell->caster || !spell->zone) { lua_interface->ResetFunctionStack(state); return 0; } + + ZoneServer* zone = spell->zone; - ZoneServer* zone = spell->caster->GetZone(); - - float x = spell->caster->GetZone()->GetSafeX(); - float y = spell->caster->GetZone()->GetSafeY(); - float z = spell->caster->GetZone()->GetSafeZ(); - float h = spell->caster->GetZone()->GetSafeHeading(); + float x = spell->zone->GetSafeX(); + float y = spell->zone->GetSafeY(); + float z = spell->zone->GetSafeZ(); + float h = spell->zone->GetSafeHeading(); int numargs = lua_interface->GetNumberOfArgs(state); @@ -11333,12 +11322,11 @@ int EQ2Emu_lua_Evac(lua_State* state) { z = lua_interface->GetFloatValue(state,3); h = lua_interface->GetFloatValue(state,4); } - + lua_interface->ResetFunctionStack(state); - - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int32 i = 0; i < spell->targets.size(); i++) { - Spawn* target2 = zone->GetSpawnByID(spell->targets.at(i)); + + for (int32 id : spell->GetTargets()) { + Spawn* target2 = zone->GetSpawnByID(id); if (!target2) continue; @@ -11346,7 +11334,7 @@ int EQ2Emu_lua_Evac(lua_State* state) { Client* client = ((Player*)target2)->GetClient(); if (client) { client->GetCurrentZone()->ClearHate(client->GetPlayer()); - + client->SetReloadingZone(true); target2->SetX(x); target2->SetY(y); @@ -11359,9 +11347,9 @@ int EQ2Emu_lua_Evac(lua_State* state) { target2->SetSpawnOrigHeading(h); target2->SetAppearancePosition(x,y,z); - + client->SetZoningCoords(x,y,z,h); - + PacketStruct* packet = configReader.getStruct("WS_TeleportWithinZone", client->GetVersion()); if (packet) { @@ -11371,14 +11359,12 @@ int EQ2Emu_lua_Evac(lua_State* state) { client->QueuePacket(packet->serialize()); safe_delete(packet); } - client->GetCurrentZone()->RemoveSpawn(target2, false, false, true, true); client->GetPlayer()->SetSpawnSentState(target2, SpawnState::SPAWN_STATE_SENT); } } } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } return 0; @@ -12324,10 +12310,10 @@ int EQ2Emu_lua_SetAlignment(lua_State* state) { lua_interface->ResetFunctionStack(state); - if (spell && spell->targets.size() > 0) { - ZoneServer* zone = spell->caster->GetZone(); - for (int8 i = 0; i < spell->targets.size(); i++) { - Spawn* target = zone->GetSpawnByID(spell->targets.at(i)); + if (spell && spell->GetTargetCount() > 0) { + ZoneServer* zone = spell->zone; + for (int32 id : spell->GetTargets()) { + Spawn* target = zone->GetSpawnByID(id); if (target && target->IsEntity()) { ((Entity*)target)->GetInfoStruct()->set_alignment((sint8)alignment); if (target->IsPlayer()) @@ -12462,12 +12448,16 @@ int EQ2Emu_lua_SetSpellData(lua_State* state) { boost::to_lower(field); - bool valSet = false; + bool setVal = false; - spell->spell->SetSpellData(state, field, fieldArg); + setVal = spell->spell->SetSpellData(state, field, fieldArg); lua_interface->ResetFunctionStack(state); - - return valSet; + if(setVal) { + spell->MarkFieldModified(field); + } + lua_interface->SetBooleanValue(state, setVal); + + return 1; } int EQ2Emu_lua_SetSpellDataIndex(lua_State* state) { @@ -12535,8 +12525,14 @@ int EQ2Emu_lua_SetSpellDataIndex(lua_State* state) { } lua_interface->ResetFunctionStack(state); + + lua_interface->SetBooleanValue(state, setVal); - return setVal; + if(setVal) { + data->needs_db_save = true; + } + + return 1; } @@ -12633,22 +12629,30 @@ int EQ2Emu_lua_SetSpellDisplayEffect(lua_State* state) { return 0; } + bool setVal = true; // do we need to lock? eh probably not this should only be used before use of the custom spell SpellDisplayEffect* effect = spell->spell->effects[idx]; - if (field == "description") + if (field == "description") { effect->description = string(lua_interface->GetStringValue(state, 4)); - else if (field == "bullet") - effect->subbullet = lua_interface->GetInt8Value(state, 4); - else if (field == "percentage") - effect->percentage = lua_interface->GetInt8Value(state, 4); - else { // no match - lua_interface->ResetFunctionStack(state); - return 0; + effect->needs_db_save = true; } - + else if (field == "bullet") { + effect->subbullet = lua_interface->GetInt8Value(state, 4); + effect->needs_db_save = true; + } + else if (field == "percentage") { + effect->percentage = lua_interface->GetInt8Value(state, 4); + effect->needs_db_save = true; + } + else { + setVal = false; + } + lua_interface->ResetFunctionStack(state); + lua_interface->SetBooleanValue(state, setVal); + return 1; } @@ -14324,11 +14328,11 @@ int EQ2Emu_lua_GetSpellInitialTarget(lua_State* state) { lua_interface->LogError("%s: LUA GetSpellInitialTarget command error, caster does not exist.", lua_interface->GetScriptName(state)); return 0; } - if(!spell->caster->GetZone()) { + if(!spell->zone) { lua_interface->LogError("%s: LUA GetSpellInitialTarget command error, zone does not exist.", lua_interface->GetScriptName(state)); return 0; } - Spawn* spawn = spell->caster->GetZone()->GetSpawnByID(spell->initial_target); + Spawn* spawn = spell->zone->GetSpawnByID(spell->initial_target); if (spawn) { lua_interface->SetSpawnValue(state, spawn); return 1; @@ -14396,22 +14400,23 @@ int EQ2Emu_lua_GetSpellTargets(lua_State* state) { lua_interface->LogError("%s: LUA GetSpellTargets command error, caster does not exist.", lua_interface->GetScriptName(state)); return 0; } - if(!spell->caster->GetZone()) { + if(!spell->zone) { lua_interface->LogError("%s: LUA GetSpellTargets command error, zone does not exist.", lua_interface->GetScriptName(state)); return 0; } - if (spell && spell->caster) { - ZoneServer* zone = spell->caster->GetZone(); - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - lua_createtable(state, spell->targets.size(), 0); + if (spell) { + ZoneServer* zone = spell->zone; + std::vector targets = spell->GetTargets(); // snapshot under lock + lua_createtable(state, targets.size(), 0); // preallocate array size int newTable = lua_gettop(state); - for (int32 i = 0; i < spell->targets.size(); i++) { - Spawn* spawn = zone->GetSpawnByID(spell->targets.at(i)); + + int i = 0; + for (int32_t id : targets) { + Spawn* spawn = zone->GetSpawnByID(id); lua_interface->SetSpawnValue(state, spawn); - lua_rawseti(state, newTable, i + 1); + lua_rawseti(state, newTable, ++i); // Lua is 1-indexed } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); return 1; } } @@ -14652,4 +14657,260 @@ int EQ2Emu_lua_GetSpellRequiredLevel(lua_State* state) { } return 1; +} + +int EQ2Emu_lua_GetExpRequiredByLevel(lua_State* state) { + int8 level = lua_interface->GetInt16Value(state); + lua_interface->ResetFunctionStack(state); + + int32 exp_required = Player::GetNeededXPByLevel(level); + + lua_interface->SetInt32Value(state, exp_required); + return 1; +} + +int EQ2Emu_lua_ShowShopWindow(lua_State* state) { + + Spawn* player = lua_interface->GetSpawn(state); + Spawn* from_spawn = lua_interface->GetSpawn(state, 2); + lua_interface->ResetFunctionStack(state); + if (player) { + Client* client = 0; + if (player->IsPlayer()) + client = ((Player*)player)->GetClient(); + if (client) { + client->OpenShopWindow(from_spawn, true); + } + } + return 0; +} + +int EQ2Emu_lua_SetSpawnHouseScript(lua_State* state) { + + Spawn* target = lua_interface->GetSpawn(state); + string lua_script = lua_interface->GetStringValue(state, 2); + lua_interface->ResetFunctionStack(state); + if (target && target->GetDatabaseID() && !target->IsPlayer() && !target->IsBot()) { + database.UpdateHouseSpawnScript(target->GetDatabaseID(), lua_script); + bool scriptActive = false; + if (lua_interface && lua_interface->GetSpawnScript(lua_script.c_str()) != 0) { + scriptActive = true; + target->SetSpawnScript(lua_script); + } + if (scriptActive) { + target->GetZone()->CallSpawnScript(target, SPAWN_SCRIPT_SPAWN); + } + } + return 0; +} + +int EQ2Emu_lua_SendBook(lua_State* state) { + + Spawn* target = lua_interface->GetSpawn(state); + int32 item_id = lua_interface->GetInt32Value(state, 2); + lua_interface->ResetFunctionStack(state); + if (target && target->IsPlayer() && ((Player*)target)->GetClient()) { + Item* item = master_item_list.GetItem(item_id); + if (item) { + ((Player*)target)->GetClient()->SendShowBook(target, item->name, item->book_language, item->book_pages); + } + } + return 0; +} + +int EQ2Emu_lua_GetPickupItemID(lua_State* state) { + Spawn* spawn = lua_interface->GetSpawn(state); + lua_interface->ResetFunctionStack(state); + if (spawn) { + lua_interface->SetInt32Value(state, spawn->GetPickupItemID()); + return 1; + } + return 0; +} + +int EQ2Emu_lua_SetHouseCharacterID(lua_State* state) { + Spawn* spawn = lua_interface->GetSpawn(state); + int32 character_id = lua_interface->GetInt32Value(state, 2); + lua_interface->ResetFunctionStack(state); + if (spawn) { + if(!character_id) { + PlayerHouse* ph = world.GetPlayerHouseByInstanceID(spawn->GetZone()->GetInstanceID()); + if(ph) + spawn->SetHouseCharacterID(ph->character_id); + } + else { + spawn->SetHouseCharacterID(character_id); + } + } + return 0; +} + +int EQ2Emu_lua_GetHouseCharacterID(lua_State* state) { + Spawn* spawn = lua_interface->GetSpawn(state); + lua_interface->ResetFunctionStack(state); + if (spawn) { + lua_interface->SetInt32Value(state, spawn->GetHouseCharacterID()); + } + else { + lua_interface->SetInt32Value(state, 0); + } + return 1; +} + +int EQ2Emu_lua_ShowHouseShopMerchant(lua_State* state) { + Spawn* spawn = lua_interface->GetSpawn(state); + Spawn* player = lua_interface->GetSpawn(state, 2); + lua_interface->ResetFunctionStack(state); + if (spawn && player && player->IsPlayer()) { + if(player->GetClient()) + player->GetClient()->SendMerchantWindow(spawn, false); + } + + return 0; +} + + +int EQ2Emu_lua_AttackAllowed(lua_State* state) { + Spawn* spawn = lua_interface->GetSpawn(state); + Spawn* target = lua_interface->GetSpawn(state, 2); + float distance = lua_interface->GetFloatValue(state, 3); + bool is_range_attack = lua_interface->GetBooleanValue(state, 4); + + lua_interface->ResetFunctionStack(state); + if (spawn && target && spawn->IsEntity() & target->IsEntity()) { + lua_interface->SetBooleanValue(state, ((Entity*)spawn)->AttackAllowed((Entity*)target, distance, is_range_attack)); + } + else { + lua_interface->SetBooleanValue(state, false); + } + return 1; +} + +int EQ2Emu_lua_IsInRaid(lua_State* state) { + Spawn* spawn = lua_interface->GetSpawn(state); + bool is_leader_group = lua_interface->GetBooleanValue(state, 2); + + lua_interface->ResetFunctionStack(state); + if (spawn->IsEntity() && ((Entity*)spawn)->GetGroupMemberInfo()) { + lua_interface->SetBooleanValue(state, world.GetGroupManager()->IsInRaidGroup(((Entity*)spawn)->GetGroupMemberInfo()->group_id, is_leader_group)); + } + else { + lua_interface->SetBooleanValue(state, false); + } + return 1; +} + +int EQ2Emu_lua_InSameRaid(lua_State* state) { + Spawn* spawn = lua_interface->GetSpawn(state); + Spawn* target = lua_interface->GetSpawn(state, 2); + bool is_leader_group = lua_interface->GetBooleanValue(state, 3); + + lua_interface->ResetFunctionStack(state); + if (spawn && spawn->IsEntity() && ((Entity*)spawn)->GetGroupMemberInfo() && + target && target->IsEntity() && ((Entity*)target)->GetGroupMemberInfo()) { + lua_interface->SetBooleanValue(state, world.GetGroupManager()->IsInRaidGroup(((Entity*)spawn)->GetGroupMemberInfo()->group_id, ((Entity*)target)->GetGroupMemberInfo()->group_id, is_leader_group)); + } + else { + lua_interface->SetBooleanValue(state, false); + } + return 1; +} + +int EQ2Emu_lua_GetRaid(lua_State* state) { + if (!lua_interface) + return 0; + + Spawn* spawn = lua_interface->GetSpawn(state); + bool players_only = lua_interface->GetBooleanValue(state, 2); + lua_interface->ResetFunctionStack(state); + if (!spawn) { + lua_interface->LogError("%s: LUA GetRaid command error: spawn is not valid", lua_interface->GetScriptName(state)); + return 0; + } + + vector groupMembers; + if (spawn->IsEntity() && ((Entity*)spawn)->GetGroupMemberInfo()) { + std::vector raidGroups; + world.GetGroupManager()->GetRaidGroups(((Entity*)spawn)->GetGroupMemberInfo()->group_id, &raidGroups); + world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__); + if(raidGroups.size() > 0) { + for (size_t i = 0; i < raidGroups.size(); i++) { + PlayerGroup* group = world.GetGroupManager()->GetGroup(raidGroups.at(i)); + deque::iterator itr; + if (group) + { + group->MGroupMembers.readlock(__FUNCTION__, __LINE__); + deque* members = group->GetMembers(); + GroupMemberInfo* info = 0; + for (itr = members->begin(); itr != members->end(); itr++) { + info = *itr; + if (info->client) + groupMembers.push_back(info->client->GetPlayer()); + else if(!players_only && info->member) + groupMembers.push_back((Spawn*)info->member); + } + group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); + } + } + } + world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__); + } + else + return 0; + + lua_createtable(state, groupMembers.size(), 0); + int newTable = lua_gettop(state); + for (int32 i = 0; i < groupMembers.size(); i++) { + lua_interface->SetSpawnValue(state, groupMembers.at(i)); + lua_rawseti(state, newTable, i + 1); + } + return 1; + +} + + +int EQ2Emu_lua_AdjustHatePosition(lua_State* state) { + Spawn* entity = lua_interface->GetSpawn(state); + Spawn* npc = lua_interface->GetSpawn(state, 2); + bool increase = lua_interface->GetBooleanValue(state, 3); + + LuaSpell* luaspell = lua_interface->GetCurrentSpell(state); + if(luaspell && luaspell->resisted) { + return 0; + } + + if (luaspell) { + for (int32 id : luaspell->GetTargets()) { + Spawn* spawn = luaspell->zone->GetSpawnByID(id); + if (spawn && npc->IsNPC() && spawn->Alive()) { + ((NPC*)npc)->Brain()->AdjustHatePosition(entity->GetID(), increase); + } + } + } + else if (npc && npc->IsNPC() && entity->Alive()) { + ((NPC*)npc)->Brain()->AdjustHatePosition(entity->GetID(), increase); + } + lua_interface->ResetFunctionStack(state); + return 0; +} + + +int EQ2Emu_lua_RemoveCharacterProperty(lua_State* state) { + Spawn* player = lua_interface->GetSpawn(state); + string propname = lua_interface->GetStringValue(state, 2); + lua_interface->ResetFunctionStack(state); + + if(!player || !player->IsPlayer()) { + lua_interface->LogError("%s: LUA RemoveCharacterProperty command error: player is not valid", lua_interface->GetScriptName(state)); + return 0; + } + + if(((Player*)player)->GetCharacterID() == 0) { + lua_interface->LogError("%s: LUA RemoveCharacterProperty player has no character id.", lua_interface->GetScriptName(state)); + return 0; + } + + Query query; + query.AddQueryAsync(((Player*)player)->GetCharacterID(), &database, Q_DELETE, "delete from character_properties where charid = %u and propname='%s'", ((Player*)player)->GetCharacterID(), propname.c_str()); + return 0; } \ No newline at end of file diff --git a/old/WorldServer/LuaFunctions.h b/old/WorldServer/LuaFunctions.h index d15461f..b66830b 100644 --- a/old/WorldServer/LuaFunctions.h +++ b/old/WorldServer/LuaFunctions.h @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #ifndef LUA_FUNCTIONS_H #define LUA_FUNCTIONS_H @@ -260,6 +261,8 @@ int EQ2Emu_lua_QuestStepIsComplete(lua_State* state); int EQ2Emu_lua_GetQuestStep(lua_State* state); int EQ2Emu_lua_RegisterQuest(lua_State* state); int EQ2Emu_lua_OfferQuest(lua_State* state); +int EQ2Emu_lua_DeleteQuest(lua_State* state); +int EQ2Emu_lua_DeleteAllQuests(lua_State* state); int EQ2Emu_lua_SetQuestPrereqLevel(lua_State* state); int EQ2Emu_lua_AddQuestPrereqQuest(lua_State* state); int EQ2Emu_lua_AddQuestPrereqItem(lua_State* state); @@ -680,4 +683,20 @@ int EQ2Emu_lua_GetZonePlayerAvgLevel(lua_State* state); int EQ2Emu_lua_GetZonePlayerFirstLevel(lua_State* state); int EQ2Emu_lua_GetSpellRequiredLevel(lua_State* state); + +int EQ2Emu_lua_GetExpRequiredByLevel(lua_State* state); + +int EQ2Emu_lua_ShowShopWindow(lua_State* state); +int EQ2Emu_lua_SetSpawnHouseScript(lua_State* state); +int EQ2Emu_lua_SendBook(lua_State* state); +int EQ2Emu_lua_GetPickupItemID(lua_State* state); +int EQ2Emu_lua_SetHouseCharacterID(lua_State* state); +int EQ2Emu_lua_GetHouseCharacterID(lua_State* state); +int EQ2Emu_lua_ShowHouseShopMerchant(lua_State* state); +int EQ2Emu_lua_AttackAllowed(lua_State* state); +int EQ2Emu_lua_IsInRaid(lua_State* state); +int EQ2Emu_lua_InSameRaid(lua_State* state); +int EQ2Emu_lua_GetRaid(lua_State* state); +int EQ2Emu_lua_AdjustHatePosition(lua_State* state); +int EQ2Emu_lua_RemoveCharacterProperty(lua_State* state); #endif \ No newline at end of file diff --git a/old/WorldServer/LuaInterface.cpp b/old/WorldServer/LuaInterface.cpp index 7a69376..857d5ce 100644 --- a/old/WorldServer/LuaInterface.cpp +++ b/old/WorldServer/LuaInterface.cpp @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,11 +17,12 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #include "LuaInterface.h" #include "LuaFunctions.h" -#include "WorldDatabase.hpp" +#include "WorldDatabase.h" #include "SpellProcess.h" -#include "../common/log.hpp" +#include "../common/Log.h" #include "World.h" #ifndef WIN32 @@ -38,6 +39,149 @@ extern WorldDatabase database; extern ZoneList zone_list; +const std::unordered_map> SpellDataFieldAccessors = { + {"spell_book_type", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->spell_book_type); }}}, + {"icon", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->icon); }}}, + {"icon_heroic_op", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->icon_heroic_op); }}}, + {"icon_backdrop", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->icon_backdrop); }}}, + {"type", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->type); }}}, + {"class_skill", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->class_skill); }}}, + {"min_class_skill_req", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->min_class_skill_req); }}}, + {"mastery_skill", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->mastery_skill); }}}, + {"ts_loc_index", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->ts_loc_index); }}}, + {"num_levels", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->num_levels); }}}, + {"tier", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->tier); }}}, + {"hp_req", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->hp_req); }}}, + {"hp_upkeep", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->hp_upkeep); }}}, + {"power_req", {SpellFieldType::Float, [](SpellData* d) { return std::to_string(d->power_req); }}}, + {"power_by_level", {SpellFieldType::Boolean, [](SpellData* d) { return d->power_by_level ? "1" : "0"; }}}, + {"power_upkeep", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->power_upkeep); }}}, + {"savagery_req", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->savagery_req); }}}, + {"savagery_upkeep", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->savagery_upkeep); }}}, + {"dissonance_req", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->dissonance_req); }}}, + {"dissonance_upkeep", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->dissonance_upkeep); }}}, + {"target_type", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->target_type); }}}, + {"cast_time", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->cast_time); }}}, + {"recovery", {SpellFieldType::Float, [](SpellData* d) { return std::to_string(d->recovery); }}}, + {"recast", {SpellFieldType::Float, [](SpellData* d) { return std::to_string(d->recast); }}}, + {"linked_timer", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->linked_timer); }}}, + {"radius", {SpellFieldType::Float, [](SpellData* d) { return std::to_string(d->radius); }}}, + {"max_aoe_targets", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->max_aoe_targets); }}}, + {"friendly_spell", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->friendly_spell); }}}, + {"req_concentration", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->req_concentration); }}}, + {"range", {SpellFieldType::Float, [](SpellData* d) { return std::to_string(d->range); }}}, + {"duration1", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->duration1); }}}, + {"duration2", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->duration2); }}}, + {"resistibility", {SpellFieldType::Float, [](SpellData* d) { return std::to_string(d->resistibility); }}}, + {"duration_until_cancel", {SpellFieldType::Boolean, [](SpellData* d) { return d->duration_until_cancel ? "1" : "0"; }}}, + {"power_req_percent", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->power_req_percent); }}}, + {"hp_req_percent", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->hp_req_percent); }}}, + {"savagery_req_percent", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->savagery_req_percent); }}}, + {"dissonance_req_percent", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->dissonance_req_percent); }}}, + {"name", {SpellFieldType::String, [](SpellData* d) { return d->name.data; }}}, + {"description", {SpellFieldType::String, [](SpellData* d) { return d->description.data; }}}, + {"success_message", {SpellFieldType::String, [](SpellData* d) { return d->success_message; }}}, + {"fade_message", {SpellFieldType::String, [](SpellData* d) { return d->fade_message; }}}, + {"fade_message_others", {SpellFieldType::String, [](SpellData* d) { return d->fade_message_others; }}}, + {"cast_type", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->cast_type).c_str(); }}}, + {"lua_script", {SpellFieldType::String, [](SpellData* d) { return d->lua_script; }}}, + {"interruptable", {SpellFieldType::Boolean, [](SpellData* d) { return d->interruptable ? "1" : "0"; }}}, + {"spell_visual", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->spell_visual); }}}, + {"effect_message", {SpellFieldType::String, [](SpellData* d) { return d->effect_message; }}}, + {"min_range", {SpellFieldType::Float, [](SpellData* d) { return std::to_string(d->min_range); }}}, + {"can_effect_raid", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->can_effect_raid); }}}, + {"affect_only_group_members", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->affect_only_group_members); }}}, + {"group_spell", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->group_spell); }}}, + {"hit_bonus", {SpellFieldType::Float, [](SpellData* d) { return std::to_string(d->hit_bonus); }}}, + {"display_spell_tier", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->display_spell_tier); }}}, + {"is_active", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->is_active); }}}, + {"det_type", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->det_type); }}}, + {"incurable", {SpellFieldType::Boolean, [](SpellData* d) { return d->incurable ? "1" : "0"; }}}, + {"control_effect_type", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->control_effect_type); }}}, + {"casting_flags", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->casting_flags); }}}, + {"cast_while_moving", {SpellFieldType::Boolean, [](SpellData* d) { return d->cast_while_moving ? "1" : "0"; }}}, + {"persist_through_death", {SpellFieldType::Boolean, [](SpellData* d) { return d->persist_through_death ? "1" : "0"; }}}, + {"not_maintained", {SpellFieldType::Boolean, [](SpellData* d) { return d->not_maintained ? "1" : "0"; }}}, + {"is_aa", {SpellFieldType::Boolean, [](SpellData* d) { return d->is_aa ? "1" : "0"; }}}, + {"savage_bar", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->savage_bar); }}}, + {"savage_bar_slot", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->savage_bar_slot); }}}, + {"soe_spell_crc", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->soe_spell_crc); }}}, + {"spell_type", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->spell_type); }}}, + {"spell_name_crc", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->spell_name_crc); }}}, + {"type_group_spell_id", {SpellFieldType::Integer, [](SpellData* d) { return std::to_string(d->type_group_spell_id); }}}, + {"can_fizzle", {SpellFieldType::Boolean, [](SpellData* d) { return d->can_fizzle ? "1" : "0"; }}} +}; + +const std::unordered_map> SpellFieldGenericSetters = { + { "spell_book_type", [](Spell* spell, const std::string& val) { spell->GetSpellData()->spell_book_type = static_cast(std::stoi(val)); } }, + { "icon", [](Spell* spell, const std::string& val) { spell->GetSpellData()->icon = static_cast(std::stoi(val)); } }, + { "icon_heroic_op", [](Spell* spell, const std::string& val) { spell->GetSpellData()->icon_heroic_op = static_cast(std::stoi(val)); } }, + { "icon_backdrop", [](Spell* spell, const std::string& val) { spell->GetSpellData()->icon_backdrop = static_cast(std::stoi(val)); } }, + { "type", [](Spell* spell, const std::string& val) { spell->GetSpellData()->type = static_cast(std::stoi(val)); } }, + { "class_skill", [](Spell* spell, const std::string& val) { spell->GetSpellData()->class_skill = static_cast(std::stoi(val)); } }, + { "min_class_skill_req", [](Spell* spell, const std::string& val) { spell->GetSpellData()->min_class_skill_req = static_cast(std::stoi(val)); } }, + { "mastery_skill", [](Spell* spell, const std::string& val) { spell->GetSpellData()->mastery_skill = static_cast(std::stoi(val)); } }, + { "ts_loc_index", [](Spell* spell, const std::string& val) { spell->GetSpellData()->ts_loc_index = static_cast(std::stoi(val)); } }, + { "num_levels", [](Spell* spell, const std::string& val) { spell->GetSpellData()->num_levels = static_cast(std::stoi(val)); } }, + { "tier", [](Spell* spell, const std::string& val) { spell->GetSpellData()->tier = static_cast(std::stoi(val)); } }, + { "hp_req", [](Spell* spell, const std::string& val) { spell->GetSpellData()->hp_req = static_cast(std::stoi(val)); } }, + { "hp_upkeep", [](Spell* spell, const std::string& val) { spell->GetSpellData()->hp_upkeep = static_cast(std::stoi(val)); } }, + { "power_req", [](Spell* spell, const std::string& val) { spell->GetSpellData()->power_req = std::stof(val); } }, + { "power_by_level", [](Spell* spell, const std::string& val) { spell->GetSpellData()->power_by_level = (val == "1" || val == "true"); } }, + { "power_upkeep", [](Spell* spell, const std::string& val) { spell->GetSpellData()->power_upkeep = static_cast(std::stoi(val)); } }, + { "savagery_req", [](Spell* spell, const std::string& val) { spell->GetSpellData()->savagery_req = static_cast(std::stoi(val)); } }, + { "savagery_upkeep", [](Spell* spell, const std::string& val) { spell->GetSpellData()->savagery_upkeep = static_cast(std::stoi(val)); } }, + { "dissonance_req", [](Spell* spell, const std::string& val) { spell->GetSpellData()->dissonance_req = static_cast(std::stoi(val)); } }, + { "dissonance_upkeep", [](Spell* spell, const std::string& val) { spell->GetSpellData()->dissonance_upkeep = static_cast(std::stoi(val)); } }, + { "target_type", [](Spell* spell, const std::string& val) { spell->GetSpellData()->target_type = static_cast(std::stoi(val)); } }, + { "cast_time", [](Spell* spell, const std::string& val) { spell->GetSpellData()->cast_time = static_cast(std::stoi(val)); } }, + { "recovery", [](Spell* spell, const std::string& val) { spell->GetSpellData()->recovery = std::stof(val); } }, + { "recast", [](Spell* spell, const std::string& val) { spell->GetSpellData()->recast = std::stof(val); } }, + { "linked_timer", [](Spell* spell, const std::string& val) { spell->GetSpellData()->linked_timer = static_cast(std::stoi(val)); } }, + { "radius", [](Spell* spell, const std::string& val) { spell->GetSpellData()->radius = std::stof(val); } }, + { "max_aoe_targets", [](Spell* spell, const std::string& val) { spell->GetSpellData()->max_aoe_targets = static_cast(std::stoi(val)); } }, + { "friendly_spell", [](Spell* spell, const std::string& val) { spell->GetSpellData()->friendly_spell = static_cast(std::stoi(val)); } }, + { "req_concentration", [](Spell* spell, const std::string& val) { spell->GetSpellData()->req_concentration = static_cast(std::stoi(val)); } }, + { "range", [](Spell* spell, const std::string& val) { spell->GetSpellData()->range = std::stof(val); } }, + { "duration1", [](Spell* spell, const std::string& val) { spell->GetSpellData()->duration1 = static_cast(std::stoi(val)); } }, + { "duration2", [](Spell* spell, const std::string& val) { spell->GetSpellData()->duration2 = static_cast(std::stoi(val)); } }, + { "resistibility", [](Spell* spell, const std::string& val) { spell->GetSpellData()->resistibility = std::stof(val); } }, + { "duration_until_cancel", [](Spell* spell, const std::string& val) { spell->GetSpellData()->duration_until_cancel = (val == "1" || val == "true"); } }, + { "power_req_percent", [](Spell* spell, const std::string& val) { spell->GetSpellData()->power_req_percent = static_cast(std::stoi(val)); } }, + { "hp_req_percent", [](Spell* spell, const std::string& val) { spell->GetSpellData()->hp_req_percent = static_cast(std::stoi(val)); } }, + { "savagery_req_percent", [](Spell* spell, const std::string& val) { spell->GetSpellData()->savagery_req_percent = static_cast(std::stoi(val)); } }, + { "dissonance_req_percent", [](Spell* spell, const std::string& val) { spell->GetSpellData()->dissonance_req_percent = static_cast(std::stoi(val)); } }, + { "name", [](Spell* spell, const std::string& val) { spell->GetSpellData()->name.data = val; } }, + { "description", [](Spell* spell, const std::string& val) { spell->GetSpellData()->description.data = val; } }, + { "success_message", [](Spell* spell, const std::string& val) { spell->GetSpellData()->success_message = val; } }, + { "fade_message", [](Spell* spell, const std::string& val) { spell->GetSpellData()->fade_message = val; } }, + { "fade_message_others", [](Spell* spell, const std::string& val) { spell->GetSpellData()->fade_message_others = val; } }, + { "cast_type", [](Spell* spell, const std::string& val) { spell->GetSpellData()->cast_type = static_cast(std::stoi(val)); } }, + { "call_frequency", [](Spell* spell, const std::string& val) { spell->GetSpellData()->call_frequency = static_cast(std::stoi(val)); } }, + { "interruptable", [](Spell* spell, const std::string& val) { spell->GetSpellData()->interruptable = (val == "1" || val == "true"); } }, + { "spell_visual", [](Spell* spell, const std::string& val) { spell->GetSpellData()->spell_visual = static_cast(std::stoi(val)); } }, + { "effect_message", [](Spell* spell, const std::string& val) { spell->GetSpellData()->effect_message = val; } }, + { "min_range", [](Spell* spell, const std::string& val) { spell->GetSpellData()->min_range = std::stof(val); } }, + { "can_effect_raid", [](Spell* spell, const std::string& val) { spell->GetSpellData()->can_effect_raid = static_cast(std::stoi(val)); } }, + { "affect_only_group_members", [](Spell* spell, const std::string& val) { spell->GetSpellData()->affect_only_group_members = static_cast(std::stoi(val)); } }, + { "group_spell", [](Spell* spell, const std::string& val) { spell->GetSpellData()->group_spell = static_cast(std::stoi(val)); } }, + { "hit_bonus", [](Spell* spell, const std::string& val) { spell->GetSpellData()->hit_bonus = std::stof(val); } }, + { "display_spell_tier", [](Spell* spell, const std::string& val) { spell->GetSpellData()->display_spell_tier = static_cast(std::stoi(val)); } }, + { "is_active", [](Spell* spell, const std::string& val) { spell->GetSpellData()->is_active = static_cast(std::stoi(val)); } }, + { "det_type", [](Spell* spell, const std::string& val) { spell->GetSpellData()->det_type = static_cast(std::stoi(val)); } }, + { "incurable", [](Spell* spell, const std::string& val) { spell->GetSpellData()->incurable = (val == "1" || val == "true"); } }, + { "control_effect_type", [](Spell* spell, const std::string& val) { spell->GetSpellData()->control_effect_type = static_cast(std::stoi(val)); } }, + { "casting_flags", [](Spell* spell, const std::string& val) { spell->GetSpellData()->casting_flags = static_cast(std::stoi(val)); } }, + { "cast_while_moving", [](Spell* spell, const std::string& val) { spell->GetSpellData()->cast_while_moving = (val == "1" || val == "true"); } }, + { "persist_through_death", [](Spell* spell, const std::string& val) { spell->GetSpellData()->persist_through_death = (val == "1" || val == "true"); } }, + { "not_maintained", [](Spell* spell, const std::string& val) { spell->GetSpellData()->not_maintained = (val == "1" || val == "true"); } }, + { "is_aa", [](Spell* spell, const std::string& val) { spell->GetSpellData()->is_aa = (val == "1" || val == "true"); } }, + { "savage_bar", [](Spell* spell, const std::string& val) { spell->GetSpellData()->savage_bar = static_cast(std::stoi(val)); } }, + { "spell_type", [](Spell* spell, const std::string& val) { spell->GetSpellData()->spell_type = static_cast(std::stoi(val)); } }, + { "type_group_spell_id", [](Spell* spell, const std::string& val) { spell->GetSpellData()->type_group_spell_id = static_cast(std::stoi(val)); } }, + { "can_fizzle", [](Spell* spell, const std::string& val) { spell->GetSpellData()->can_fizzle = (val == "1" || val == "true"); } }, +}; + LuaInterface::LuaInterface() { shutting_down = false; lua_system_reloading = false; @@ -45,6 +189,7 @@ LuaInterface::LuaInterface() { MSpells.SetName("LuaInterface::MSpells"); MSpawnScripts.SetName("LuaInterface::MSpawnScripts"); MZoneScripts.SetName("LuaInterface::MZoneScripts"); + MPlayerScripts.SetName("LuaInterface::MPlayerScripts"); MQuests.SetName("LuaInterface::MQuests"); MLUAMain.SetName("LuaInterface::MLUAMain"); MItemScripts.SetName("LuaInterface::MItemScripts"); @@ -119,6 +264,7 @@ LuaInterface::~LuaInterface() { DestroyQuests(); DestroyItemScripts(); DestroyZoneScripts(); + DestroyPlayerScripts(); DestroyRegionScripts(); DeleteUserDataPtrs(true); DeletePendingSpells(true); @@ -241,6 +387,25 @@ void LuaInterface::DestroyZoneScripts() { MZoneScripts.releasewritelock(__FUNCTION__, __LINE__); } +void LuaInterface::DestroyPlayerScripts() { + map >::iterator itr; + map::iterator state_itr; + Mutex* mutex = 0; + MPlayerScripts.writelock(__FUNCTION__, __LINE__); + for (itr = player_scripts.begin(); itr != player_scripts.end(); itr++){ + mutex = GetPlayerScriptMutex(itr->first.c_str()); + mutex->writelock(__FUNCTION__, __LINE__); + for(state_itr = itr->second.begin(); state_itr != itr->second.end(); state_itr++) + lua_close(state_itr->first); + mutex->releasewritelock(__FUNCTION__, __LINE__); + safe_delete(mutex); + } + player_scripts.clear(); + player_inverse_scripts.clear(); + player_scripts_mutex.clear(); + MPlayerScripts.releasewritelock(__FUNCTION__, __LINE__); +} + void LuaInterface::DestroyRegionScripts() { map >::iterator itr; map::iterator state_itr; @@ -306,6 +471,20 @@ bool LuaInterface::LoadZoneScript(const char* name) { return ret; } +bool LuaInterface::LoadPlayerScript(const char* name) { + bool ret = false; + if (name) { + lua_State* state = LoadLuaFile(name); + if (state) { + MPlayerScripts.writelock(__FUNCTION__, __LINE__); + player_scripts[name][state] = false; + MPlayerScripts.releasewritelock(__FUNCTION__, __LINE__); + ret = true; + } + } + return ret; +} + bool LuaInterface::LoadRegionScript(const char* name) { bool ret = false; if (name) { @@ -469,6 +648,16 @@ const char* LuaInterface::GetScriptName(lua_State* state) } MZoneScripts.releasewritelock(__FUNCTION__, __LINE__); + MPlayerScripts.writelock(__FUNCTION__, __LINE__); + itr = player_inverse_scripts.find(state); + if (itr != player_inverse_scripts.end()) + { + const char* scriptName = itr->second.c_str(); + MPlayerScripts.releasewritelock(__FUNCTION__, __LINE__); + return scriptName; + } + MPlayerScripts.releasewritelock(__FUNCTION__, __LINE__); + MRegionScripts.writelock(__FUNCTION__, __LINE__); itr = region_inverse_scripts.find(state); if (itr != region_inverse_scripts.end()) @@ -500,6 +689,10 @@ bool LuaInterface::LoadZoneScript(string name) { return LoadZoneScript(name.c_str()); } +bool LuaInterface::LoadPlayerScript(string name) { + return LoadPlayerScript(name.c_str()); +} + bool LuaInterface::LoadRegionScript(string name) { return LoadRegionScript(name.c_str()); } @@ -822,6 +1015,20 @@ lua_State* LuaInterface::LoadLuaFile(const char* name) { return 0; } +void LuaInterface::RemoveSpawnFromSpell(LuaSpell* spell, Spawn* spawn) { + if(spawn->IsEntity()) { + ((Entity*)spawn)->RemoveProc(0, spell); + ((Entity*)spawn)->RemoveSpellEffect(spell); + ((Entity*)spawn)->RemoveSpellBonus(spell); + ((Entity*)spawn)->RemoveEffectsFromLuaSpell(spell); + ((Entity*)spawn)->RemoveWard(spell); + ((Entity*)spawn)->RemoveMaintainedSpell(spell); + + if(spell->spell && spell->spell->GetSpellData() && spell->spell->GetSpellData()->det_type > 0 && (spell->spell->GetSpellDuration() > 0 || spell->spell->GetSpellData()->duration_until_cancel)) + ((Entity*)spawn)->RemoveDetrimentalSpell(spell); + } +} + void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool can_delete, string reason, bool removing_all_spells, bool return_after_call_remove, Spawn* overrideTarget) { if(call_remove_function){ lua_getglobal(spell->state, "remove"); @@ -894,46 +1101,32 @@ void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool if(return_after_call_remove) { if(overrideTarget && overrideTarget->IsEntity()) { - ((Entity*)overrideTarget)->RemoveProc(0, spell); - ((Entity*)overrideTarget)->RemoveSpellEffect(spell); - ((Entity*)overrideTarget)->RemoveSpellBonus(spell); - ((Entity*)overrideTarget)->RemoveEffectsFromLuaSpell(spell); + RemoveSpawnFromSpell(spell, overrideTarget); } return; } - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int8 i = 0; i < spell->targets.size(); i++) { + for (int32 id : spell->GetTargets()) { if(!spell->zone) break; - Spawn* target = spell->zone->GetSpawnByID(spell->targets.at(i)); + Spawn* target = spell->zone->GetSpawnByID(id); if (!target || !target->IsEntity()) continue; - ((Entity*)target)->RemoveProc(0, spell); - ((Entity*)target)->RemoveSpellEffect(spell); - ((Entity*)target)->RemoveSpellBonus(spell); - ((Entity*)target)->RemoveEffectsFromLuaSpell(spell); + RemoveSpawnFromSpell(spell, target); } - - multimap::iterator entries; - for(entries = spell->char_id_targets.begin(); entries != spell->char_id_targets.end(); entries++) - { - Client* tmpClient = zone_list.GetClientByCharID(entries->first); + + for (const auto& [char_id, pet_type] : spell->GetCharIDTargets()) { + Client* tmpClient = zone_list.GetClientByCharID(char_id); if(tmpClient && tmpClient->GetPlayer()) { - tmpClient->GetPlayer()->RemoveProc(0, spell); - tmpClient->GetPlayer()->RemoveSpellEffect(spell); - tmpClient->GetPlayer()->RemoveSpellBonus(spell); - tmpClient->GetPlayer()->RemoveEffectsFromLuaSpell(spell); + RemoveSpawnFromSpell(spell, tmpClient->GetPlayer()); } } - spell->char_id_targets.clear(); // TODO: reach out to those clients in different + spell->ClearCharTargets(); // TODO: reach out to those clients in different spell->timer.Disable(); - - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); if(removing_all_spells) { if(spell->zone && spell->zone->GetSpellProcess()) { @@ -947,10 +1140,7 @@ void LuaInterface::RemoveSpell(LuaSpell* spell, bool call_remove_function, bool if(spell->zone && spell->zone->GetSpellProcess()) { spell->zone->GetSpellProcess()->RemoveSpellScriptTimerBySpell(spell, false); } - spell->caster->RemoveProc(0, spell); - spell->caster->RemoveSpellEffect(spell); - spell->caster->RemoveMaintainedSpell(spell); - spell->caster->RemoveEffectsFromLuaSpell(spell); + RemoveSpawnFromSpell(spell, spell->caster); if(spell->spell && spell->spell->GetSpellData() && spell->caster->IsPlayer() && !removing_all_spells) { @@ -1262,6 +1452,8 @@ void LuaInterface::RegisterFunctions(lua_State* state) { lua_register(state, "UpdateQuestZone", EQ2Emu_lua_UpdateQuestZone); lua_register(state, "SetCompletedDescription", EQ2Emu_lua_SetCompletedDescription); lua_register(state, "OfferQuest", EQ2Emu_lua_OfferQuest); + lua_register(state, "DeleteQuest", EQ2Emu_lua_DeleteQuest); + lua_register(state, "DeleteAllQuests", EQ2Emu_lua_DeleteAllQuests); lua_register(state, "ProvidesQuest", EQ2Emu_lua_ProvidesQuest); lua_register(state, "HasQuest", EQ2Emu_lua_HasQuest); lua_register(state, "HasPendingQuest", EQ2Emu_lua_HasPendingQuest); @@ -1632,6 +1824,21 @@ void LuaInterface::RegisterFunctions(lua_State* state) { lua_register(state,"GetZonePlayerFirstLevel", EQ2Emu_lua_GetZonePlayerFirstLevel); lua_register(state,"GetSpellRequiredLevel", EQ2Emu_lua_GetSpellRequiredLevel); + lua_register(state,"GetExpRequiredByLevel", EQ2Emu_lua_GetExpRequiredByLevel); + + lua_register(state,"ShowShopWindow", EQ2Emu_lua_ShowShopWindow); + lua_register(state,"SetSpawnHouseScript", EQ2Emu_lua_SetSpawnHouseScript); + lua_register(state,"SendBook", EQ2Emu_lua_SendBook); + lua_register(state,"GetPickupItemID", EQ2Emu_lua_GetPickupItemID); + lua_register(state,"SetHouseCharacterID", EQ2Emu_lua_SetHouseCharacterID); + lua_register(state,"GetHouseCharacterID", EQ2Emu_lua_GetHouseCharacterID); + lua_register(state,"ShowHouseShopMerchant", EQ2Emu_lua_ShowHouseShopMerchant); + lua_register(state,"AttackAllowed", EQ2Emu_lua_AttackAllowed); + lua_register(state,"IsInRaid", EQ2Emu_lua_IsInRaid); + lua_register(state,"InSameRaid", EQ2Emu_lua_InSameRaid); + lua_register(state,"GetRaid", EQ2Emu_lua_GetRaid); + lua_register(state,"AdjustHatePosition", EQ2Emu_lua_AdjustHatePosition); + lua_register(state,"RemoveCharacterProperty", EQ2Emu_lua_RemoveCharacterProperty); } void LuaInterface::LogError(const char* error, ...) { @@ -1661,7 +1868,7 @@ void LuaInterface::AddUserDataPtr(LUAUserData* data, void* data_ptr) { user_data[data] = Timer::GetCurrentTime2() + 300000; //allow a function to use this pointer for 5 minutes } -void LuaInterface::DeletePendingSpells(bool all) { +void LuaInterface::DeletePendingSpells(bool all, ZoneServer* zone) { MSpells.lock(); MSpellDelete.lock(); if (spells_pending_delete.size() > 0) { @@ -1670,7 +1877,7 @@ void LuaInterface::DeletePendingSpells(bool all) { vector tmp_deletes; vector::iterator del_itr; for (itr = spells_pending_delete.begin(); itr != spells_pending_delete.end(); itr++) { - if (all || time >= itr->second) + if ((!zone && (all || time >= itr->second)) || (zone && itr->first->zone == zone)) tmp_deletes.push_back(itr->first); } LuaSpell* spell = 0; @@ -1684,27 +1891,21 @@ void LuaInterface::DeletePendingSpells(bool all) { if(!all) { // rely on targets the spell->caster could be corrupt - bool spellDeleted = false; - if(spell->targets.size() > 0) { - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int8 i = 0; i < spell->targets.size(); i++) { - Spawn* target = spell->zone->GetSpawnByID(spell->targets.at(i)); + for (int32 id : spell->GetTargets()) { + Spawn* target = spell->zone->GetSpawnByID(id); if (!target || !target->IsEntity()) continue; - - if(!spellDeleted && spell->zone && spell->zone->GetSpellProcess()) - spell->zone->GetSpellProcess()->DeleteActiveSpell(spell, true); - - spellDeleted = true; + + if(target->IsEntity()) { + RemoveSpawnFromSpell(spell, target); + } } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); - } - if(!spellDeleted && spell->zone && spell->zone->GetSpellProcess()) { + if(spell->zone && spell->zone->GetSpellProcess()) { spell->zone->GetSpellProcess()->DeleteActiveSpell(spell, true); } } - + if (spell->spell->IsCopiedSpell()) { RemoveCustomSpell(spell->spell->GetSpellID()); @@ -2111,7 +2312,6 @@ LuaSpell* LuaInterface::LoadSpellScript(const char* name) { spell->interrupted = false; spell->last_spellattack_hit = false; spell->crit = false; - spell->MSpellTargets.SetName("LuaSpell.MSpellTargets"); spell->cancel_after_all_triggers = false; spell->num_triggers = 0; spell->num_calls = 0; @@ -2127,6 +2327,7 @@ LuaSpell* LuaInterface::LoadSpellScript(const char* name) { spell->initial_target_char_id = 0; spell->zone = nullptr; spell->initial_caster_level = 0; + spell->is_loaded_recast = false; MSpells.lock(); current_spells[spell->state] = spell; @@ -2172,6 +2373,17 @@ Mutex* LuaInterface::GetZoneScriptMutex(const char* name) { return mutex; } +Mutex* LuaInterface::GetPlayerScriptMutex(const char* name) { + Mutex* mutex = 0; + if(player_scripts_mutex.count(name) > 0) + mutex = player_scripts_mutex[name]; + if(!mutex){ + mutex = new Mutex(); + player_scripts_mutex[name] = mutex; + } + return mutex; +} + Mutex* LuaInterface::GetRegionScriptMutex(const char* name) { Mutex* mutex = 0; if(region_scripts_mutex.count(name) > 0) @@ -2216,6 +2428,14 @@ void LuaInterface::UseZoneScript(const char* name, lua_State* state, bool val) { MZoneScripts.releasewritelock(__FUNCTION__, __LINE__); } +void LuaInterface::UsePlayerScript(const char* name, lua_State* state, bool val) { + + MPlayerScripts.writelock(__FUNCTION__, __LINE__); + player_scripts[name][state] = val; + player_inverse_scripts[state] = name; + MPlayerScripts.releasewritelock(__FUNCTION__, __LINE__); +} + void LuaInterface::UseRegionScript(const char* name, lua_State* state, bool val) { MRegionScripts.writelock(__FUNCTION__, __LINE__); @@ -2328,6 +2548,40 @@ lua_State* LuaInterface::GetZoneScript(const char* name, bool create_new, bool u return ret; } +lua_State* LuaInterface::GetPlayerScript(const char* name, bool create_new, bool use) { + map >::iterator itr; + map::iterator zone_script_itr; + lua_State* ret = 0; + Mutex* mutex = 0; + + itr = player_scripts.find(name); + if(itr != player_scripts.end()) { + mutex = GetPlayerScriptMutex(name); + mutex->readlock(__FUNCTION__, __LINE__); + for(zone_script_itr = itr->second.begin(); zone_script_itr != itr->second.end(); zone_script_itr++){ + if(!zone_script_itr->second){ //not in use + ret = zone_script_itr->first; + + if (use) + { + zone_script_itr->second = true; + break; // don't keep iterating, we already have our result + } + } + } + mutex->releasereadlock(__FUNCTION__, __LINE__); + } + if(!ret && create_new){ + if(name && LoadPlayerScript(name)) + ret = GetPlayerScript(name, create_new, use); + else{ + LogError("Error LUA Zone Script '%s'", name); + return 0; + } + } + return ret; +} + lua_State* LuaInterface::GetRegionScript(const char* name, bool create_new, bool use) { map >::iterator itr; map::iterator region_script_itr; @@ -2408,7 +2662,6 @@ LuaSpell* LuaInterface::CreateSpellScript(const char* name, lua_State* existStat new_spell->interrupted = false; new_spell->crit = false; new_spell->last_spellattack_hit = false; - new_spell->MSpellTargets.SetName("LuaSpell.MSpellTargets"); new_spell->cancel_after_all_triggers = false; new_spell->num_triggers = 0; new_spell->num_calls = 0; @@ -2427,6 +2680,7 @@ LuaSpell* LuaInterface::CreateSpellScript(const char* name, lua_State* existStat new_spell->initial_target_char_id = 0; new_spell->zone = nullptr; new_spell->initial_caster_level = 0; + new_spell->is_loaded_recast = false; current_spells[new_spell->state] = new_spell; return new_spell; @@ -2699,6 +2953,57 @@ bool LuaInterface::RunZoneScriptWithReturn(string script_name, const char* funct return false; } +bool LuaInterface::RunPlayerScriptWithReturn(const string script_name, const char* function_name, const std::vector& args, sint32* returnValue) { + lua_State* state = GetPlayerScript(script_name.c_str(), true, true); + if (state) { + Mutex* mutex = GetPlayerScriptMutex(script_name.c_str()); + if (mutex) + mutex->readlock(__FUNCTION__, __LINE__); + else { + LogError("Error getting lock for '%s'", script_name.c_str()); + UsePlayerScript(script_name.c_str(), state, false); + return false; + } + lua_getglobal(state, function_name); + if (!lua_isfunction(state, lua_gettop(state))) { + lua_pop(state, 1); + mutex->releasereadlock(__FUNCTION__); + UsePlayerScript(script_name.c_str(), state, false); + return false; + } + int8 num_params = 0; + for (const LuaArg& arg : args) { + switch (arg.type) { + case LuaArgType::SINT64: SetSInt64Value(state, arg.si); num_params++; break; + case LuaArgType::SINT: SetSInt32Value(state, arg.low_si); num_params++; break; + case LuaArgType::INT64: SetInt64Value(state, arg.i); num_params++; break; + case LuaArgType::INT: SetInt32Value(state, arg.low_i); num_params++; break; + case LuaArgType::FLOAT: SetFloatValue(state, arg.f); num_params++; break; + case LuaArgType::STRING: SetStringValue(state, arg.s.c_str()); num_params++; break; + case LuaArgType::BOOL: SetBooleanValue(state, arg.b); num_params++; break; + case LuaArgType::SPAWN: SetSpawnValue(state, arg.spawn); num_params++; break; + case LuaArgType::ZONE: SetZoneValue(state, arg.zone); num_params++; break; + case LuaArgType::SKILL: SetSkillValue(state, arg.skill); num_params++; break; + case LuaArgType::ITEM: SetItemValue(state, arg.item); num_params++; break; + case LuaArgType::QUEST: SetQuestValue(state, arg.quest); num_params++; break; + case LuaArgType::SPELL: SetSpellValue(state, arg.spell); num_params++; break; + } + } + if (!CallScriptSInt32(state, num_params, returnValue)) { + if (mutex) + mutex->releasereadlock(__FUNCTION__, __LINE__); + UsePlayerScript(script_name.c_str(), state, false); + return false; + } + if (mutex) + mutex->releasereadlock(__FUNCTION__, __LINE__); + UsePlayerScript(script_name.c_str(), state, false); + return true; + } + else + return false; +} + bool LuaInterface::RunRegionScript(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn, sint32 int32_arg1, int32* returnValue) { if (!zone) @@ -2803,6 +3108,15 @@ void LuaInterface::SetLuaUserDataStale(void* ptr) { } } +bool LuaInterface::IsLuaUserDataValid(void* ptr) { + std::shared_lock lock(MLUAUserData); + std::map::iterator itr = user_data_ptr.find(ptr); + if(itr != user_data_ptr.end()) { + return itr->second->correctly_initialized; + } + return false; +} + LUAUserData::LUAUserData(){ correctly_initialized = false; quest = 0; @@ -2912,4 +3226,41 @@ LUASpellWrapper::LUASpellWrapper() { bool LUASpellWrapper::IsSpell() { return true; +} + +bool LuaSpell::SetSpellDataIndex(int idx, const std::string& value, const std::string& value2) { + if (!spell || spell->lua_data.size() <= idx) + return false; + + LUAData* data = spell->lua_data[idx]; + if (!data) + return false; + + bool setVal = true; + + switch (data->type) { + case 0: // int + int + data->int_value = std::stoi(value); + data->int_value2 = std::stoi(value2); + break; + case 1: // float + float + data->float_value = std::stof(value); + data->float_value2 = std::stof(value2); + break; + case 2: // bool + data->bool_value = (value == "1" || value == "true"); + break; + case 3: // string + string + data->string_value = value; + data->string_value2 = value2; + break; + default: + setVal = false; + break; + } + + if (setVal) + data->needs_db_save = true; + + return setVal; } \ No newline at end of file diff --git a/old/WorldServer/LuaInterface.h b/old/WorldServer/LuaInterface.h index 382f7dd..7590319 100644 --- a/old/WorldServer/LuaInterface.h +++ b/old/WorldServer/LuaInterface.h @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,15 +17,17 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #ifndef LUA_INTERFACE_H #define LUA_INTERFACE_H #include #include +#include #include "Spawn.h" #include "Spells.h" - +#include "../common/Mutex.h" #include "Quests.h" #include "zoneserver.h" #include "client.h" @@ -73,13 +75,27 @@ struct OptionWindowOption { #define EFFECT_FLAG_FEAR_IMMUNE 2097152 #define EFFECT_FLAG_SAFEFALL 4194304 +enum class SpellFieldType { + Integer, + Float, + Boolean, + String +}; + +using SpellFieldGetter = std::function; +extern const std::unordered_map> SpellFieldGenericSetters; +extern const std::unordered_map> SpellDataFieldAccessors; +extern std::unordered_map> SpellDataFieldSetters; + struct LuaSpell{ Entity* caster; int32 initial_caster_char_id; int32 initial_target; int32 initial_target_char_id; + mutable std::shared_mutex targets_mutex; vector targets; vector removed_targets; // previously cancelled, expired, used, so on + mutable std::shared_mutex char_id_targets_mutex; multimap char_id_targets; Spell* spell; lua_State* state; @@ -99,13 +115,239 @@ struct LuaSpell{ bool cancel_after_all_triggers; bool had_triggers; bool had_dmg_remaining; - Mutex MSpellTargets; Mutex MScriptMutex; int32 effect_bitmask; bool restored; // restored spell cross zone std::atomic has_proc; ZoneServer* zone; int16 initial_caster_level; + bool is_loaded_recast; + + std::unordered_set modified_fields; + mutable std::shared_mutex spell_modify_mutex; + + void AddTarget(int32 target_id) { + std::unique_lock lock(targets_mutex); + bool hasTarget = std::find(targets.begin(), targets.end(), target_id) != targets.end(); + if(!hasTarget) + targets.push_back(target_id); + } + + int32 GetPrimaryTargetID() const { + std::shared_lock lock(targets_mutex); + return targets.empty() ? -1 : targets[0]; + } + + std::optional GetPrimaryTarget() const { + std::shared_lock lock(targets_mutex); + if (!targets.empty()) + return targets[0]; + return std::nullopt; + } + + + std::vector GetTargets() { + std::shared_lock lock(targets_mutex); + return targets; + } + + bool HasNoTargets() const { + std::shared_lock lock(targets_mutex); + return targets.empty(); + } + + int32 GetTargetCount() const { + std::shared_lock lock(targets_mutex); + return static_cast(targets.size()); + } + + bool HasTarget(int32 id) const { + std::shared_lock lock(targets_mutex); + return std::find(targets.begin(), targets.end(), id) != targets.end(); + } + + bool HasAnyTarget(const std::vector& ids) const { + std::shared_lock lock(targets_mutex); + return std::any_of( + ids.begin(), ids.end(), + [this](int32 id){ + return std::find(targets.begin(), targets.end(), id) != targets.end(); + } + ); + } + + std::vector GetRemovedTargets() const { + std::shared_lock lock(targets_mutex); + return removed_targets; + } + + void RemoveTarget(int32 target_id) { + std::unique_lock lock(targets_mutex); + auto& v = targets; + v.erase(std::remove(v.begin(), v.end(), target_id), v.end()); + removed_targets.push_back(target_id); + } + + void AddRemoveTarget(int32 target_id) { + std::unique_lock lock(targets_mutex); + removed_targets.push_back(target_id); + } + + void SwapTargets(std::vector& new_targets) { + std::unique_lock lock(targets_mutex); + targets.swap(new_targets); + } + + void ClearTargets() { + std::unique_lock lock(targets_mutex); + targets.clear(); + removed_targets.clear(); + } + + std::multimap GetCharIDTargets() const { + std::shared_lock lock(char_id_targets_mutex); + return char_id_targets; + } + + void AddCharIDTarget(int32 char_id, int8 value) { + std::unique_lock lock(char_id_targets_mutex); + + bool exists = false; + auto range = char_id_targets.equal_range(char_id); + for (auto it = range.first; it != range.second; ++it) { + if (it->second == value) { + exists = true; + break; + } + } + + if(!exists) + char_id_targets.insert({char_id, value}); + } + + bool HasNoCharIDTargets() const { + std::shared_lock lock(char_id_targets_mutex); + return char_id_targets.empty(); + } + + void RemoveCharIDTarget(int32 char_id) { + std::unique_lock lock(char_id_targets_mutex); + char_id_targets.erase(char_id); // removes all entries with that key + } + + void RemoveCharIDTargetAndType(int32 char_id, int8 type) { + std::unique_lock lock(char_id_targets_mutex); + + auto range = char_id_targets.equal_range(char_id); + for (auto it = range.first; it != range.second; ++it) { + if (it->second == type) { + char_id_targets.erase(it); + break; // remove only one matching pair + } + } + } + + void ClearCharTargets() { + std::unique_lock lock(char_id_targets_mutex); + char_id_targets.clear(); + } + + void MarkFieldModified(const std::string& field) { + std::unique_lock lock(spell_modify_mutex); + modified_fields.insert(field); + } + + bool IsFieldModified(const std::string& field) const { + std::shared_lock lock(spell_modify_mutex); + return modified_fields.find(field) != modified_fields.end(); + } + + void ClearFieldModifications() { + std::unique_lock lock(spell_modify_mutex); + modified_fields.clear(); + } + + std::unordered_set GetModifiedFieldsCopy() const { + std::shared_lock lock(spell_modify_mutex); + return modified_fields; // safe shallow copy + } + + bool SetSpellDataGeneric(const std::string& field, int value) { + return SetSpellDataGeneric(field, std::to_string(value)); + } + + bool SetSpellDataGeneric(const std::string& field, float value) { + return SetSpellDataGeneric(field, std::to_string(value)); + } + + bool SetSpellDataGeneric(const std::string& field, bool value) { + return SetSpellDataGeneric(field, value ? "1" : "0"); + } + + bool SetSpellDataGeneric(const std::string& field, const std::string& value) { + auto it = SpellFieldGenericSetters.find(field); + if (it == SpellFieldGenericSetters.end()) + return false; + + if (!spell) + return false; + + it->second(spell, value); + return true; + } + + bool SetSpellDataIndex(int idx, const std::string& value, const std::string& value2 = ""); + + bool SetSpellDataIndex(int idx, int value, int value2) { + return SetSpellDataIndex(idx, std::to_string(value), std::to_string(value2)); + } + + bool SetSpellDataIndex(int idx, float value, float value2) { + return SetSpellDataIndex(idx, std::to_string(value), std::to_string(value2)); + } + + bool SetSpellDataIndex(int idx, bool value) { + return SetSpellDataIndex(idx, value ? "1" : "0"); + } +}; + +enum class LuaArgType { SINT64, INT64, SINT, INT, FLOAT, STRING, BOOL, SPAWN, ZONE, SKILL, ITEM, QUEST, SPELL /* etc */ }; + +struct LuaArg { + LuaArgType type; + union { + sint64 si; + sint64 i; + int32 low_i; + sint32 low_si; + float f; + bool b; + }; + std::string s; + Spawn* spawn = nullptr; + ZoneServer* zone = nullptr; + Skill* skill = nullptr; + Item* item = nullptr; + Quest* quest = nullptr; + LuaSpell* spell = nullptr; + + LuaArg(sint64 val) : type(LuaArgType::SINT64), si(val) {} + LuaArg(sint32 val) : type(LuaArgType::SINT), low_si(val) {} + LuaArg(sint16 val) : type(LuaArgType::SINT), low_si(val) {} + LuaArg(sint8 val) : type(LuaArgType::SINT), low_si(val) {} + LuaArg(int64 val) : type(LuaArgType::INT64), i(val) {} + LuaArg(int32 val) : type(LuaArgType::INT), low_i(val) {} + LuaArg(int16 val) : type(LuaArgType::INT), low_i(val) {} + LuaArg(int8 val) : type(LuaArgType::INT), low_i(val) {} + LuaArg(float val) : type(LuaArgType::FLOAT), f(val) {} + LuaArg(bool val) : type(LuaArgType::BOOL), b(val) {} + LuaArg(const std::string& val) : type(LuaArgType::STRING), s(val) {} + LuaArg(Spawn* val) : type(LuaArgType::SPAWN), spawn(val) {} + LuaArg(ZoneServer* val) : type(LuaArgType::ZONE), zone(val) {} + LuaArg(Skill* val) : type(LuaArgType::SKILL), skill(val) {} + LuaArg(Item* val) : type(LuaArgType::ITEM), item(val) {} + LuaArg(Quest* val) : type(LuaArgType::QUEST), quest(val) {} + LuaArg(LuaSpell* val) : type(LuaArgType::SPELL), spell(val) {} }; class LUAUserData{ @@ -192,10 +434,13 @@ public: bool LoadSpawnScript(const char* name); bool LoadZoneScript(string name); bool LoadZoneScript(const char* name); + bool LoadPlayerScript(string name); + bool LoadPlayerScript(const char* name); bool LoadRegionScript(string name); bool LoadRegionScript(const char* name); LuaSpell* LoadSpellScript(string name); LuaSpell* LoadSpellScript(const char* name); + void RemoveSpawnFromSpell(LuaSpell* spell, Spawn* spawn); void RemoveSpell(LuaSpell* spell, bool call_remove_function = true, bool can_delete = true, string reason = "", bool removing_all_spells = false, bool return_after_call_remove = false, Spawn* overrideTarget = nullptr); Spawn* GetSpawn(lua_State* state, int8 arg_num = 1); Item* GetItem(lua_State* state, int8 arg_num = 1); @@ -242,10 +487,12 @@ public: void UseItemScript(const char* name, lua_State* state, bool val); void UseSpawnScript(const char* name, lua_State* state, bool val); void UseZoneScript(const char* name, lua_State* state, bool val); + void UsePlayerScript(const char* name, lua_State* state, bool val); void UseRegionScript(const char* name, lua_State* state, bool val); lua_State* GetItemScript(const char* name, bool create_new = true, bool use = false); lua_State* GetSpawnScript(const char* name, bool create_new = true, bool use = false); lua_State* GetZoneScript(const char* name, bool create_new = true, bool use = false); + lua_State* GetPlayerScript(const char* name, bool create_new = true, bool use = false); lua_State* GetRegionScript(const char* name, bool create_new = true, bool use = false); LuaSpell* GetSpellScript(const char* name, bool create_new = true, bool use = true); LuaSpell* CreateSpellScript(const char* name, lua_State* existState); @@ -263,6 +510,7 @@ public: bool CallSpawnScript(lua_State* state, int8 num_parameters); bool RunZoneScript(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn = 0, int32 int32_arg1 = 0, const char* str_arg1 = 0, Spawn* spawn_arg1 = 0, int32 int32_arg2 = 0, const char* str_arg2 = 0, Spawn* spawn_arg2 = 0); bool RunZoneScriptWithReturn(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn, int32 int32_arg1, int32 int32_arg2, int32 int32_arg3, int32* returnValue = 0); + bool RunPlayerScriptWithReturn(const string script_name, const char* function_name, const std::vector& args, sint32* returnValue = 0); bool CallScriptInt32(lua_State* state, int8 num_parameters, int32* returnValue = 0); bool CallScriptSInt32(lua_State* state, int8 num_parameters, sint32* returnValue = 0); bool RunRegionScript(string script_name, const char* function_name, ZoneServer* zone, Spawn* spawn = 0, sint32 int32_arg1 = 0, int32* returnValue = 0); @@ -273,6 +521,7 @@ public: void DestroyItemScripts(); void DestroyQuests(bool reload = false); void DestroyZoneScripts(); + void DestroyPlayerScripts(); void DestroyRegionScripts(); void SimpleLogError(const char* error); void LogError(const char* error, ...); @@ -285,11 +534,12 @@ public: map GetDebugClients(){ return debug_clients; } void AddUserDataPtr(LUAUserData* data, void* data_ptr = 0); void DeleteUserDataPtrs(bool all); - void DeletePendingSpells(bool all); + void DeletePendingSpells(bool all, ZoneServer* zone = nullptr); void DeletePendingSpell(LuaSpell* spell); Mutex* GetSpawnScriptMutex(const char* name); Mutex* GetItemScriptMutex(const char* name); Mutex* GetZoneScriptMutex(const char* name); + Mutex* GetPlayerScriptMutex(const char* name); Mutex* GetRegionScriptMutex(const char* name); Mutex* GetSpellScriptMutex(const char* name); Mutex* GetQuestMutex(Quest* quest); @@ -309,6 +559,7 @@ public: int32 GetFreeCustomSpellID(); void SetLuaUserDataStale(void* ptr); + bool IsLuaUserDataValid(void* ptr); private: bool shutting_down; @@ -330,6 +581,7 @@ private: map > item_scripts; map > spawn_scripts; map > zone_scripts; + map > player_scripts; map > region_scripts; map > spell_scripts; @@ -339,11 +591,13 @@ private: map item_inverse_scripts; map spawn_inverse_scripts; map zone_inverse_scripts; + map player_inverse_scripts; map region_inverse_scripts; map item_scripts_mutex; map spawn_scripts_mutex; map zone_scripts_mutex; + map player_scripts_mutex; map quests_mutex; map region_scripts_mutex; map spell_scripts_mutex; @@ -353,6 +607,7 @@ private: Mutex MSpawnScripts; Mutex MItemScripts; Mutex MZoneScripts; + Mutex MPlayerScripts; Mutex MQuests; Mutex MLUAMain; Mutex MSpellDelete; diff --git a/old/WorldServer/MutexHelper.h b/old/WorldServer/MutexHelper.h index 2eaa635..58c3aa7 100644 --- a/old/WorldServer/MutexHelper.h +++ b/old/WorldServer/MutexHelper.h @@ -20,7 +20,8 @@ #ifndef MUTEXHELPER_H #define MUTEXHELPER_H -#include "../common/timer.hpp" +#include "../common/timer.h" +#include "../common/Mutex.h" #include #include diff --git a/old/WorldServer/NPC.cpp b/old/WorldServer/NPC.cpp index c1e3293..41123cf 100644 --- a/old/WorldServer/NPC.cpp +++ b/old/WorldServer/NPC.cpp @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,13 +17,14 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #include "NPC.h" -#include "WorldDatabase.hpp" +#include "WorldDatabase.h" #include #include "client.h" #include "World.h" #include "races.h" -#include "../common/log.hpp" +#include "../common/Log.h" #include "NPC_AI.h" #include "Appearances.h" #include "SpellProcess.h" @@ -108,6 +109,8 @@ NPC::NPC(NPC* old_npc){ SetLootDropType(old_npc->GetLootDropType()); has_spells = old_npc->HasSpells(); SetScaredByStrongPlayers(old_npc->IsScaredByStrongPlayers()); + if(old_npc->GetSpawnScriptSetDB() && old_npc->GetSpawnScript()) + SetSpawnScript(std::string(old_npc->GetSpawnScript())); } } diff --git a/old/WorldServer/NPC_AI.cpp b/old/WorldServer/NPC_AI.cpp index 21374dd..3e84ec2 100644 --- a/old/WorldServer/NPC_AI.cpp +++ b/old/WorldServer/NPC_AI.cpp @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -22,7 +22,7 @@ #include "Combat.h" #include "zoneserver.h" #include "Spells.h" -#include "../common/log.hpp" +#include "../common/Log.h" #include "LuaInterface.h" #include "World.h" #include "Rules/Rules.h" @@ -271,13 +271,57 @@ void Brain::AddHate(Entity* entity, sint32 hate) { } } -void Brain::ClearHate() { +bool Brain::AdjustHatePosition(int32 id, bool increase) { + // Lock the hate list, we are altering the list so use a write lock + MHateList.writelock(__FUNCTION__, __LINE__); + + auto it = m_hatelist.find(id); + if (it == m_hatelist.end()) { + MHateList.releasewritelock(__FUNCTION__, __LINE__); + return false; + } + + // Build a vector of (id, hate), sorted highest→lowest hate + std::vector> sorted; + sorted.reserve(m_hatelist.size()); + for (auto &kv : m_hatelist) + sorted.emplace_back(kv.first, kv.second); + std::sort(sorted.begin(), sorted.end(), + [](auto &a, auto &b){ return a.second > b.second; }); + + // Locate our position + auto posIt = std::find_if(sorted.begin(), sorted.end(), + [&](auto &p){ return p.first == id; }); + size_t idx = std::distance(sorted.begin(), posIt); + + if (increase) { + if (idx == 0) { + MHateList.releasewritelock(__FUNCTION__, __LINE__); + return false; // no higher to go + } + sint32 aboveHate = sorted[idx - 1].second; + it->second = aboveHate + 1; // move up + } + else { + if (idx + 1 >= sorted.size()) { + MHateList.releasewritelock(__FUNCTION__, __LINE__); + return false; // already at bottom + } + sint32 belowHate = sorted[idx + 1].second; + it->second = belowHate - 1; // move lower + } + + MHateList.releasewritelock(__FUNCTION__, __LINE__); + return true; +} + +void Brain::ClearHate(bool lockSpawnList) { // Lock the hate list, we are altering the list so use a write lock MHateList.writelock(__FUNCTION__, __LINE__); map::iterator itr; for (itr = m_hatelist.begin(); itr != m_hatelist.end(); itr++) { - Spawn* spawn = m_body->GetZone()->GetSpawnByID(itr->first); + Spawn* spawn = m_body->GetZone()->GetSpawnByID(itr->first, lockSpawnList); if (spawn && spawn->IsEntity()) { ((Entity*)spawn)->MHatedBy.lock(); diff --git a/old/WorldServer/NPC_AI.h b/old/WorldServer/NPC_AI.h index cbb8166..c4aaa5c 100644 --- a/old/WorldServer/NPC_AI.h +++ b/old/WorldServer/NPC_AI.h @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #ifndef __NPC_AI_H__ #define __NPC_AI_H__ #include "NPC.h" @@ -58,8 +59,10 @@ public: /// The entity we are adding to this NPC's hate list /// The amount of hate to add virtual void AddHate(Entity* entity, sint32 hate); + virtual bool AdjustHatePosition(int32 id, bool increase); + /// Completely clears the hate list for this npc - void ClearHate(); + void ClearHate(bool lockSpawnList = false); /// Removes the given entity from this NPC's hate list /// Entity to remove from this NPC's hate list void ClearHate(Entity* entity); diff --git a/old/WorldServer/Object.cpp b/old/WorldServer/Object.cpp index 971e789..c53bcad 100644 --- a/old/WorldServer/Object.cpp +++ b/old/WorldServer/Object.cpp @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #include "World.h" #include "Object.h" #include "Spells.h" @@ -95,5 +96,7 @@ Object* Object::Copy(){ new_spawn->SetOmittedByDBFlag(IsOmittedByDBFlag()); new_spawn->SetLootTier(GetLootTier()); new_spawn->SetLootDropType(GetLootDropType()); + if(GetSpawnScriptSetDB() && GetSpawnScript()) + new_spawn->SetSpawnScript(std::string(GetSpawnScript())); return new_spawn; } diff --git a/old/WorldServer/Player.cpp b/old/WorldServer/Player.cpp index 52deb31..30ea840 100644 --- a/old/WorldServer/Player.cpp +++ b/old/WorldServer/Player.cpp @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,14 +17,15 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #include "Player.h" -#include "../common/misc_functions.hpp" +#include "../common/MiscFunctions.h" #include "World.h" -#include "WorldDatabase.hpp" +#include "WorldDatabase.h" #include #include "classes.h" #include "LuaInterface.h" -#include "../common/log.hpp" +#include "../common/Log.h" #include "Rules/Rules.h" #include "Titles.h" #include "Languages.h" @@ -46,6 +47,7 @@ extern MasterItemList master_item_list; extern RuleManager rule_manager; extern MasterTitlesList master_titles_list; extern MasterLanguagesList master_languages_list; +std::map Player::m_levelXPReq; Player::Player(){ tutorial_step = 0; @@ -135,6 +137,7 @@ Player::Player(){ active_drink_unique_id = 0; raidsheet_changed = false; hassent_raid = false; + house_vault_slots = 0; } Player::~Player(){ SetSaveSpellEffects(true); @@ -597,6 +600,7 @@ PacketStruct* PlayerInfo::serialize2(int16 version){ packet->setDataByName("unknown20", 50, 75); */ //packet->setDataByName("rain2", -102.24); + player->GetSpellEffectMutex()->readlock(__FUNCTION__, __LINE__); for(int i=0;i<45;i++){ if(i < 30){ packet->setSubstructDataByName("maintained_effects", "name", info_struct->maintained_effects[i].name, i, 0); @@ -620,6 +624,7 @@ PacketStruct* PlayerInfo::serialize2(int16 version){ packet->setSubstructDataByName("spell_effects", "icon", info_struct->spell_effects[i].icon, i, 0); packet->setSubstructDataByName("spell_effects", "icon_type", info_struct->spell_effects[i].icon_backdrop, i, 0); } + player->GetSpellEffectMutex()->releasereadlock(__FUNCTION__, __LINE__); return packet; } return 0; @@ -949,7 +954,7 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal packet->setDataByName("rain2", info_struct->get_wind()); //-102.24); packet->setDataByName("status_points", info_struct->get_status_points()); packet->setDataByName("guild_status", 888888); - + packet->setDataByName("vault_slots", player->GetHouseVaultSlots()); if (house_zone_id > 0){ string house_name = database.GetZoneName(house_zone_id); if(house_name.length() > 0) @@ -998,8 +1003,8 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal packet->setDataByName("decrease_falling_dmg", 169); if (version <= 561) { - packet->setDataByName("exp_yellow", info_struct->get_xp_yellow() / 10); - packet->setDataByName("exp_blue", info_struct->get_xp_blue()/100); + packet->setDataByName("exp_yellow", info_struct->get_xp_yellow() / 10); + packet->setDataByName("exp_blue", ((int16)info_struct->get_xp_yellow() % 100) + (info_struct->get_xp_blue() / 100)); } else { packet->setDataByName("exp_yellow", info_struct->get_xp_yellow()); @@ -1065,6 +1070,27 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal packet->setDataByName("increase_max_power2", 128); packet->setDataByName("vision", info_struct->get_vision()); + switch(info_struct->get_vision()) { + case 1: // ultravision + case 255: + packet->setDataByName("spell_state_ultravision", 1); + break; + case 2: // infravision + packet->setDataByName("spell_state_infravision", 1); + break; + case 3: //sonicvision + packet->setDataByName("spell_state_sonicvision", 1); + break; + case 4: // fishvision + packet->setDataByName("spell_state_fishvision", 1); + break; + case 5: // auravision + packet->setDataByName("spell_state_auravision", 1); + break; + } + packet->setDataByName("spell_prop_redlight", info_struct->get_redlight()); + packet->setDataByName("spell_prop_greenlight", info_struct->get_greenlight()); + packet->setDataByName("spell_prop_bluelight", info_struct->get_bluelight()); packet->setDataByName("breathe_underwater", info_struct->get_breathe_underwater()); int32 expireTimestamp = 0; @@ -1480,7 +1506,7 @@ vector Player::UnequipItem(int16 index, sint32 bag_id, int8 slot, in SetCharSheetChanged(true); SetEquipment(0, equip_slot_id ? equip_slot_id : old_slot); } - else if (item_list.AssignItemToFreeSlot(item)) { + else if (item_list.AssignItemToFreeSlot(item, true)) { if(appearance_type) database.DeleteItem(GetCharacterID(), item, "APPEARANCE"); else @@ -1641,8 +1667,8 @@ vector Player::UnequipItem(int16 index, sint32 bag_id, int8 slot, in } } else { - if ((bag_id == 0 && slot < NUM_INV_SLOTS) || (bag_id == -3 && slot < NUM_BANK_SLOTS) || (bag_id == -4 && slot < NUM_SHARED_BANK_SLOTS)) { - if (bag_id == -4 && item->CheckFlag(NO_TRADE)) { + if ((bag_id == 0 && slot < NUM_INV_SLOTS) || (bag_id == InventorySlotType::BANK && slot < NUM_BANK_SLOTS) || (bag_id == InventorySlotType::SHARED_BANK && slot < NUM_SHARED_BANK_SLOTS) || (bag_id == InventorySlotType::HOUSE_VAULT && slot < GetHouseVaultSlots())) { + if ((bag_id == InventorySlotType::SHARED_BANK || bag_id == InventorySlotType::HOUSE_VAULT) && !item_list.SharedBankAddAllowed(item)) { PacketStruct* packet = configReader.getStruct("WS_DisplayText", version); if (packet) { packet->setDataByName("color", CHANNEL_COLOR_YELLOW); @@ -2014,7 +2040,7 @@ vector Player::EquipItem(int16 index, int16 version, int8 appearance const char* zone_script = world.GetZoneScript(GetZone()->GetZoneID()); if (zone_script && lua_interface) lua_interface->RunZoneScript(zone_script, "item_equipped", GetZone(), this, item->details.item_id, item->name.c_str(), 0, item->details.unique_id); - int32 bag_id = item->details.inv_slot_id; + sint32 bag_id = item->details.inv_slot_id; if (item->generic_info.condition == 0) { Client* client = GetClient(); if (client) { @@ -2052,19 +2078,21 @@ vector Player::EquipItem(int16 index, int16 version, int8 appearance if(slot < 255) { if (slot == EQ2_FOOD_SLOT && item->IsFoodFood() && get_character_flag(CF_FOOD_AUTO_CONSUME)) { Item* item = GetEquipmentList()->GetItem(EQ2_FOOD_SLOT); - if(item && GetClient() && GetClient()->CheckConsumptionAllowed(slot, false)) - GetClient()->ConsumeFoodDrink(item, EQ2_FOOD_SLOT); if(item) SetActiveFoodUniqueID(item->details.unique_id); + + if(item && GetClient() && GetClient()->CheckConsumptionAllowed(slot, false)) + GetClient()->ConsumeFoodDrink(item, EQ2_FOOD_SLOT); } else if (slot == EQ2_DRINK_SLOT && item->IsFoodDrink() && get_character_flag(CF_DRINK_AUTO_CONSUME)) { Item* item = GetEquipmentList()->GetItem(EQ2_DRINK_SLOT); - if(item && GetClient() && GetClient()->CheckConsumptionAllowed(slot, false)) - GetClient()->ConsumeFoodDrink(item, EQ2_DRINK_SLOT); if(item) SetActiveDrinkUniqueID(item->details.unique_id); + + if(item && GetClient() && GetClient()->CheckConsumptionAllowed(slot, false)) + GetClient()->ConsumeFoodDrink(item, EQ2_DRINK_SLOT); } } @@ -2104,7 +2132,7 @@ bool Player::AddItem(Item* item, AddItemType type) { safe_delete(item); return false; } - else if (item_list.AssignItemToFreeSlot(item)) { + else if (item_list.AssignItemToFreeSlot(item, true)) { item->save_needed = true; CalculateApplyWeight(); return true; @@ -2136,7 +2164,7 @@ bool Player::AddItemToBank(Item* item) { } EQ2Packet* Player::SendInventoryUpdate(int16 version) { // assure any inventory updates are reflected in sell window - if(GetClient() && GetClient()->GetMerchantTransaction()) + if(GetClient() && GetClient()->GetMerchantTransactionID()) GetClient()->SendSellMerchantList(); return item_list.serialize(this, version); @@ -2156,7 +2184,7 @@ void Player::UpdateInventory(int32 bag_id) { EQ2Packet* Player::MoveInventoryItem(sint32 to_bag_id, int16 from_index, int8 new_slot, int8 charges, int8 appearance_type, bool* item_deleted, int16 version) { Item* item = item_list.GetItemFromIndex(from_index); - bool isOverflow = ((item != nullptr) && (item->details.inv_slot_id == -2)); + bool isOverflow = ((item != nullptr) && (item->details.inv_slot_id == InventorySlotType::OVERFLOW)); int8 result = item_list.MoveItem(to_bag_id, from_index, new_slot, appearance_type, charges); if (result == 1) { if(isOverflow && item->details.inv_slot_id != -2) { @@ -3280,11 +3308,18 @@ PlayerInfo::PlayerInfo(Player* in_player){ for(int i=0;i<45;i++){ if(i<30){ info_struct->maintained_effects[i].spell_id = 0xFFFFFFFF; + info_struct->maintained_effects[i].inherited_spell_id = 0; info_struct->maintained_effects[i].icon = 0xFFFF; info_struct->maintained_effects[i].spell = nullptr; } - info_struct->spell_effects[i].spell_id = 0xFFFFFFFF; - info_struct->spell_effects[i].spell = nullptr; + info_struct->spell_effects[i].spell_id = 0xFFFFFFFF; + info_struct->spell_effects[i].inherited_spell_id = 0; + info_struct->spell_effects[i].icon = 0; + info_struct->spell_effects[i].icon_backdrop = 0; + info_struct->spell_effects[i].tier = 0; + info_struct->spell_effects[i].total_time = 0.0f; + info_struct->spell_effects[i].expire_timestamp = 0; + info_struct->spell_effects[i].spell = nullptr; } house_zone_id = 0; @@ -3315,12 +3350,12 @@ MaintainedEffects* Player::GetFreeMaintainedSpellSlot(){ return ret; } -MaintainedEffects* Player::GetMaintainedSpell(int32 id){ +MaintainedEffects* Player::GetMaintainedSpell(int32 id, bool on_char_load){ MaintainedEffects* ret = 0; InfoStruct* info = GetInfoStruct(); GetMaintainedMutex()->readlock(__FUNCTION__, __LINE__); for(int i=0;imaintained_effects[i].spell_id == id){ + if(info->maintained_effects[i].spell_id == id || (on_char_load && info->maintained_effects[i].inherited_spell_id == id)){ ret = &info->maintained_effects[i]; break; } @@ -3423,6 +3458,11 @@ void Player::AddMaintainedSpell(LuaSpell* luaspell){ if(!luaspell) return; + if(luaspell->spell->GetSpellData()->not_maintained || luaspell->spell->GetSpellData()->duration1 == 0) { + LogWrite(PLAYER__INFO, 0, "NPC", "AddMaintainedSpell Spell ID: %u, Concentration: %u disallowed, not_maintained true (%u) or duration is 0 (%u).", luaspell->spell->GetSpellData()->id, luaspell->spell->GetSpellData()->req_concentration, luaspell->spell->GetSpellData()->not_maintained, luaspell->spell->GetSpellData()->duration1); + return; + } + Spell* spell = luaspell->spell; MaintainedEffects* effect = GetFreeMaintainedSpellSlot(); int32 target_type = 0; @@ -3465,8 +3505,11 @@ void Player::AddMaintainedSpell(LuaSpell* luaspell){ void Player::AddSpellEffect(LuaSpell* luaspell, int32 override_expire_time){ if(!luaspell || !luaspell->caster) return; - + Spell* spell = luaspell->spell; + if(spell->GetSpellData() && spell->GetSpellData()->icon == 0 && spell->GetSpellData()->duration1 == 0 && spell->GetSpellData()->duration2 == 0) + return; + SpellEffects* old_effect = GetSpellEffect(spell->GetSpellID(), luaspell->caster); SpellEffects* effect = 0; if (old_effect){ @@ -3548,6 +3591,7 @@ void Player::RemoveMaintainedSpell(LuaSpell* luaspell){ if (found) { memset(&GetInfoStruct()->maintained_effects[29], 0, sizeof(MaintainedEffects)); GetInfoStruct()->maintained_effects[29].spell_id = 0xFFFFFFFF; + GetInfoStruct()->maintained_effects[29].inherited_spell_id = 0; GetInfoStruct()->maintained_effects[29].icon = 0xFFFF; GetInfoStruct()->maintained_effects[29].spell = nullptr; charsheet_changed = true; @@ -3568,6 +3612,7 @@ void Player::RemoveSpellEffect(LuaSpell* spell){ if (found) { memset(&GetInfoStruct()->spell_effects[44], 0, sizeof(SpellEffects)); GetInfoStruct()->spell_effects[44].spell_id = 0xFFFFFFFF; + GetInfoStruct()->spell_effects[44].inherited_spell_id = 0; GetInfoStruct()->spell_effects[44].spell = nullptr; changed = true; info_changed = true; @@ -4121,7 +4166,7 @@ void Player::InCombat(bool val, bool range) { bool update_regen = false; if(GetInfoStruct()->get_engaged_encounter()) { - if(!IsAggroed() || !IsEngagedInEncounter()) { + if(!IsEngagedInEncounter()) { GetInfoStruct()->set_engaged_encounter(0); update_regen = true; } @@ -4419,11 +4464,20 @@ void Player::SetNeededXP(){ //GetInfoStruct()->xp_needed = GetLevel() * 100; // Get xp needed to get to the next level int16 level = GetLevel() + 1; - // If next level is beyond what we have in the map multiply the last value we have by how many levels we are over plus one - if (level > 95) - SetNeededXP(database.GetMysqlExpCurve(95)* ((level - 95) + 1)); + SetNeededXP(GetNeededXPByLevel(level)); +} + +int32 Player::GetNeededXPByLevel(int8 level) { + int32 exp_required = 0; + if (!Player::m_levelXPReq.count(level) && level > 95 && Player::m_levelXPReq.count(95)) { + exp_required = (Player::m_levelXPReq[95] * ((level - 95) + 1)); + } + else if(Player::m_levelXPReq.count(level)) + exp_required = Player::m_levelXPReq[level]; else - SetNeededXP(database.GetMysqlExpCurve(level)); + exp_required = 0; + + return exp_required; } void Player::SetXP(int32 val){ @@ -4519,10 +4573,24 @@ bool Player::AddXP(int32 xp_amount){ SetCharSheetChanged(true); return false; } + int32 prev_xp_amount = xp_amount; xp_amount -= GetNeededXP() - GetXP(); - SetLevel(GetLevel() + 1); + if(GetClient()->ChangeLevel(GetLevel(), GetLevel()+1, prev_xp_amount)) + SetLevel(GetLevel() + 1); + else { + SetXP(GetXP() + prev_xp_amount); + SetCharSheetChanged(true); + return false; + } } + + // set the actual end xp_amount result SetXP(GetXP() + xp_amount); + + if(GetClient()) { + GetClient()->Message(CHANNEL_REWARD, "You gain %u experience!", (int32)xp_amount); + } + GetPlayerInfo()->CalculateXPPercentages(); current_xp_percent = ((float)GetXP()/(float)GetNeededXP())*100; if(miniding_min_percent > 0.0f && current_xp_percent >= miniding_min_percent){ @@ -4532,15 +4600,8 @@ bool Player::AddXP(int32 xp_amount){ SetPower(GetTotalPower()); GetZone()->SendCastSpellPacket(332, this, this); //send mini level up spell effect } - - if(GetClient()) { - GetClient()->Message(CHANNEL_REWARD, "You gain %u experience!", (int32)xp_amount); - - if (prev_level != GetLevel()) - GetClient()->ChangeLevel(prev_level, GetLevel()); - } - SetCharSheetChanged(true); + SetCharSheetChanged(true); return true; } @@ -4550,7 +4611,16 @@ bool Player::AddTSXP(int32 xp_amount){ MStats.unlock(); float current_xp_percent = ((float)GetTSXP()/(float)GetNeededTSXP())*100; - float miniding_min_percent = ((int)(current_xp_percent/10)+1)*10; + + int32 mini_ding_pct = rule_manager.GetGlobalRule(R_Player, MiniDingPercentage)->GetInt32(); + float miniding_min_percent = 0.0f; + if(mini_ding_pct < 10 || mini_ding_pct > 50) { + mini_ding_pct = 0; + } + else { + miniding_min_percent = ((int)(current_xp_percent/mini_ding_pct)+1)*mini_ding_pct; + } + while((xp_amount + GetTSXP()) >= GetNeededTSXP()){ if (!CheckLevelStatus(GetTSLevel() + 1)) { if(GetClient()) { @@ -4558,10 +4628,18 @@ bool Player::AddTSXP(int32 xp_amount){ } return false; } + int32 prev_xp_amount = xp_amount; xp_amount -= GetNeededTSXP() - GetTSXP(); - SetTSLevel(GetTSLevel() + 1); - SetTSXP(0); - SetNeededTSXP(); + if(GetClient()->ChangeTSLevel(GetLevel(), GetLevel()+1, prev_xp_amount)) { + SetTSLevel(GetTSLevel() + 1); + SetTSXP(0); + SetNeededTSXP(); + } + else { + SetTSXP(GetTSXP() + prev_xp_amount); + SetCharSheetChanged(true); + return false; + } } SetTSXP(GetTSXP() + xp_amount); GetPlayerInfo()->CalculateXPPercentages(); @@ -4577,6 +4655,8 @@ bool Player::AddTSXP(int32 xp_amount){ GetInfoStruct()->set_tradeskill_class2(1); GetInfoStruct()->set_tradeskill_class3(1); } + + SetCharSheetChanged(true); return true; } @@ -6423,7 +6503,8 @@ int32 CharacterInstances::GetInstanceCount() { return instanceList.size(); } -void Player::SetPlayerAdventureClass(int8 new_class){ +void Player::SetPlayerAdventureClass(int8 new_class, bool set_by_gm_command ){ + int8 old_class = GetAdventureClass(); SetAdventureClass(new_class); GetInfoStruct()->set_class1(classes.GetBaseClass(new_class)); GetInfoStruct()->set_class2(classes.GetSecondaryBaseClass(new_class)); @@ -6433,6 +6514,23 @@ void Player::SetPlayerAdventureClass(int8 new_class){ GetZone()->TriggerCharSheetTimer(); if(GetClient()) GetClient()->UpdateTimeStampFlag ( CLASS_UPDATE_FLAG ); + + const char* playerScript = world.GetPlayerScript(0); // 0 = global script + const char* playerZoneScript = world.GetPlayerScript(GetZoneID()); // zone script + if(playerScript || playerZoneScript) { + std::vector args = { + LuaArg(GetZone()), + LuaArg(this), + LuaArg(old_class), + LuaArg(new_class) + }; + if(playerScript) { + lua_interface->RunPlayerScriptWithReturn(playerScript, "on_class_change", args); + } + if(playerZoneScript) { + lua_interface->RunPlayerScriptWithReturn(playerZoneScript, "on_class_change", args); + } + } } void Player::AddSkillBonus(int32 spell_id, int32 skill_id, float value) { @@ -7266,11 +7364,115 @@ NPC* Player::InstantiateSpiritShard(float origX, float origY, float origZ, float return npc; } +void Player::SaveCustomSpellFields(LuaSpell* luaspell) { + if (!luaspell || !luaspell->spell || !luaspell->spell->IsCopiedSpell()) + return; + + auto spell_data = luaspell->spell->GetSpellData(); + std::unordered_set modified_fields = luaspell->GetModifiedFieldsCopy(); + + Query savedEffects; + for (const std::string& field : modified_fields) { + auto it = SpellDataFieldAccessors.find(field); + if (it == SpellDataFieldAccessors.end()) + continue; + + const auto& [type, getter] = it->second; + std::string value = getter(spell_data); + + std::string type_str; + switch (type) { + case SpellFieldType::Integer: type_str = "int"; break; + case SpellFieldType::Float: type_str = "float"; break; + case SpellFieldType::Boolean: type_str = "bool"; break; + case SpellFieldType::String: type_str = "string"; break; + default: continue; + } + + savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, "INSERT IGNORE INTO character_custom_spell_data (charid, spell_id, field, type, value) VALUES (%u, %u, '%s', '%s', '%s')", + GetCharacterID(), + luaspell->spell->GetSpellData()->inherited_spell_id, + database.getSafeEscapeString(field.c_str()).c_str(), + type_str.c_str(), + database.getSafeEscapeString(value.c_str()).c_str()); + } +} + + +void Player::SaveCustomSpellDataIndex(LuaSpell* luaspell) { + if (!luaspell || !luaspell->spell || !luaspell->spell->IsCopiedSpell()) + return; + + auto& vec = luaspell->spell->lua_data; + + Query savedEffects; + for (int i = 0; i < vec.size(); ++i) { + LUAData* data = vec[i]; + if (!data || !data->needs_db_save) + continue; + + std::string value1, value2, type; + switch (data->type) { + case 0: + value1 = std::to_string(data->int_value); + value2 = std::to_string(data->int_value2); + type = "int"; + break; + case 1: + value1 = std::to_string(data->float_value); + value2 = std::to_string(data->float_value2); + type = "float"; + break; + case 2: + value1 = data->bool_value ? "1" : "0"; + type = "bool"; + break; + case 3: + value1 = database.getSafeEscapeString(data->string_value.c_str()); + value2 = database.getSafeEscapeString(data->string_value2.c_str()); + type = "string"; + break; + default: + continue; + } + + savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, "INSERT IGNORE INTO character_custom_spell_dataindex (charid, spell_id, idx, type, value1, value2) VALUES (%u, %u, %d, '%s', '%s', '%s')", GetCharacterID(), + luaspell->spell->GetSpellData()->inherited_spell_id, + i, + type.c_str(), value1.c_str(), value2.c_str()); + } +} + +void Player::SaveCustomSpellEffectsDisplay(LuaSpell* luaspell) { + if (!luaspell || !luaspell->spell || !luaspell->spell->IsCopiedSpell()) + return; + + auto& vec = luaspell->spell->effects; + + Query savedEffects; + for (int i = 0; i < vec.size(); ++i) { + SpellDisplayEffect* eff = vec[i]; + if (!eff || !eff->needs_db_save) + continue; + + std::string charid = std::to_string(GetCharacterID()); + + savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, "INSERT IGNORE INTO character_custom_spell_display (charid, spell_id, idx, field, value) VALUES (%u, %u, %d, 'description', '%s')", + GetCharacterID(), luaspell->spell->GetSpellData()->inherited_spell_id, i, + database.getSafeEscapeString(eff->description.c_str()).c_str()); + + savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, "INSERT IGNORE INTO character_custom_spell_display (charid, spell_id, idx, field, value) VALUES (%u, %u, %d, 'bullet', '%d')", + GetCharacterID(), luaspell->spell->GetSpellData()->inherited_spell_id, i, eff->subbullet); + + savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, "INSERT IGNORE INTO character_custom_spell_display (charid, spell_id, idx, field, value) VALUES (%u, %u, %d, 'percentage', '%d')", + GetCharacterID(), luaspell->spell->GetSpellData()->inherited_spell_id, i, eff->percentage); + } +} void Player::SaveSpellEffects() { if(stop_save_spell_effects) { - LogWrite(PLAYER__WARNING, 0, "Player", "SaveSpellEffects called while player constructing / deconstructing!"); + LogWrite(PLAYER__WARNING, 0, "Player", "%s: SaveSpellEffects called while player constructing / deconstructing!", GetName()); return; } @@ -7281,9 +7483,12 @@ void Player::SaveSpellEffects() Query savedEffects; savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_spell_effects where charid=%u", GetCharacterID()); savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_spell_effect_targets where caster_char_id=%u", GetCharacterID()); + savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_custom_spell_dataindex where charid=%u", GetCharacterID()); + savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_custom_spell_display where charid=%u", GetCharacterID()); + savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_DELETE, "delete from character_custom_spell_data where charid=%u", GetCharacterID()); InfoStruct* info = GetInfoStruct(); - MSpellEffects.readlock(__FUNCTION__, __LINE__); MMaintainedSpells.readlock(__FUNCTION__, __LINE__); + MSpellEffects.readlock(__FUNCTION__, __LINE__); for(int i = 0; i < 45; i++) { if(info->spell_effects[i].spell_id != 0xFFFFFFFF) { @@ -7311,8 +7516,13 @@ void Player::SaveSpellEffects() info->spell_effects[i].total_time, timestamp, database.getSafeEscapeString(info->spell_effects[i].spell->file_name.c_str()).c_str(), info->spell_effects[i].spell->spell->IsCopiedSpell(), GetCharacterID(), info->spell_effects[i].spell->damage_remaining, info->spell_effects[i].spell->effect_bitmask, info->spell_effects[i].spell->num_triggers, info->spell_effects[i].spell->had_triggers, info->spell_effects[i].spell->cancel_after_all_triggers, info->spell_effects[i].spell->crit, info->spell_effects[i].spell->last_spellattack_hit, info->spell_effects[i].spell->interrupted, info->spell_effects[i].spell->resisted, info->spell_effects[i].spell->has_damaged, (info->maintained_effects[i].expire_timestamp) == 0xFFFFFFFF ? "" : database.getSafeEscapeString(spellProcess->SpellScriptTimerCustomFunction(info->spell_effects[i].spell).c_str()).c_str(), info->spell_effects[i].spell->initial_caster_level); + + SaveCustomSpellFields(info->spell_effects[i].spell); + SaveCustomSpellDataIndex(info->spell_effects[i].spell); + SaveCustomSpellEffectsDisplay(info->spell_effects[i].spell); } - if (i < NUM_MAINTAINED_EFFECTS && info->maintained_effects[i].spell_id != 0xFFFFFFFF){ + if (i < NUM_MAINTAINED_EFFECTS && info->maintained_effects[i].spell && info->maintained_effects[i].spell_id != 0xFFFFFFFF){ + LogWrite(PLAYER__INFO, 0, "Player", "Saving slot %u maintained effect %u", i, info->maintained_effects[i].spell_id); Spawn* spawn = GetZone()->GetSpawnByID(info->maintained_effects[i].spell->initial_target); int32 target_char_id = 0; @@ -7339,13 +7549,15 @@ void Player::SaveSpellEffects() info->maintained_effects[i].spell->damage_remaining, info->maintained_effects[i].spell->effect_bitmask, info->maintained_effects[i].spell->num_triggers, info->maintained_effects[i].spell->had_triggers, info->maintained_effects[i].spell->cancel_after_all_triggers, info->maintained_effects[i].spell->crit, info->maintained_effects[i].spell->last_spellattack_hit, info->maintained_effects[i].spell->interrupted, info->maintained_effects[i].spell->resisted, info->maintained_effects[i].spell->has_damaged, (info->maintained_effects[i].expire_timestamp) == 0xFFFFFFFF ? "" : database.getSafeEscapeString(spellProcess->SpellScriptTimerCustomFunction(info->maintained_effects[i].spell).c_str()).c_str(), info->maintained_effects[i].spell->initial_caster_level); - info->maintained_effects[i].spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); + SaveCustomSpellFields(info->maintained_effects[i].spell); + SaveCustomSpellDataIndex(info->maintained_effects[i].spell); + SaveCustomSpellEffectsDisplay(info->maintained_effects[i].spell); + std::string insertTargets = string("insert into character_spell_effect_targets (caster_char_id, target_char_id, target_type, db_effect_type, spell_id, effect_slot, slot_pos) values "); bool firstTarget = true; map targetsInserted; - for (int8 t = 0; t < info->maintained_effects[i].spell->targets.size(); t++) { - int32 spawn_id = info->maintained_effects[i].spell->targets.at(t); - Spawn* spawn = GetZone()->GetSpawnByID(spawn_id); + for (int32 id : info->maintained_effects[i].spell->GetTargets()) { + Spawn* spawn = GetZone()->GetSpawnByID(id); LogWrite(SPELL__DEBUG, 0, "Spell", "%s has target %u to identify for spell %s", GetName(), spawn_id, info->maintained_effects[i].spell->spell->GetName()); if(spawn && (spawn->IsPlayer() || spawn->IsPet())) { @@ -7381,28 +7593,27 @@ void Player::SaveSpellEffects() ", " + std::to_string(info->maintained_effects[i].slot_pos) + ")"); firstTarget = false; } - } - multimap::iterator entries; - for(entries = info->maintained_effects[i].spell->char_id_targets.begin(); entries != info->maintained_effects[i].spell->char_id_targets.end(); entries++) - { - if(!firstTarget) - insertTargets.append(", "); + } + for (const auto& [char_id, pet_type] : info->maintained_effects[i].spell->GetCharIDTargets()) { + { + if(!firstTarget) + insertTargets.append(", "); - LogWrite(SPELL__DEBUG, 0, "Spell", "%s has target %s (%u) added to spell %s", GetName(), spawn ? spawn->GetName() : "NA", entries->first, info->maintained_effects[i].spell->spell->GetName()); - insertTargets.append("(" + std::to_string(caster_char_id) + ", " + std::to_string(entries->first) + ", " + std::to_string(entries->second) + ", " + - std::to_string(DB_TYPE_MAINTAINEDEFFECTS) + ", " + std::to_string(info->maintained_effects[i].spell_id) + ", " + std::to_string(i) + - ", " + std::to_string(info->maintained_effects[i].slot_pos) + ")"); + LogWrite(SPELL__DEBUG, 0, "Spell", "%s has target (%u) added to spell %s", GetName(), char_id, info->maintained_effects[i].spell->spell->GetName()); + insertTargets.append("(" + std::to_string(caster_char_id) + ", " + std::to_string(char_id) + ", " + std::to_string(pet_type) + ", " + + std::to_string(DB_TYPE_MAINTAINEDEFFECTS) + ", " + std::to_string(info->maintained_effects[i].spell_id) + ", " + std::to_string(i) + + ", " + std::to_string(info->maintained_effects[i].slot_pos) + ")"); - firstTarget = false; - } - info->maintained_effects[i].spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); - if(!firstTarget) { - savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, insertTargets.c_str()); + firstTarget = false; + } + if(!firstTarget) { + savedEffects.AddQueryAsync(GetCharacterID(), &database, Q_INSERT, insertTargets.c_str()); + } } } } - MMaintainedSpells.releasereadlock(__FUNCTION__, __LINE__); MSpellEffects.releasereadlock(__FUNCTION__, __LINE__); + MMaintainedSpells.releasereadlock(__FUNCTION__, __LINE__); } void Player::MentorTarget() @@ -7720,7 +7931,7 @@ void Player::CalculatePlayerHPPower(int16 new_level) { bool Player::IsAllowedCombatEquip(int8 slot, bool send_message) { bool rule_pass = true; - if(EngagedInCombat() && rule_manager.GetZoneRule(GetZoneID(), R_Player, AllowPlayerEquipCombat)->GetInt8() == 0) { + if(GetInfoStruct()->get_engaged_encounter() && rule_manager.GetZoneRule(GetZoneID(), R_Player, AllowPlayerEquipCombat)->GetInt8() == 0) { switch(slot) { case EQ2_PRIMARY_SLOT: case EQ2_SECONDARY_SLOT: diff --git a/old/WorldServer/Player.h b/old/WorldServer/Player.h index cb1a499..305fa58 100644 --- a/old/WorldServer/Player.h +++ b/old/WorldServer/Player.h @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #ifndef __EQ2_PLAYER__ #define __EQ2_PLAYER__ @@ -224,7 +225,7 @@ struct QuickBarItem{ int16 icon_type; int32 id; int8 tier; - int32 unique_id; + int64 unique_id; EQ2_16BitString text; }; @@ -588,7 +589,7 @@ public: EQ2Packet* MoveInventoryItem(sint32 to_bag_id, int16 from_index, int8 new_slot, int8 charges, int8 appearance_type, bool* item_deleted, int16 version = 1); bool IsPlayer(){ return true; } MaintainedEffects* GetFreeMaintainedSpellSlot(); - MaintainedEffects* GetMaintainedSpell(int32 id); + MaintainedEffects* GetMaintainedSpell(int32 id, bool on_char_load = false); MaintainedEffects* GetMaintainedSpellBySlot(int8 slot); MaintainedEffects* GetMaintainedSpells(); SpellEffects* GetFreeSpellEffectSlot(); @@ -608,6 +609,7 @@ public: bool TradeskillXPEnabled(); void SetNeededXP(int32 val); void SetNeededXP(); + static int32 GetNeededXPByLevel(int8 level); void SetXP(int32 val); void SetNeededTSXP(int32 val); void SetNeededTSXP(); @@ -789,7 +791,7 @@ public: bool GetIsTracking() const { return is_tracking; } void SetBiography(string new_biography) { biography = new_biography; } string GetBiography() const { return biography; } - void SetPlayerAdventureClass(int8 new_class); + void SetPlayerAdventureClass(int8 new_class, bool set_by_gm_command = false); void SetGuild(Guild* new_guild) { guild = new_guild; } Guild* GetGuild() { return guild; } void AddSkillBonus(int32 spell_id, int32 skill_id, float value); @@ -1048,7 +1050,10 @@ public: void DismissAllPets(); void SaveSpellEffects(); - + void SaveCustomSpellFields(LuaSpell* luaspell); + void SaveCustomSpellDataIndex(LuaSpell* luaspell); + void SaveCustomSpellEffectsDisplay(LuaSpell* luaspell); + void SetSaveSpellEffects(bool val) { stop_save_spell_effects = val; } AppearanceData SavedApp; CharFeatures SavedFeatures; @@ -1098,8 +1103,11 @@ public: void SetActiveFoodUniqueID(int32 unique_id, bool update_db = true); void SetActiveDrinkUniqueID(int32 unique_id, bool update_db = true); - int32 GetActiveFoodUniqueID() { return active_food_unique_id; } - int32 GetActiveDrinkUniqueID() { return active_drink_unique_id; } + int64 GetActiveFoodUniqueID() { return active_food_unique_id; } + int64 GetActiveDrinkUniqueID() { return active_drink_unique_id; } + + void SetHouseVaultSlots(int8 allowed_slots) { house_vault_slots = allowed_slots; } + int8 GetHouseVaultSlots() { return house_vault_slots; } Mutex MPlayerQuests; float pos_packet_speed; @@ -1112,8 +1120,8 @@ public: mutable std::shared_mutex trait_mutex; std::atomic need_trait_update; - void InitXPTable(); - map m_levelXPReq; + static void InitXPTable(); + static map m_levelXPReq; mutable std::shared_mutex spell_packet_update_mutex; mutable std::shared_mutex raid_update_mutex; @@ -1214,8 +1222,6 @@ private: void AddSpellStatus(SpellBookEntry* spell, sint16 value, bool modify_recast = true, int16 recast = 0); void RemoveSpellStatus(SpellBookEntry* spell, sint16 value, bool modify_recast = true, int16 recast = 0); void SetSpellEntryRecast(SpellBookEntry* spell, bool modify_recast, int16 recast); -// void InitXPTable(); -// map m_levelXPReq; //The following variables are for serializing spawn packets PacketStruct* spawn_pos_struct; @@ -1261,8 +1267,10 @@ private: Quest* GetAnyQuest(int32 quest_id); Quest* GetCompletedQuest(int32 quest_id); - std::atomic active_food_unique_id; - std::atomic active_drink_unique_id; + std::atomic active_food_unique_id; + std::atomic active_drink_unique_id; + + int8 house_vault_slots; }; #pragma pack() #endif diff --git a/old/WorldServer/PlayerGroups.cpp b/old/WorldServer/PlayerGroups.cpp index fc362b2..a163a47 100644 --- a/old/WorldServer/PlayerGroups.cpp +++ b/old/WorldServer/PlayerGroups.cpp @@ -1,25 +1,25 @@ -/* -EQ2Emulator: Everquest II Server Emulator -Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) -This file is part of EQ2Emulator. + This file is part of EQ2Emulator. -EQ2Emulator 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. + EQ2Emulator 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. -EQ2Emulator 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. + EQ2Emulator 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 EQ2Emulator. If not, see . + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . */ #include "PlayerGroups.h" -#include "../common/log.hpp" +#include "../common/Log.h" #include "World.h" #include "Spells.h" #include "LuaInterface.h" @@ -27,13 +27,14 @@ along with EQ2Emulator. If not, see . #include "SpellProcess.h" #include "Rules/Rules.h" #include "Web/PeerManager.h" -#include "WorldDatabase.hpp" +#include "WorldDatabase.h" extern ConfigReader configReader; extern ZoneList zone_list; extern RuleManager rule_manager; extern PeerManager peer_manager; extern WorldDatabase database; +extern LuaInterface* lua_interface; /******************************************************** PlayerGroup ********************************************************/ PlayerGroup::PlayerGroup(int32 id) { @@ -906,25 +907,6 @@ int32 PlayerGroupManager::GetGroupSize(int32 group_id) { return ret; } -void PlayerGroupManager::SendGroupQuests(int32 group_id, Client* client) { - std::shared_lock lock(MGroups); - GroupMemberInfo* info = 0; - if (m_groups.count(group_id) > 0) { - m_groups[group_id]->MGroupMembers.readlock(__FUNCTION__, __LINE__); - deque* members = m_groups[group_id]->GetMembers(); - deque::iterator itr; - for (itr = members->begin(); itr != members->end(); itr++) { - info = *itr; - if (info->client) { - LogWrite(PLAYER__DEBUG, 0, "Player", "Send Quest Journal..."); - info->client->SendQuestJournal(false, client); - client->SendQuestJournal(false, info->client); - } - } - m_groups[group_id]->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); - } -} - bool PlayerGroupManager::HasGroupCompletedQuest(int32 group_id, int32 quest_id) { std::shared_lock lock(MGroups); bool questComplete = true; @@ -1113,6 +1095,7 @@ void PlayerGroupManager::UpdateGroupBuffs() { caster = *vitr; caster->GetMaintainedMutex()->readlock(__FUNCTION__, __LINE__); // go through the player's maintained spells + bool skipSpell = false; me = caster->GetMaintainedSpells(); for (i = 0; i < NUM_MAINTAINED_EFFECTS; i++) { if (me[i].spell_id == 0xFFFFFFFF) @@ -1132,10 +1115,7 @@ void PlayerGroupManager::UpdateGroupBuffs() { if (spell && spell->GetSpellData()->group_spell && spell->GetSpellData()->friendly_spell && (spell->GetSpellData()->target_type == SPELL_TARGET_GROUP_AE || spell->GetSpellData()->target_type == SPELL_TARGET_RAID_AE)) { - - luaspell->MSpellTargets.writelock(__FUNCTION__, __LINE__); - luaspell->char_id_targets.clear(); - + luaspell->ClearCharTargets(); for (target_itr = group->GetMembers()->begin(); target_itr != group->GetMembers()->end(); target_itr++) { group_member = (*target_itr)->member; @@ -1147,13 +1127,45 @@ void PlayerGroupManager::UpdateGroupBuffs() { client = (*target_itr)->client; + LuaSpell* conflictSpell = caster->HasLinkedTimerID(luaspell, group_member, false, true); + if(conflictSpell && group_member && group_member->IsEntity()) + { + if(conflictSpell->spell->GetSpellData()->min_class_skill_req > 0 && spell->GetSpellData()->min_class_skill_req > 0) + { + if(conflictSpell->spell->GetSpellData()->min_class_skill_req <= spell->GetSpellData()->min_class_skill_req) + { + if(spell->GetSpellData()->duration_until_cancel && !luaspell->num_triggers) + { + for (int32 id : conflictSpell->GetTargets()) { + Spawn* tmpTarget = caster->GetZone()->GetSpawnByID(id); + if(tmpTarget && tmpTarget->IsEntity()) + { + ((Entity*)tmpTarget)->RemoveEffectsFromLuaSpell(conflictSpell); + caster->GetZone()->RemoveTargetFromSpell(conflictSpell, tmpTarget, false); + caster->GetZone()->GetSpellProcess()->CheckRemoveTargetFromSpell(conflictSpell); + lua_interface->RemoveSpawnFromSpell(conflictSpell, tmpTarget); + } + } + } + } + else + { + // this is a spell that is no good, have to abort! + caster->GetMaintainedMutex()->releasereadlock(__FUNCTION__, __LINE__); + caster->GetZone()->GetSpellProcess()->SpellCannotStack(caster->GetZone(), client, caster, luaspell, conflictSpell); + skipSpell = true; + break; + } + } + } has_effect = false; - + if (group_member->GetSpellEffect(spell->GetSpellID(), caster)) { has_effect = true; } - if (!has_effect && (std::find(luaspell->removed_targets.begin(), - luaspell->removed_targets.end(), group_member->GetID()) != luaspell->removed_targets.end())) { + std::vector removed_targets = luaspell->GetRemovedTargets(); + if (!has_effect && (std::find(removed_targets.begin(), + removed_targets.end(), group_member->GetID()) != removed_targets.end())) { continue; } // Check if player is within range of the caster @@ -1245,14 +1257,18 @@ void PlayerGroupManager::UpdateGroupBuffs() { client->QueuePacket(packet); } } - - luaspell->targets.swap(new_target_list); - SpellProcess::AddSelfAndPet(luaspell, caster); - luaspell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__); - new_target_list.clear(); + if(!skipSpell) { + luaspell->SwapTargets(new_target_list); + SpellProcess::AddSelfAndPet(luaspell, caster); + new_target_list.clear(); + } + else + break; } } - caster->GetMaintainedMutex()->releasereadlock(__FUNCTION__, __LINE__); + + if(!skipSpell) + caster->GetMaintainedMutex()->releasereadlock(__FUNCTION__, __LINE__); } } } @@ -1910,6 +1926,11 @@ bool PlayerGroupManager::IdentifyMemberInGroupOrRaid(ZoneChangeDetails* details, succeed = true; break; } + else { + succeed = zone_list.GetZoneByInstance(details, (*itr)->instance_id, (*itr)->zone_id, true, false); + if(succeed) + break; + } } } group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); @@ -1951,6 +1972,11 @@ bool PlayerGroupManager::IdentifyMemberInGroupOrRaid(ZoneChangeDetails* details, succeed = true; break; } + else { + succeed = zone_list.GetZoneByInstance(details, (*itr)->instance_id, (*itr)->zone_id, true, false); + if(succeed) + break; + } } } group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__); diff --git a/old/WorldServer/PlayerGroups.h b/old/WorldServer/PlayerGroups.h index 7df815e..f65c8cc 100644 --- a/old/WorldServer/PlayerGroups.h +++ b/old/WorldServer/PlayerGroups.h @@ -27,7 +27,7 @@ along with EQ2Emulator. If not, see . #include #include "Spells.h" -#include "../common/types.hpp" +#include "../common/types.h" #include "Entity.h" using namespace std; @@ -220,7 +220,6 @@ public: int32 GetGroupSize(int32 group_id); - void SendGroupQuests(int32 group_id, Client* client); bool HasGroupCompletedQuest(int32 group_id, int32 quest_id); void SimpleGroupMessage(int32 group_id, const char* message); diff --git a/old/WorldServer/Quests.cpp b/old/WorldServer/Quests.cpp index 72f239a..e6274ce 100644 --- a/old/WorldServer/Quests.cpp +++ b/old/WorldServer/Quests.cpp @@ -18,12 +18,12 @@ along with EQ2Emulator. If not, see . */ #include "Quests.h" -#include "../common/config_reader.hpp" +#include "../common/ConfigReader.h" #include "Player.h" #include "LuaInterface.h" #include "Spells.h" #include "RaceTypes/RaceTypes.h" -#include "../common/log.hpp" +#include "../common/Log.h" #ifdef WIN32 #include diff --git a/old/WorldServer/RaceTypes/RaceTypes.h b/old/WorldServer/RaceTypes/RaceTypes.h index 2d4808b..899fbab 100644 --- a/old/WorldServer/RaceTypes/RaceTypes.h +++ b/old/WorldServer/RaceTypes/RaceTypes.h @@ -21,7 +21,7 @@ #ifndef __RACETYPES_H__ #define __RACETYPES_H__ -#include "../../common/types.hpp" +#include "../../common/types.h" #include #define DRAGONKIND 101 diff --git a/old/WorldServer/RaceTypes/RaceTypesDB.cpp b/old/WorldServer/RaceTypes/RaceTypesDB.cpp index 8fd058a..9b4008a 100644 --- a/old/WorldServer/RaceTypes/RaceTypesDB.cpp +++ b/old/WorldServer/RaceTypes/RaceTypesDB.cpp @@ -18,7 +18,7 @@ along with EQ2Emulator. If not, see . */ -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "../../common/Log.h" #include "RaceTypes.h" diff --git a/old/WorldServer/Recipes/Recipe.cpp b/old/WorldServer/Recipes/Recipe.cpp index 36ff17c..6608506 100644 --- a/old/WorldServer/Recipes/Recipe.cpp +++ b/old/WorldServer/Recipes/Recipe.cpp @@ -1,6 +1,6 @@ -/* +/* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,12 +17,13 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #include -#include "../../common/debug.hpp" +#include "../../common/debug.h" #include "../../common/Log.h" -#include "../../common/database.hpp" +#include "../../common/database.h" #include "Recipe.h" -#include "../../common/config_reader.hpp" +#include "../../common/ConfigReader.h" #include "../Items/Items.h" #include "../World.h" @@ -734,7 +735,12 @@ bool Recipe::PlayerHasComponentByItemID(Client* client, vector* player_co cur_qty = track_req_qty; track_req_qty -= cur_qty; - itemss[i]->details.item_locked = true; + if(!itemss[i]->TryLockItem(LockReason::LockReason_Crafting)) { + for (int8 s = 0; s < i; s++) { + itemss[i]->TryUnlockItem(LockReason::LockReason_Crafting); + } + return false; + } player_component_pair_qty->push_back({itemss[i]->details.unique_id, cur_qty}); player_components->push_back(itemss[i]); if(have_qty >= required_qty) diff --git a/old/WorldServer/Recipes/Recipe.h b/old/WorldServer/Recipes/Recipe.h index 808566d..596e3fc 100644 --- a/old/WorldServer/Recipes/Recipe.h +++ b/old/WorldServer/Recipes/Recipe.h @@ -20,7 +20,8 @@ #ifndef RECIPE_H_ #define RECIPE_H_ -#include "../../common/types.hpp" +#include "../../common/types.h" +#include "../../common/Mutex.h" #include "../classes.h" #include diff --git a/old/WorldServer/Recipes/RecipeDB.cpp b/old/WorldServer/Recipes/RecipeDB.cpp index ad192dc..785b34f 100644 --- a/old/WorldServer/Recipes/RecipeDB.cpp +++ b/old/WorldServer/Recipes/RecipeDB.cpp @@ -24,7 +24,7 @@ #include #include #include "../../common/Log.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "Recipe.h" extern MasterRecipeList master_recipe_list; diff --git a/old/WorldServer/Rules/Rules.cpp b/old/WorldServer/Rules/Rules.cpp index 5c7f7dc..3e7298c 100644 --- a/old/WorldServer/Rules/Rules.cpp +++ b/old/WorldServer/Rules/Rules.cpp @@ -19,9 +19,9 @@ */ #include -#include "../../common/debug.hpp" +#include "../../common/debug.h" #include "../../common/Log.h" -#include "../../common/database.hpp" +#include "../../common/database.h" #include "Rules.h" extern RuleManager rule_manager; diff --git a/old/WorldServer/Rules/Rules.h b/old/WorldServer/Rules/Rules.h index 15f032f..bc1a1f9 100644 --- a/old/WorldServer/Rules/Rules.h +++ b/old/WorldServer/Rules/Rules.h @@ -22,8 +22,8 @@ #include #include - -#include "../../common/types.hpp" +#include "../../common/Mutex.h" +#include "../../common/types.h" using namespace std; diff --git a/old/WorldServer/Rules/RulesDB.cpp b/old/WorldServer/Rules/RulesDB.cpp index df35fdd..c5c57f7 100644 --- a/old/WorldServer/Rules/RulesDB.cpp +++ b/old/WorldServer/Rules/RulesDB.cpp @@ -20,7 +20,7 @@ #include #include "../../common/Log.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" extern RuleManager rule_manager; diff --git a/old/WorldServer/Sign.cpp b/old/WorldServer/Sign.cpp index 3b2ac63..d9c3170 100644 --- a/old/WorldServer/Sign.cpp +++ b/old/WorldServer/Sign.cpp @@ -1,28 +1,29 @@ /* -EQ2Emulator: Everquest II Server Emulator -Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) -This file is part of EQ2Emulator. + This file is part of EQ2Emulator. -EQ2Emulator 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. + EQ2Emulator 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. -EQ2Emulator 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. + EQ2Emulator 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 EQ2Emulator. If not, see . + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . */ + #include "Sign.h" -#include "../common/config_reader.hpp" -#include "WorldDatabase.hpp" +#include "../common/ConfigReader.h" +#include "WorldDatabase.h" #include "World.h" -#include "../common/log.hpp" +#include "../common/Log.h" extern World world; extern ConfigReader configReader; @@ -152,6 +153,8 @@ Sign* Sign::Copy(){ new_spawn->SetLootTier(GetLootTier()); new_spawn->SetLootDropType(GetLootDropType()); new_spawn->SetLanguage(GetLanguage()); + if(GetSpawnScriptSetDB() && GetSpawnScript()) + new_spawn->SetSpawnScript(std::string(GetSpawnScript())); return new_spawn; } diff --git a/old/WorldServer/Skills.cpp b/old/WorldServer/Skills.cpp index fe184f7..7bffcf9 100644 --- a/old/WorldServer/Skills.cpp +++ b/old/WorldServer/Skills.cpp @@ -20,7 +20,7 @@ #include "Skills.h" #include "Spawn.h" #include "LuaInterface.h" -#include "../common/log.hpp" +#include "../common/Log.h" extern ConfigReader configReader; extern LuaInterface* lua_interface; diff --git a/old/WorldServer/Skills.h b/old/WorldServer/Skills.h index d41d64d..7d59f1f 100644 --- a/old/WorldServer/Skills.h +++ b/old/WorldServer/Skills.h @@ -24,8 +24,8 @@ #include #include -#include "../common/config_reader.hpp" -#include "../common/types.hpp" +#include "../common/ConfigReader.h" +#include "../common/types.h" #include "MutexMap.h" #define SKILL_TYPE_WEAPONRY 1 diff --git a/old/WorldServer/Spawn.cpp b/old/WorldServer/Spawn.cpp index 389cff9..a1030d9 100644 --- a/old/WorldServer/Spawn.cpp +++ b/old/WorldServer/Spawn.cpp @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -25,8 +25,8 @@ #include "Entity.h" #include "Widget.h" #include "Sign.h" -#include "../common/misc_functions.hpp" -#include "../common/log.hpp" +#include "../common/MiscFunctions.h" +#include "../common/Log.h" #include "Rules/Rules.h" #include "World.h" #include "LuaInterface.h" @@ -120,6 +120,7 @@ Spawn::Spawn(){ has_spawn_proximities = false; pickup_item_id = 0; pickup_unique_item_id = 0; + house_character_id = 0; disable_sounds = false; has_quests_required = false; has_history_required = false; @@ -153,6 +154,7 @@ Spawn::Spawn(){ respawn_offset_high = 0; duplicated_spawn = true; ResetKnockedBack(); + spawn_script_setdb = false; } Spawn::~Spawn(){ @@ -1925,8 +1927,9 @@ const char* Spawn::GetSpawnScript(){ return 0; } -void Spawn::SetSpawnScript(string name){ +void Spawn::SetSpawnScript(string name, bool db_set){ spawn_script = name; + spawn_script_setdb = db_set; } void Spawn::SetPrimaryCommand(const char* name, const char* command, float distance){ @@ -2180,7 +2183,7 @@ void Spawn::InitializePosPacketData(Player* player, PacketStruct* packet, bool b packet->setDataByName("pos_grid_id", 0); } else { - packet->setDataByName("pos_grid_id", new_grid_id != 0 ? new_grid_id : GetLocation()); + packet->setDataByName("pos_grid_id", (new_grid_id != 0 && player != this) ? new_grid_id : GetLocation()); } bool include_heading = true; @@ -2197,18 +2200,25 @@ void Spawn::InitializePosPacketData(Player* player, PacketStruct* packet, bool b if (size == 0) size = 32; + float scaled = 0.0f; + + sint16 new_size = size; + if(IsEntity() && !IsPlayer()) { // doesn't work correctly for Player, only non-Player (NPC/Object/etc) + scaled = static_cast(size) + (((Entity*)this)->GetInfoStruct()->get_size_mod() * size); + // Round and cast to sint16 + new_size = static_cast(std::round(scaled)); + // Enforce minimum size of 1 + new_size = std::max(new_size, 1); + } + if (version <= 910) { packet->setDataByName("pos_collision_radius", appearance.pos.collision_radius > 0 ? appearance.pos.collision_radius : 32); - packet->setDataByName("pos_size", size > 0 ? size : 32); + packet->setDataByName("pos_size", new_size > 0 ? new_size : 32); } else { packet->setDataByName("pos_collision_radius", appearance.pos.collision_radius > 0 ? appearance.pos.collision_radius : 32); + packet->setDataByName("pos_size", new_size > 0 ? (((float)new_size) / 32.0f) : 1.0f); // float not an integer - if(!IsPlayer()) - packet->setDataByName("pos_size", size > 0 ? (((float)size) / 32.0f) : 1.0f); // float not an integer - else - packet->setDataByName("pos_size", 1.0f); - // please do not remove! This makes it so NPCs for example do not resize large/small when you are in combat with them! packet->setDataByName("pos_size_ratio", 1.0f); } @@ -2234,7 +2244,7 @@ void Spawn::InitializePosPacketData(Player* player, PacketStruct* packet, bool b else { packet->setDataByName("pos_x", appearance.pos.X); float result_y = appearance.pos.Y; - if(!IsWidget() && !IsSign() && !IsObject() && !(IsFlyingCreature() || IsWaterCreature() || InWater())) { + if(!IsWidget() && !IsSign() && !IsObject() && !(IsFlyingCreature() || IsWaterCreature() || InWater()) && player != this) { result_y = new_y; } if(GetMap() != player->GetMap()) { @@ -2487,11 +2497,6 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) { packet->setDataByName("npc", 1); if (GetMerchantID() > 0) packet->setDataByName("merchant", 1); - - if(IsEntity() && ((Entity*)this)->GetInfoStruct()->get_size_mod() != 0.0f) { - float mod = ((Entity*)this)->GetInfoStruct()->get_size_mod(); // e.g., -0.25 or +0.25 - //packet->setDataByName("temporary_scale", mod); //TODO: Understand what these mod values should be, anything we send makes the client shrink to nothing - } packet->setDataByName("effective_level", IsEntity() && ((Entity*)this)->GetInfoStruct()->get_effective_level() != 0 ? (int8)((Entity*)this)->GetInfoStruct()->get_effective_level() : (int8)GetLevel()); packet->setDataByName("level", (int8)GetLevel()); @@ -2981,8 +2986,9 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) { spell_id = info->spell_effects[i].spell_id; if(spell_id > 0) spell_id = 0xFFFFFFFF - spell_id; - else + else { spell_id = 0; + } packet->setSubstructDataByName("spell_effects", "spell_id", spell_id, i); //Change value of spell icon for this packet if spell exists @@ -3025,7 +3031,7 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) { packet->setSubstructDataByName("spell_effects", "spell_icon_backdrop", backdrop, i); spell = info->spell_effects[i].spell; - if (spell) + if (spell_id && spell_id != 0xFFFFFFFF && spell) packet->setSubstructDataByName("spell_effects", "spell_triggercount", spell->num_triggers, i); i++; } @@ -3360,8 +3366,9 @@ void Spawn::ProcessMovement(bool isSpawnListLocked){ } if (!movementCase && IsRunning() && !IsPauseMovementTimerActive()) { + // 6/7/2025: Fixes glitchy movement when spawns are close together and keep warping forward and back to the original position CalculateRunningLocation(); - //last_movement_update = Timer::GetCurrentTime2(); + last_movement_update = Timer::GetCurrentTime2(); } else if(movementCase) { @@ -3866,8 +3873,6 @@ void Spawn::FaceTarget(Spawn* target, bool disable_action_state){ if(!target) return; - FaceTarget(target->GetX(), target->GetZ()); - if(GetHP() > 0 && target->IsPlayer() && !EngagedInCombat()){ if(!IsPet() && disable_action_state) { if(IsNPC()) { @@ -3877,6 +3882,8 @@ void Spawn::FaceTarget(Spawn* target, bool disable_action_state){ SetTempActionState(0); } } + + FaceTarget(target->GetX(), target->GetZ()); } bool Spawn::MeetsSpawnAccessRequirements(Player* player){ diff --git a/old/WorldServer/Spawn.h b/old/WorldServer/Spawn.h index 0e4d198..542151b 100644 --- a/old/WorldServer/Spawn.h +++ b/old/WorldServer/Spawn.h @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,26 +17,28 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #ifndef __EQ2_SPAWN__ #define __EQ2_SPAWN__ #include -#include "../common/types.hpp" -#include "../common/packet/eq_packet.hpp" -#include "../common/eq_common_structs.hpp" -#include "../common/misc_functions.hpp" -#include "../common/opcodes/opcode_manager.hpp" -#include "../common/timer.hpp" +#include "../common/types.h" +#include "../common/EQPacket.h" +#include "../common/EQ2_Common_Structs.h" +#include "../common/MiscFunctions.h" +#include "../common/opcodemgr.h" +#include "../common/timer.h" #include "Commands/Commands.h" #include "Zone/position.h" #include "SpawnLists.h" #include -#include "../common/config_reader.hpp" +#include "../common/ConfigReader.h" #include "Items/Items.h" #include "Zone/map.h" #include "Zone/region_map.h" #include "Zone/region_map_v1.h" +#include "../common/Mutex.h" #include "MutexList.h" #include #include // needed for LS to compile properly on linux @@ -1088,8 +1090,9 @@ public: bool HasProvidedQuests(){ return (quest_ids.size() > 0); } - void SetSpawnScript(string name); + void SetSpawnScript(string name, bool db_set = false); const char* GetSpawnScript(); + bool GetSpawnScriptSetDB() { return spawn_script_setdb; } vector* GetSpawnGroup(); bool HasSpawnGroup(); @@ -1320,9 +1323,15 @@ public: { pickup_unique_item_id = uniqueid; } + + void SetHouseCharacterID(int32 charid) + { + house_character_id = charid; + } int32 GetPickupItemID() { return pickup_item_id; } int32 GetPickupUniqueItemID() { return pickup_unique_item_id; } + int32 GetHouseCharacterID() { return house_character_id; } bool IsSoundsDisabled() { return disable_sounds; } void SetSoundsDisabled(bool val) { disable_sounds = val; } @@ -1467,6 +1476,7 @@ protected: int32 transporter_id; int32 pickup_item_id; int32 pickup_unique_item_id; + int32 house_character_id; map* > required_quests; map required_history; @@ -1505,6 +1515,7 @@ private: int tmp_action_state; int32 running_to; string spawn_script; + bool spawn_script_setdb; static int32 next_id; ZoneServer* zone; int32 spawn_location_id; diff --git a/old/WorldServer/SpawnLists.h b/old/WorldServer/SpawnLists.h index 03236e0..546989b 100644 --- a/old/WorldServer/SpawnLists.h +++ b/old/WorldServer/SpawnLists.h @@ -19,7 +19,7 @@ */ #ifndef EQ2_SPAWN_LISTS #define EQ2_SPAWN_LISTS -#include "../common/types.hpp" +#include "../common/types.h" #include #include diff --git a/old/WorldServer/SpellProcess.cpp b/old/WorldServer/SpellProcess.cpp index 35dc470..59b15c1 100644 --- a/old/WorldServer/SpellProcess.cpp +++ b/old/WorldServer/SpellProcess.cpp @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,11 +17,13 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #include "SpellProcess.h" -#include "../common/log.hpp" +#include "../common/Log.h" #include "Tradeskills/Tradeskills.h" #include "ClientPacketFunctions.h" #include "Rules/Rules.h" +#include "races.h" extern MasterSpellList master_spell_list; extern MasterSkillList master_skill_list; @@ -30,6 +32,7 @@ extern LuaInterface* lua_interface; extern Commands commands; extern World world; extern RuleManager rule_manager; +extern Races races; SpellProcess::SpellProcess(){ last_checked_time = 0; @@ -422,6 +425,7 @@ bool SpellProcess::DeleteCasterSpell(Spawn* caster, Spell* spell, string reason) return ret; } + bool SpellProcess::DeleteCasterSpell(LuaSpell* spell, string reason, bool removing_all_spells, Spawn* remove_target, bool zone_shutting_down, bool shared_lock_spell){ if(shared_lock_spell && !removing_all_spells) { MSpellProcess.lock_shared(); @@ -430,117 +434,104 @@ bool SpellProcess::DeleteCasterSpell(LuaSpell* spell, string reason, bool removi bool ret = false; Spawn* target = 0; bool target_valid = false; + if(spell) { - spell->MSpellTargets.writelock(__FUNCTION__, __LINE__); - if(remove_target && spell->targets.size() > 1) { - for (int32 i = 0; i < spell->targets.size(); i++) { - if(remove_target->GetID() == spell->targets.at(i)) { + std::string spellName; + if(spell->spell) + spellName = std::string(spell->spell->GetName()); + LogWrite(SPELL__INFO, 0, "Spell", "SpellProcess::DeleteCasterSpell Spell: %s, Reason: %s, RemoveTarget: %s.", + spellName.c_str(), reason.c_str(), remove_target ? "Yes" : "All"); + if(remove_target) { + for (int32 id : spell->GetTargets()) { + if(remove_target->GetID() == id) { + LogWrite(SPELL__INFO, 0, "Spell", "SpellProcess::DeleteCasterSpell RemoveTarget Spell: %s, Reason: %s, Target: %s.", + spellName.c_str(), reason.c_str(), remove_target->GetName()); if(remove_target->IsEntity()){ - spell->removed_targets.push_back(remove_target->GetID()); - ((Entity*)remove_target)->RemoveProc(0, spell); - ((Entity*)remove_target)->RemoveSpellEffect(spell); - ((Entity*)remove_target)->RemoveSpellBonus(spell); - if(spell->spell->GetSpellData()->det_type > 0 && (spell->spell->GetSpellDuration() > 0 || spell->spell->GetSpellData()->duration_until_cancel)) - ((Entity*)remove_target)->RemoveDetrimentalSpell(spell); + spell->RemoveTarget(remove_target->GetID()); + lua_interface->RemoveSpawnFromSpell(spell, remove_target); } - spell->targets.erase(spell->targets.begin()+i, spell->targets.begin()+i+1); target_valid = true; break; } } - spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__); if(shared_lock_spell && !removing_all_spells) { MSpellProcess.unlock_shared(); } return target_valid; } - spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__); if (!zone_shutting_down && spell->caster) { // spell->caster ptr cannot be trusted during zone shutdown - if(spell->caster->GetThreatTransfer() && spell->caster->GetThreatTransfer()->Spell == spell) { - spell->caster->SetThreatTransfer(nullptr); - } - if (spell->spell->GetSpellData()->cast_type == SPELL_CAST_TYPE_TOGGLE){ - - int8 actual_concentration = spell->spell->GetSpellData()->req_concentration; + if(spell->caster) { + if(spell->caster->GetThreatTransfer() && spell->caster->GetThreatTransfer()->Spell == spell) { + spell->caster->SetThreatTransfer(nullptr); + } + if (spell->spell && spell->spell->GetSpellData()->cast_type == SPELL_CAST_TYPE_TOGGLE){ + + int8 actual_concentration = spell->spell->GetSpellData()->req_concentration; - if (actual_concentration > 0) - { - int8 curConcentration = spell->caster->GetInfoStruct()->get_cur_concentration(); - if(curConcentration >= actual_concentration) + if (actual_concentration > 0) { - spell->caster->GetInfoStruct()->set_cur_concentration(curConcentration - actual_concentration); - if (spell->caster->IsPlayer()&& spell->caster->GetZone()) - spell->caster->GetZone()->TriggerCharSheetTimer(); + int8 curConcentration = spell->caster->GetInfoStruct()->get_cur_concentration(); + if(curConcentration >= actual_concentration) + { + spell->caster->GetInfoStruct()->set_cur_concentration(curConcentration - actual_concentration); + if (spell->caster->IsPlayer()&& spell->caster->GetZone()) + spell->caster->GetZone()->TriggerCharSheetTimer(); + } + } + if(!spell->spell->GetSpellData()->duration_until_cancel) + { + CheckRecast(spell->spell, spell->caster); + if (spell->caster && spell->caster->IsPlayer()) + SendSpellBookUpdate(((Player*)spell->caster)->GetClient()); } } - if(!spell->spell->GetSpellData()->duration_until_cancel) - { - CheckRecast(spell->spell, spell->caster); - if (spell->caster && spell->caster->IsPlayer()) - SendSpellBookUpdate(((Player*)spell->caster)->GetClient()); + if(spell->spell && IsReady(spell->spell, spell->caster) && spell->caster->IsPlayer()) { + ((Player*)spell->caster)->UnlockSpell(spell->spell); + SendSpellBookUpdate(((Player*)spell->caster)->GetClient()); } - } - if(IsReady(spell->spell, spell->caster) && spell->caster->IsPlayer()) { - ((Player*)spell->caster)->UnlockSpell(spell->spell); - SendSpellBookUpdate(((Player*)spell->caster)->GetClient()); + + spell->caster->RemoveProc(0, spell); + spell->caster->RemoveMaintainedSpell(spell); + CheckRemoveTargetFromSpell(spell, removing_all_spells, removing_all_spells); } - spell->caster->RemoveProc(0, spell); - spell->caster->RemoveMaintainedSpell(spell); - CheckRemoveTargetFromSpell(spell, removing_all_spells, removing_all_spells); - ZoneServer* zone = spell->caster->GetZone(); + ZoneServer* zone = spell->zone; if(zone) { - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int32 i = 0; i < spell->targets.size(); i++) { - target = zone->GetSpawnByID(spell->targets.at(i)); + LogWrite(SPELL__DEBUG, 0, "Spell", "SpellProcess::DeleteCasterSpell RemoveTargets Spell: %s.", + spellName.c_str()); + for (int32 id : spell->GetTargets()) { + target = zone->GetSpawnByID(id); if(target && target->IsEntity()){ - spell->removed_targets.push_back(target->GetID()); - ((Entity*)target)->RemoveProc(0, spell); - ((Entity*)target)->RemoveSpellEffect(spell); - ((Entity*)target)->RemoveSpellBonus(spell); - if(spell->spell->GetSpellData()->det_type > 0 && (spell->spell->GetSpellDuration() > 0 || spell->spell->GetSpellData()->duration_until_cancel)) - ((Entity*)target)->RemoveDetrimentalSpell(spell); + LogWrite(SPELL__INFO, 0, "Spell", "SpellProcess::DeleteCasterSpell RemoveTargets Spell: %s, Reason: %s, CurrentTarget: %s (%u).", + spellName.c_str(), reason.c_str(), target->GetName(), id); + spell->RemoveTarget(target->GetID()); + lua_interface->RemoveSpawnFromSpell(spell, target); } else{ - spell->caster->RemoveSpellEffect(spell); - spell->caster->RemoveSpellBonus(spell); - if(spell->spell->GetSpellData()->det_type > 0 && (spell->spell->GetSpellDuration() > 0 || spell->spell->GetSpellData()->duration_until_cancel)) - spell->caster->RemoveDetrimentalSpell(spell); + LogWrite(SPELL__INFO, 0, "Spell", "SpellProcess::DeleteCasterSpell RemoveTarget Spell: %s, Reason: %s, CurrentTarget: ??Unknown??.", + spellName.c_str(), reason.c_str()); + spell->RemoveTarget(spell->caster->GetID()); + lua_interface->RemoveSpawnFromSpell(spell, spell->caster); } - if(target && target->IsPlayer() && spell->spell->GetSpellData()->fade_message.length() > 0){ + if(target && target->IsPlayer() && spell->spell && spell->spell->GetSpellData()->fade_message.length() > 0){ Client* client = ((Player*)target)->GetClient(); if(client){ - bool send_to_sender = true; string fade_message = spell->spell->GetSpellData()->fade_message; - if(fade_message.find("%t") != string::npos) - fade_message.replace(fade_message.find("%t"), 2, target->GetName()); + ReplaceEffectTokens(fade_message, spell->caster, target); client->Message(CHANNEL_SPELLS_OTHER, fade_message.c_str()); } } - if (target && target->IsPlayer() && spell->spell->GetSpellData()->fade_message.length() > 0) { + if (target && target->IsPlayer() && spell->spell && spell->spell->GetSpellData()->fade_message_others.length() > 0) { Client* client = ((Player*)target)->GetClient(); if (client) { - bool send_to_sender = true; string fade_message_others = spell->spell->GetSpellData()->fade_message_others; - if (fade_message_others.find("%t") != string::npos) - fade_message_others.replace(fade_message_others.find("%t"), 2, target->GetName()); - if (fade_message_others.find("%c") != string::npos) - fade_message_others.replace(fade_message_others.find("%c"), 2, spell->caster->GetName()); - if (fade_message_others.find("%T") != string::npos) { - fade_message_others.replace(fade_message_others.find("%T"), 2, target->GetName()); - send_to_sender = false; - } - if (fade_message_others.find("%C") != string::npos) { - fade_message_others.replace(fade_message_others.find("%C"), 2, spell->caster->GetName()); - send_to_sender = false; - } + ReplaceEffectTokens(fade_message_others, spell->caster, target); if(spell->caster->GetZone()) { - spell->caster->GetZone()->SimpleMessage(CHANNEL_SPELLS_OTHER, fade_message_others.c_str(), target, 50, send_to_sender); + spell->caster->GetZone()->SimpleMessage(CHANNEL_SPELLS_OTHER, fade_message_others.c_str(), target, 50); } } } } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } ret = true; } @@ -565,10 +556,9 @@ bool SpellProcess::ProcessSpell(LuaSpell* spell, bool first_cast, const char* fu else if(lua_interface && !spell->interrupted){ if(all_targets) { - for(int t=0;ttargets.size();t++) - { - if(spell->caster->GetZone()) { - Spawn* altSpawn = spell->caster->GetZone()->GetSpawnByID(spell->targets[t]); + for (int32 id : spell->GetTargets()) { + if(spell->zone) { + Spawn* altSpawn = spell->zone->GetSpawnByID(id); if(altSpawn) { std::string functionCall = ApplyLuaFunction(spell, first_cast, function, timer, altSpawn); @@ -582,7 +572,7 @@ bool SpellProcess::ProcessSpell(LuaSpell* spell, bool first_cast, const char* fu } } spell->MScriptMutex.releasewritelock(__FUNCTION__, __LINE__); - return true; + return ret; } std::string functionCall = ApplyLuaFunction(spell, first_cast, function, timer); if(functionCall.length() < 1) { @@ -664,7 +654,9 @@ bool SpellProcess::CastInstant(Spell* spell, Entity* caster, Entity* target, boo lua_spell->spell = spell; lua_spell->initial_target = target->GetID(); lua_spell->initial_target_char_id = (target && target->IsPlayer()) ? ((Player*)target)->GetCharacterID() : 0; + lua_spell->is_loaded_recast = false; GetSpellTargets(lua_spell); + PrintTargets(lua_spell, "instant"); if (!lua_spell->spell->IsCopiedSpell()) { @@ -1047,6 +1039,23 @@ Spell* SpellProcess::GetSpell(Entity* caster){ return spell; } +void SpellProcess::PrintTargets(LuaSpell* spell, std::string stage) { + std::vector targets = spell->GetTargets(); + if(spell->zone) { + for (int32_t id : targets) { + Spawn* tmp = spell->zone->GetSpawnByID(id); + if(tmp) { + LogWrite(SPELL__INFO, 0, "Spell", "SpellProcess::PrintTarget(%s) Spell: %s, Spawn: %s (%u) (DBID: %u).", + stage.c_str(), spell->spell->GetName(), tmp->GetName(), tmp->GetID(), tmp->GetDatabaseID()); + } + else { + LogWrite(SPELL__ERROR, 0, "Spell", "SpellProcess::PrintTarget(%s) Spell: %s SpawnID: %u - ??Unknown??.", + stage.c_str(), spell->spell->GetName(), id); + } + } + } +} + void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster, Spawn* target, bool lock, bool harvest_spell, LuaSpell* customSpell, int16 custom_cast_time, bool in_heroic_opp) { if((customSpell != 0 || spell != 0) && caster) @@ -1090,6 +1099,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster, lua_spell->zone = caster->GetZone(); lua_spell->initial_caster_char_id = (caster && caster->IsPlayer()) ? ((Player*)caster)->GetCharacterID() : 0; lua_spell->spell = spell; + lua_spell->is_loaded_recast = false; int32 target_id = target->GetID(); lua_spell->initial_target = target_id; @@ -1201,6 +1211,8 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster, return; } + PrintTargets(lua_spell, "cast"); + LuaSpell* conflictSpell = caster->HasLinkedTimerID(lua_spell, target, (spell_type == SPELL_TYPE_DD || spell_type == SPELL_TYPE_DOT)); if(conflictSpell) { @@ -1213,18 +1225,17 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster, if(spell->GetSpellData()->friendly_spell) { ZoneServer* zone = caster->GetZone(); - Spawn* tmpTarget = zone->GetSpawnByID(conflictSpell->initial_target); - if(tmpTarget && tmpTarget->IsEntity()) - { - ((Entity*)tmpTarget)->RemoveEffectsFromLuaSpell(conflictSpell); - zone->RemoveTargetFromSpell(conflictSpell, tmpTarget, false); - CheckRemoveTargetFromSpell(conflictSpell); - ((Entity*)tmpTarget)->RemoveSpellEffect(conflictSpell); - if(client && IsReady(conflictSpell->spell, client->GetPlayer())) - UnlockSpell(client, conflictSpell->spell); + + for (int32 id : conflictSpell->GetTargets()) { + Spawn* tmpTarget = zone->GetSpawnByID(id); + if(tmpTarget && tmpTarget->IsEntity()) + { + ((Entity*)tmpTarget)->RemoveEffectsFromLuaSpell(conflictSpell); + zone->RemoveTargetFromSpell(conflictSpell, tmpTarget, false); + CheckRemoveTargetFromSpell(conflictSpell); + lua_interface->RemoveSpawnFromSpell(conflictSpell, tmpTarget); + } } - DeleteSpell(lua_spell); - return; } else if(lua_spell->spell->GetSpellData()->spell_type == SPELL_TYPE_DEBUFF) { @@ -1233,11 +1244,11 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster, } } } - else - { - SpellCannotStack(zone, client, lua_spell->caster, lua_spell, conflictSpell); - return; - } + else + { + SpellCannotStack(zone, client, lua_spell->caster, lua_spell, conflictSpell); + return; + } } } @@ -1490,7 +1501,7 @@ void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster, } } - if (lua_spell->targets.size() == 0 && spell->GetSpellData()->max_aoe_targets == 0) + if (lua_spell->GetTargetCount() == 0 && spell->GetSpellData()->max_aoe_targets == 0) { LogWrite(SPELL__ERROR, 0, "Spell", "SpellProcess::ProcessSpell Unable to find any spell targets for spell '%s', spell type: %u, target type %u.", spell->GetName(), spell->GetSpellData()->spell_type, spell->GetSpellData()->target_type); @@ -1731,9 +1742,9 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive, bool in_her Client* client = 0; if(spell->caster && spell->caster->IsPlayer()) client = ((Player*)spell->caster)->GetClient(); - if (spell->spell->GetSpellData()->max_aoe_targets > 0 && spell->targets.size() == 0) { + if (spell->spell->GetSpellData()->max_aoe_targets > 0 && spell->GetTargetCount() == 0) { GetSpellTargetsTrueAOE(spell); - if (spell->targets.size() == 0) { + if (spell->GetTargetCount() == 0) { if(client) { client->GetPlayer()->UnlockAllSpells(true); @@ -1745,8 +1756,8 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive, bool in_her } } - if(client && client->GetCurrentZone() && - !spell->spell->GetSpellData()->friendly_spell) + PrintTargets(spell, "castprocess"); + if(client && client->GetCurrentZone()) { ZoneServer* zone = client->GetCurrentZone(); Spawn* tmpTarget = zone->GetSpawnByID(spell->initial_target); @@ -1754,8 +1765,30 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive, bool in_her LuaSpell* conflictSpell = spell->caster->HasLinkedTimerID(spell, tmpTarget, (spell_type == SPELL_TYPE_DD || spell_type == SPELL_TYPE_DOT)); if(conflictSpell && tmpTarget && tmpTarget->IsEntity()) { - ((Entity*)tmpTarget)->RemoveSpellEffect(conflictSpell); - zone->RemoveTargetFromSpell(conflictSpell, tmpTarget); + if(conflictSpell->spell->GetSpellData()->min_class_skill_req > 0 && spell->spell->GetSpellData()->min_class_skill_req > 0) + { + if(conflictSpell->spell->GetSpellData()->min_class_skill_req <= spell->spell->GetSpellData()->min_class_skill_req) + { + if(spell->spell->GetSpellData()->duration_until_cancel && !spell->num_triggers) + { + for (int32 id : conflictSpell->GetTargets()) { + Spawn* tmpTarget = zone->GetSpawnByID(id); + if(tmpTarget && tmpTarget->IsEntity()) + { + ((Entity*)tmpTarget)->RemoveEffectsFromLuaSpell(conflictSpell); + zone->RemoveTargetFromSpell(conflictSpell, tmpTarget, false); + CheckRemoveTargetFromSpell(conflictSpell); + lua_interface->RemoveSpawnFromSpell(conflictSpell, tmpTarget); + } + } + } + } + else + { + SpellCannotStack(zone, client, spell->caster, spell, conflictSpell); + return false; + } + } } } @@ -1784,8 +1817,8 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive, bool in_her ZoneServer* zone = spell->caster->GetZone(); Spawn* target = 0; if(processedSpell){ - for (int32 i = 0; i < spell->targets.size(); i++) { - target = zone->GetSpawnByID(spell->targets[i]); + for (int32 id : spell->GetTargets()) { + target = zone->GetSpawnByID(id); if (!target) continue; @@ -1803,29 +1836,16 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive, bool in_her if(spell->spell->GetSpellData()->success_message.length() > 0){ if(client){ string success_message = spell->spell->GetSpellData()->success_message; - if(success_message.find("%t") != string::npos) - success_message.replace(success_message.find("%t"), 2, spell->caster->GetName()); + ReplaceEffectTokens(success_message, spell->caster, target); client->Message(CHANNEL_SPELLS, success_message.c_str()); } } if(spell->spell->GetSpellData()->effect_message.length() > 0){ string effect_message = spell->spell->GetSpellData()->effect_message; - bool send_to_sender = true; - if(effect_message.find("%t") != string::npos) - effect_message.replace(effect_message.find("%t"), 2, target->GetName()); - if (effect_message.find("%c") != string::npos) - effect_message.replace(effect_message.find("%c"), 2, spell->caster->GetName()); - if (effect_message.find("%T") != string::npos) { - effect_message.replace(effect_message.find("%T"), 2, target->GetName()); - send_to_sender = false; - } - if (effect_message.find("%C") != string::npos) { - effect_message.replace(effect_message.find("%C"), 2, spell->caster->GetName()); - send_to_sender = false; - } + ReplaceEffectTokens(effect_message, spell->caster, target); if(spell->caster && spell->caster->GetZone()) { - spell->caster->GetZone()->SimpleMessage(CHANNEL_SPELLS_OTHER, effect_message.c_str(), target, 50, send_to_sender); + spell->caster->GetZone()->SimpleMessage(CHANNEL_SPELLS_OTHER, effect_message.c_str(), target, 50); } } if(target->GetZone()) { @@ -1840,11 +1860,12 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive, bool in_her return false; } if(!spell->resisted && (spell->spell->GetSpellDuration() > 0 || spell->spell->GetSpellData()->duration_until_cancel || spell->spell->GetSpellData()->spell_book_type == SPELL_BOOK_TYPE_NOT_SHOWN)) { - for (int32 i = 0; i < spell->targets.size(); i++) { + bool firstTarget = true; + for (int32 id : spell->GetTargets()) { //LogWrite(SPELL__ERROR, 0, "Spell", "No precast function found for %s", ((Entity*)target)->GetName()); - target = zone->GetSpawnByID(spell->targets.at(i)); - if (!target && spell->targets.at(i) == spell->caster->GetID()) { + target = zone->GetSpawnByID(id); + if (!target && id == spell->caster->GetID()) { target = spell->caster; } if (!target) { @@ -1853,18 +1874,37 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive, bool in_her } continue; } - if (i == 0 && !spell->spell->GetSpellData()->not_maintained) { - spell->caster->AddMaintainedSpell(spell); - } SpellEffects* effect = ((Entity*)target)->GetSpellEffect(spell->spell->GetSpellID()); - if (effect && effect->tier > spell->spell->GetSpellTier()) { + + if(!effect) + effect = ((Entity*)target)->GetSpellEffectWithLinkedTimer(spell->spell->GetSpellID(), spell->spell->GetSpellData()->linked_timer, spell->spell->GetSpellData()->type_group_spell_id, spell->caster, true); + + if (effect && effect->spell != spell && effect->tier > spell->spell->GetSpellTier()) { if(client) { spell->caster->GetZone()->SendSpellFailedPacket(client, SPELL_ERROR_TAKE_EFFECT_MOREPOWERFUL); client->SimpleMessage(CHANNEL_COLOR_YELLOW, "The spell did not take hold as the target already has a better spell of this type."); + + if (!passive) + SendFinishedCast(spell, client); + + if(spell->zone) + spell->zone->GetSpellProcess()->RemoveSpellScriptTimerBySpell(spell); + DeleteActiveSpell(spell, true); + return false; } } else{ + if(effect && effect->spell != spell) { + if(effect->spell->zone) + effect->spell->zone->GetSpellProcess()->RemoveSpellScriptTimerBySpell(effect->spell); + + DeleteActiveSpell(effect->spell, true); + } + if (firstTarget) { + spell->caster->AddMaintainedSpell(spell); + } + firstTarget = false; ((Entity*)target)->AddSpellEffect(spell); if(spell->spell->GetSpellData()->det_type > 0) ((Entity*)target)->AddDetrimentalSpell(spell); @@ -1910,16 +1950,14 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive, bool in_her ho = soloItr->second; bool match = false; LogWrite(SPELL__DEBUG, 0, "HO", "target = %u", ho->GetTarget()); - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (int8 i = 0; i < spell->targets.size(); i++) { - LogWrite(SPELL__DEBUG, 0, "HO", "Target ID: %u", spell->targets.at(i)); - if (spell->targets.at(i) == ho->GetTarget() || spell->spell->GetSpellData()->friendly_spell) { + for (int32 id : spell->GetTargets()) { + LogWrite(SPELL__DEBUG, 0, "HO", "Target ID: %u", id); + if (id == ho->GetTarget() || spell->spell->GetSpellData()->friendly_spell) { match = true; LogWrite(SPELL__DEBUG, 0, "HO", "match found"); break; } } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); if(match && !spell->spell) LogWrite(SPELL__ERROR, 0, "HO", "%s: spell->spell is nullptr", client->GetPlayer()->GetName()); @@ -1946,10 +1984,9 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive, bool in_her if (groupItr != m_groupHO.end()) { ho = groupItr->second; int32 group_id = client->GetPlayer()->GetGroupMemberInfo()->group_id; - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - if (((spell->targets.size() > 0 && spell->targets.at(0) == ho->GetTarget()) || spell->spell->GetSpellData()->friendly_spell) + int32 first_target = spell->GetPrimaryTargetID(); + if (((first_target != 0xFFFFFFFF && first_target == ho->GetTarget()) || spell->spell->GetSpellData()->friendly_spell) && ho->UpdateHeroicOP(spell->spell->GetSpellIconHeroicOp())) { - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__); deque::iterator itr; @@ -1976,8 +2013,6 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive, bool in_her m_groupHO.erase(groupItr); } } - else - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } } MGroupHO.releasewritelock(__FUNCTION__, __LINE__); @@ -2081,26 +2116,27 @@ void SpellProcess::RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all, boo MutexList::iterator itr = active_spells.begin(); while(itr.Next()){ spell = itr->value; - if (!spell) + + if (!spell || !spell->spell || !spell->spell->GetSpellData()) continue; + + if (spell->spell->GetSpellData()->persist_through_death) + continue; + if(spell->caster == spawn && call_expire_function){ DeleteCasterSpell(spell, "expired", remove_all, nullptr, false, lock_spell_process); continue; } - if (spell->spell->GetSpellData()->persist_through_death) - continue; bool foundMatch = false; if(spawn != spell->caster) { - spell->MSpellTargets.readlock(__FUNCTION__, __LINE__); - for (i = 0; i < spell->targets.size(); i++){ - if (spawn->GetID() == spell->targets.at(i)){ + for (int32 id : spell->GetTargets()) { + if (spawn->GetID() == id){ foundMatch = true; break; } } - spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); } if(foundMatch) { if (spawn->IsEntity()) @@ -2192,7 +2228,8 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell) luaspell->initial_target = target->GetID(); luaspell->initial_target_char_id = (target && target->IsPlayer()) ? ((Player*)target)->GetCharacterID() : 0; AddLuaSpellTarget(luaspell, target->GetID()); - GetPlayerGroupTargets((Player*)target, caster, luaspell); + if(target_type == SPELL_TARGET_OTHER_GROUP_AE) + GetPlayerGroupTargets((Player*)target, caster, luaspell); } else if (secondary_target && target->IsPlayer() && ((Entity*)caster)->AttackAllowed((Entity*)secondary_target)) @@ -2201,7 +2238,12 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell) luaspell->initial_target = secondary_target->GetID(); luaspell->initial_target_char_id = (secondary_target && secondary_target->IsPlayer()) ? ((Player*)secondary_target)->GetCharacterID() : 0; AddLuaSpellTarget(luaspell, secondary_target->GetID()); - GetPlayerGroupTargets((Player*)secondary_target, caster, luaspell); + if(target_type == SPELL_TARGET_OTHER_GROUP_AE) + GetPlayerGroupTargets((Player*)secondary_target, caster, luaspell); + } + else if(target->IsNPC() && ((Entity*)caster)->AttackAllowed((Entity*)target)) { + // caster is NPC and potentially in a spawn group with other NPCs + AddNPCGroupOrSelfTarget(luaspell, target); } } } // end friendly spell check @@ -2223,6 +2265,10 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell) luaspell->initial_target_char_id = (target && target->IsPlayer()) ? ((Player*)target)->GetCharacterID() : 0; AddLuaSpellTarget(luaspell, target->GetID()); } + else if(target->IsNPC() && ((Entity*)caster)->AttackAllowed((Entity*)target)) { + // caster is NPC and potentially in a spawn group with other NPCs + AddNPCGroupOrSelfTarget(luaspell, target); + } } } } @@ -2284,6 +2330,8 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell) } else // default self cast for group/raid AE { + if(caster->IsPlayer()) + GetPlayerGroupTargets((Player*)caster, caster, luaspell, true); target = caster; luaspell->initial_target = caster->GetID(); luaspell->initial_target_char_id = (caster && caster->IsPlayer()) ? ((Player*)caster)->GetCharacterID() : 0; @@ -2311,7 +2359,6 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell) luaspell->initial_target_char_id = (secondary_target && secondary_target->IsPlayer()) ? ((Player*)secondary_target)->GetCharacterID() : 0; } - luaspell->MSpellTargets.writelock(__FUNCTION__, __LINE__); // Group AE type NOTE: Add support for RAID AE to affect raid members once raids have been completed if (target_type == SPELL_TARGET_GROUP_AE || spell_type == SPELL_TYPE_ALLGROUPTARGETS || target_type == SPELL_TARGET_RAID_AE) { @@ -2324,7 +2371,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell) else if (data->friendly_spell) { // caster is an Entity - if (luaspell->caster->IsEntity()) + if (caster->IsPlayer()) { // entity caster is in a player group if (((Entity*)caster)->GetGroupMemberInfo()) @@ -2383,7 +2430,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell) } else if (caster->IsNPC()) // caster is NOT a player { - // caster is NPC and in a spawn group with other NPCs + // caster is NPC and in a spawn group with other NPCs check FRIENDLY vector* group = ((NPC*)caster)->GetSpawnGroup(); if (group) { @@ -2393,7 +2440,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell) { Spawn* group_member = *itr; - if (group_member->IsNPC() && group_member->Alive() && ((Entity*)caster)->AttackAllowed(((Entity*)group_member))){ + if (group_member->IsNPC() && group_member->Alive()){ AddLuaSpellTarget(luaspell, group_member->GetID(), false); if (((Entity*)group_member)->HasPet()){ Entity* pet = ((Entity*)group_member)->GetPet(); @@ -2410,7 +2457,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell) AddSelfAndPet(luaspell, caster); safe_delete(group); - } // end is player + } // end is npc } // end is friendly } // end is Group AE @@ -2440,7 +2487,7 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell) } else if (target->IsPlayer() || target->IsBot() || (target->IsNPC() && ((Entity*)target)->GetInfoStruct()->get_friendly_target_npc())) // else it is not raid, group only or group spell AddLuaSpellTarget(luaspell, target->GetID(), false); // return target for single spell - else if ((luaspell->targets.size() < 1) || (!target->IsPet() || (((Entity*)target)->GetOwner() && !((Entity*)target)->GetOwner()->IsPlayer()))) + else if ((luaspell->HasNoTargets()) || (!target->IsPet() || (((Entity*)target)->GetOwner() && !((Entity*)target)->GetOwner()->IsPlayer()))) AddLuaSpellTarget(luaspell, caster->GetID(), false); // and if no target, cast on self } else if (caster->IsNPC()) // caster is an NPC @@ -2584,11 +2631,10 @@ void SpellProcess::GetSpellTargets(LuaSpell* luaspell) } } } - luaspell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__); } - if (luaspell && luaspell->targets.size() > 20) - LogWrite(SPELL__WARNING, 0, "Spell", "Warning in %s: Size of targets array is %u", __FUNCTION__, luaspell->targets.size()); + if (luaspell && luaspell->GetTargetCount() > 20) + LogWrite(SPELL__WARNING, 0, "Spell", "Warning in %s: Size of targets array is %u", __FUNCTION__, luaspell->GetTargetCount()); } bool SpellProcess::GetPlayerGroupTargets(Player* target, Spawn* caster, LuaSpell* luaspell, bool bypassSpellChecks, bool bypassRangeChecks) @@ -2642,7 +2688,6 @@ void SpellProcess::GetSpellTargetsTrueAOE(LuaSpell* luaspell) { } int32 ignore_target = 0; std::vector> spawns = luaspell->caster->GetZone()->GetAttackableSpawnsByDistance(luaspell->caster, luaspell->spell->GetSpellData()->radius); - luaspell->MSpellTargets.writelock(__FUNCTION__, __LINE__); int32 i = 0; for (const auto& pair : spawns) { if (i == 0){ @@ -2656,7 +2701,7 @@ void SpellProcess::GetSpellTargetsTrueAOE(LuaSpell* luaspell) { i++; - if (luaspell->targets.size() >= luaspell->spell->GetSpellData()->max_aoe_targets) + if (luaspell->GetTargetCount() >= luaspell->spell->GetSpellData()->max_aoe_targets) break; int32 target_id = pair.first; @@ -2675,14 +2720,13 @@ void SpellProcess::GetSpellTargetsTrueAOE(LuaSpell* luaspell) { AddLuaSpellTarget(luaspell, spawn->GetID(), false); } - if (luaspell->targets.size() >= luaspell->spell->GetSpellData()->max_aoe_targets) + if (luaspell->GetTargetCount() >= luaspell->spell->GetSpellData()->max_aoe_targets) break; } - luaspell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__); } } - if (luaspell->targets.size() > 20) - LogWrite(SPELL__DEBUG, 0, "Spell", "Warning in SpellProcess::GetSpellTargetsTrueAOE Size of targets array is %u", luaspell->targets.size()); + if (luaspell->GetTargetCount() > 20) + LogWrite(SPELL__DEBUG, 0, "Spell", "Warning in SpellProcess::GetSpellTargetsTrueAOE Size of targets array is %u", luaspell->GetTargetCount()); } void SpellProcess::AddSpellScriptTimer(SpellScriptTimer* timer) { @@ -2833,58 +2877,46 @@ void SpellProcess::CheckRemoveTargetFromSpell(LuaSpell* spell, bool allow_delete MRemoveTargetList.writelock(__FUNCTION__, __LINE__); if (remove_target_list.size() > 0){ vector spawnsToRemove; - map*>::iterator remove_itr; vector::iterator remove_target_itr; vector::iterator target_itr; - vector* targets; vector* remove_targets = 0; Spawn* remove_spawn = 0; bool should_delete = false; bool dropped_lock = false; bool targets_empty = false; - for (remove_itr = remove_target_list.begin(); remove_itr != remove_target_list.end(); remove_itr++){ - if (remove_itr->first == spell){ - targets = &spell->targets; - remove_targets = remove_itr->second; - if (remove_targets && targets){ - spell->MSpellTargets.writelock(__FUNCTION__, __LINE__); - for (remove_target_itr = remove_targets->begin(); remove_target_itr != remove_targets->end(); remove_target_itr++){ - remove_spawn = spell->zone->GetSpawnByID((*remove_target_itr)); - if (remove_spawn) { - bool found_target = false; - if(remove_spawn->IsPlayer()) - { - multimap::iterator entries; - while((entries = spell->char_id_targets.find(((Player*)remove_spawn)->GetCharacterID())) != spell->char_id_targets.end()) - { - spell->char_id_targets.erase(entries); - } - } - for (target_itr = targets->begin(); target_itr != targets->end(); target_itr++) { - if (remove_spawn->GetID() == (*target_itr)) { - found_target = true; - ((Entity*)remove_spawn)->RemoveProc(0, spell); - ((Entity*)remove_spawn)->RemoveMaintainedSpell(spell); - LogWrite(SPELL__DEBUG, 0, "Spell", "%s CheckRemoveTargetFromSpell %s (%u).", spell->spell->GetName(), remove_spawn->GetName(), remove_spawn->GetID()); - targets->erase(target_itr); - if(remove_spawn && std::find(spawnsToRemove.begin(), spawnsToRemove.end(), remove_spawn) == spawnsToRemove.end()) - spawnsToRemove.push_back(remove_spawn); - break; - } - } - targets_empty = (targets->size() == 0 && spell->char_id_targets.size() == 0); - if (targets_empty && allow_delete) { - should_delete = true; + auto it = remove_target_list.find(spell); + if (it != remove_target_list.end()) { + remove_targets = it->second; + if (remove_targets){ + for (remove_target_itr = remove_targets->begin(); remove_target_itr != remove_targets->end(); remove_target_itr++){ + remove_spawn = spell->zone->GetSpawnByID((*remove_target_itr)); + if (remove_spawn) { + bool found_target = false; + if(remove_spawn->IsPlayer()) + { + spell->RemoveCharIDTarget(((Player*)remove_spawn)->GetCharacterID()); + } + for (int32 id : spell->GetTargets()) { + if (remove_spawn->GetID() == id) { + found_target = true; + + spell->RemoveTarget(id); + LogWrite(SPELL__DEBUG, 0, "Spell", "SpellProcess::CheckRemoveTargetFromSpell Spell: %s, Spawn: %s (%u).", spell->spell->GetName(), remove_spawn->GetName(), remove_spawn->GetID()); + if(remove_spawn && std::find(spawnsToRemove.begin(), spawnsToRemove.end(), remove_spawn) == spawnsToRemove.end()) + spawnsToRemove.push_back(remove_spawn); break; } - else if(remove_spawn && std::find(spawnsToRemove.begin(), spawnsToRemove.end(), remove_spawn) == spawnsToRemove.end()) { - spawnsToRemove.push_back(remove_spawn); - } + } + targets_empty = (spell->HasNoTargets() && spell->HasNoCharIDTargets()); + if (targets_empty && allow_delete) { + should_delete = true; + break; + } + else if(remove_spawn && std::find(spawnsToRemove.begin(), spawnsToRemove.end(), remove_spawn) == spawnsToRemove.end()) { + spawnsToRemove.push_back(remove_spawn); } } - spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__); } - break; } } @@ -2897,8 +2929,7 @@ void SpellProcess::CheckRemoveTargetFromSpell(LuaSpell* spell, bool allow_delete Spawn* target = spawnsToRemove.at(s); if(target) { bool not_last_itr = (s != spawnsToRemove.size() - 1); - if(target->IsEntity()) - ((Entity*)target)->RemoveEffectsFromLuaSpell(spell); + lua_interface->RemoveSpawnFromSpell(spell, target); if(targets_empty && allow_delete) { lua_interface->RemoveSpell(spell, true, false, !target->Alive() ? "target_dead" : "target_removed", false, not_last_itr, target); } @@ -3027,6 +3058,14 @@ void SpellProcess::DeleteSpell(LuaSpell* spell) lua_interface->RemoveCustomSpell(spell->spell->GetSpellID()); safe_delete(spell->spell); } + for (int32 id : spell->GetTargets()) { + Spawn* target = spell->zone->GetSpawnByID(id); + if (!target || !target->IsEntity()) + continue; + + spell->RemoveTarget(target->GetID()); + lua_interface->RemoveSpawnFromSpell(spell, target); + } lua_interface->SetLuaUserDataStale(spell); @@ -3069,34 +3108,279 @@ void SpellProcess::AddSelfAndPetToCharTargets(LuaSpell* spell, Spawn* caster, bo int32 charID = player->GetCharacterID(); if(player->HasPet() && player->GetPet()) - spell->char_id_targets.insert(make_pair(charID, player->GetPet()->GetPetType())); + spell->AddCharIDTarget(charID, player->GetPet()->GetPetType()); if(player->HasPet() && player->GetCharmedPet()) - spell->char_id_targets.insert(make_pair(charID, player->GetPet()->GetPetType())); + spell->AddCharIDTarget(charID, player->GetPet()->GetPetType()); if(!onlyPet) - spell->char_id_targets.insert(make_pair(charID, 0x00)); + spell->AddCharIDTarget(charID, 0x00); } -void SpellProcess::DeleteActiveSpell(LuaSpell* spell, bool skipRemoveCurrent) { - if(!skipRemoveCurrent) +void SpellProcess::DeleteActiveSpell(LuaSpell* spell, bool skipRemoveCurrent) { + lua_interface->SetLuaUserDataStale(spell); + if(spell->zone) { + for (int32 id : spell->GetTargets()) { + Spawn* target = spell->zone->GetSpawnByID(id); + if(target) + lua_interface->RemoveSpawnFromSpell(spell, target); + } + } + if (spell->caster) { + ((Entity*)spell->caster)->RemoveMaintainedSpell(spell); + } + if(!skipRemoveCurrent) { lua_interface->RemoveCurrentSpell(spell->state, spell, true); + lua_interface->DeletePendingSpell(spell); + } active_spells.Remove(spell, true, 10000); } bool SpellProcess::AddLuaSpellTarget(LuaSpell* lua_spell, int32 id, bool lock_spell_targets) { bool ret = false; - if(lock_spell_targets) - lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__); - if(std::find(lua_spell->targets.begin(), lua_spell->targets.end(), id) != lua_spell->targets.end()) { + if(lua_spell->HasTarget(id)) { ret = true; } - else if(std::find(lua_spell->removed_targets.begin(), lua_spell->removed_targets.end(), id) == lua_spell->removed_targets.end()) { - lua_spell->targets.push_back(id); + else if(!lua_spell->HasTarget(id)) { + lua_spell->AddTarget(id); ret = true; } - - if(lock_spell_targets) - lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__); - + + if(lua_spell->zone) { + Spawn* target = lua_spell->zone->GetSpawnByID(id); + if(target && target->IsPlayer()){ + lua_spell->AddCharIDTarget(((Player*)target)->GetCharacterID(), 0); + if (((Entity*)target)->HasPet()){ + Entity* pet = ((Entity*)target)->GetPet(); + if (pet) + lua_spell->AddCharIDTarget(((Player*)target)->GetCharacterID(), PET_TYPE_COMBAT); + pet = ((Entity*)target)->GetCharmedPet(); + if (pet) + lua_spell->AddCharIDTarget(((Player*)target)->GetCharacterID(), PET_TYPE_CHARMED); + } + } + } return ret; +} + +void SpellProcess::AddNPCGroupOrSelfTarget(LuaSpell* luaspell, Spawn* target) { + if(!target->IsNPC()) + return; + + int8 target_type = luaspell->spell->GetSpellData()->target_type; + Spawn* caster = luaspell->caster; + vector* group = ((NPC*)target)->GetSpawnGroup(); + if (target_type == SPELL_TARGET_OTHER_GROUP_AE && group) + { + vector::iterator itr; + + for (itr = group->begin(); itr != group->end(); itr++) + { + Spawn* group_member = *itr; + + if (group_member->IsNPC() && group_member->Alive() && ((Entity*)caster)->AttackAllowed(((Entity*)group_member))){ + AddLuaSpellTarget(luaspell, group_member->GetID(), false); + if (((Entity*)group_member)->HasPet()){ + Entity* pet = ((Entity*)group_member)->GetPet(); + if (pet) + AddLuaSpellTarget(luaspell, pet->GetID(), false); + pet = ((Entity*)group_member)->GetCharmedPet(); + if (pet) + AddLuaSpellTarget(luaspell, pet->GetID(), false); + } + } + } + } + else + AddLuaSpellTarget(luaspell, target->GetID()); + + safe_delete(group); +} + +void SpellProcess::ReplaceEffectTokens(std::string& message, Spawn* in_caster, Spawn* in_target) { + std::string caster = in_caster ? std::string(in_caster->GetName()) : "Unknown"; + std::string target = in_target ? std::string(in_target->GetName()) : "Unknown"; + size_t pos; + + if(message.find("%", 0) == std::string::npos) + return; + + // Replace all %C and suppress send + pos = 0; + while ((pos = message.find("%C", pos)) != std::string::npos) { + message.replace(pos, 2, caster); + pos += caster.length(); + } + + // Replace all %c (no suppression) + pos = 0; + while ((pos = message.find("%c", pos)) != std::string::npos) { + message.replace(pos, 2, caster); + pos += caster.length(); + } + + // Replace all %T and suppress send + pos = 0; + while ((pos = message.find("%T", pos)) != std::string::npos) { + message.replace(pos, 2, target); + pos += target.length(); + } + + // Replace all %t (no suppression) + pos = 0; + while ((pos = message.find("%t", pos)) != std::string::npos) { + message.replace(pos, 2, target); + pos += target.length(); + } + + std::string target_assist = in_target && in_target->GetTarget() ? std::string(in_target->GetTarget()->GetName()) : "Unknown"; + + if(target_assist == "Unknown" && target != "Unknown") + target_assist = target; + // Replace all %T and suppress send + pos = 0; + while ((pos = message.find("%A", pos)) != std::string::npos) { + message.replace(pos, 2, target_assist); + pos += target_assist.length(); + } + + // Replace all %t (no suppression) + pos = 0; + while ((pos = message.find("%a", pos)) != std::string::npos) { + message.replace(pos, 2, target_assist); + pos += target_assist.length(); + } + + std::string gender_name = "Unknown"; + std::string gender_oname = "It"; + std::string gender_sname = "It"; + std::string gender_pname = "Its"; + + if(in_target) { + if(in_target->GetGender() == 1) { + gender_name = "Male"; + gender_oname = "Him"; + gender_sname = "He"; + gender_pname = "His"; + } + else if(in_target->GetGender() == 0) { + gender_name = "Female"; + gender_oname = "Her"; + gender_sname = "She"; + gender_pname = "Her"; + } + } + + // Replace all %T and suppress send + pos = 0; + while ((pos = message.find("%G", pos)) != std::string::npos) { + message.replace(pos, 2, gender_name); + pos += gender_name.length(); + } + + // Replace all %t (no suppression) + pos = 0; + while ((pos = message.find("%g", pos)) != std::string::npos) { + message.replace(pos, 2, gender_name); + pos += gender_name.length(); + } + + pos = 0; + while ((pos = message.find("%O", pos)) != std::string::npos) { + message.replace(pos, 2, gender_oname); + pos += gender_oname.length(); + } + + // Replace all %t (no suppression) + pos = 0; + while ((pos = message.find("%o", pos)) != std::string::npos) { + message.replace(pos, 2, gender_oname); + pos += gender_oname.length(); + } + + + pos = 0; + while ((pos = message.find("%S", pos)) != std::string::npos) { + message.replace(pos, 2, gender_sname); + pos += gender_sname.length(); + } + + // Replace all %t (no suppression) + pos = 0; + while ((pos = message.find("%s", pos)) != std::string::npos) { + message.replace(pos, 2, gender_sname); + pos += gender_sname.length(); + } + + + pos = 0; + while ((pos = message.find("%P", pos)) != std::string::npos) { + message.replace(pos, 2, gender_pname); + pos += gender_pname.length(); + } + + // Replace all %t (no suppression) + pos = 0; + while ((pos = message.find("%p", pos)) != std::string::npos) { + message.replace(pos, 2, gender_pname); + pos += gender_pname.length(); + } + + std::string raceName = "Unknown"; + if(in_target) { + const char* raceCaseName = races.GetRaceNameCase(in_target->GetRace()); + if(raceCaseName) + raceName = std::string(raceCaseName); + } + + + std::string tell_name = "Unknown"; + if(in_caster && in_caster->IsPlayer()) { + if(((Player*)in_caster)->GetClient() && ((Player*)in_caster)->GetClient()->GetLastTellName().size() > 0) + tell_name = ((Player*)in_caster)->GetClient()->GetLastTellName(); + } + pos = 0; + while ((pos = message.find("%RT", pos)) != std::string::npos) { + message.replace(pos, 3, tell_name); + pos += tell_name.length(); + } + // Replace all %t (no suppression) + pos = 0; + while ((pos = message.find("%rt", pos)) != std::string::npos) { + message.replace(pos, 3, tell_name); + pos += tell_name.length(); + } + + pos = 0; + while ((pos = message.find("%R", pos)) != std::string::npos) { + message.replace(pos, 2, raceName); + pos += raceName.length(); + } + + // Replace all %t (no suppression) + pos = 0; + while ((pos = message.find("%r", pos)) != std::string::npos) { + message.replace(pos, 2, raceName); + pos += raceName.length(); + } + + + + std::string petName = "Unknown"; + if(in_caster && in_caster->IsEntity() && ((Entity*)in_caster)->GetPet()) { + petName = std::string(((Entity*)in_caster)->GetPet()->GetName()); + } + + + pos = 0; + while ((pos = message.find("%M", pos)) != std::string::npos) { + message.replace(pos, 2, petName); + pos += petName.length(); + } + + // Replace all %t (no suppression) + pos = 0; + while ((pos = message.find("%m", pos)) != std::string::npos) { + message.replace(pos, 2, petName); + pos += petName.length(); + } } \ No newline at end of file diff --git a/old/WorldServer/SpellProcess.h b/old/WorldServer/SpellProcess.h index b421a06..2afb327 100644 --- a/old/WorldServer/SpellProcess.h +++ b/old/WorldServer/SpellProcess.h @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #ifndef __EQ2_SPELL_PROCESS__ #define __EQ2_SPELL_PROCESS__ #include @@ -182,7 +183,8 @@ public: /// ??? not currently used /// Is this a harvest spell? void ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster, Spawn* target = 0, bool lock = true, bool harvest_spell = false, LuaSpell* customSpell = 0, int16 custom_cast_time = 0, bool in_heroic_opp = false); - + void PrintTargets(LuaSpell* spell, std::string stage); + /// Cast an EntityCommand (right click menu) /// The current ZoneServer /// the EntityCommand to cast @@ -400,7 +402,10 @@ public: static void AddSelfAndPetToCharTargets(LuaSpell* spell, Spawn* caster, bool onlyPet=false); void DeleteActiveSpell(LuaSpell* spell, bool skipRemoveCurrent = false); static bool AddLuaSpellTarget(LuaSpell* lua_spell, int32 id, bool lock_spell_targets = true); + static void AddNPCGroupOrSelfTarget(LuaSpell* luaspell, Spawn* target); mutable std::shared_mutex MSpellProcess; + + static void ReplaceEffectTokens(std::string& message, Spawn* in_caster, Spawn* in_target); private: MutexMap spell_que; MutexList active_spells; diff --git a/old/WorldServer/Spells.cpp b/old/WorldServer/Spells.cpp index f7bc1e5..3127f0c 100644 --- a/old/WorldServer/Spells.cpp +++ b/old/WorldServer/Spells.cpp @@ -18,9 +18,9 @@ along with EQ2Emulator. If not, see . */ #include "Spells.h" -#include "../common/config_reader.hpp" -#include "WorldDatabase.hpp" -#include "../common/log.hpp" +#include "../common/ConfigReader.h" +#include "WorldDatabase.h" +#include "../common/Log.h" #include "Traits/Traits.h" #include "AltAdvancement/AltAdvancement.h" #include @@ -222,7 +222,7 @@ void Spell::AddSpellLuaData(int8 type, int int_value, int int_value2, float floa data->string_value = string_value; data->string_value2 = string_value2; data->string_helper = helper; - + data->needs_db_save = false; lua_data.push_back(data); } @@ -237,6 +237,7 @@ void Spell::AddSpellLuaDataInt(int value, int value2, string helper) { data->float_value2 = 0; data->bool_value = false; data->string_helper = helper; + data->needs_db_save = false; lua_data.push_back(data); } @@ -252,6 +253,7 @@ void Spell::AddSpellLuaDataFloat(float value, float value2, string helper) { data->float_value2 = value2; data->bool_value = false; data->string_helper = helper; + data->needs_db_save = false; lua_data.push_back(data); } @@ -265,6 +267,7 @@ void Spell::AddSpellLuaDataBool(bool value, string helper) { data->float_value = 0; data->bool_value = value; data->string_helper = helper; + data->needs_db_save = false; lua_data.push_back(data); } @@ -282,6 +285,7 @@ void Spell::AddSpellLuaDataString(string value, string value2,string helper) { data->string_value = value; data->string_value2 = value2; data->string_helper = helper; + data->needs_db_save = false; lua_data.push_back(data); } @@ -1154,6 +1158,7 @@ void Spell::AddSpellEffect(int8 percentage, int8 subbullet, string description){ effect->description = description; effect->subbullet = subbullet; effect->percentage = percentage; + effect->needs_db_save = false; effects.push_back(effect); } diff --git a/old/WorldServer/Spells.h b/old/WorldServer/Spells.h index c164810..73fcdcc 100644 --- a/old/WorldServer/Spells.h +++ b/old/WorldServer/Spells.h @@ -23,12 +23,12 @@ #include #include #include -#include "../common/types.hpp" -#include "../common/packet/eq_packet.hpp" -#include "../common/misc_functions.hpp" +#include "../common/types.h" +#include "../common/EQPacket.h" +#include "../common/MiscFunctions.h" #include "client.h" #include "classes.h" - +#include "../common/Mutex.h" #include "AltAdvancement/AltAdvancement.h" #include @@ -205,6 +205,7 @@ struct LUAData{ sint32 int_value2; float float_value2; string string_helper; + bool needs_db_save; }; struct SpellScriptTimer { LuaSpell* spell; @@ -224,6 +225,7 @@ struct SpellDisplayEffect{ int8 percentage; int8 subbullet; string description; + bool needs_db_save; }; enum GivenByType { @@ -352,6 +354,12 @@ public: int16 GetSavageryRequired(Spawn* spawn); int16 GetDissonanceRequired(Spawn* spawn); SpellData* GetSpellData(); + SpellDisplayEffect* GetSpellDisplayEffectSafe(int index) const { + if (index < 0 || index >= effects.size()) + return nullptr; + return effects[index]; + } + bool GetSpellData(lua_State* state, std::string field); bool SetSpellData(lua_State* state, std::string field, int8 fieldArg); bool ScribeAllowed(Player* player); diff --git a/old/WorldServer/Titles.cpp b/old/WorldServer/Titles.cpp index 64de0f1..8c0e39c 100644 --- a/old/WorldServer/Titles.cpp +++ b/old/WorldServer/Titles.cpp @@ -21,7 +21,7 @@ #include #include #include "Titles.h" -#include "../common/misc_functions.hpp" +#include "../common/MiscFunctions.h" Title::Title(){ id = 0; diff --git a/old/WorldServer/Titles.h b/old/WorldServer/Titles.h index 360aa4e..78e6f80 100644 --- a/old/WorldServer/Titles.h +++ b/old/WorldServer/Titles.h @@ -23,8 +23,8 @@ #include #include #include - -#include "../common/types.hpp" +#include "../common/Mutex.h" +#include "../common/types.h" using namespace std; diff --git a/old/WorldServer/Trade.cpp b/old/WorldServer/Trade.cpp index fff61bf..94b622f 100644 --- a/old/WorldServer/Trade.cpp +++ b/old/WorldServer/Trade.cpp @@ -2,7 +2,7 @@ #include "Items/Items.h" #include "Entity.h" #include "Bots/Bot.h" -#include "../common/log.hpp" +#include "../common/Log.h" #include "Rules/Rules.h" extern ConfigReader configReader; @@ -486,8 +486,6 @@ void Trade::SendTradePacket() { packet->setDataByName("their_gold", gold); packet->setDataByName("their_plat", plat); - LogWrite(PLAYER__ERROR, 0, "Trade", "packet sent"); - packet->PrintPacket(); client->QueuePacket(packet->serialize()); safe_delete(packet); } @@ -557,8 +555,6 @@ void Trade::SendTradePacket() { packet2->setDataByName("their_gold", gold); packet2->setDataByName("their_plat", plat); - LogWrite(PLAYER__ERROR, 0, "Trade", "packet sent #2"); - packet2->PrintPacket(); client->QueuePacket(packet2->serialize()); safe_delete(packet2); } diff --git a/old/WorldServer/Trade.h b/old/WorldServer/Trade.h index eb1585a..2292a4f 100644 --- a/old/WorldServer/Trade.h +++ b/old/WorldServer/Trade.h @@ -1,6 +1,6 @@ #pragma once -#include "../common/types.hpp" +#include "../common/types.h" #include class Item; diff --git a/old/WorldServer/Tradeskills/Tradeskills.cpp b/old/WorldServer/Tradeskills/Tradeskills.cpp index ec1b804..5b9636c 100644 --- a/old/WorldServer/Tradeskills/Tradeskills.cpp +++ b/old/WorldServer/Tradeskills/Tradeskills.cpp @@ -1,6 +1,6 @@ -/* +/* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,14 +17,15 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #include #include #include "Tradeskills.h" #include "../client.h" -#include "../../common/config_reader.hpp" +#include "../../common/ConfigReader.h" #include "../classes.h" -//#include "../../common/debug.hpp" +//#include "../../common/debug.h" #include "../../common/Log.h" //#include "../zoneserver.h" //#include "../Skills.h" @@ -32,7 +33,7 @@ #include "../World.h" //#include "../LuaInterface.h" #include "../ClientPacketFunctions.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "../Rules/Rules.h" extern Classes classes; @@ -253,6 +254,7 @@ void TradeskillMgr::BeginCrafting(Client* client, vector> comp // TODO: use the vecotr to lock inventory slots vector>::iterator itr; + vector>::iterator itr2; bool missingItem = false; int32 itemid = 0; vector tmpItems; @@ -266,7 +268,16 @@ void TradeskillMgr::BeginCrafting(Client* client, vector> comp break; } - item->details.item_locked = true; + if(!item->TryLockItem(LockReason::LockReason_Crafting)) { + for (itr = components.begin(); itr != components.end(); itr++) { + itemid = itr->first; + Item* item = client->GetPlayer()->item_list.GetItemFromUniqueID(itemid); + if(item) + item->TryUnlockItem(LockReason::LockReason_Crafting); + } + missingItem = true; + break; + } tmpItems.push_back(item); } @@ -280,7 +291,8 @@ void TradeskillMgr::BeginCrafting(Client* client, vector> comp vector::iterator itemitr; for (itemitr = tmpItems.begin(); itemitr != tmpItems.end(); itemitr++) { Item* tmpItem = *itemitr; - tmpItem->details.item_locked = false; + if(tmpItem) + tmpItem->TryUnlockItem(LockReason::LockReason_Crafting); } ClientPacketFunctions::StopCrafting(client); return; @@ -358,13 +370,13 @@ void TradeskillMgr::StopCrafting(Client* client, bool lock) { item = client->GetPlayer()->item_list.GetItemFromUniqueID(itmid); if (item && item->details.count <= qty) { - item->details.item_locked = false; + item->TryUnlockItem(LockReason::LockReason_Crafting); client->GetPlayer()->item_list.RemoveItem(item); updateInvReq = true; } else if(item) { item->details.count -= qty; - item->details.item_locked = false; + item->TryUnlockItem(LockReason::LockReason_Crafting); item->save_needed = true; updateInvReq = true; } diff --git a/old/WorldServer/Tradeskills/Tradeskills.h b/old/WorldServer/Tradeskills/Tradeskills.h index f5f06e4..882c331 100644 --- a/old/WorldServer/Tradeskills/Tradeskills.h +++ b/old/WorldServer/Tradeskills/Tradeskills.h @@ -20,8 +20,8 @@ #ifndef __EQ2_TRADESKILLS__ #define __EQ2_TRADESKILLS__ -#include "../../common/types.hpp" - +#include "../../common/types.h" +#include "../../common/Mutex.h" #include "../Items/Items.h" #include class Player; diff --git a/old/WorldServer/Tradeskills/TradeskillsDB.cpp b/old/WorldServer/Tradeskills/TradeskillsDB.cpp index 621bf5a..f5c3e7f 100644 --- a/old/WorldServer/Tradeskills/TradeskillsDB.cpp +++ b/old/WorldServer/Tradeskills/TradeskillsDB.cpp @@ -24,7 +24,7 @@ #include #include #include "../../common/Log.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "Tradeskills.h" extern MasterTradeskillEventsList master_tradeskillevent_list; diff --git a/old/WorldServer/Tradeskills/TradeskillsPackets.cpp b/old/WorldServer/Tradeskills/TradeskillsPackets.cpp index 2f44352..ec1d98c 100644 --- a/old/WorldServer/Tradeskills/TradeskillsPackets.cpp +++ b/old/WorldServer/Tradeskills/TradeskillsPackets.cpp @@ -21,12 +21,12 @@ #include #include "../ClientPacketFunctions.h" #include "../client.h" -#include "../../common/config_reader.hpp" +#include "../../common/ConfigReader.h" #include "../../common/PacketStruct.h" #include "../Recipes/Recipe.h" #include "../../common/Log.h" #include "../Spells.h" -#include "../../common/misc_functions.hpp" +#include "../../common/MiscFunctions.h" #include "../World.h" extern ConfigReader configReader; @@ -417,7 +417,6 @@ void ClientPacketFunctions::SendCreateFromRecipe(Client* client, int32 recipeID) packet->setDataByName("recipe_id", recipeID); - packet->PrintPacket(); EQ2Packet* outapp = packet->serialize(); //DumpPacket(outapp); // Send the packet @@ -586,7 +585,6 @@ void ClientPacketFunctions::SendItemCreationUI(Client* client, Recipe* recipe) { } - packet->PrintPacket(); EQ2Packet* outapp = packet->serialize(); //DumpPacket(outapp); client->QueuePacket(outapp); diff --git a/old/WorldServer/Traits/Traits.cpp b/old/WorldServer/Traits/Traits.cpp index 12c19dd..46840a9 100644 --- a/old/WorldServer/Traits/Traits.cpp +++ b/old/WorldServer/Traits/Traits.cpp @@ -19,10 +19,10 @@ along with EQ2Emulator. If not, see . */ #include "Traits.h" -#include "../../common/config_reader.hpp" +#include "../../common/ConfigReader.h" #include "../../common/Log.h" #include "../Spells.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "../client.h" #include "../Rules/Rules.h" #include diff --git a/old/WorldServer/Traits/Traits.h b/old/WorldServer/Traits/Traits.h index f56f110..678a59c 100644 --- a/old/WorldServer/Traits/Traits.h +++ b/old/WorldServer/Traits/Traits.h @@ -23,9 +23,9 @@ along with EQ2Emulator. If not, see . #include #include - -#include "../../common/types.hpp" -#include "../../common/packet/eq_packet.hpp" +#include "../../common/Mutex.h" +#include "../../common/types.h" +#include "../../common/EQPacket.h" class Client; diff --git a/old/WorldServer/Transmute.cpp b/old/WorldServer/Transmute.cpp index 76cb12f..f6a05f0 100644 --- a/old/WorldServer/Transmute.cpp +++ b/old/WorldServer/Transmute.cpp @@ -1,5 +1,5 @@ #include "Transmute.h" -#include "../common/misc_functions.hpp" +#include "../common/MiscFunctions.h" #include "../common/PacketStruct.h" #include "client.h" #include "Items/Items.h" @@ -7,8 +7,8 @@ #include #include "zoneserver.h" #include "SpellProcess.h" -#include "../common/log.hpp" -#include "WorldDatabase.hpp" +#include "../common/Log.h" +#include "WorldDatabase.h" extern ConfigReader configReader; extern MasterSpellList master_spell_list; diff --git a/old/WorldServer/Transmute.h b/old/WorldServer/Transmute.h index 3045035..bd87aec 100644 --- a/old/WorldServer/Transmute.h +++ b/old/WorldServer/Transmute.h @@ -1,7 +1,7 @@ #ifndef TRANSMUTE_H #define TRANSMUTE_H -#include "../common/types.hpp" +#include "../common/types.h" #include class Client; diff --git a/old/WorldServer/VisualStates.h b/old/WorldServer/VisualStates.h index d35c52d..b4e4094 100644 --- a/old/WorldServer/VisualStates.h +++ b/old/WorldServer/VisualStates.h @@ -17,8 +17,8 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ -#include "../common/log.hpp" -#include "../common/misc_functions.hpp" +#include "../common/Log.h" +#include "../common/MiscFunctions.h" #include using namespace std; diff --git a/old/WorldServer/Web/HTTPSClient.cpp b/old/WorldServer/Web/HTTPSClient.cpp index b68d6d4..bdb594e 100644 --- a/old/WorldServer/Web/HTTPSClient.cpp +++ b/old/WorldServer/Web/HTTPSClient.cpp @@ -1,21 +1,21 @@ -/* -EQ2Emu: Everquest II Server Emulator -Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com) +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) -This file is part of EQ2Emu. + This file is part of EQ2Emulator. -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. + EQ2Emulator 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. + EQ2Emulator 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 . + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . */ #include "HTTPSClient.h" @@ -86,17 +86,28 @@ std::string base64_encode(const std::string& input) { return encoded_string; } -HTTPSClient::HTTPSClient(const std::string& certFile, const std::string& keyFile) - : certFile(certFile), keyFile(keyFile) { - // SSL and TCP setup - sslCtx = createSSLContext(); - } +HTTPSClient::HTTPSClient(const std::string& certFile, + const std::string& keyFile) + : certFile(certFile) + , keyFile(keyFile) + , ioc_() + , workGuard_(boost::asio::make_work_guard(ioc_)) // ◀︎ keep run() from returning + , sslCtx(createSSLContext()) + , pool_(ioc_, *sslCtx) // pass sslCtx here +{ + // fire up the background I/O thread + runner_ = std::thread([&]{ ioc_.run(); }); +} + +HTTPSClient::~HTTPSClient() { + workGuard_.reset(); + ioc_.stop(); + runner_.join(); +} 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; @@ -127,324 +138,281 @@ std::string HTTPSClient::buildCookieHeader() const { return cookieHeader; } -std::string HTTPSClient::sendRequest(const std::string& server, const std::string& port, const std::string& target) { - try { - boost::asio::io_context ioContext; +std::string HTTPSClient::sendRequest( + const std::string& server, + const std::string& port, + const std::string& target) { + // promise/future to block until async completes + std::promise> p; + auto f = p.get_future(); - auto stream = std::make_shared>(ioContext, *sslCtx); - auto resolver = std::make_shared(ioContext); - auto results = resolver->resolve(server, port); + // call the async overload + sendRequest(server, port, target, + [&p](boost::system::error_code ec, std::string body) { + p.set_value({ec, std::move(body)}); + }); - // 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 streamdata(set_cookie_value); - std::string token; - - // Parse "Set-Cookie" field for name-value pairs - while (std::getline(streamdata, 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 {}; - } + auto [ec, body] = f.get(); + if (ec) { + LogWrite(PEERING__ERROR, 0, "Peering", + "%s: Request Error %s", __FUNCTION__, ec.message().c_str()); + return {}; + } + return body; } -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; +// async GET +void HTTPSClient::sendRequest( + const std::string& server, + const std::string& port, + const std::string& target, + std::function done) +{ + pool_.acquire(server, port, + [this, server, port, target, done](auto ps, auto ec) { + if (ec) return done(ec, ""); - // SSL and TCP setup - auto stream = std::make_shared>(ioContext, *sslCtx); - auto resolver = std::make_shared(ioContext); - auto results = resolver->resolve(server, port); + auto req = std::make_shared< + http::request>( + http::verb::get, target, 11); - // 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); + req->set(http::field::host, server); + req->set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); + req->set(http::field::connection, "keep-alive"); + if (!cookies.empty()) { + req->set(http::field::cookie, buildCookieHeader()); + } else { + auto creds = net.GetCmdUser() + ":" + net.GetCmdPassword(); + req->set(http::field::authorization, + "Basic " + base64_encode(creds)); + } - // 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())); + auto buffer = std::make_shared(); + auto res = std::make_shared< + http::response>(); + auto write_timer = std::make_shared(ioc_); + auto read_timer = std::make_shared(ioc_); + + write_timer->expires_after(std::chrono::seconds(2)); + write_timer->async_wait([ps](auto ec){ + if (!ec) { + // cancel the write if it’s still pending + ps->stream.lowest_layer().cancel(); } - - // 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) { + }); + // capture 'req' so it sticks around till write completes + http::async_write(ps->stream, *req, + [this, ps, req, buffer, res, write_timer, read_timer, server, port, done] + (boost::system::error_code ec, std::size_t) { + write_timer->cancel(); + + if (ec) { + // write failed—drop this connection entirely + ps->stream.lowest_layer().close(); + return done(ec, ""); + } + + read_timer->expires_after(std::chrono::seconds(5)); + read_timer->async_wait([ps](auto 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); + // cancel the read if it’s still pending + ps->stream.lowest_layer().cancel(); } - }); + }); + + http::async_read(ps->stream, *buffer, *res, + [this, ps, buffer, res, read_timer, server, port, done] + (boost::system::error_code ec, std::size_t) { + read_timer->cancel(); + + if (ec) { + // read failed or timed out—drop it + ps->stream.lowest_layer().close(); + return done(ec, ""); + } + + pool_.release(server, port, ps); - 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; + auto status = res->result(); + if (status == http::status::unauthorized) { + cookies.clear(); // clear out any bad cookies + return done({}, + "Unauthorized"); } - - // 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 (status != http::status::ok) { + LogWrite(PEERING__ERROR, 0, "Peering", + "%s: HTTP error %u", __FUNCTION__, status); + return done( + boost::system::error_code( + static_cast(status), + boost::asio::error::get_ssl_category() + ), + ""); } + // cookie logic + if (res->base().count(http::field::set_cookie)) { + auto hdr = res->base()[http::field::set_cookie] + .to_string(); + std::istringstream ss(hdr); + std::string token; + while (std::getline(ss, token, ';')) { + auto pos = token.find('='); + if (pos!=std::string::npos) { + cookies[token.substr(0,pos)] = + token.substr(pos+1); + } + } + } + if (res->body() == "Unauthorized") + cookies.clear(); + + done({}, res->body()); + }); + }); + }); +} + + +std::string HTTPSClient::sendPostRequest( + const std::string& server, + const std::string& port, + const std::string& target, + const std::string& jsonPayload) { + std::promise> p; + auto f = p.get_future(); + + // call the async version internally + sendPostRequest(server, port, target, jsonPayload, + [&p](boost::system::error_code ec, std::string body) { + p.set_value({ec, std::move(body)}); + }); + + auto [ec, body] = f.get(); + if (ec) { + LogWrite(PEERING__ERROR, 0, "Peering", + "%s: error %s", __FUNCTION__, ec.message().c_str()); + return {}; + } + return body; +} + +// async POST +void HTTPSClient::sendPostRequest( + const std::string& server, + const std::string& port, + const std::string& target, + const std::string& jsonPayload, + std::function done) +{ + pool_.acquire(server, port, + [this, server, port, target, jsonPayload, done](auto ps, auto ec) { + if (ec) return done(ec, ""); + + // — heap-allocated POST req — + auto req = std::make_shared< + http::request>( + http::verb::post, target, 11); + + req->set(http::field::host, server); + req->set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); + req->set(http::field::connection, "keep-alive"); + req->set(http::field::content_type, + "application/json"); + if (!cookies.empty()) { + req->set(http::field::cookie, + buildCookieHeader()); + } else { + auto creds = net.GetCmdUser() + ":" + + net.GetCmdPassword(); + req->set(http::field::authorization, + "Basic " + base64_encode(creds)); + } + + req->body() = jsonPayload; + req->prepare_payload(); + + auto buffer = std::make_shared< + boost::beast::flat_buffer>(); + auto res = std::make_shared< + http::response>(); + auto write_timer = std::make_shared(ioc_); + auto read_timer = std::make_shared(ioc_); + + write_timer->expires_after(std::chrono::seconds(2)); + write_timer->async_wait([ps](auto ec){ + if (!ec) { + // cancel the write if it’s still pending + ps->stream.lowest_layer().cancel(); + } + }); + // keep 'req' alive until write finishes + http::async_write(ps->stream, *req, + [this, ps, req, buffer, res, write_timer, read_timer, server, port, done] + (boost::system::error_code ec, std::size_t) { + write_timer->cancel(); + + if (ec) { + // write failed—drop this connection entirely + ps->stream.lowest_layer().close(); + return done(ec, ""); + } + + read_timer->expires_after(std::chrono::seconds(5)); + read_timer->async_wait([ps](auto ec){ + if (!ec) { + // cancel the read if it’s still pending + ps->stream.lowest_layer().cancel(); } - } + }); - if (res->body() == "Unauthorized") { - cookies.clear(); - } + http::async_read(ps->stream, *buffer, *res, + [this, ps, buffer, res, read_timer, server, port, done] + (boost::system::error_code ec, std::size_t) { + read_timer->cancel(); + + if (ec) { + // read failed or timed out—drop it + ps->stream.lowest_layer().close(); + return done(ec, ""); + } + + pool_.release(server, port, ps); - // 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 + auto status = res->result(); + if (status == http::status::unauthorized) { + cookies.clear(); // clear out any bad cookies + return done({}, + "Unauthorized"); + } + if (status != http::status::ok) { + LogWrite(PEERING__ERROR, 0, "Peering", + "%s: HTTP error %u", __FUNCTION__, status); + return done( + boost::system::error_code( + static_cast(status), + boost::asio::error::get_ssl_category() + ), + ""); + } + // cookie logic + if (res->base().count(http::field::set_cookie)) { + auto hdr = res->base()[http::field::set_cookie] + .to_string(); + std::istringstream ss(hdr); + std::string token; + while (std::getline(ss, token, ';')) { + auto pos = token.find('='); + if (pos!=std::string::npos) { + cookies[token.substr(0,pos)] = + token.substr(pos+1); + } + } + } + if (res->body() == "Unauthorized") + cookies.clear(); + + done({}, res->body()); + }); + }); + }); +} diff --git a/old/WorldServer/Web/HTTPSClient.h b/old/WorldServer/Web/HTTPSClient.h index a5b05ff..8c5bb2e 100644 --- a/old/WorldServer/Web/HTTPSClient.h +++ b/old/WorldServer/Web/HTTPSClient.h @@ -1,21 +1,21 @@ -/* -EQ2Emu: Everquest II Server Emulator -Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com) +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) -This file is part of EQ2Emu. + This file is part of EQ2Emulator. -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. + EQ2Emulator 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. + EQ2Emulator 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 . + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . */ #ifndef HTTPSCLIENT_H @@ -26,21 +26,165 @@ along with EQ2Emu. If not, see . #include #include #include +#include +#include +#include +#include // for SSL_set_tlsext_host_name namespace ssl = boost::asio::ssl; namespace asio = boost::asio; namespace beast = boost::beast; namespace http = beast::http; +struct PooledStream { + boost::asio::ssl::stream stream; + boost::asio::ip::tcp::resolver resolver; + boost::asio::steady_timer connect_timer; + boost::asio::steady_timer handshake_timer; + + PooledStream(boost::asio::io_context& ioc, + boost::asio::ssl::context& ssl_ctx) + : stream(ioc, ssl_ctx) + , resolver(ioc) + , connect_timer(ioc) + , handshake_timer(ioc) + {} + + void prepare(const std::string& server, + const std::string& port, + std::function on_ready) + { + auto endpoints = resolver.resolve(server, port); + + // — Step 1: async connect + 2s timeout + connect_timer.expires_after(std::chrono::seconds(2)); + connect_timer.async_wait([this, on_ready](auto ec){ + if (!ec) { + stream.lowest_layer().cancel(); + on_ready(boost::asio::error::timed_out); + } + }); + + boost::asio::async_connect( + stream.lowest_layer(), endpoints, + [this, server, on_ready](auto ec, auto){ + connect_timer.cancel(); + if (ec) return on_ready(ec); + + // ** SNI: must set the host name for TLS before handshake ** + if (!SSL_set_tlsext_host_name(stream.native_handle(), server.c_str())) { + // pull the OpenSSL error and report it + auto err = ::ERR_get_error(); + return on_ready( + boost::system::error_code( + static_cast(err), + boost::asio::error::get_ssl_category() + ) + ); + } + + // — Step 2: async handshake + 2s timeout + handshake_timer.expires_after(std::chrono::seconds(2)); + handshake_timer.async_wait([this](auto ec){ + if (!ec) stream.lowest_layer().cancel(); + }); + + stream.async_handshake( + boost::asio::ssl::stream_base::client, + [this, on_ready](auto ec){ + handshake_timer.cancel(); + on_ready(ec); + } + ); + } + ); + } +}; + +// --- ConnectionPool.h --------------------------------------- +class ConnectionPool { +public: + ConnectionPool(boost::asio::io_context& ioc, + boost::asio::ssl::context& ssl_ctx) + : ioc_(ioc), ssl_ctx_(ssl_ctx) {} + + // Acquire a ready PooledStream (connected + handshaken), + // or create one and run prepare(). + void acquire(const std::string& server, + const std::string& port, + std::function, boost::system::error_code)> cb) + { + std::string key = server + ":" + port; + { + std::lock_guard lk(mutex_); + auto &dq = free_[key]; + if (!dq.empty()) { + auto ps = dq.front(); + dq.pop_front(); + return cb(ps, {}); + } + } + + // no free stream → make a new one and prepare it + auto ps = std::make_shared(ioc_, ssl_ctx_); + ps->prepare(server, port, [this, server, port, ps, cb](auto ec) { + if (ec) { + cb(nullptr, ec); + } else { + cb(ps, {}); + } + }); + } + + // Return a stream to the free list + void release(const std::string& server, + const std::string& port, + std::shared_ptr ps) + { + std::string key = server + ":" + port; + // clear any leftover data in the buffer + // (you might want to reset ps->buffer here if it’s stored inside) + std::lock_guard lk(mutex_); + free_[key].push_back(ps); + } + +private: + boost::asio::io_context& ioc_; + boost::asio::ssl::context& ssl_ctx_; + std::mutex mutex_; + std::unordered_map>> free_; +}; + class HTTPSClient { public: HTTPSClient(const std::string& certFile, const std::string& keyFile); + ~HTTPSClient(); + + // — Async overloads — + void sendRequest( + const std::string& server, + const std::string& port, + const std::string& target, + std::function done); - // 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); + void sendPostRequest( + const std::string& server, + const std::string& port, + const std::string& target, + const std::string& jsonPayload, + std::function done); - // 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); + // — Legacy synchronous wrappers — + std::string sendRequest( + const std::string& server, + const std::string& port, + const std::string& target); + + 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; } @@ -48,7 +192,11 @@ public: private: std::unordered_map cookies; std::shared_ptr sslCtx; - + boost::asio::io_context ioc_; + boost::asio::executor_work_guard workGuard_; + ConnectionPool pool_; + std::thread runner_; + std::string certFile; std::string keyFile; std::string server; diff --git a/old/WorldServer/Web/HTTPSClientPool.cpp b/old/WorldServer/Web/HTTPSClientPool.cpp index 4df6c00..ae9aee2 100644 --- a/old/WorldServer/Web/HTTPSClientPool.cpp +++ b/old/WorldServer/Web/HTTPSClientPool.cpp @@ -23,7 +23,7 @@ along with EQ2Emu. If not, see . #include "../net.h" #include "../World.h" #include "../LoginServer.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "../Guilds/Guild.h" #include "../../common/Log.h" #include diff --git a/old/WorldServer/Web/PeerManager.cpp b/old/WorldServer/Web/PeerManager.cpp index c0fbbdf..713094b 100644 --- a/old/WorldServer/Web/PeerManager.cpp +++ b/old/WorldServer/Web/PeerManager.cpp @@ -1,21 +1,21 @@ -/* -EQ2Emu: Everquest II Server Emulator -Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com) +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) -This file is part of EQ2Emu. + This file is part of EQ2Emulator. -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. + EQ2Emulator 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. + EQ2Emulator 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 . + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . */ #include "PeerManager.h" @@ -837,4 +837,102 @@ void PeerManager::sendPeersMessage(const std::string& endpoint, int32 command, i peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), endpoint.c_str(), jsonPayload); } +} + +void PeerManager::sendPeersActiveQuery(int32 character_id, bool remove) { + + boost::property_tree::ptree root; + + root.put("char_id", character_id); + root.put("remove", remove); + + std::ostringstream jsonStream; + boost::property_tree::write_json(jsonStream, root); + std::string jsonPayload = jsonStream.str(); + for (const auto& [id, peer] : peers) { + if (peer->healthCheck.status != HealthStatus::OK) + continue; + + peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/activequery", jsonPayload); + } +} + +void PeerManager::sendPeersAddSeller(int32 character_id, int32 house_id, std::string name, bool saleEnabled, bool invEnabled) { + boost::property_tree::ptree root; + root.put("character_id", character_id); + root.put("house_id", house_id); + root.put("name", name); + root.put("sale_enabled", saleEnabled); + root.put("inventory_enabled", invEnabled); + + std::ostringstream jsonStream; + boost::property_tree::write_json(jsonStream, root); + std::string jsonPayload = jsonStream.str(); + LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Add Seller, CharID: %u", __FUNCTION__, character_id); + + for (const auto& [id, peer] : peers) { + if (peer->healthCheck.status != HealthStatus::OK) + continue; + peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/addseller", jsonPayload); + } +} + +void PeerManager::sendPeersRemoveSeller(int32 character_id) { + boost::property_tree::ptree root; + root.put("character_id", character_id); + + std::ostringstream jsonStream; + boost::property_tree::write_json(jsonStream, root); + std::string jsonPayload = jsonStream.str(); + LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Remove Seller, CharID: %u", __FUNCTION__, character_id); + + for (const auto& [id, peer] : peers) { + if (peer->healthCheck.status != HealthStatus::OK) + continue; + peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/removeseller", jsonPayload); + } +} + +void PeerManager::sendPeersAddItemSale(int32 character_id, int32 house_id, int32 itemID, int64 uniqueID, int64 price, sint32 invSlotID, int16 slotID, int16 count, bool inInventory, bool forSale, std::string itemCreator) { + boost::property_tree::ptree root; + root.put("character_id", character_id); + root.put("house_id", house_id); + root.put("item_creator", itemCreator); + root.put("item_id", itemID); + root.put("unique_id", uniqueID); + root.put("price", price); + root.put("inv_slot_id", invSlotID); + root.put("slot_id", slotID); + root.put("count", count); + root.put("in_inventory", inInventory); + root.put("for_sale", forSale); + + std::ostringstream jsonStream; + boost::property_tree::write_json(jsonStream, root); + std::string jsonPayload = jsonStream.str(); + LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Add Item Sale, CharID: %u, UniqueID: %u, ItemID: %u", __FUNCTION__, character_id, uniqueID, itemID); + + for (const auto& [id, peer] : peers) { + if (peer->healthCheck.status != HealthStatus::OK) + continue; + peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/additemsale", jsonPayload); + } +} + + +void PeerManager::sendPeersRemoveItemSale(int32 character_id, int64 uniqueID) { + boost::property_tree::ptree root; + root.put("character_id", character_id); + root.put("unique_id", uniqueID); + + std::ostringstream jsonStream; + boost::property_tree::write_json(jsonStream, root); + std::string jsonPayload = jsonStream.str(); + LogWrite(PEERING__DEBUG, 0, "Peering", "%s: Remove Item Sale, CharID: %u, UniqueID: %u", __FUNCTION__, character_id, uniqueID); + + for (const auto& [id, peer] : peers) { + if (peer->healthCheck.status != HealthStatus::OK) + continue; + peer_https_pool.sendPostRequestToPeerAsync(peer->id, peer->webAddr, std::to_string(peer->webPort), "/removeitemsale", jsonPayload); + } } \ No newline at end of file diff --git a/old/WorldServer/Web/PeerManager.h b/old/WorldServer/Web/PeerManager.h index a276fad..0bb27ff 100644 --- a/old/WorldServer/Web/PeerManager.h +++ b/old/WorldServer/Web/PeerManager.h @@ -1,21 +1,21 @@ -/* -EQ2Emu: Everquest II Server Emulator -Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com) +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) -This file is part of EQ2Emu. + This file is part of EQ2Emulator. -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. + EQ2Emulator 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. + EQ2Emulator 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 . + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . */ #ifndef PEERMANAGER_H @@ -31,7 +31,7 @@ along with EQ2Emu. If not, see . #include #include #include -#include "../../common/types.hpp" +#include "../../common/types.h" class Client; @@ -176,10 +176,10 @@ public: 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, bool isCityZone, void* zonePtr = nullptr); - bool IsClientConnectedPeer(int32 account_id); + bool IsClientConnectedPeer(int32 account_id, int32 char_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 SendPeersChannelMessage(int32 group_id, std::string fromName, std::string message, int16 channel, int32 language_id = 0, int8 custom_type = 0); + void SendPeersGuildChannelMessage(int32 guild_id, std::string fromName, std::string message, int16 channel, int32 language_id = 0, int8 custom_type = 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, int32 matchDuplicatedId = 0); int32 getZoneHighestDuplicateId(const std::string& inc_zone_name, int32 inc_zone_id, bool increment_new_value = true); @@ -220,10 +220,17 @@ public: std::shared_ptr getCurrentPrimary(); void sendPeersMessage(const std::string& endpoint, int32 command, int32 sub_command = 0); + void sendPeersActiveQuery(int32 character_id, bool remove = false); void sendZonePlayerList(std::vector* queries, std::vector* peer_list, bool isGM); bool GetClientGuildDetails(int32 matchCharID, GuildMember* member_details); + + + void sendPeersAddSeller(int32 character_id, int32 house_id, std::string name, bool saleEnabled, bool invEnabled); + void sendPeersRemoveSeller(int32 character_id); + void sendPeersAddItemSale(int32 character_id, int32 house_id, int32 itemID, int64 uniqueID, int64 price, sint32 invSlotID, int16 slotID, int16 count, bool inInventory, bool forSale, std::string itemCreator); + void sendPeersRemoveItemSale(int32 character_id, int64 uniqueID); }; diff --git a/old/WorldServer/Web/WorldWeb.cpp b/old/WorldServer/Web/WorldWeb.cpp index 309cb98..bc38e52 100644 --- a/old/WorldServer/Web/WorldWeb.cpp +++ b/old/WorldServer/Web/WorldWeb.cpp @@ -1,29 +1,30 @@ -/* -EQ2Emu: Everquest II Server Emulator -Copyright (C) 2007-2025 EQ2Emu Development Team (https://www.eq2emu.com) +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) -This file is part of EQ2Emu. + This file is part of EQ2Emulator. -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. + EQ2Emulator 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. + EQ2Emulator 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 . + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . */ #include "HTTPSClientPool.h" #include "../World.h" -#include "../Worlddatabase.hpp" +#include "../WorldDatabase.h" #include "../LoginServer.h" #include "../LuaInterface.h" #include "../Guilds/Guild.h" +#include "../Broker/BrokerManager.h" #include #include @@ -44,6 +45,7 @@ extern MasterSpellList master_spell_list; extern MasterFactionList master_faction_list; extern ClientList client_list; extern GuildList guild_list; +extern BrokerManager broker; PeerManager peer_manager; HTTPSClientPool peer_https_pool; @@ -273,6 +275,15 @@ void World::Web_worldhandle_reloadcommand(const http::request world.RemoveReloadingSubSystem("ZoneScripts"); break; } + case COMMAND_RELOAD_PLAYERSCRIPTS: { + world.SetReloadingSubsystem("PlayerScripts"); + world.ResetPlayerScripts(); + world.LoadPlayerScripts(); + if (lua_interface) + lua_interface->DestroyPlayerScripts(); + world.RemoveReloadingSubSystem("PlayerScripts"); + break; + } case COMMAND_RELOAD_FACTIONS: { world.SetReloadingSubsystem("Factions"); master_faction_list.Clear(); @@ -368,6 +379,14 @@ void World::Web_worldhandle_reloadcommand(const http::request break; } + case COMMAND_RELOAD_ITEMS: { + database.ReloadItemList(sub_command); + + if(!sub_command) { + database.LoadMerchantInformation(); // we skip if there is only a reload of single item not all items + } + break; + } default: { success = 0; break; @@ -520,7 +539,7 @@ void World::Web_worldhandle_addcharauth(const http::request& int32 account_id = 0; int32 key = 0; - int32 zoneId, instanceId; + int32 zoneId = 0, instanceId = 0; bool firstLogin = false; std::string clientIP(""); @@ -723,6 +742,7 @@ void World::Web_worldhandle_sendglobalmessage(const http::request("to_name")) { toName = name.get(); } @@ -744,6 +764,9 @@ void World::Web_worldhandle_sendglobalmessage(const http::request("guild_id")) { guild_id = guildID.get(); } + if (auto customType = json_tree.get_optional("custom_type")) { + custom_type = customType.get(); + } Client* find_client = zone_list.GetClientByCharName(toName.c_str()); if (find_client && find_client->GetPlayer()->IsIgnored(fromName.c_str())) @@ -754,6 +777,7 @@ void World::Web_worldhandle_sendglobalmessage(const http::requestGetPlayer()) { success = 1; toName = std::string(find_client->GetPlayer()->GetName()); + find_client->SetLastTellName(fromName); find_client->HandleTellMessage(fromName.c_str(), msg.c_str(), toName.c_str(), language); if (find_client->GetPlayer()->get_character_flag(CF_AFK)) { find_client->HandleTellMessage(toName.c_str(), find_client->GetPlayer()->GetAwayMessage().c_str(), fromName.c_str(), find_client->GetPlayer()->GetCurrentLanguage()); @@ -819,6 +843,17 @@ void World::Web_worldhandle_sendglobalmessage(const http::request& r zone_list.PopulateZoneList(pt); zone_list.PopulateClientList(pt); + std::ostringstream oss; + boost::property_tree::write_json(oss, pt); + std::string json = oss.str(); + res.body() = json; + res.prepare_payload(); +} + +void World::Web_worldhandle_activequery(const http::request& req, http::response& res) { + res.set(http::field::content_type, "application/json; charset=utf-8"); + boost::property_tree::ptree pt, json_tree; + + std::istringstream json_stream(req.body()); + boost::property_tree::read_json(json_stream, json_tree); + + int32 character_id = 0; + bool remove_query = false; + if (auto char_id = json_tree.get_optional("char_id")) { + character_id = char_id.get(); + } + if (auto remove = json_tree.get_optional("remove")) { + remove_query = remove.get(); + } + + if(character_id) { + if(remove_query) { + database.RemovePeerActiveQuery(character_id); + } + else { + database.AddPeerActiveQuery(character_id); + } + } + + pt.put("success", 1); + std::ostringstream oss; + boost::property_tree::write_json(oss, pt); + std::string json = oss.str(); + res.body() = json; + res.prepare_payload(); +} + +void World::Web_worldhandle_addseller(const http::request& req, http::response& res) { + res.set(http::field::content_type, "application/json; charset=utf-8"); + boost::property_tree::ptree pt, json_tree; + + std::istringstream json_stream(req.body()); + boost::property_tree::read_json(json_stream, json_tree); + std::string playerName(""); + int32 houseID = 0, charID = 0; + bool saleEnabled = false, invEnabled = false, success = false; + if (auto character_id = json_tree.get_optional("character_id")) { + charID = character_id.get(); + } + if (auto name = json_tree.get_optional("name")) { + playerName = name.get(); + } + if (auto house_id = json_tree.get_optional("house_id")) { + houseID = house_id.get(); + } + if (auto sale_enabled = json_tree.get_optional("sale_enabled")) { + saleEnabled = sale_enabled.get(); + } + if (auto inventory_enabled = json_tree.get_optional("inventory_enabled")) { + invEnabled = inventory_enabled.get(); + } + + if(charID) { + broker.AddSeller(charID, playerName, houseID, saleEnabled, invEnabled); + success = true; + } + + pt.put("success", success); + pt.put("character_id", charID); + std::ostringstream oss; + boost::property_tree::write_json(oss, pt); + std::string json = oss.str(); + res.body() = json; + res.prepare_payload(); +} + +void World::Web_worldhandle_removeseller(const http::request& req, http::response& res) { + res.set(http::field::content_type, "application/json; charset=utf-8"); + boost::property_tree::ptree pt, json_tree; + + std::istringstream json_stream(req.body()); + boost::property_tree::read_json(json_stream, json_tree); + int32 charID = 0; + bool success = false; + if (auto character_id = json_tree.get_optional("character_id")) { + charID = character_id.get(); + } + + if(charID) { + broker.RemoveSeller(charID, true); + success = true; + } + + pt.put("success", success); + pt.put("character_id", charID); + std::ostringstream oss; + boost::property_tree::write_json(oss, pt); + std::string json = oss.str(); + res.body() = json; + res.prepare_payload(); +} + +void World::Web_worldhandle_additemsale(const http::request& req, http::response& res) { + res.set(http::field::content_type, "application/json; charset=utf-8"); + boost::property_tree::ptree pt, json_tree; + + std::istringstream json_stream(req.body()); + boost::property_tree::read_json(json_stream, json_tree); + int32 houseID = 0, charID = 0; + bool success = false; + int32 itemID = 0; + int64 uniqueID = 0, inPrice = 0; + sint32 invSlotID = 0; + int16 slotID = 0, inCount = 0; + bool inInventory = false; + bool forSale = false; + std::string itemCreator(""); + if (auto character_id = json_tree.get_optional("character_id")) { + charID = character_id.get(); + } + if (auto house_id = json_tree.get_optional("house_id")) { + houseID = house_id.get(); + } + if (auto item_id = json_tree.get_optional("item_id")) { + itemID = item_id.get(); + } + if (auto unique_id = json_tree.get_optional("unique_id")) { + uniqueID = unique_id.get(); + } + if (auto price = json_tree.get_optional("price")) { + inPrice = price.get(); + } + if (auto inv_slot_id = json_tree.get_optional("inv_slot_id")) { + invSlotID = inv_slot_id.get(); + } + if (auto slot_id = json_tree.get_optional("slot_id")) { + slotID = slot_id.get(); + } + if (auto count = json_tree.get_optional("count")) { + inCount = count.get(); + } + if (auto in_inventory = json_tree.get_optional("in_inventory")) { + inInventory = in_inventory.get(); + } + if (auto for_sale = json_tree.get_optional("for_sale")) { + forSale = for_sale.get(); + } + if (auto item_creator = json_tree.get_optional("item_creator")) { + itemCreator = item_creator.get(); + } + + + if(charID && uniqueID && itemID) { + SaleItem it{}; + it.unique_id = uniqueID; + it.character_id = charID; + it.house_id = houseID; + it.item_id = itemID; + it.cost_copper = inPrice; + it.for_sale = forSale; + it.inv_slot_id = invSlotID; + it.slot_id = slotID; + it.count = inCount; + it.from_inventory = inInventory; + it.creator = itemCreator; + broker.AddItem(it, true); + success = true; + } + + pt.put("success", success); + pt.put("character_id", charID); + std::ostringstream oss; + boost::property_tree::write_json(oss, pt); + std::string json = oss.str(); + res.body() = json; + res.prepare_payload(); +} + +void World::Web_worldhandle_removeitemsale(const http::request& req, http::response& res) { + res.set(http::field::content_type, "application/json; charset=utf-8"); + boost::property_tree::ptree pt, json_tree; + + std::istringstream json_stream(req.body()); + boost::property_tree::read_json(json_stream, json_tree); + int32 charID = 0; + int64 uniqueID = 0; + bool success = false; + if (auto character_id = json_tree.get_optional("character_id")) { + charID = character_id.get(); + } + if (auto unique_id = json_tree.get_optional("unique_id")) { + uniqueID = unique_id.get(); + } + + if(charID && uniqueID) { + broker.OnPeerRemoveItem(charID, uniqueID); + success = true; + } + + pt.put("success", success); + pt.put("character_id", charID); std::ostringstream oss; boost::property_tree::write_json(oss, pt); std::string json = oss.str(); diff --git a/old/WorldServer/Widget.cpp b/old/WorldServer/Widget.cpp index 38d0137..774c175 100644 --- a/old/WorldServer/Widget.cpp +++ b/old/WorldServer/Widget.cpp @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -22,10 +22,10 @@ #include #include "Widget.h" -#include "../common/config_reader.hpp" +#include "../common/ConfigReader.h" #include "Spells.h" #include "World.h" -#include "../common/log.hpp" +#include "../common/Log.h" #include "ClientPacketFunctions.h" #include "LuaInterface.h" @@ -167,6 +167,8 @@ Widget* Widget::Copy(){ new_spawn->SetOpenZ(GetOpenZ()); new_spawn->SetMultiFloorLift(multi_floor_lift); new_spawn->SetSoundsDisabled(IsSoundsDisabled()); + if(GetSpawnScriptSetDB() && GetSpawnScript()) + new_spawn->SetSpawnScript(std::string(GetSpawnScript())); return new_spawn; } @@ -417,6 +419,7 @@ void Widget::HandleUse(Client* client, string command, int8 overrideWidgetType){ ClientPacketFunctions::SendHouseVisitWindow(client, world.GetAllPlayerHousesByHouseID(hz->id)); ClientPacketFunctions::SendBaseHouseWindow(client, hz, ph, id); + client->GetPlayer()->SetTarget(this); } } } @@ -445,6 +448,7 @@ void Widget::HandleUse(Client* client, string command, int8 overrideWidgetType){ ClientPacketFunctions::SendBaseHouseWindow(client, hz, ph, id); client->GetCurrentZone()->SendHouseItems(client); + client->GetPlayer()->SetTarget(this); } else { if (hz) diff --git a/old/WorldServer/World.cpp b/old/WorldServer/World.cpp index 24889f0..5c9909a 100644 --- a/old/WorldServer/World.cpp +++ b/old/WorldServer/World.cpp @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2005 - 2025 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,6 +17,12 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + +#include +#include +#include +#include +#include #include #include "World.h" #include "Items/Items.h" @@ -24,8 +30,8 @@ #include "Items/Items_DoV.h" #include "Spells.h" #include "client.h" -#include "WorldDatabase.hpp" -#include "../common/debug.hpp" +#include "WorldDatabase.h" +#include "../common/debug.h" #include "races.h" #include "classes.h" #include "VisualStates.h" @@ -39,15 +45,15 @@ #include "Achievements/Achievements.h" #include "Recipes/Recipe.h" #include "Rules/Rules.h" -#include "../common/log.hpp" +#include "../common/Log.h" #include "Traits/Traits.h" #include "Chat/Chat.h" #include "Tradeskills/Tradeskills.h" #include "AltAdvancement/AltAdvancement.h" -#include "LuaInterface.h" #include "HeroicOp/HeroicOp.h" #include "RaceTypes/RaceTypes.h" #include "LuaInterface.h" +#include "SpellProcess.h" #include "../common/version.h" #include "Player.h" @@ -55,10 +61,6 @@ #include "Web/PeerManager.h" #include "Web/HTTPSClientPool.h" -#include -#include -#include - MasterQuestList master_quest_list; MasterItemList master_item_list; MasterSpellList master_spell_list; @@ -90,7 +92,6 @@ map EQOpcodeVersions; WorldDatabase database; GuildList guild_list; Chat chat; -Player player; extern ConfigReader configReader; extern LoginServer loginserver; @@ -103,6 +104,8 @@ extern sint32 numclients; extern PeerManager peer_manager; extern HTTPSClientPool peer_https_pool; +namespace fs = std::filesystem; + World::World() : save_time_timer(300000), time_tick_timer(3000), vitality_timer(3600000), player_stats_timer(60000), server_stats_timer(60000), /*remove_grouped_player(30000),*/ guilds_timer(60000), lotto_players_timer(500), watchdog_timer(10000) { save_time_timer.Start(); time_tick_timer.Start(); @@ -195,7 +198,7 @@ void World::init(std::string web_ipaddr, int16 web_port, std::string cert_file, LoadVoiceOvers(); LogWrite(WORLD__DEBUG, 1, "World", "-Loading EXP Curve From DB..."); - player.InitXPTable(); + Player::InitXPTable(); LogWrite(WORLD__DEBUG, 1, "World", "-Loading EXP Curve From DB Complete!"); LogWrite(WORLD__DEBUG, 1, "World", "-Setting system parameters..."); @@ -279,6 +282,12 @@ void World::init(std::string web_ipaddr, int16 web_port, std::string cert_file, world_webserver->register_route("/setguildeventfilter", World::Web_worldhandle_setguildeventfilter); world_webserver->register_route("/peerstatus", World::Web_worldhandle_peerstatus); + world_webserver->register_route("/activequery", World::Web_worldhandle_activequery); + + world_webserver->register_route("/addseller", World::Web_worldhandle_addseller); + world_webserver->register_route("/removeseller", World::Web_worldhandle_removeseller); + world_webserver->register_route("/additemsale", World::Web_worldhandle_additemsale); + world_webserver->register_route("/removeitemsale", World::Web_worldhandle_removeitemsale); world_webserver->run(); LogWrite(INIT__INFO, 0, "Init", "World Web Server is listening on %s:%u..", web_ipaddr.c_str(), web_port); web_success = true; @@ -374,8 +383,6 @@ void World::Process(){ WritePlayerStatistics(); if (server_stats_timer.Check()) WriteServerStatistics(); - /*if(remove_grouped_player.Check()) - CheckRemoveGroupedPlayer();*/ if (group_buff_updates.Check()) GetGroupManager()->UpdateGroupBuffs(); if (guilds_timer.Check()) @@ -537,6 +544,9 @@ bool ZoneList::HandleGlobalChatMessage(Client* from, char* to, int16 channel, co LogWrite(WORLD__ERROR, 0, "World", "HandleGlobalChatMessage() called with an invalid client"); return false; } + + std::string tokenedMsg = std::string(message); + SpellProcess::ReplaceEffectTokens(tokenedMsg, from->GetPlayer(), from->GetPlayer()->GetTarget()); if(channel == CHANNEL_PRIVATE_TELL){ Client* find_client = zone_list.GetClientByCharName(to); if(find_client && find_client->GetPlayer()->IsIgnored(from->GetPlayer()->GetName())) @@ -549,7 +559,7 @@ bool ZoneList::HandleGlobalChatMessage(Client* from, char* to, int16 channel, co boost::property_tree::ptree root; root.put("from_name", from->GetPlayer()->GetName()); root.put("to_name", to); - root.put("message", message); + root.put("message", tokenedMsg.c_str()); root.put("from_language", current_language_id); root.put("channel", channel); std::ostringstream jsonStream; @@ -572,8 +582,9 @@ bool ZoneList::HandleGlobalChatMessage(Client* from, char* to, int16 channel, co else { const char* whoto = find_client->GetPlayer()->GetName(); - find_client->HandleTellMessage(from->GetPlayer()->GetName(), message, whoto, from->GetPlayer()->GetCurrentLanguage()); - from->HandleTellMessage(from->GetPlayer()->GetName(), message, whoto, from->GetPlayer()->GetCurrentLanguage()); + find_client->SetLastTellName(std::string(from->GetPlayer()->GetName())); + find_client->HandleTellMessage(from->GetPlayer()->GetName(), tokenedMsg.c_str(), whoto, from->GetPlayer()->GetCurrentLanguage()); + from->HandleTellMessage(from->GetPlayer()->GetName(), tokenedMsg.c_str(), whoto, from->GetPlayer()->GetCurrentLanguage()); if (find_client->GetPlayer()->get_character_flag(CF_AFK)) { find_client->HandleTellMessage(find_client->GetPlayer()->GetName(), find_client->GetPlayer()->GetAwayMessage().c_str(),whoto, from->GetPlayer()->GetCurrentLanguage()); from->HandleTellMessage(find_client->GetPlayer()->GetName(), find_client->GetPlayer()->GetAwayMessage().c_str(),whoto, from->GetPlayer()->GetCurrentLanguage()); @@ -641,6 +652,11 @@ void ZoneList::DeleteSpellProcess(){ MZoneList.releasereadlock(__FUNCTION__, __LINE__); } +void ZoneList::TransmitBroadcast(const char* message) { + HandleGlobalBroadcast(message); + peer_manager.SendPeersChannelMessage(0, "", std::string(message), CHANNEL_BROADCAST, 0); +} + void ZoneList::HandleGlobalBroadcast(const char* message) { list::iterator zone_iter; ZoneServer* zone = 0; @@ -653,6 +669,11 @@ void ZoneList::HandleGlobalBroadcast(const char* message) { MZoneList.releasereadlock(__FUNCTION__, __LINE__); } +void ZoneList::TransmitGlobalAnnouncement(const char* message) { + HandleGlobalAnnouncement(message); + peer_manager.SendPeersChannelMessage(0, "", std::string(message), CHANNEL_BROADCAST, 1); +} + void ZoneList::HandleGlobalAnnouncement(const char* message) { list::iterator zone_iter; ZoneServer* zone = 0; @@ -894,7 +915,7 @@ bool ZoneList::GetZoneByInstance(ZoneChangeDetails* zone_details, int32 instance return (tmp != nullptr) ? true : false; } -bool PeerManager::IsClientConnectedPeer(int32 account_id) { +bool PeerManager::IsClientConnectedPeer(int32 account_id, int32 char_id) { for (auto& [peerId, peer] : peers) { if(peer->healthCheck.status != HealthStatus::OK) continue; @@ -903,6 +924,7 @@ bool PeerManager::IsClientConnectedPeer(int32 account_id) { for (const auto& zone : peer->client_tree->get_child("Clients")) { // Access each field within the current zone int32 client_acct_id = zone.second.get("account_id"); + int32 in_character_id = zone.second.get("character_id"); bool is_linkdead = zone.second.get("is_linkdead"); bool is_zoning = zone.second.get("is_zoning"); bool in_zone = zone.second.get("in_zone"); @@ -910,7 +932,7 @@ bool PeerManager::IsClientConnectedPeer(int32 account_id) { if(client_acct_id == account_id) { if(is_zoning) return true; - else if(is_linkdead) + else if(is_linkdead && in_character_id == char_id) return false; else if(in_zone) return true; @@ -945,12 +967,13 @@ std::string PeerManager::GetCharacterPeerId(std::string charName) { return ""; } -void PeerManager::SendPeersChannelMessage(int32 group_id, std::string fromName, std::string message, int16 channel, int32 language_id) { +void PeerManager::SendPeersChannelMessage(int32 group_id, std::string fromName, std::string message, int16 channel, int32 language_id, int8 custom_type) { boost::property_tree::ptree root; root.put("message", message); root.put("channel", channel); root.put("group_id", group_id); root.put("from_language", language_id); + root.put("custom_type", custom_type); root.put("from_name", fromName); std::ostringstream jsonStream; boost::property_tree::write_json(jsonStream, root); @@ -979,13 +1002,14 @@ void PeerManager::SendPeersChannelMessage(int32 group_id, std::string fromName, } } -void PeerManager::SendPeersGuildChannelMessage(int32 guild_id, std::string fromName, std::string message, int16 channel, int32 language_id) { +void PeerManager::SendPeersGuildChannelMessage(int32 guild_id, std::string fromName, std::string message, int16 channel, int32 language_id, int8 custom_type) { boost::property_tree::ptree root; root.put("message", message); root.put("channel", channel); root.put("guild_id", guild_id); root.put("from_language", language_id); root.put("from_name", fromName); + root.put("custom_type", custom_type); std::ostringstream jsonStream; boost::property_tree::write_json(jsonStream, root); std::string jsonPayload = jsonStream.str(); @@ -1261,7 +1285,7 @@ int32 ZoneList::GetZonesPlayersCount() { return ret; } -bool ZoneList::ClientConnected(int32 account_id){ +bool ZoneList::ClientConnected(int32 account_id, int32 char_id){ bool ret = false; map::iterator itr; MClientList.lock(); @@ -1275,7 +1299,9 @@ bool ZoneList::ClientConnected(int32 account_id){ if(client_map.size() > 0){ itr=client_map.begin(); if(itr == client_map.end()){ - if(itr->second && itr->second->GetAccountID() == account_id && (itr->second->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) == 0) + if(itr->second && itr->second->GetAccountID() == account_id && itr->second->GetCharacterID() != char_id) + ret = true; + if(itr->second && itr->second->GetAccountID() == account_id && itr->second->GetCharacterID() == char_id && ((itr->second->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) == 0)) ret = true; break; } @@ -1285,7 +1311,7 @@ bool ZoneList::ClientConnected(int32 account_id){ } } if(!ret) { - ret = peer_manager.IsClientConnectedPeer(account_id); + ret = peer_manager.IsClientConnectedPeer(account_id, char_id); } MClientList.unlock(); return ret; @@ -1794,6 +1820,38 @@ void World::AddZoneScript(int32 id, const char* name) { MZoneScripts.unlock(); } +void World::LoadPlayerScripts() { + const fs::path scriptDir = "PlayerScripts"; + bool hasGlobalLua = false; + + if (!fs::exists(scriptDir) || !fs::is_directory(scriptDir)) { + std::cerr << "Directory does not exist: " << scriptDir << std::endl; + return; + } + + for (const auto& entry : fs::directory_iterator(scriptDir)) { + if (entry.is_regular_file() && entry.path().extension() == ".lua") { + std::string baseName = entry.path().stem().string(); // Strips extension + const std::string filename = entry.path().string(); + int32 zoneID = database.GetZoneID(baseName.c_str()); + if(zoneID) { + AddPlayerScript(zoneID, filename.c_str()); + } + else if (baseName == "global") { + AddPlayerScript(0, filename.c_str()); + hasGlobalLua = true; + } + } + } +} + +void World::AddPlayerScript(int32 zone_id, const char* zone_name) { + MPlayerScripts.lock(); + if (zone_name) + player_scripts[zone_id] = string(zone_name); + MPlayerScripts.unlock(); +} + const char* World::GetSpawnScript(int32 id){ LogWrite(SPAWN__TRACE, 1, "Spawn", "Enter %s", __FUNCTION__); const char* ret = 0; @@ -1836,6 +1894,15 @@ const char* World::GetZoneScript(int32 id) { return ret; } +const char* World::GetPlayerScript(int32 zone_id) { + const char* ret = 0; + MPlayerScripts.lock(); + if (player_scripts.count(zone_id) > 0) + ret = player_scripts[zone_id].c_str(); + MPlayerScripts.unlock(); + return ret; +} + void World::ResetSpawnScripts(){ MSpawnScripts.lock(); spawn_scripts.clear(); @@ -1850,7 +1917,11 @@ void World::ResetZoneScripts() { MZoneScripts.unlock(); } - +void World::ResetPlayerScripts() { + MPlayerScripts.lock(); + player_scripts.clear(); + MPlayerScripts.unlock(); +} vector* World::GetMerchantItemList(int32 merchant_id, int8 merchant_type, Player* player) { @@ -2036,57 +2107,11 @@ void World::DeleteMerchantsInfo(){ void World::DeleteSpawns(){ - //reloading = true; - //ClearLootTables(); - /* - map::iterator npc_list_iter; - for(npc_list_iter=npc_list.begin();npc_list_iter!=npc_list.end();npc_list_iter++) { - safe_delete(npc_list_iter->second); - } - npc_list.clear(); - map::iterator object_list_iter; - for(object_list_iter=object_list.begin();object_list_iter!=object_list.end();object_list_iter++) { - safe_delete(object_list_iter->second); - } - object_list.clear(); - map::iterator groundspawn_list_iter; - for(groundspawn_list_iter=groundspawn_list.begin();groundspawn_list_iter!=groundspawn_list.end();groundspawn_list_iter++) { - safe_delete(groundspawn_list_iter->second); - } - groundspawn_list.clear(); - map::iterator widget_list_iter; - for(widget_list_iter=widget_list.begin();widget_list_iter!=widget_list.end();widget_list_iter++) { - safe_delete(widget_list_iter->second); - } - widget_list.clear(); - map::iterator sign_list_iter; - for(sign_list_iter=sign_list.begin();sign_list_iter!=sign_list.end();sign_list_iter++) { - safe_delete(sign_list_iter->second); - } - sign_list.clear();*/ map::iterator appearance_list_iter; for(appearance_list_iter=npc_appearance_list.begin();appearance_list_iter!=npc_appearance_list.end();appearance_list_iter++) { safe_delete(appearance_list_iter->second); } npc_appearance_list.clear(); - - /* - map* >::iterator command_list_iter; - for(command_list_iter=entity_command_list.begin();command_list_iter!=entity_command_list.end();command_list_iter++) { - vector* v = command_list_iter->second; - if(v){ - for(int32 i=0;isize();i++){ - safe_delete(v->at(i)); - } - safe_delete(v); - } - } - entity_command_list.clear(); - */ - - //DeleteGroundSpawnItems(); - //DeleteTransporters(); - //DeleteTransporterMaps(); } void World::ReloadGuilds() { @@ -2148,46 +2173,6 @@ void World::RemoveServerStatistics() { server_statistics.clear(); } -void World::SendGroupQuests(PlayerGroup* group, Client* client){ - return; - /*if(!group) - return; - GroupMemberInfo* info = 0; - MGroups.readlock(__FUNCTION__, __LINE__); - deque::iterator itr; - for(itr = group->members.begin(); itr != group->members.end(); itr++){ - info = *itr; - if(info->client){ - LogWrite(PLAYER__DEBUG, 0, "Player", "Send Quest Journal..."); - info->client->SendQuestJournal(false, client); - client->SendQuestJournal(false, info->client); - } - } - MGroups.releasereadlock(__FUNCTION__, __LINE__);*/ -} - -/*void World::CheckRemoveGroupedPlayer(){ - map::iterator itr; - GroupMemberInfo* found = 0; - MGroups.readlock(__FUNCTION__, __LINE__); - for(itr = group_removal_pending.begin(); itr != group_removal_pending.end(); itr++){ - if(itr->second < Timer::GetCurrentTime2()){ - found = itr->first; - break; - } - } - MGroups.releasereadlock(__FUNCTION__, __LINE__); - if(found){ - if(!found->client || (found->client && found->client->IsConnected() == false)) - DeleteGroupMember(found); - else{ - MGroups.writelock(__FUNCTION__, __LINE__); - group_removal_pending.erase(found); - MGroups.releasewritelock(__FUNCTION__, __LINE__); - } - } -}*/ - bool World::RejoinGroup(Client* client, int32 group_id){ if (!group_id) // no need if no group id! return false; @@ -2595,10 +2580,10 @@ void World::CheckLottoPlayers() { memset(announcement, 0, sizeof(announcement)); sprintf(coin_message, "%s", client->GetCoinMessage(jackpot).c_str()); sprintf(message, "Congratulations! You have won %s!", coin_message); - sprintf(announcement, "%s as won the jackpot containing a total of %s!", client->GetPlayer()->GetName(), coin_message); + sprintf(announcement, "%s has won the jackpot containing a total of %s!", client->GetPlayer()->GetName(), coin_message); client->Message(CHANNEL_COLOR_YELLOW, "You receive %s.", coin_message); client->SendPopupMessage(0, message, "", 2, 0xFF, 0xFF, 0x99); - zone_list.HandleGlobalAnnouncement(announcement); + zone_list.TransmitGlobalAnnouncement(announcement); client->GetPlayer()->AddCoins(jackpot); client->GetPlayer()->GetZone()->SendCastSpellPacket(843, client->GetPlayer()); client->GetPlayer()->GetZone()->SendCastSpellPacket(1413, client->GetPlayer()); @@ -2660,6 +2645,7 @@ void World::AddPlayerHouse(int32 char_id, int32 house_id, int64 unique_id, int32 ph->escrow_status = escrow_status; ph->upkeep_due = upkeep_due; ph->player_name = player_name; + ph->character_id = char_id; ReloadHouseData(ph); m_playerHouses[house_id][char_id] = ph; } @@ -3728,4 +3714,5 @@ void ZoneInfoMemory::LoadFromDatabaseRow(MYSQL_ROW row) { canGate = atoul(row[24]); cityZone = atoul(row[25]); canEvac = atoul(row[26]); + zoneLuaScript = (row[27] != nullptr) ? row[27] : ""; } \ No newline at end of file diff --git a/old/WorldServer/World.h b/old/WorldServer/World.h index b68f6a5..8c6e2c3 100644 --- a/old/WorldServer/World.h +++ b/old/WorldServer/World.h @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #ifndef EQ2_WORLD_H #define EQ2_WORLD_H @@ -39,7 +40,7 @@ #include "MutexList.h" #include "PlayerGroups.h" -#include "../common/web_server.hpp" +#include "../common/Web/WebServer.h" #include "./Zone/region_map.h" #include "./Zone/map.h" @@ -157,6 +158,7 @@ struct LocationTransportDestination{ int32 cost; int32 faction_id; int32 faction_value; + bool force_zone; }; struct LottoPlayer { @@ -227,6 +229,7 @@ struct PlayerHouse { int64 escrow_coins; int32 escrow_status; string player_name; + int32 character_id; list deposits; map depositsMap; list history; @@ -347,6 +350,7 @@ struct GlobalLoot { #define TRANSPORT_TYPE_ZONE 1 #define TRANSPORT_TYPE_GENERIC 2 #define TRANSPORT_TYPE_FLIGHT 3 +#define TRANSPORT_TYPE_FORCEZONE 4 // structs MUST start with class_id and race_id, in that order as int8's @@ -422,6 +426,7 @@ public: std::string zoneDescription; std::string zoneMotd; std::string zoneSkyFile; + std::string zoneLuaScript; float underworld; float safeX, safeY, safeZ, safeHeading; int16 minimumLevel, maximumLevel, minimumVersion; @@ -454,7 +459,7 @@ class ZoneList { bool GetZone(ZoneChangeDetails* zone_details, int32 opt_zone_id, std::string opt_zone_name = "", bool loadZone = true, bool skip_existing_zones = false, bool increment_zone = true, bool check_peers = true, bool check_instances = false, bool only_always_loaded = false, bool skip_self = false, bool duplicated_zone = false, int32 minLevel = 0, int32 maxLevel = 0, int32 avgLevel = 0, int32 firstLevel = 0); bool GetZoneByInstance(ZoneChangeDetails* zone_details, int32 instance_id, int32 zone_id = 0, bool loadZone = true, bool skip_existing_zones = false, bool increment_zone = true, bool check_peers = true, int32 minLevel = 0, int32 maxLevel = 0, int32 avgLevel = 0, int32 firstLevel = 0); - bool IsClientConnectedPeer(int32 account_id); + bool IsClientConnectedPeer(int32 account_id, int32 char_id); //ZoneServer* Get(int32 id, bool loadZone = true, bool skip_existing_zones = false, bool increment_zone = true); //ZoneServer* Get(const char* zone_name, bool loadZone=true, bool skip_existing_zones = false, bool increment_zone = true); //ZoneServer* GetByInstanceID(int32 id, int32 zone_id=0, bool skip_existing_zones = false, bool increment_zone = true); @@ -476,6 +481,10 @@ class ZoneList { void CheckFriendZoned(Client* client); // move to Chat/Chat.h? + void TransmitBroadcast(const char* message); + void TransmitGlobalAnnouncement(const char* message); + + // these are handled internally by the transmit and peering bool HandleGlobalChatMessage(Client* from, char* to, int16 channel, const char* message, const char* channel_name = 0, int32 current_language_id = 0); void HandleGlobalBroadcast(const char* message); void HandleGlobalAnnouncement(const char* message); @@ -527,7 +536,7 @@ class ZoneList { client_map.erase(name); MClientList.unlock(); } - bool ClientConnected(int32 account_id); + bool ClientConnected(int32 account_id, int32 char_id); void RemoveClientZoneReference(ZoneServer* zone); void ReloadClientQuests(); bool DepopFinished(); @@ -589,12 +598,16 @@ public: void AddSpawnEntryScript(int32 id, const char* name); void AddSpawnLocationScript(int32 id, const char* name); void AddZoneScript(int32 id, const char* name); + void LoadPlayerScripts(); + void AddPlayerScript(int32 zone_id, const char* zone_name); const char* GetSpawnScript(int32 id); const char* GetSpawnEntryScript(int32 id); const char* GetSpawnLocationScript(int32 id); const char* GetZoneScript(int32 id); + const char* GetPlayerScript(int32 zone_id); void ResetSpawnScripts(); void ResetZoneScripts(); + void ResetPlayerScripts(); int16 GetMerchantItemQuantity(int32 merchant_id, int32 item_id); void DecreaseMerchantQuantity(int32 merchant_id, int32 item_id, int16 amount); int32 GetInventoryID(int32 merchant_id, int32 item_id); @@ -616,24 +629,7 @@ public: sint32 GetServerStatisticValue(int32 stat_id); void RemoveServerStatistics(); - //PlayerGroup* AddGroup(Client* leader); - //void AddGroupMember(PlayerGroup* group, Client* member); - //void RemoveGroupMember(Client* member, bool immediate = false); - //void DisbandGroup(PlayerGroup* group, bool lock = true); - void SendGroupQuests(PlayerGroup* group, Client* client); - //void UpdateGroupBuffs(); - //void RemoveGroupBuffs(PlayerGroup *group, Client *client); - //void SetPendingGroup(char* name, char* leader); - //void GroupMessage(PlayerGroup* group, const char* message, ...); - //void SimpleGroupMessage(PlayerGroup* group, const char* message); - //void GroupChatMessage(PlayerGroup* group, Spawn* from, const char* message); - //const char* GetPendingGroup(string name); - //void GroupReadLock(); - //void GroupReadUnLock(); - //void CheckRemoveGroupedPlayer(); - //void SendGroupUpdate(PlayerGroup* group, Client* exclude = 0); bool RejoinGroup(Client* client, int32 group_id); - //bool MakeLeader(Client* leader, string new_leader); void AddBonuses(Item* item, ItemStatsValues* values, int16 type, float value, Entity* entity); int32 CreateGuild(const char* guild_name, Client* leader = 0, int32 group_id = 0); @@ -743,6 +739,11 @@ public: static void Web_worldhandle_setguildpermission(const http::request& req, http::response& res); static void Web_worldhandle_setguildeventfilter(const http::request& req, http::response& res); static void Web_worldhandle_peerstatus(const http::request& req, http::response& res); + static void Web_worldhandle_activequery(const http::request& req, http::response& res); + static void Web_worldhandle_addseller(const http::request& req, http::response& res); + static void Web_worldhandle_removeseller(const http::request& req, http::response& res); + static void Web_worldhandle_additemsale(const http::request& req, http::response& res); + static void Web_worldhandle_removeitemsale(const http::request& req, http::response& res); static void Web_populate_status(boost::property_tree::ptree& pt); @@ -765,6 +766,7 @@ private: Mutex MMerchantList; Mutex MSpawnScripts; Mutex MZoneScripts; + Mutex MPlayerScripts; //Mutex MGroups; mutable std::shared_mutex MNPCSpells; @@ -783,6 +785,7 @@ private: map spawnentry_scripts; map spawnlocation_scripts; map zone_scripts; + map player_scripts; //vector player_groups; //map group_removal_pending; //map pending_groups; diff --git a/old/WorldServer/WorldDatabase.cpp b/old/WorldServer/WorldDatabase.cpp index 39e1ec1..7061bc2 100644 --- a/old/WorldServer/WorldDatabase.cpp +++ b/old/WorldServer/WorldDatabase.cpp @@ -1,21 +1,21 @@ /* -EQ2Emulator: Everquest II Server Emulator -Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) -This file is part of EQ2Emulator. + This file is part of EQ2Emulator. -EQ2Emulator 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. + EQ2Emulator 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. -EQ2Emulator 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. + EQ2Emulator 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 EQ2Emulator. If not, see . + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . */ #include @@ -29,8 +29,8 @@ along with EQ2Emulator. If not, see . #include "WorldDatabase.h" -#include "../common/debug.hpp" -#include "../common/packet/packet_dump.hpp" +#include "../common/debug.h" +#include "../common/packet_dump.h" #include "../common/GlobalHeaders.h" #include "Items/Items.h" #include "Factions.h" @@ -42,7 +42,7 @@ along with EQ2Emulator. If not, see . #include "Quests.h" #include "LuaInterface.h" #include "classes.h" -#include "../common/log.hpp" +#include "../common/Log.h" #include "Rules/Rules.h" #include "Titles.h" #include "Languages.h" @@ -53,6 +53,7 @@ along with EQ2Emulator. If not, see . #include "SpellProcess.h" #include "races.h" #include "Web/PeerManager.h" +#include "Broker/BrokerManager.h" extern Classes classes; extern Commands commands; @@ -75,6 +76,7 @@ extern RuleManager rule_manager; extern MasterLanguagesList master_languages_list; extern ChestTrapList chest_trap_list; extern PeerManager peer_manager; +BrokerManager broker; //devn00b: Fix for linux builds since we dont use stricmp we use strcasecmp #if defined(__GNUC__) @@ -953,23 +955,29 @@ int32 WorldDatabase::LoadAppearances(ZoneServer* zone, Client* client){ return count; } -void WorldDatabase::LoadNPCs(ZoneServer* zone){ +void WorldDatabase::LoadNPCs(ZoneServer* zone, bool isInstanceType){ Query query; MYSQL_ROW row; NPC* npc = 0; int32 id = 0; int32 total = 0; - MYSQL_RES* result = query.RunQuery2(Q_SELECT,"SELECT npc.spawn_id, s.name, npc.min_level, npc.max_level, npc.enc_level, s.race, s.model_type, npc.class_, npc.gender, s.command_primary, s.command_secondary, s.show_name, npc.min_group_size, npc.max_group_size, npc.hair_type_id, npc.facial_hair_type_id, npc.wing_type_id, npc.chest_type_id, npc.legs_type_id, npc.soga_hair_type_id, npc.soga_facial_hair_type_id, s.attackable, s.show_level, s.targetable, s.show_command_icon, s.display_hand_icon, s.hp, s.power, s.size, s.collision_radius, npc.action_state, s.visual_state, npc.mood_state, npc.initial_state, npc.activity_status, s.faction_id, s.sub_title, s.merchant_id, s.merchant_type, s.size_offset, npc.attack_type, npc.ai_strategy+0, npc.spell_list_id, npc.secondary_spell_list_id, npc.skill_list_id, npc.secondary_skill_list_id, npc.equipment_list_id, npc.str, npc.sta, npc.wis, npc.intel, npc.agi, npc.heat, npc.cold, npc.magic, npc.mental, npc.divine, npc.disease, npc.poison, npc.aggro_radius, npc.cast_percentage, npc.randomize, npc.soga_model_type, npc.heroic_flag, npc.alignment, npc.elemental, npc.arcane, npc.noxious, s.savagery, s.dissonance, npc.hide_hood, npc.emote_state, s.prefix, s.suffix, s.last_name, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, npc.water_type, npc.flying_type, s.loot_tier, s.loot_drop_type, npc.scared_by_strong_players, npc.action_state_str\n" - "FROM spawn s\n" - "INNER JOIN spawn_npcs npc\n" + + std::string houseTable(""); + if(isInstanceType) { + houseTable.append("_houses"); + } + + MYSQL_RES* result = query.RunQuery2(Q_SELECT,"SELECT npc.spawn_id, s.name, npc.min_level, npc.max_level, npc.enc_level, s.race, s.model_type, npc.class_, npc.gender, s.command_primary, s.command_secondary, s.show_name, npc.min_group_size, npc.max_group_size, npc.hair_type_id, npc.facial_hair_type_id, npc.wing_type_id, npc.chest_type_id, npc.legs_type_id, npc.soga_hair_type_id, npc.soga_facial_hair_type_id, s.attackable, s.show_level, s.targetable, s.show_command_icon, s.display_hand_icon, s.hp, s.power, s.size, s.collision_radius, npc.action_state, s.visual_state, npc.mood_state, npc.initial_state, npc.activity_status, s.faction_id, s.sub_title, s.merchant_id, s.merchant_type, s.size_offset, npc.attack_type, npc.ai_strategy+0, npc.spell_list_id, npc.secondary_spell_list_id, npc.skill_list_id, npc.secondary_skill_list_id, npc.equipment_list_id, npc.str, npc.sta, npc.wis, npc.intel, npc.agi, npc.heat, npc.cold, npc.magic, npc.mental, npc.divine, npc.disease, npc.poison, npc.aggro_radius, npc.cast_percentage, npc.randomize, npc.soga_model_type, npc.heroic_flag, npc.alignment, npc.elemental, npc.arcane, npc.noxious, s.savagery, s.dissonance, npc.hide_hood, npc.emote_state, s.prefix, s.suffix, s.last_name, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, npc.water_type, npc.flying_type, s.loot_tier, s.loot_drop_type, npc.scared_by_strong_players, npc.action_state_str, s.lua_script\n" + "FROM spawn%s s\n" + "INNER JOIN spawn_npcs%s npc\n" "ON s.id = npc.spawn_id\n" - "INNER JOIN spawn_location_entry le\n" + "INNER JOIN spawn_location_entry%s le\n" "ON npc.spawn_id = le.spawn_id\n" - "INNER JOIN spawn_location_placement lp\n" + "INNER JOIN spawn_location_placement%s lp\n" "ON le.spawn_location_id = lp.spawn_location_id\n" "WHERE lp.zone_id = %u and (lp.instance_id = 0 or lp.instance_id = %u)\n" "GROUP BY s.id", - zone->GetZoneID(), zone->GetInstanceID()); + houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), zone->GetZoneID(), zone->GetInstanceID()); while(result && (row = mysql_fetch_row(result))){ /*npc->SetAppearanceID(atoi(row[12])); AppearanceData* appearance = world.GetNPCAppearance(npc->GetAppearanceID()); @@ -1128,6 +1136,11 @@ void WorldDatabase::LoadNPCs(ZoneServer* zone){ npc->GetInfoStruct()->set_action_state(action_state_str); } + if(row[87]){ + std::string lua_script = std::string(row[87]); + npc->SetSpawnScript(lua_script, true); + } + zone->AddNPC(id, npc); total++; LogWrite(NPC__DEBUG, 5, "NPC", "---Loading NPC: '%s' (%u)", npc->appearance.name, id); @@ -1243,23 +1256,27 @@ void WorldDatabase::LoadSpiritShards(ZoneServer* zone){ LogWrite(NPC__INFO, 0, "NPC", "--Loaded %i Spirit Shard(s).", total); } -void WorldDatabase::LoadSigns(ZoneServer* zone){ +void WorldDatabase::LoadSigns(ZoneServer* zone, bool isInstanceType){ Query query; MYSQL_ROW row; Sign* sign = 0; int32 id = 0; int32 total = 0; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT ss.spawn_id, s.name, s.model_type, s.size, s.show_command_icon, ss.widget_id, ss.widget_x, ss.widget_y, ss.widget_z, s.command_primary, s.command_secondary, s.collision_radius, ss.icon, ss.type, ss.title, ss.description, ss.sign_distance, ss.zone_id, ss.zone_x, ss.zone_y, ss.zone_z, ss.zone_heading, ss.include_heading, ss.include_location, s.transport_id, s.size_offset, s.display_hand_icon, s.visual_state, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, ss.language\n" - "FROM spawn s\n" - "INNER JOIN spawn_signs ss\n" + std::string houseTable(""); + if(isInstanceType) { + houseTable.append("_houses"); + } + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT ss.spawn_id, s.name, s.model_type, s.size, s.show_command_icon, ss.widget_id, ss.widget_x, ss.widget_y, ss.widget_z, s.command_primary, s.command_secondary, s.collision_radius, ss.icon, ss.type, ss.title, ss.description, ss.sign_distance, ss.zone_id, ss.zone_x, ss.zone_y, ss.zone_z, ss.zone_heading, ss.include_heading, ss.include_location, s.transport_id, s.size_offset, s.display_hand_icon, s.visual_state, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, ss.language, s.lua_script\n" + "FROM spawn%s s\n" + "INNER JOIN spawn_signs%s ss\n" "ON s.id = ss.spawn_id\n" - "INNER JOIN spawn_location_entry le\n" + "INNER JOIN spawn_location_entry%s le\n" "ON ss.spawn_id = le.spawn_id\n" - "INNER JOIN spawn_location_placement lp\n" + "INNER JOIN spawn_location_placement%s lp\n" "ON le.spawn_location_id = lp.spawn_location_id\n" "WHERE lp.zone_id = %u and (lp.instance_id = 0 or lp.instance_id = %u)\n" "GROUP BY s.id", - zone->GetZoneID(), zone->GetInstanceID()); + houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), zone->GetZoneID(), zone->GetInstanceID()); while(result && (row = mysql_fetch_row(result))){ int32 signXpackFlag = atoul(row[28]); @@ -1330,6 +1347,11 @@ void WorldDatabase::LoadSigns(ZoneServer* zone){ sign->SetLanguage(atoul(row[36])); + if(row[37]){ + std::string lua_script = std::string(row[37]); + sign->SetSpawnScript(lua_script, true); + } + zone->AddSign(id, sign); total++; @@ -1339,23 +1361,28 @@ void WorldDatabase::LoadSigns(ZoneServer* zone){ LogWrite(SIGN__DEBUG, 0, "Sign", "--Loaded %i Sign(s)", total); } -void WorldDatabase::LoadWidgets(ZoneServer* zone){ +void WorldDatabase::LoadWidgets(ZoneServer* zone, bool isInstanceType){ Query query; MYSQL_ROW row; Widget* widget = 0; int32 id = 0; int32 total = 0; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT sw.spawn_id, s.name, s.model_type, s.size, s.show_command_icon, sw.widget_id, sw.widget_x, sw.widget_y, sw.widget_z, s.command_primary, s.command_secondary, s.collision_radius, sw.include_heading, sw.include_location, sw.icon, sw.type, sw.open_heading, sw.open_y, sw.action_spawn_id, sw.open_sound_file, sw.close_sound_file, sw.open_duration, sw.closed_heading, sw.linked_spawn_id, sw.close_y, s.transport_id, s.size_offset, sw.house_id, sw.open_x, sw.open_z, sw.close_x, sw.close_z, s.display_hand_icon, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type\n" - "FROM spawn s\n" - "INNER JOIN spawn_widgets sw\n" + + std::string houseTable(""); + if(isInstanceType) { + houseTable.append("_houses"); + } + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT sw.spawn_id, s.name, s.model_type, s.size, s.show_command_icon, sw.widget_id, sw.widget_x, sw.widget_y, sw.widget_z, s.command_primary, s.command_secondary, s.collision_radius, sw.include_heading, sw.include_location, sw.icon, sw.type, sw.open_heading, sw.open_y, sw.action_spawn_id, sw.open_sound_file, sw.close_sound_file, sw.open_duration, sw.closed_heading, sw.linked_spawn_id, sw.close_y, s.transport_id, s.size_offset, sw.house_id, sw.open_x, sw.open_z, sw.close_x, sw.close_z, s.display_hand_icon, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, s.lua_script\n" + "FROM spawn%s s\n" + "INNER JOIN spawn_widgets%s sw\n" "ON s.id = sw.spawn_id\n" - "INNER JOIN spawn_location_entry le\n" + "INNER JOIN spawn_location_entry%s le\n" "ON sw.spawn_id = le.spawn_id\n" - "INNER JOIN spawn_location_placement lp\n" + "INNER JOIN spawn_location_placement%s lp\n" "ON le.spawn_location_id = lp.spawn_location_id\n" "WHERE lp.zone_id = %u and (lp.instance_id = 0 or lp.instance_id = %u)\n" "GROUP BY s.id", - zone->GetZoneID(), zone->GetInstanceID()); + houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), zone->GetZoneID(), zone->GetInstanceID()); while(result && (row = mysql_fetch_row(result))){ int32 widgetXpackFlag = atoul(row[33]); int32 widgetHolidayFlag = atoul(row[34]); @@ -1437,7 +1464,12 @@ void WorldDatabase::LoadWidgets(ZoneServer* zone){ widget->SetLootTier(atoul(row[39])); widget->SetLootDropType(atoul(row[40])); - + + if(row[41]){ + std::string lua_script = std::string(row[41]); + widget->SetSpawnScript(lua_script, true); + } + zone->AddWidget(id, widget); total++; @@ -1447,23 +1479,28 @@ void WorldDatabase::LoadWidgets(ZoneServer* zone){ LogWrite(WIDGET__DEBUG, 0, "Widget", "--Loaded %i Widget(s)", total); } -void WorldDatabase::LoadObjects(ZoneServer* zone){ +void WorldDatabase::LoadObjects(ZoneServer* zone, bool isInstanceType){ Query query; MYSQL_ROW row; Object* object = 0; int32 id = 0; int32 total = 0; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT so.spawn_id, s.name, s.race, s.model_type, s.command_primary, s.command_secondary, s.targetable, s.size, s.show_name, s.visual_state, s.attackable, s.show_level, s.show_command_icon, s.display_hand_icon, s.faction_id, s.collision_radius, s.transport_id, s.size_offset, so.device_id, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type\n" - "FROM spawn s\n" - "INNER JOIN spawn_objects so\n" + + std::string houseTable(""); + if(isInstanceType) { + houseTable.append("_houses"); + } + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT so.spawn_id, s.name, s.race, s.model_type, s.command_primary, s.command_secondary, s.targetable, s.size, s.show_name, s.visual_state, s.attackable, s.show_level, s.show_command_icon, s.display_hand_icon, s.faction_id, s.collision_radius, s.transport_id, s.size_offset, so.device_id, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, s.lua_script\n" + "FROM spawn%s s\n" + "INNER JOIN spawn_objects%s so\n" "ON s.id = so.spawn_id\n" - "INNER JOIN spawn_location_entry le\n" + "INNER JOIN spawn_location_entry%s le\n" "ON so.spawn_id = le.spawn_id\n" - "INNER JOIN spawn_location_placement lp\n" + "INNER JOIN spawn_location_placement%s lp\n" "ON le.spawn_location_id = lp.spawn_location_id\n" "WHERE lp.zone_id = %u and (lp.instance_id = 0 or lp.instance_id = %u)\n" - "GROUP BY s.id", - zone->GetZoneID(), zone->GetInstanceID()); + "GROUP BY s.id", + houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), zone->GetZoneID(), zone->GetInstanceID()); while(result && (row = mysql_fetch_row(result))){ @@ -1519,6 +1556,11 @@ void WorldDatabase::LoadObjects(ZoneServer* zone){ object->SetLootDropType(atoul(row[26])); + if(row[27]){ + std::string lua_script = std::string(row[27]); + object->SetSpawnScript(lua_script, true); + } + zone->AddObject(id, object); total++; @@ -1528,23 +1570,28 @@ void WorldDatabase::LoadObjects(ZoneServer* zone){ LogWrite(OBJECT__DEBUG, 0, "Object", "--Loaded %i Object(s)", total); } -void WorldDatabase::LoadGroundSpawns(ZoneServer* zone){ +void WorldDatabase::LoadGroundSpawns(ZoneServer* zone, bool isInstanceType){ Query query; MYSQL_ROW row; GroundSpawn* spawn = 0; int32 id = 0; int32 total = 0; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT sg.spawn_id, s.name, s.race, s.model_type, s.command_primary, s.command_secondary, s.targetable, s.size, s.show_name, s.visual_state, s.attackable, s.show_level, s.show_command_icon, s.display_hand_icon, s.faction_id, s.collision_radius, sg.number_harvests, sg.num_attempts_per_harvest, sg.groundspawn_id, sg.collection_skill, s.size_offset, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.aaxp_rewards, s.loot_tier, s.loot_drop_type\n" - "FROM spawn s\n" - "INNER JOIN spawn_ground sg\n" + + std::string houseTable(""); + if(isInstanceType) { + houseTable.append("_houses"); + } + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT sg.spawn_id, s.name, s.race, s.model_type, s.command_primary, s.command_secondary, s.targetable, s.size, s.show_name, s.visual_state, s.attackable, s.show_level, s.show_command_icon, s.display_hand_icon, s.faction_id, s.collision_radius, sg.number_harvests, sg.num_attempts_per_harvest, sg.groundspawn_id, sg.collection_skill, s.size_offset, s.expansion_flag, s.holiday_flag, s.disable_sounds, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, s.lua_script\n" + "FROM spawn%s s\n" + "INNER JOIN spawn_ground%s sg\n" "ON s.id = sg.spawn_id\n" - "INNER JOIN spawn_location_entry le\n" + "INNER JOIN spawn_location_entry%s le\n" "ON sg.spawn_id = le.spawn_id\n" - "INNER JOIN spawn_location_placement lp\n" + "INNER JOIN spawn_location_placement%s lp\n" "ON le.spawn_location_id = lp.spawn_location_id\n" "WHERE lp.zone_id = %u and (lp.instance_id = 0 or lp.instance_id = %u)\n" "GROUP BY s.id", - zone->GetZoneID(), zone->GetInstanceID()); + houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), houseTable.c_str(), zone->GetZoneID(), zone->GetInstanceID()); while(result && (row = mysql_fetch_row(result))){ int32 gsXpackFlag = atoul(row[21]); @@ -1601,6 +1648,11 @@ void WorldDatabase::LoadGroundSpawns(ZoneServer* zone){ spawn->SetLootDropType(atoul(row[26])); + if(row[27]){ + std::string lua_script = std::string(row[27]); + spawn->SetSpawnScript(lua_script, true); + } + zone->AddGroundSpawn(id, spawn); total++; LogWrite(GROUNDSPAWN__DEBUG, 5, "GSpawn", "---Loading GroundSpawn: '%s' (%u)", spawn->appearance.name, id); @@ -1780,7 +1832,7 @@ bool WorldDatabase::loadCharacter(const char* ch_name, int32 account_id, Client* MYSQL_ROW row, row4; int32 id = 0; query.escaped_name = getEscapeString(ch_name); - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, current_zone_id, x, y, z, heading, admin_status, race, model_type, class, deity, level, gender, tradeskill_class, tradeskill_level, wing_type, hair_type, chest_type, legs_type, soga_wing_type, soga_hair_type, soga_chest_type, soga_legs_type, 0xFFFFFFFF - crc32(name), facial_hair_type, soga_facial_hair_type, instance_id, group_id, last_saved, DATEDIFF(curdate(), created_date) as accage, alignment, first_world_login, zone_duplicating_id FROM characters where name='%s' and account_id=%i AND deleted = 0", query.escaped_name, account_id); + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, current_zone_id, x, y, z, heading, admin_status, race, model_type, class, deity, level, gender, tradeskill_class, tradeskill_level, wing_type, hair_type, chest_type, legs_type, soga_wing_type, soga_hair_type, soga_chest_type, soga_legs_type, 0xFFFFFFFF - crc32(name), facial_hair_type, soga_facial_hair_type, instance_id, group_id, last_saved, DATEDIFF(curdate(), created_date) as accage, alignment, first_world_login, zone_duplicating_id, grid_id FROM characters where name='%s' and account_id=%i AND deleted = 0", query.escaped_name, account_id); // no character found if ( result == NULL ) { LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character for '%s'", ch_name); @@ -1798,7 +1850,7 @@ bool WorldDatabase::loadCharacter(const char* ch_name, int32 account_id, Client* client->SetAccountID(account_id); client->GetPlayer()->SetName(ch_name); client->GetPlayer()->SetX(atof(row[2])); - client->GetPlayer()->SetY(atof(row[3])); + client->GetPlayer()->SetY(atof(row[3]), false, true); client->GetPlayer()->SetZ(atof(row[4])); client->GetPlayer()->SetHeading(atof(row[5])); client->SetAdminStatus(atoi(row[6])); @@ -1858,6 +1910,8 @@ SOGA chars looked ok in LoginServer screen tho... odd. client->GetPlayer()->GetInfoStruct()->set_first_world_login(atoi(row[31])); + client->GetPlayer()->SetLocation(atoul(row[33])); + LoadCharacterFriendsIgnoreList(client->GetPlayer()); MYSQL_RES* result4 = query4.RunQuery2(Q_SELECT, "SELECT `guild_id` FROM `guild_members` WHERE `char_id`=%u", id); if (result4 && (row4 = mysql_fetch_row(result4))) { @@ -2092,125 +2146,187 @@ bool WorldDatabase::insertCharacterProperty(Client* client, char* propName, char return true; } -bool WorldDatabase::loadCharacterProperties(Client* client) { +bool WorldDatabase::insertCharacterProperty(int32 charID, char* propName, char* propValue) { + Query query, query2; + + string update_status = string("update character_properties set propvalue='%s' where charid=%i and propname='%s'"); + query.RunQuery2(Q_UPDATE, update_status.c_str(), propValue, charID, propName); + if (!query.GetAffectedRows()) + { + query2.RunQuery2(Q_UPDATE, "insert into character_properties (charid, propname, propvalue) values(%i, '%s', '%s')", charID, propName, propValue); + if (query2.GetErrorNumber() && query2.GetError() && query2.GetErrorNumber() < 0xFFFFFFFF) { + LogWrite(WORLD__ERROR, 0, "World", "Error in insertCharacterProperty query '%s': %s", query.GetQuery(), query.GetError()); + return false; + } + } + return true; +} + +bool WorldDatabase::loadCharacterProperties(Client* client, bool preload) { Query query; MYSQL_ROW row; int32 id = 0; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT propname, propvalue FROM character_properties where charid = %i", client->GetCharacterID()); + MYSQL_RES* result = query.RunQuery2(Q_SELECT, + "SELECT propname, propvalue FROM character_properties WHERE charid = %i", + client->GetCharacterID()); + // no character found if (result == NULL) { - LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character properties for '%s'", client->GetPlayer()->GetName()); + LogWrite(PLAYER__ERROR, 0, "Player", "Error loading character properties for '%s'", + client->GetPlayer()->GetName()); return false; } while (result && (row = mysql_fetch_row(result))) { - char* prop_name = row[0]; + char* prop_name = row[0]; char* prop_value = row[1]; if (!prop_name || !prop_value) continue; - if (!stricmp(prop_name, CHAR_PROPERTY_SPEED)) - { - float new_speed = atof(prop_value); - client->GetPlayer()->SetSpeed(new_speed, true); - client->GetPlayer()->SetCharSheetChanged(true); - } - else if (!stricmp(prop_name, CHAR_PROPERTY_FLYMODE)) - { - int8 flymode = atoul(prop_value); - if (flymode) // avoid fly mode notification unless enabled - ClientPacketFunctions::SendFlyMode(client, flymode, false); - } - else if (!stricmp(prop_name, CHAR_PROPERTY_INVUL)) - { - int8 invul = atoul(prop_value); - client->GetPlayer()->SetInvulnerable(invul == 1); - if (client->GetPlayer()->GetInvulnerable()) - client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are now invulnerable!"); - } - else if (!stricmp(prop_name, CHAR_PROPERTY_GMVISION)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->SetGMVision(val == 1); - client->GetCurrentZone()->SendAllSpawnsForVisChange(client, false); - if (val) - client->SimpleMessage(CHANNEL_COLOR_YELLOW, "GM Vision Enabled!"); - } - else if (!stricmp(prop_name, CHAR_PROPERTY_REGIONDEBUG)) - { - int8 val = atoul(prop_value); + bool matched = false; - client->SetRegionDebug(val == 1); - if (val) - client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Region Debug Enabled!"); - } - else if (!stricmp(prop_name, CHAR_PROPERTY_LUADEBUG)) - { - int8 val = atoul(prop_value); - if (val) - { - client->SetLuaDebugClient(true); - if (lua_interface) - lua_interface->UpdateDebugClients(client); - - client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You will now receive LUA error messages."); + if (!stricmp(prop_name, CHAR_PROPERTY_SPEED)) { + matched = true; + if (!preload) { + float new_speed = (float)atof(prop_value); + client->GetPlayer()->SetSpeed(new_speed, true); + client->GetPlayer()->SetCharSheetChanged(true); } } - else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPLOOTMETHOD)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_group_loot_method(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_FLYMODE)) { + matched = true; + if (!preload) { + int8 flymode = (int8)atoul(prop_value); + if (flymode) // avoid fly mode notification unless enabled + ClientPacketFunctions::SendFlyMode(client, flymode, false); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPLOOTITEMRARITY)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_group_loot_items_rarity(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_INVUL)) { + matched = true; + if (!preload) { + int8 invul = (int8)atoul(prop_value); + client->GetPlayer()->SetInvulnerable(invul == 1); + if (client->GetPlayer()->GetInvulnerable()) + client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are now invulnerable!"); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPAUTOSPLIT)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_group_auto_split(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_GMVISION)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->SetGMVision(val == 1); + client->GetCurrentZone()->SendAllSpawnsForVisChange(client, false); + if (val) + client->SimpleMessage(CHANNEL_COLOR_YELLOW, "GM Vision Enabled!"); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPDEFAULTYELL)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_group_default_yell(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_REGIONDEBUG)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->SetRegionDebug(val == 1); + if (val) + client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Region Debug Enabled!"); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPAUTOLOCK)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_group_autolock(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_LUADEBUG)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + if (val) { + client->SetLuaDebugClient(true); + if (lua_interface) + lua_interface->UpdateDebugClients(client); + client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You will now receive LUA error messages."); + } + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPSOLOAUTOLOCK)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_group_solo_autolock(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPLOOTMETHOD)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_group_loot_method(val); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_AUTOLOOTMETHOD)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_group_auto_loot_method(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPLOOTITEMRARITY)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_group_loot_items_rarity(val); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPLOCKMETHOD)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_group_lock_method(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPAUTOSPLIT)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_group_auto_split(val); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_ASSISTAUTOATTACK)) - { - int8 val = atoul(prop_value); - client->GetPlayer()->GetInfoStruct()->set_assist_auto_attack(val); + else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPDEFAULTYELL)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_group_default_yell(val); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_SETACTIVEFOOD)) - { - int32 val = atoul(prop_value); - client->GetPlayer()->SetActiveFoodUniqueID(val, false); + else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPAUTOLOCK)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_group_autolock(val); + } } - else if (!stricmp(prop_name, CHAR_PROPERTY_SETACTIVEDRINK)) - { - int32 val = atoul(prop_value); - client->GetPlayer()->SetActiveDrinkUniqueID(val, false); + else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPSOLOAUTOLOCK)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_group_solo_autolock(val); + } + } + else if (!stricmp(prop_name, CHAR_PROPERTY_AUTOLOOTMETHOD)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_group_auto_loot_method(val); + } + } + else if (!stricmp(prop_name, CHAR_PROPERTY_GROUPLOCKMETHOD)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_group_lock_method(val); + } + } + else if (!stricmp(prop_name, CHAR_PROPERTY_ASSISTAUTOATTACK)) { + matched = true; + if (!preload) { + int8 val = (int8)atoul(prop_value); + client->GetPlayer()->GetInfoStruct()->set_assist_auto_attack(val); + } + } + else if (!stricmp(prop_name, CHAR_PROPERTY_SETACTIVEFOOD)) { + matched = true; + if (!preload) { + int32 val = (int32)atoul(prop_value); + client->GetPlayer()->SetActiveFoodUniqueID(val, false); + } + } + else if (!stricmp(prop_name, CHAR_PROPERTY_SETACTIVEDRINK)) { + matched = true; + if (!preload) { + int32 val = (int32)atoul(prop_value); + client->GetPlayer()->SetActiveDrinkUniqueID(val, false); + } + } + else { + // Unknown property: only act during preload AND only if no stricmp matched + // (we're in the 'else', so matched == false by definition) + if (preload) { + client->GetPlayer()->RegisterProperty(std::string(prop_name)); + client->GetPlayer()->SetProperty(std::string(prop_name), std::string(prop_value)); + } + // When preload == false, we intentionally ignore unknown properties. } } @@ -2460,8 +2576,6 @@ int32 WorldDatabase::SaveCharacter(PacketStruct* create, int32 loginID){ class_id = 0; //Classic Server Only } - create->PrintPacket(); - int8 gender_id = create->getType_int8_ByName("gender"); sint16 auto_admin_status = 0; @@ -3251,8 +3365,9 @@ string WorldDatabase::GetExpansionIDByVersion(int16 version) } -void WorldDatabase::LoadSpecialZones(){ - LogWrite(ZONE__INFO, 0, "Zone", "Starting static zones..."); +void WorldDatabase::LoadSpecialZones(bool silent){ + if(!silent) + LogWrite(ZONE__INFO, 0, "Zone", "Starting static zones..."); Query query; ZoneServer* zone = 0; MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, name, always_loaded, peer_priority FROM zones where always_loaded = 1"); @@ -3550,6 +3665,8 @@ void WorldDatabase::LoadSpawns(ZoneServer* zone) LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter LoadSpawns"); + int8 isInstanceType = (zone->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE); + if(zone->GetInstanceID() == 0) { npcs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_npcs sn where sn.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_NPC); objects = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_objects so where so.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_OBJECT); @@ -3560,6 +3677,19 @@ void WorldDatabase::LoadSpawns(ZoneServer* zone) spawn_group_associations = LoadSpawnLocationGroupAssociations(zone); spawn_group_chances = LoadSpawnGroupChances(zone); } + else if(isInstanceType) { + npcs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement_houses slp, spawn_location_name_houses sln, spawn_location_entry_houses sle, spawn_npcs_houses sn where sn.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_NPC); + objects = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement_houses slp, spawn_location_name_houses sln, spawn_location_entry_houses sle, spawn_objects_houses so where so.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_OBJECT); + widgets = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement_houses slp, spawn_location_name_houses sln, spawn_location_entry_houses sle, spawn_widgets_houses sw where sw.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_WIDGET); + signs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement_houses slp, spawn_location_name_houses sln, spawn_location_entry_houses sle, spawn_signs_houses ss where ss.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_SIGN); + ground_spawns = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement_houses slp, spawn_location_name_houses sln, spawn_location_entry_houses sle, spawn_ground_houses sg where sg.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_GROUNDSPAWN); + + npcs += ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_npcs sn where sn.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_NPC); + objects += ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_objects so where so.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_OBJECT); + widgets += ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_widgets sw where sw.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_WIDGET); + signs += ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_signs ss where ss.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_SIGN); + ground_spawns += ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_ground sg where sg.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_GROUNDSPAWN); + } else { npcs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_npcs sn where sn.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_NPC); objects = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_objects so where so.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_OBJECT); @@ -3569,17 +3699,22 @@ void WorldDatabase::LoadSpawns(ZoneServer* zone) spawn_groups = LoadSpawnLocationGroups(zone); spawn_group_associations = LoadSpawnLocationGroupAssociations(zone); spawn_group_chances = LoadSpawnGroupChances(zone); - } - LogWrite(SPAWN__INFO, 0, "Spawn", "Loaded for zone '%s' (%u):\n\t%u NPC(s), %u Object(s), %u Widget(s)\n\t%u Sign(s), %u Ground Spawn(s), %u Spawn Group(s)\n\t%u Spawn Group Association(s), %u Spawn Group Chance(s)", zone->GetZoneName(), zone->GetZoneID(), npcs, objects, widgets, signs, ground_spawns, spawn_groups, spawn_group_associations, spawn_group_chances); + LogWrite(SPAWN__INFO, 0, "Spawn", "Loaded for zone '%s' (%u):\n\t%u NPC(s), %u Object(s), %u Widget(s)\n\t%u Sign(s), %u Ground Spawn(s), %u Spawn Group(s)\n\t%u Spawn Group Association(s), %u Spawn Group Chance(s), %u InstanceHouse", zone->GetZoneName(), zone->GetZoneID(), npcs, objects, widgets, signs, ground_spawns, spawn_groups, spawn_group_associations, spawn_group_chances, isInstanceType); LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit LoadSpawns"); } bool WorldDatabase::UpdateSpawnLocationSpawns(Spawn* spawn) { Query query; - query.RunQuery2(Q_UPDATE, "update spawn_location_placement set x=%f, y=%f, z=%f, heading=%f, x_offset=%f, y_offset=%f, z_offset=%f, respawn=%u, expire_timer=%u, expire_offset=%u, grid_id=%u, pitch=%f, roll=%f where id = %u", - spawn->GetX(), spawn->GetY(), spawn->GetZ(), spawn->GetHeading(), spawn->GetXOffset(), spawn->GetYOffset(), spawn->GetZOffset(), spawn->GetRespawnTime(), spawn->GetExpireTime(), spawn->GetExpireOffsetTime(), spawn->GetLocation(), spawn->GetPitch(), spawn->GetRoll(), spawn->GetSpawnLocationPlacementID()); + int8 isInstanceType = (spawn && spawn->GetZone() && spawn->GetZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE); + std::string houseTable(""); + if(isInstanceType) { + houseTable.append("_houses"); + } + + query.RunQuery2(Q_UPDATE, "update spawn_location_placement%s set x=%f, y=%f, z=%f, heading=%f, x_offset=%f, y_offset=%f, z_offset=%f, respawn=%u, expire_timer=%u, expire_offset=%u, grid_id=%u, pitch=%f, roll=%f where id = %u", + houseTable.c_str(), spawn->GetX(), spawn->GetY(), spawn->GetZ(), spawn->GetHeading(), spawn->GetXOffset(), spawn->GetYOffset(), spawn->GetZOffset(), spawn->GetRespawnTime(), spawn->GetExpireTime(), spawn->GetExpireOffsetTime(), spawn->GetLocation(), spawn->GetPitch(), spawn->GetRoll(), spawn->GetSpawnLocationPlacementID()); if (query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF) { LogWrite(WORLD__ERROR, 0, "World", "Error in UpdateSpawnLocationSpawns query '%s': %s", query.GetQuery(), query.GetError()); return false; @@ -3587,10 +3722,10 @@ bool WorldDatabase::UpdateSpawnLocationSpawns(Spawn* spawn) { return true; } -bool WorldDatabase::UpdateSpawnWidget(int32 widget_id, char* queryString) { +bool WorldDatabase::UpdateSpawnWidget(int32 widget_id, char* queryString,bool is_house) { Query query; - query.RunQuery2(Q_UPDATE, "update spawn_widgets set %s where widget_id = %u", - queryString, widget_id); + query.RunQuery2(Q_UPDATE, "update spawn_widgets%s set %s where widget_id = %u", + is_house ? "_houses" : "", queryString, widget_id); if (query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF) { LogWrite(WORLD__ERROR, 0, "World", "Error in UpdateSpawnWidget query '%s': %s", query.GetQuery(), query.GetError()); return false; @@ -3665,11 +3800,11 @@ void WorldDatabase::LoadRevivePoints(vector* revive_points, int32 } } -int32 WorldDatabase::GetNextSpawnIDInZone(int32 zone_id) +int32 WorldDatabase::GetNextSpawnIDInZone(int32 zone_id, bool isInstanceType) { Query query; int32 ret = 0; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT MAX(id) FROM spawn where id LIKE '%i____'", zone_id); + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT MAX(id) FROM spawn%s where id LIKE '%i____'", isInstanceType ? "_houses" : "", zone_id); if(result && mysql_num_rows(result) > 0) { MYSQL_ROW row; row = mysql_fetch_row(result); @@ -3691,14 +3826,18 @@ bool WorldDatabase::SaveSpawnInfo(Spawn* spawn){ string suffix = getSafeEscapeString(spawn->GetSuffixTitle()); string prefix = getSafeEscapeString(spawn->GetPrefixTitle()); string last_name = getSafeEscapeString(spawn->GetLastName()); + + int8 isInstanceType = (spawn->GetZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE); + std::string houseTable(""); + if(isInstanceType) { + houseTable.append("_houses"); + } if(spawn->GetDatabaseID() == 0){ + + int32 new_spawn_id = GetNextSpawnIDInZone(spawn->GetZone()->GetZoneID(), isInstanceType); - int8 isInstanceType = (spawn->GetZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE); - - int32 new_spawn_id = GetNextSpawnIDInZone(spawn->GetZone()->GetZoneID()); - - query.RunQuery2(Q_INSERT, "insert into spawn (id, name, race, model_type, size, targetable, show_name, command_primary, command_secondary, visual_state, attackable, show_level, show_command_icon, display_hand_icon, faction_id, collision_radius, hp, power, prefix, suffix, last_name, is_instanced_spawn, merchant_min_level, merchant_max_level) values(%u, '%s', %i, %i, %i, %i, %i, %u, %u, %i, %i, %i, %i, %i, %u, %i, %u, %u, '%s', '%s', '%s', %u, %u, %u)", - new_spawn_id, name.c_str(), spawn->GetRace(), spawn->GetModelType(), spawn->GetSize(), spawn->appearance.targetable, spawn->appearance.display_name, spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, 0, spawn->appearance.pos.collision_radius, spawn->GetTotalHP(), spawn->GetTotalPower(), prefix.c_str(), suffix.c_str(), last_name.c_str(), isInstanceType, spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel()); + query.RunQuery2(Q_INSERT, "insert into spawn%s (id, name, race, model_type, size, targetable, show_name, command_primary, command_secondary, visual_state, attackable, show_level, show_command_icon, display_hand_icon, faction_id, collision_radius, hp, power, prefix, suffix, last_name, is_instanced_spawn, merchant_min_level, merchant_max_level) values(%u, '%s', %i, %i, %i, %i, %i, %u, %u, %i, %i, %i, %i, %i, %u, %i, %u, %u, '%s', '%s', '%s', %u, %u, %u)", + houseTable.c_str(), new_spawn_id, name.c_str(), spawn->GetRace(), spawn->GetModelType(), spawn->GetSize(), spawn->appearance.targetable, spawn->appearance.display_name, spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, 0, spawn->appearance.pos.collision_radius, spawn->GetTotalHP(), spawn->GetTotalPower(), prefix.c_str(), suffix.c_str(), last_name.c_str(), isInstanceType, spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel()); if( new_spawn_id > 0 ) spawn->SetDatabaseID(new_spawn_id); // use the new zone_id range @@ -3708,40 +3847,40 @@ bool WorldDatabase::SaveSpawnInfo(Spawn* spawn){ return false; // else, hang your head in shame as you are an utter failure if(spawn->IsNPC()){ - query.RunQuery2(Q_INSERT, "insert into spawn_npcs (spawn_id, min_level, max_level, enc_level, class_, gender, min_group_size, max_group_size, hair_type_id, facial_hair_type_id, wing_type_id, chest_type_id, legs_type_id, soga_hair_type_id, soga_facial_hair_type_id, soga_model_type, heroic_flag, action_state, mood_state, initial_state, activity_status, hide_hood, emote_state) values(%u, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i)", - spawn->GetDatabaseID(), spawn->GetLevel(), spawn->GetLevel(), spawn->GetDifficulty(), spawn->GetAdventureClass(), spawn->GetGender(), 0, 0, ((NPC*)spawn)->features.hair_type, ((NPC*)spawn)->features.hair_face_type, + query.RunQuery2(Q_INSERT, "insert into spawn_npcs%s (spawn_id, min_level, max_level, enc_level, class_, gender, min_group_size, max_group_size, hair_type_id, facial_hair_type_id, wing_type_id, chest_type_id, legs_type_id, soga_hair_type_id, soga_facial_hair_type_id, soga_model_type, heroic_flag, action_state, mood_state, initial_state, activity_status, hide_hood, emote_state) values(%u, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i, %i)", + houseTable.c_str(), spawn->GetDatabaseID(), spawn->GetLevel(), spawn->GetLevel(), spawn->GetDifficulty(), spawn->GetAdventureClass(), spawn->GetGender(), 0, 0, ((NPC*)spawn)->features.hair_type, ((NPC*)spawn)->features.hair_face_type, ((NPC*)spawn)->features.wing_type, ((NPC*)spawn)->features.chest_type, ((NPC*)spawn)->features.legs_type, ((NPC*)spawn)->features.soga_hair_type, ((NPC*)spawn)->features.soga_hair_face_type, spawn->appearance.soga_model_type, spawn->appearance.heroic_flag, spawn->GetActionState(), spawn->GetMoodState(), spawn->GetInitialState(), spawn->GetActivityStatus(), spawn->appearance.hide_hood, spawn->appearance.emote_state); } else if(spawn->IsObject()){ - query.RunQuery2(Q_INSERT, "insert into spawn_objects (spawn_id) values(%u)", spawn->GetDatabaseID()); + query.RunQuery2(Q_INSERT, "insert into spawn_objects%s (spawn_id) values(%u)", houseTable.c_str(), spawn->GetDatabaseID()); } else if(spawn->IsWidget()){ Widget* widget = (Widget*)spawn; - query.RunQuery2(Q_INSERT, "insert into spawn_widgets (spawn_id, widget_id) values(%u, %u)", spawn->GetDatabaseID(), widget->GetWidgetID()); + query.RunQuery2(Q_INSERT, "insert into spawn_widgets%s (spawn_id, widget_id) values(%u, %u)", houseTable.c_str(), spawn->GetDatabaseID(), widget->GetWidgetID()); } else if(spawn->IsSign()){ - query.RunQuery2(Q_INSERT, "insert into spawn_signs (spawn_id, description) values(%u, 'change me')", spawn->GetDatabaseID()); + query.RunQuery2(Q_INSERT, "insert into spawn_signs%s (spawn_id, description) values(%u, 'change me')", houseTable.c_str(), spawn->GetDatabaseID()); } else if (spawn->IsGroundSpawn()) { - query.RunQuery2(Q_INSERT, "insert into spawn_ground (spawn_id) values(%u)", spawn->GetDatabaseID()); + query.RunQuery2(Q_INSERT, "insert into spawn_ground%s (spawn_id) values(%u)", houseTable.c_str(), spawn->GetDatabaseID()); } } else{ if(spawn->IsNPC()){ - query.RunQuery2(Q_UPDATE, "update spawn_npcs, spawn set name='%s', min_level=%i, max_level=%i, enc_level=%i, race=%i, model_type=%i, class_=%i, gender=%i, show_name=%i, attackable=%i, show_level=%i, targetable=%i, show_command_icon=%i, display_hand_icon=%i, hair_type_id=%i, facial_hair_type_id=%i, wing_type_id=%i, chest_type_id=%i, legs_type_id=%i, soga_hair_type_id=%i, soga_facial_hair_type_id=%i, soga_model_type=%i, size=%i, hp=%u, heroic_flag=%i, power=%u, collision_radius=%i, command_primary=%u, command_secondary=%u, visual_state=%i, action_state=%i, mood_state=%i, initial_state=%i, activity_status=%i, alignment=%i, faction_id=%u, hide_hood=%i, emote_state=%i, suffix ='%s', prefix='%s', last_name='%s', merchant_min_level = %u, merchant_max_level = %u where spawn_npcs.spawn_id = spawn.id and spawn.id = %u", - name.c_str(), spawn->GetLevel(), spawn->GetLevel(), spawn->GetDifficulty(), spawn->GetRace(), spawn->GetModelType(), + query.RunQuery2(Q_UPDATE, "update spawn_npcs%s, spawn set name='%s', min_level=%i, max_level=%i, enc_level=%i, race=%i, model_type=%i, class_=%i, gender=%i, show_name=%i, attackable=%i, show_level=%i, targetable=%i, show_command_icon=%i, display_hand_icon=%i, hair_type_id=%i, facial_hair_type_id=%i, wing_type_id=%i, chest_type_id=%i, legs_type_id=%i, soga_hair_type_id=%i, soga_facial_hair_type_id=%i, soga_model_type=%i, size=%i, hp=%u, heroic_flag=%i, power=%u, collision_radius=%i, command_primary=%u, command_secondary=%u, visual_state=%i, action_state=%i, mood_state=%i, initial_state=%i, activity_status=%i, alignment=%i, faction_id=%u, hide_hood=%i, emote_state=%i, suffix ='%s', prefix='%s', last_name='%s', merchant_min_level = %u, merchant_max_level = %u where spawn_npcs%s.spawn_id = spawn.id and spawn.id = %u", + houseTable.c_str(), name.c_str(), spawn->GetLevel(), spawn->GetLevel(), spawn->GetDifficulty(), spawn->GetRace(), spawn->GetModelType(), spawn->GetAdventureClass(), spawn->GetGender(), spawn->appearance.display_name, spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.targetable, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, ((NPC*)spawn)->features.hair_type, ((NPC*)spawn)->features.hair_face_type, ((NPC*)spawn)->features.wing_type, ((NPC*)spawn)->features.chest_type, ((NPC*)spawn)->features.legs_type, ((NPC*)spawn)->features.soga_hair_type, ((NPC*)spawn)->features.soga_hair_face_type, spawn->appearance.soga_model_type, spawn->GetSize(), spawn->GetTotalHPBase(), spawn->appearance.heroic_flag, spawn->GetTotalPowerBase(), spawn->GetCollisionRadius(), spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->GetActionState(), spawn->GetMoodState(), spawn->GetInitialState(), spawn->GetActivityStatus(), ((NPC*)spawn)->GetAlignment(), spawn->GetFactionID(), spawn->appearance.hide_hood, spawn->appearance.emote_state, - suffix.c_str(), prefix.c_str(), last_name.c_str(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), spawn->GetDatabaseID()); + suffix.c_str(), prefix.c_str(), last_name.c_str(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), houseTable.c_str(), spawn->GetDatabaseID()); } else if(spawn->IsObject()){ - query.RunQuery2(Q_UPDATE, "update spawn_objects, spawn set name='%s', model_type=%i, show_name=%i, targetable=%i, size=%i, command_primary=%u, command_secondary=%u, visual_state=%i, attackable=%i, show_level=%i, show_command_icon=%i, display_hand_icon=%i, collision_radius=%i, hp = %u, power = %u, device_id = %i, merchant_min_level = %u, merchant_max_level = %u where spawn_objects.spawn_id = spawn.id and spawn.id = %u", - name.c_str(), spawn->GetModelType(), spawn->appearance.display_name, spawn->appearance.targetable, spawn->GetSize(), spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, - spawn->GetCollisionRadius(), spawn->GetTotalHP(), spawn->GetTotalPower(), ((Object*)spawn)->GetDeviceID(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), spawn->GetDatabaseID()); + query.RunQuery2(Q_UPDATE, "update spawn_objects%s, spawn set name='%s', model_type=%i, show_name=%i, targetable=%i, size=%i, command_primary=%u, command_secondary=%u, visual_state=%i, attackable=%i, show_level=%i, show_command_icon=%i, display_hand_icon=%i, collision_radius=%i, hp = %u, power = %u, device_id = %i, merchant_min_level = %u, merchant_max_level = %u where spawn_objects%s.spawn_id = spawn.id and spawn.id = %u", + houseTable.c_str(), name.c_str(), spawn->GetModelType(), spawn->appearance.display_name, spawn->appearance.targetable, spawn->GetSize(), spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, + spawn->GetCollisionRadius(), spawn->GetTotalHP(), spawn->GetTotalPower(), ((Object*)spawn)->GetDeviceID(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), houseTable.c_str(), spawn->GetDatabaseID()); } else if(spawn->IsWidget()){ Widget* widget = (Widget*)spawn; @@ -3749,25 +3888,25 @@ bool WorldDatabase::SaveSpawnInfo(Spawn* spawn){ char* closeSound = 0; if (widget->GetOpenSound() != NULL) openSound = (char*)widget->GetOpenSound(); else openSound = (char*)string("0").c_str(); if (widget->GetCloseSound() != NULL) closeSound = (char*)widget->GetCloseSound(); else closeSound = (char*)string("0").c_str(); - query.RunQuery2(Q_UPDATE, "update spawn_widgets, spawn set name='%s', race=%i, model_type=%i, show_name=%i, attackable=%i, show_level=%i, show_command_icon=%i, display_hand_icon=%i, size=%i, hp=%u, power=%u, collision_radius=%i, command_primary=%u, command_secondary=%u, visual_state=%i, faction_id=%u, suffix ='%s', prefix='%s', last_name='%s',widget_id = %u,widget_x = %f,widget_y = %f,widget_z = %f,include_heading = %u,include_location = %u,icon = %u,type='%s',open_heading = %f,closed_heading = %f,open_x = %f,open_y = %f,open_z = %f,action_spawn_id = %u,open_sound_file='%s',close_sound_file='%s',open_duration = %u,close_x = %f,close_y=%f,close_z=%f,linked_spawn_id = %u,house_id = %u, merchant_min_level = %u, merchant_max_level = %u where spawn_widgets.spawn_id = spawn.id and spawn.id = %u", - name.c_str(), spawn->GetRace(), spawn->GetModelType(), spawn->appearance.display_name, spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, spawn->GetSize(), + query.RunQuery2(Q_UPDATE, "update spawn_widgets%s, spawn set name='%s', race=%i, model_type=%i, show_name=%i, attackable=%i, show_level=%i, show_command_icon=%i, display_hand_icon=%i, size=%i, hp=%u, power=%u, collision_radius=%i, command_primary=%u, command_secondary=%u, visual_state=%i, faction_id=%u, suffix ='%s', prefix='%s', last_name='%s',widget_id = %u,widget_x = %f,widget_y = %f,widget_z = %f,include_heading = %u,include_location = %u,icon = %u,type='%s',open_heading = %f,closed_heading = %f,open_x = %f,open_y = %f,open_z = %f,action_spawn_id = %u,open_sound_file='%s',close_sound_file='%s',open_duration = %u,close_x = %f,close_y=%f,close_z=%f,linked_spawn_id = %u,house_id = %u, merchant_min_level = %u, merchant_max_level = %u where spawn_widgets%s.spawn_id = spawn.id and spawn.id = %u", + houseTable.c_str(), name.c_str(), spawn->GetRace(), spawn->GetModelType(), spawn->appearance.display_name, spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, spawn->GetSize(), spawn->GetTotalHP(), spawn->GetTotalPower(), spawn->GetCollisionRadius(), spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->GetFactionID(), suffix.c_str(), prefix.c_str(), last_name.c_str(), widget->GetWidgetID(), widget->GetX(), widget->GetY(), widget->GetZ(), widget->GetIncludeHeading(), widget->GetIncludeLocation(), widget->GetIconValue(), Widget::GetWidgetTypeNameByTypeID(widget->GetWidgetType()).c_str(), widget->GetOpenHeading(), widget->GetClosedHeading(), widget->GetOpenX(), widget->GetOpenY(), widget->GetOpenZ(), widget->GetActionSpawnID(), openSound, closeSound,widget->GetOpenDuration(), widget->GetCloseX(),widget->GetCloseY(),widget->GetCloseZ(),widget->GetLinkedSpawnID(),widget->GetHouseID(), - spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), spawn->GetDatabaseID()); + spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), houseTable.c_str(), spawn->GetDatabaseID()); } else if(spawn->IsSign()){ Sign* sign = (Sign*)spawn; - query.RunQuery2(Q_UPDATE, "update spawn_signs, spawn set name='%s', race=%i, model_type=%i, show_name=%i, attackable=%i, show_level=%i, show_command_icon=%i, display_hand_icon=%i, size=%i, hp=%u, power=%u, collision_radius=%i, command_primary=%u, command_secondary=%u, visual_state=%i, faction_id=%u, suffix ='%s', prefix='%s', last_name='%s', `type`='%s', zone_id = %u, widget_id = %u, title='%s', widget_x = %f, widget_y = %f, widget_z = %f, icon = %u, description='%s', sign_distance = %f, zone_x = %f, zone_y = %f, zone_z = %f, zone_heading = %f, include_heading = %u, include_location = %u, merchant_min_level = %u, merchant_max_level = %u, language = %u where spawn_signs.spawn_id = spawn.id and spawn.id = %u", - name.c_str(), spawn->GetRace(), spawn->GetModelType(), spawn->appearance.display_name, spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, spawn->GetSize(), + query.RunQuery2(Q_UPDATE, "update spawn_signs%s, spawn set name='%s', race=%i, model_type=%i, show_name=%i, attackable=%i, show_level=%i, show_command_icon=%i, display_hand_icon=%i, size=%i, hp=%u, power=%u, collision_radius=%i, command_primary=%u, command_secondary=%u, visual_state=%i, faction_id=%u, suffix ='%s', prefix='%s', last_name='%s', `type`='%s', zone_id = %u, widget_id = %u, title='%s', widget_x = %f, widget_y = %f, widget_z = %f, icon = %u, description='%s', sign_distance = %f, zone_x = %f, zone_y = %f, zone_z = %f, zone_heading = %f, include_heading = %u, include_location = %u, merchant_min_level = %u, merchant_max_level = %u, language = %u where spawn_signs%s.spawn_id = spawn.id and spawn.id = %u", + houseTable.c_str(), name.c_str(), spawn->GetRace(), spawn->GetModelType(), spawn->appearance.display_name, spawn->appearance.attackable, spawn->appearance.show_level, spawn->appearance.show_command_icon, spawn->appearance.display_hand_icon, spawn->GetSize(), spawn->GetTotalHP(), spawn->GetTotalPower(), spawn->GetCollisionRadius(), spawn->GetPrimaryCommandListID(), spawn->GetSecondaryCommandListID(), spawn->GetVisualState(), spawn->GetFactionID(), suffix.c_str(), prefix.c_str(), last_name.c_str(), sign->GetSignType() == SIGN_TYPE_GENERIC ? "Generic" : "Zone", sign->GetSignZoneID(), sign->GetWidgetID(), sign->GetSignTitle(), sign->GetWidgetX(), sign->GetWidgetY(), sign->GetWidgetZ(), sign->GetIconValue(), sign->GetSignDescription(), sign->GetSignDistance(), sign->GetSignZoneX(), sign->GetSignZoneY(), sign->GetSignZoneZ(), sign->GetSignZoneHeading(), sign->GetIncludeHeading(), - sign->GetIncludeLocation(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), sign->GetLanguage(), spawn->GetDatabaseID()); + sign->GetIncludeLocation(), spawn->GetMerchantMinLevel(), spawn->GetMerchantMaxLevel(), sign->GetLanguage(), houseTable.c_str(), spawn->GetDatabaseID()); } } if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){ @@ -3793,7 +3932,7 @@ int32 WorldDatabase::SaveCombinedSpawnLocation(ZoneServer* zone, Spawn* in_spawn spawnLocationID = spawn_location_id; if(!name) name = "Combine SpawnGroup Generated"; - if(!CreateNewSpawnLocation(spawn_location_id, name)){ + if(!CreateNewSpawnLocation(spawn_location_id, name, (zone->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE))){ safe_delete(spawns); return 0; } @@ -3840,21 +3979,28 @@ bool WorldDatabase::SaveSpawnEntry(Spawn* spawn, const char* spawn_location_name Query query; Query query2; int32 count = 0; + + int8 isInstanceType = (spawn->GetZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE); + std::string houseTable(""); + if(isInstanceType) { + houseTable.append("_houses"); + } + if(create_spawnlocation){ - count = GetSpawnLocationCount(spawn->GetSpawnLocationID()); + count = GetSpawnLocationCount(spawn->GetSpawnLocationID(), spawn); if(count == 0){ - if(!CreateNewSpawnLocation(spawn->GetSpawnLocationID(), spawn_location_name)) + if(!CreateNewSpawnLocation(spawn->GetSpawnLocationID(), spawn_location_name, isInstanceType)) return false; } } - query.RunQuery2(Q_INSERT, "insert into spawn_location_entry (spawn_id, spawn_location_id, spawnpercentage) values(%u, %u, %i)", spawn->GetDatabaseID(), spawn->GetSpawnLocationID(), percent); + query.RunQuery2(Q_INSERT, "insert into spawn_location_entry%s (spawn_id, spawn_location_id, spawnpercentage) values(%u, %u, %i)", houseTable.c_str(), spawn->GetDatabaseID(), spawn->GetSpawnLocationID(), percent); if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){ LogWrite(SPAWN__ERROR, 0, "Spawn", "Error in SaveSpawnEntry query '%s': %s", query.GetQuery(), query.GetError()); return false; } if(save_zonespawn){ - query2.RunQuery2(Q_INSERT, "insert into spawn_location_placement (zone_id, instance_id, spawn_location_id, x, y, z, x_offset, y_offset, z_offset, heading, grid_id) values(%u, %u, %u, %f, %f, %f, %f, %f, %f, %f, %u)", spawn->GetZone()->GetZoneID(), spawn->GetZone()->GetInstanceID(), spawn->GetSpawnLocationID(), spawn->GetX(), spawn->GetY(), spawn->GetZ(),x_offset, y_offset, z_offset, spawn->GetHeading(), spawn->GetLocation()); + query2.RunQuery2(Q_INSERT, "insert into spawn_location_placement%s (zone_id, instance_id, spawn_location_id, x, y, z, x_offset, y_offset, z_offset, heading, grid_id) values(%u, %u, %u, %f, %f, %f, %f, %f, %f, %f, %u)", houseTable.c_str(), spawn->GetZone()->GetZoneID(), spawn->GetZone()->GetInstanceID(), spawn->GetSpawnLocationID(), spawn->GetX(), spawn->GetY(), spawn->GetZ(),x_offset, y_offset, z_offset, spawn->GetHeading(), spawn->GetLocation()); if(query2.GetErrorNumber() && query2.GetError() && query2.GetErrorNumber() < 0xFFFFFFFF){ LogWrite(SPAWN__ERROR, 0, "Spawn", "Error in SaveSpawnEntry query '%s': %s", query2.GetQuery(), query2.GetError()); return false; @@ -3900,12 +4046,12 @@ float WorldDatabase::GetSpawnLocationPlacementOffsetZ(int32 location_id) { return ret; } -bool WorldDatabase::CreateNewSpawnLocation(int32 id, const char* name){ +bool WorldDatabase::CreateNewSpawnLocation(int32 id, const char* name, bool isHouseType){ Query query; if(!name) name = "Unknown Spawn Location Name"; string str_name = getSafeEscapeString(name); - query.RunQuery2(Q_INSERT, "insert into spawn_location_name (id, name) values(%u, '%s')", id, str_name.c_str()); + query.RunQuery2(Q_INSERT, "insert into spawn_location_name%s (id, name) values(%u, '%s')", isHouseType ? "_houses" : "", id, str_name.c_str()); if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){ LogWrite(SPAWN__ERROR, 0, "Spawn", "Error in CreateNewSpawnLocation query '%s': %s", query.GetQuery(), query.GetError()); return false; @@ -3917,10 +4063,16 @@ int32 WorldDatabase::GetSpawnLocationCount(int32 location, Spawn* spawn){ Query query; int32 ret = 0; MYSQL_RES* result = 0; + + int8 isInstanceType = (spawn && spawn->GetZone() && spawn->GetZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE); + std::string houseTable(""); + if(isInstanceType) { + houseTable.append("_houses"); + } if(spawn) - result = query.RunQuery2(Q_SELECT, "SELECT count(id) FROM spawn_location_entry where spawn_location_id=%u and spawn_id=%u", location, spawn->GetDatabaseID()); + result = query.RunQuery2(Q_SELECT, "SELECT count(id) FROM spawn_location_entry%s where spawn_location_id=%u and spawn_id=%u", houseTable.c_str(), location, spawn->GetDatabaseID()); else - result = query.RunQuery2(Q_SELECT, "SELECT count(id) FROM spawn_location_entry where spawn_location_id=%u", location); + result = query.RunQuery2(Q_SELECT, "SELECT count(id) FROM spawn_location_entry%s where spawn_location_id=%u", houseTable.c_str(), location); if(result && mysql_num_rows(result) > 0){ MYSQL_ROW row; while(result && (row = mysql_fetch_row(result)) && row[0]){ @@ -3930,10 +4082,10 @@ int32 WorldDatabase::GetSpawnLocationCount(int32 location, Spawn* spawn){ return ret; } -int32 WorldDatabase::GetNextSpawnLocation(){ +int32 WorldDatabase::GetNextSpawnLocation(bool isInstanceType){ Query query; int32 ret = 0; - MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT max(id) FROM spawn_location_name"); + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT max(id) FROM spawn_location_name%s", isInstanceType ? "_houses" : ""); if(result && mysql_num_rows(result) > 0){ MYSQL_ROW row; while(result && (row = mysql_fetch_row(result)) && row[0]){ @@ -3949,11 +4101,17 @@ bool WorldDatabase::RemoveSpawnFromSpawnLocation(Spawn* spawn){ Query query2; int32 count = GetSpawnLocationCount(spawn->GetSpawnLocationID(), spawn); - query.RunQuery2(Q_DELETE, "delete FROM spawn_location_placement where spawn_location_id=%u", spawn->GetSpawnLocationID()); + int8 isInstanceType = (spawn && spawn->GetZone() && spawn->GetZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE); + std::string houseTable(""); + if(isInstanceType) { + houseTable.append("_houses"); + } + + query.RunQuery2(Q_DELETE, "delete FROM spawn_location_placement%s where spawn_location_id=%u", houseTable.c_str(), spawn->GetSpawnLocationID()); if(count == 1) - query.RunQuery2(Q_DELETE, "delete FROM spawn_location_name where id=%u", spawn->GetSpawnLocationID()); + query.RunQuery2(Q_DELETE, "delete FROM spawn_location_name%s where id=%u", houseTable.c_str(), spawn->GetSpawnLocationID()); - query2.RunQuery2(Q_DELETE, "delete FROM spawn_location_entry where spawn_id=%u and spawn_location_id = %u", spawn->GetDatabaseID(), spawn->GetSpawnLocationID()); + query2.RunQuery2(Q_DELETE, "delete FROM spawn_location_entry%s where spawn_id=%u and spawn_location_id = %u", houseTable.c_str(), spawn->GetDatabaseID(), spawn->GetSpawnLocationID()); if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){ LogWrite(WORLD__ERROR, 0, "World", "Error in RemoveSpawnFromSpawnLocation query '%s': %s", query.GetQuery(), query.GetError()); return false; @@ -4392,10 +4550,10 @@ void WorldDatabase::Save(Client* client){ zone_id = client->GetCurrentZone()->GetZoneID(); char charName[64]; - strncpy(charName, client->GetPlayer()->GetInfoStruct()->get_name().c_str(),64); - charName[63] = '\0'; - query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update characters set name='%s',current_zone_id=%u, x=%f, y=%f, z=%f, heading=%f, level=%i,instance_id=%i,last_saved=%i, `class`=%i, `tradeskill_level`=%i, `tradeskill_class`=%i, `group_id`=%u, deity = %u, alignment = %u, zone_duplicating_id = %u where id = %u", charName, zone_id, player->GetX(), player->GetY(), player->GetZ(), player->GetHeading(), player->GetLevel(), instance_id, client->GetLastSavedTimeStamp(), client->GetPlayer()->GetAdventureClass(), client->GetPlayer()->GetTSLevel(), client->GetPlayer()->GetTradeskillClass(), client->GetPlayer()->GetGroupMemberInfo() ? client->GetPlayer()->GetGroupMemberInfo()->group_id : client->GetRejoinGroupID(), client->GetPlayer()->GetDeity(), client->GetPlayer()->GetInfoStruct()->get_alignment(), client->GetDuplicatingZoneID(), client->GetCharacterID()); - query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update character_details set hp=%u, power=%u, str=%i, sta=%i, agi=%i, wis=%i, intel=%i, heat=%i, cold=%i, magic=%i, mental=%i, divine=%i, disease=%i, poison=%i, coin_copper=%u, coin_silver=%u, coin_gold=%u, coin_plat=%u, max_hp = %u, max_power=%u, xp = %u, xp_needed = %u, xp_debt = %f, xp_vitality = %f, tradeskill_xp = %u, tradeskill_xp_needed = %u, tradeskill_xp_vitality = %f, bank_copper = %u, bank_silver = %u, bank_gold = %u, bank_plat = %u, status_points = %u, bind_zone_id=%u, bind_x = %f, bind_y = %f, bind_z = %f, bind_heading = %f, house_zone_id=%u, combat_voice = %i, emote_voice = %i, biography='%s', flags=%u, flags2=%u, last_name='%s', assigned_aa = %i, unassigned_aa = %i, tradeskill_aa = %i, unassigned_tradeskill_aa = %i, prestige_aa = %i, unassigned_prestige_aa = %i, tradeskill_prestige_aa = %i, unassigned_tradeskill_prestige_aa = %i, pet_name = '%s' where char_id = %u", + strncpy(charName, client->GetPlayer()->GetName(),64); + query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update characters set name='%s',current_zone_id=%u, x=%f, y=%f, z=%f, heading=%f, level=%i,instance_id=%i,last_saved=%i, `class`=%i, `tradeskill_level`=%i, `tradeskill_class`=%i, `group_id`=%u, deity = %u, alignment = %u, zone_duplicating_id = %u, grid_id = %u where id = %u", getSafeEscapeString(charName).c_str(), zone_id, player->GetX(), player->GetY(), player->GetZ(), player->GetHeading(), player->GetLevel(), instance_id, client->GetLastSavedTimeStamp(), client->GetPlayer()->GetAdventureClass(), client->GetPlayer()->GetTSLevel(), client->GetPlayer()->GetTradeskillClass(), client->GetPlayer()->GetGroupMemberInfo() ? client->GetPlayer()->GetGroupMemberInfo()->group_id : client->GetRejoinGroupID(), client->GetPlayer()->GetDeity(), client->GetPlayer()->GetInfoStruct()->get_alignment(), client->GetDuplicatingZoneID(), client->GetPlayer()->GetLocation(), client->GetCharacterID()); + Query query2; + query2.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update character_details set hp=%u, power=%u, str=%i, sta=%i, agi=%i, wis=%i, intel=%i, heat=%i, cold=%i, magic=%i, mental=%i, divine=%i, disease=%i, poison=%i, coin_copper=%u, coin_silver=%u, coin_gold=%u, coin_plat=%u, max_hp = %u, max_power=%u, xp = %u, xp_needed = %u, xp_debt = %f, xp_vitality = %f, tradeskill_xp = %u, tradeskill_xp_needed = %u, tradeskill_xp_vitality = %f, bank_copper = %u, bank_silver = %u, bank_gold = %u, bank_plat = %u, status_points = %u, bind_zone_id=%u, bind_x = %f, bind_y = %f, bind_z = %f, bind_heading = %f, house_zone_id=%u, combat_voice = %i, emote_voice = %i, biography='%s', flags=%u, flags2=%u, last_name='%s', assigned_aa = %i, unassigned_aa = %i, tradeskill_aa = %i, unassigned_tradeskill_aa = %i, prestige_aa = %i, unassigned_prestige_aa = %i, tradeskill_prestige_aa = %i, unassigned_tradeskill_prestige_aa = %i, pet_name = '%s' where char_id = %u", player->GetHP(), player->GetPower(), player->GetStrBase(), player->GetStaBase(), player->GetAgiBase(), player->GetWisBase(), player->GetIntBase(), player->GetHeatResistanceBase(), player->GetColdResistanceBase(), player->GetMagicResistanceBase(), player->GetMentalResistanceBase(), player->GetDivineResistanceBase(), player->GetDiseaseResistanceBase(), player->GetPoisonResistanceBase(), player->GetCoinsCopper(), player->GetCoinsSilver(), player->GetCoinsGold(), player->GetCoinsPlat(), player->GetTotalHPBase(), player->GetTotalPowerBase(), player->GetXP(), player->GetNeededXP(), player->GetXPDebt(), player->GetXPVitality(), player->GetTSXP(), player->GetNeededTSXP(), player->GetTSXPVitality(), player->GetBankCoinsCopper(), player->GetBankCoinsSilver(), player->GetBankCoinsGold(), player->GetBankCoinsPlat(), player->GetStatusPoints(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneID(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneX(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneY(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneZ(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneHeading(), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), @@ -4407,11 +4565,13 @@ void WorldDatabase::Save(Client* client){ if(friends && friends->size() > 0){ for(itr = friends->begin(); itr != friends->end(); itr++){ if(itr->second == 1){ - query.AddQueryAsync(client->GetCharacterID(), this, Q_INSERT, "insert ignore into character_social (char_id, name, type) values(%u, '%s', 'FRIEND')", client->GetCharacterID(), getSafeEscapeString(itr->first.c_str()).c_str()); + Query query3; + query3.AddQueryAsync(client->GetCharacterID(), this, Q_INSERT, "insert ignore into character_social (char_id, name, type) values(%u, '%s', 'FRIEND')", client->GetCharacterID(), getSafeEscapeString(itr->first.c_str()).c_str()); itr->second = 0; } else if(itr->second == 2){ - query.AddQueryAsync(client->GetCharacterID(), this, Q_DELETE, "delete FROM character_social where char_id = %u and name = '%s'", client->GetCharacterID(), getSafeEscapeString(itr->first.c_str()).c_str()); + Query query3; + query3.AddQueryAsync(client->GetCharacterID(), this, Q_DELETE, "delete FROM character_social where char_id = %u and name = '%s'", client->GetCharacterID(), getSafeEscapeString(itr->first.c_str()).c_str()); itr->second = 3; } } @@ -4420,11 +4580,13 @@ void WorldDatabase::Save(Client* client){ if(ignored && ignored->size() > 0){ for(itr = ignored->begin(); itr != ignored->end(); itr++){ if(itr->second == 1){ - query.AddQueryAsync(client->GetCharacterID(), this, Q_INSERT, "insert ignore into character_social (char_id, name, type) values(%u, '%s', 'IGNORE')", client->GetCharacterID(), getSafeEscapeString(itr->first.c_str()).c_str()); + Query query3; + query3.AddQueryAsync(client->GetCharacterID(), this, Q_INSERT, "insert ignore into character_social (char_id, name, type) values(%u, '%s', 'IGNORE')", client->GetCharacterID(), getSafeEscapeString(itr->first.c_str()).c_str()); itr->second = 0; } else if(itr->second == 2){ - query.AddQueryAsync(client->GetCharacterID(), this, Q_DELETE, "delete FROM character_social where char_id = %u and name = '%s'", client->GetCharacterID(), getSafeEscapeString(itr->first.c_str()).c_str()); + Query query3; + query3.AddQueryAsync(client->GetCharacterID(), this, Q_DELETE, "delete FROM character_social where char_id = %u and name = '%s'", client->GetCharacterID(), getSafeEscapeString(itr->first.c_str()).c_str()); itr->second = 3; } } @@ -5282,7 +5444,13 @@ bool WorldDatabase::DeleteCharacter(int32 account_id, int32 character_id){ query.AddQueryAsync(character_id, this, Q_DELETE, "delete from character_titles where char_id = %u", character_id); query.AddQueryAsync(character_id, this, Q_DELETE, "delete from char_colors where char_id = %u", character_id); query.AddQueryAsync(character_id, this, Q_DELETE, "delete from statistics where char_id = %u", character_id); - + query.AddQueryAsync(character_id, this, Q_DELETE, "delete from character_spell_effects where charid=%u", character_id); + query.AddQueryAsync(character_id, this, Q_DELETE, "delete from character_spell_effect_targets where caster_char_id=%u", character_id); + query.AddQueryAsync(character_id, this, Q_DELETE, "delete from character_custom_spell_dataindex where charid=%u", character_id); + query.AddQueryAsync(character_id, this, Q_DELETE, "delete from character_custom_spell_display where charid=%u", character_id); + query.AddQueryAsync(character_id, this, Q_DELETE, "delete from character_custom_spell_data where charid=%u", character_id); + broker.RemoveSeller(character_id); + return true; } @@ -5589,6 +5757,7 @@ string WorldDatabase::GetMerchantDescription(int32 merchant_id) { void WorldDatabase::LoadTransporters(ZoneServer* zone){ int32 total = 0; + zone->DeleteTransporters(); zone->DeleteGlobalTransporters(); Query query; MYSQL_ROW row; @@ -5613,8 +5782,8 @@ void WorldDatabase::LoadTransporters(ZoneServer* zone){ zone->AddTransporter(atoul(row[0]), TRANSPORT_TYPE_ZONE, name, message, atoul(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atof(row[8]), atoul(row[14]), atoul(row[15]), atoi(row[16]), atoi(row[17]), atoul(row[18]), atoi(row[19]), atoul(row[20]), atoul(row[21]), atoul(row[22]), atoul(row[23]), atoul(row[24]), atoul(row[25]), atoul(row[26]), atoul(row[27]), atoul(row[28]), atoul(row[29]), atoul(row[30]), atoul(row[31])); else if (row[1] && strcmp(row[1], "Flight") == 0) zone->AddTransporter(atoul(row[0]), TRANSPORT_TYPE_FLIGHT, name, message, atoul(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atof(row[8]), atoul(row[14]), atoul(row[15]), atoi(row[16]), atoi(row[17]), atoul(row[18]), atoi(row[19]), atoul(row[20]), atoul(row[21]), atoul(row[22]), atoul(row[23]), atoul(row[24]), atoul(row[25]), atoul(row[26]), atoul(row[27]), atoul(row[28]), atoul(row[29]), atoul(row[30]), atoul(row[31])); - else if(row[1] && strcmp(row[1], "Location") == 0) - zone->AddLocationTransporter(atoul(row[9]), message, atof(row[10]), atof(row[11]), atof(row[12]), atof(row[13]), atoul(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atof(row[8]), atoul(row[14]), atoul(row[15])); + else if(row[1] && (strcmp(row[1], "Location") == 0 || strcmp(row[1], "ForceZone") == 0)) + zone->AddLocationTransporter(atoul(row[9]), message, atof(row[10]), atof(row[11]), atof(row[12]), atof(row[13]), atoul(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atof(row[8]), atoul(row[14]), atoul(row[15]), (strcmp(row[1], "ForceZone") == 0) ? true : false); else zone->AddTransporter(atoul(row[0]), TRANSPORT_TYPE_GENERIC, "", message, atoul(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atof(row[8]), atoul(row[14]), atoul(row[15]), atoi(row[16]), atoi(row[17]), atoul(row[18]), atoi(row[19]), atoul(row[20]), atoul(row[21]), atoul(row[22]), atoul(row[23]), atoul(row[24]), atoul(row[25]), atoul(row[26]), atoul(row[27]), atoul(row[28]), atoul(row[29]), atoul(row[30]), atoul(row[31])); total++; @@ -5798,7 +5967,8 @@ map* WorldDatabase::GetInstanceRemovedSpawns(int32 instance_id, int if( !database_new.Select(&result, "SELECT spawn_location_entry_id, respawn_time FROM instance_spawns_removed WHERE instance_id = %i AND spawn_type = %i", instance_id, type) ) { - LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in GetInstanceRemovedSpawns() '%s': %i", database_new.GetErrorMsg(), database_new.GetError()); + if(database_new.GetError()) // reduce spam in logs, only post error if actual error back from DB server 0 means no results + LogWrite(INSTANCE__ERROR, 0, "Instance", "Error in GetInstanceRemovedSpawns() '%s': %i", database_new.GetErrorMsg(), database_new.GetError()); return ret; } else @@ -7121,13 +7291,17 @@ bool WorldDatabase::LoadNPC(ZoneServer* zone, int32 spawn_id) { NPC* npc = nullptr; int32 id = 0; DatabaseResult result; - - database_new.Select(&result, "SELECT npc.spawn_id, s.name, npc.min_level, npc.max_level, npc.enc_level, s.race, s.model_type, npc.class_, npc.gender, s.command_primary, s.command_secondary, s.show_name, npc.min_group_size, npc.max_group_size, npc.hair_type_id, npc.facial_hair_type_id, npc.wing_type_id, npc.chest_type_id, npc.legs_type_id, npc.soga_hair_type_id, npc.soga_facial_hair_type_id, s.attackable, s.show_level, s.targetable, s.show_command_icon, s.display_hand_icon, s.hp, s.power, s.size, s.collision_radius, npc.action_state, s.visual_state, npc.mood_state, npc.initial_state, npc.activity_status, s.faction_id, s.sub_title, s.merchant_id, s.merchant_type, s.size_offset, npc.attack_type, npc.ai_strategy+0, npc.spell_list_id, npc.secondary_spell_list_id, npc.skill_list_id, npc.secondary_skill_list_id, npc.equipment_list_id, npc.str, npc.sta, npc.wis, npc.intel, npc.agi, npc.heat, npc.cold, npc.magic, npc.mental, npc.divine, npc.disease, npc.poison, npc.aggro_radius, npc.cast_percentage, npc.randomize, npc.soga_model_type, npc.heroic_flag, npc.alignment, npc.elemental, npc.arcane, npc.noxious, s.savagery, s.dissonance, npc.hide_hood, npc.emote_state, s.prefix, s.suffix, s.last_name, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, npc.scared_by_strong_players, npc.action_state_str\n" - "FROM spawn s\n" - "INNER JOIN spawn_npcs npc\n" + int8 isInstanceType = (zone->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE); + std::string houseTable(""); + if(isInstanceType) { + houseTable.append("_houses"); + } + database_new.Select(&result, "SELECT npc.spawn_id, s.name, npc.min_level, npc.max_level, npc.enc_level, s.race, s.model_type, npc.class_, npc.gender, s.command_primary, s.command_secondary, s.show_name, npc.min_group_size, npc.max_group_size, npc.hair_type_id, npc.facial_hair_type_id, npc.wing_type_id, npc.chest_type_id, npc.legs_type_id, npc.soga_hair_type_id, npc.soga_facial_hair_type_id, s.attackable, s.show_level, s.targetable, s.show_command_icon, s.display_hand_icon, s.hp, s.power, s.size, s.collision_radius, npc.action_state, s.visual_state, npc.mood_state, npc.initial_state, npc.activity_status, s.faction_id, s.sub_title, s.merchant_id, s.merchant_type, s.size_offset, npc.attack_type, npc.ai_strategy+0, npc.spell_list_id, npc.secondary_spell_list_id, npc.skill_list_id, npc.secondary_skill_list_id, npc.equipment_list_id, npc.str, npc.sta, npc.wis, npc.intel, npc.agi, npc.heat, npc.cold, npc.magic, npc.mental, npc.divine, npc.disease, npc.poison, npc.aggro_radius, npc.cast_percentage, npc.randomize, npc.soga_model_type, npc.heroic_flag, npc.alignment, npc.elemental, npc.arcane, npc.noxious, s.savagery, s.dissonance, npc.hide_hood, npc.emote_state, s.prefix, s.suffix, s.last_name, s.disable_sounds, s.merchant_min_level, s.merchant_max_level, s.aaxp_rewards, s.loot_tier, s.loot_drop_type, npc.scared_by_strong_players, npc.action_state_str, s.lua_script\n" + "FROM spawn%s s\n" + "INNER JOIN spawn_npcs%s npc\n" "ON npc.spawn_id = s.id\n" "WHERE s.id = %u", - spawn_id); + houseTable.c_str(), houseTable.c_str(), spawn_id); if (result.GetNumRows() > 0 && result.Next()) { id = result.GetInt32(0); @@ -7265,6 +7439,11 @@ bool WorldDatabase::LoadNPC(ZoneServer* zone, int32 spawn_id) { npc->GetInfoStruct()->set_action_state(action_state_str); } + if(!result.IsNull(83)){ + std::string lua_script = std::string(result.GetString(83)); + npc->SetSpawnScript(lua_script, true); + } + zone->AddNPC(id, npc); //skipped spells/skills/equipment as it is all loaded, the following rely on a spawn to load @@ -7753,9 +7932,6 @@ void WorldDatabase::GetHouseSpawnInstanceData(ZoneServer* zone, Spawn* spawn) if (!spawn) return; - if (zone->house_object_database_lookup.count(spawn->GetModelType()) < 1) - zone->house_object_database_lookup.Put(spawn->GetModelType(), spawn->GetDatabaseID()); - DatabaseResult result; database_new.Select(&result, "SELECT pickup_item_id, pickup_unique_item_id\n" @@ -7967,6 +8143,94 @@ int32 WorldDatabase::CreateSpiritShard(const char* name, int32 level, int8 race, return query.GetLastInsertedID(); } +void WorldDatabase::LoadCustomSpellData(Client* client, LuaSpell* luaspell) { + if (!luaspell || !luaspell->spell || !luaspell->spell->GetSpellData() || !luaspell->spell->IsCopiedSpell()) + return; + + auto spell_data = luaspell->spell->GetSpellData(); + + DatabaseResult result; + if (!database_new.Select(&result, "SELECT field, type, value FROM character_custom_spell_data WHERE charid = %u AND spell_id = %u", + client->GetPlayer()->GetCharacterID(), luaspell->spell->GetSpellData()->inherited_spell_id)) + return; + + while (result.Next()) { + std::string field = result.GetStringStr("field"); + std::string type = result.GetStringStr("type"); + std::string value = result.GetStringStr("value"); + + if (type == "int") + luaspell->SetSpellDataGeneric(field, atoi(value.c_str())); + else if (type == "float") + luaspell->SetSpellDataGeneric(field, static_cast(atof(value.c_str()))); + else if (type == "bool") + luaspell->SetSpellDataGeneric(field, value == "1"); + else if (type == "string") + luaspell->SetSpellDataGeneric(field, value); + + luaspell->MarkFieldModified(field); + } +} + +void WorldDatabase::LoadCustomSpellDataIndex(Client* client, LuaSpell* luaspell) { + if (!luaspell || !luaspell->spell || !luaspell->spell->GetSpellData() || !luaspell->spell->IsCopiedSpell()) + return; + + DatabaseResult result; + if (!database_new.Select(&result, "SELECT idx, type, value1, value2 FROM character_custom_spell_dataindex WHERE charid = %u AND spell_id = %u", + client->GetPlayer()->GetCharacterID(), luaspell->spell->GetSpellData()->inherited_spell_id)) + return; + + while (result.Next()) { + int idx = result.GetInt32Str("idx"); + std::string type = result.GetStringStr("type"); + std::string v1 = result.GetStringStr("value1"); + std::string v2 = result.GetStringStr("value2"); + + if (type == "int") + luaspell->SetSpellDataIndex(idx, atoi(v1.c_str()), atoi(v2.c_str())); + else if (type == "float") + luaspell->SetSpellDataIndex(idx, static_cast(atof(v1.c_str())), static_cast(atof(v2.c_str()))); + else if (type == "bool") + luaspell->SetSpellDataIndex(idx, v1 == "1"); + else if (type == "string") + luaspell->SetSpellDataIndex(idx, v1, v2); + } +} + +void WorldDatabase::LoadCustomSpellDisplayEffects(Client* client, LuaSpell* luaspell) { + if (!luaspell || !luaspell->spell || !luaspell->spell->GetSpellData() || !luaspell->spell->IsCopiedSpell()) + return; + + DatabaseResult result; + if (!database_new.Select(&result, "SELECT idx, field, value FROM character_custom_spell_display WHERE charid = %u AND spell_id = %u", + client->GetPlayer()->GetCharacterID(), luaspell->spell->GetSpellData()->inherited_spell_id)) + return; + + while (result.Next()) { + int idx = result.GetInt32Str("idx"); + std::string field = result.GetStringStr("field"); + std::string value = result.GetStringStr("value"); + + SpellDisplayEffect* effect = luaspell->spell->GetSpellDisplayEffectSafe(idx); + if (!effect) + continue; + + if (field == "description") { + effect->description = value; + effect->needs_db_save = true; + } + else if (field == "bullet") { + effect->subbullet = atoi(value.c_str()); + effect->needs_db_save = true; + } + else if (field == "percentage") { + effect->percentage = atoi(value.c_str()); + effect->needs_db_save = true; + } + } +} + void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int8 db_spell_type) { SpellProcess* spellProcess = client->GetCurrentZone()->GetSpellProcess(); @@ -8046,7 +8310,7 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int bool isExistingLuaSpell = false; MaintainedEffects* effect = nullptr; Client* tmpCaster = nullptr; - if(caster_char_id == player->GetCharacterID() && (target_char_id == 0xFFFFFFFF || target_char_id == player->GetCharacterID()) && (effect = player->GetMaintainedSpell(spell_id)) != nullptr) + if(caster_char_id == player->GetCharacterID() && (target_char_id == 0xFFFFFFFF || target_char_id == player->GetCharacterID()) && (effect = player->GetMaintainedSpell(spell_id, true)) != nullptr) { safe_delete(lua_spell); lua_spell = effect->spell; @@ -8056,9 +8320,9 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int isExistingLuaSpell = true; } else if ( caster_char_id != player->GetCharacterID() && (tmpCaster = zone_list.GetClientByCharID(caster_char_id)) != nullptr - && tmpCaster->GetPlayer() && (effect = tmpCaster->GetPlayer()->GetMaintainedSpell(spell_id)) != nullptr) + && tmpCaster->GetPlayer() && (effect = tmpCaster->GetPlayer()->GetMaintainedSpell(spell_id, true)) != nullptr) { - if(effect->spell && effect->spell_id == spell_id) + if(effect->spell && (effect->spell_id == spell_id || effect->inherited_spell_id == spell_id)) { safe_delete(lua_spell); if(tmpCaster->GetCurrentZone() == player->GetZone()) @@ -8143,6 +8407,8 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int lua_spell->is_damage_spell = has_damaged; lua_spell->initial_caster_level = caster_level; } + + lua_spell->is_loaded_recast = true; if(lua_spell->initial_target == 0 && target_char_id == 0xFFFFFFFF && player->HasPet()) { @@ -8152,6 +8418,10 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int //lua_spell->num_calls ?? //if(target_char_id == player->GetCharacterID()) // lua_spell->targets.push_back(player->GetID()); + LogWrite(LUA__WARNING, 0, "LUA", "WorldDatabase::LoadCustomSpell: %s (%u) lua_spell caster %s (%u), caster char id: %u. IsCopiedSpell: %u", lua_spell->spell->GetName(), lua_spell->spell->GetSpellID(), lua_spell->caster ? lua_spell->caster->GetName() : "", lua_spell->caster ? lua_spell->caster->GetID() : 0, caster_char_id, lua_spell->spell->IsCopiedSpell()); + LoadCustomSpellData(client, lua_spell); + LoadCustomSpellDataIndex(client, lua_spell); + LoadCustomSpellDisplayEffects(client, lua_spell); if(db_spell_type == DB_TYPE_SPELLEFFECTS) { @@ -8186,9 +8456,7 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int } else { - lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__); - lua_spell->char_id_targets.insert(make_pair(player->GetCharacterID(),0)); - lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__); + spellProcess->AddLuaSpellTarget(lua_spell, player->GetID()); } player->MSpellEffects.releasewritelock(); continue; @@ -8211,25 +8479,20 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int info->spell_effects[effect_slot].expire_timestamp = Timer::GetCurrentTime2() + expire_timestamp; info->spell_effects[effect_slot].icon = icon; info->spell_effects[effect_slot].icon_backdrop = icon_backdrop; - info->spell_effects[effect_slot].spell_id = spell_id; + info->spell_effects[effect_slot].spell_id = lua_spell->spell->GetSpellID(); + info->spell_effects[effect_slot].inherited_spell_id = lua_spell->spell->GetSpellData()->inherited_spell_id; info->spell_effects[effect_slot].tier = tier; info->spell_effects[effect_slot].total_time = total_time; info->spell_effects[effect_slot].spell = lua_spell; - lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__); - multimap::iterator entries; - while((entries = lua_spell->char_id_targets.find(player->GetCharacterID())) != lua_spell->char_id_targets.end()) - { - lua_spell->char_id_targets.erase(entries); - } - lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__); - lua_spell->slot_pos = slot_pos; if(!isExistingLuaSpell) lua_spell->caster = player; // TODO: get actual player lua_spell->zone = player->GetZone(); + spellProcess->AddLuaSpellTarget(lua_spell, player->GetID()); + player->MSpellEffects.releasewritelock(); if(!isMaintained) @@ -8272,8 +8535,6 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int { if(client != client2) { - lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__); - if(client2->GetPlayer()->GetPet() && maintained_target_type == PET_TYPE_COMBAT) { restoreSpells.insert(make_pair(lua_spell, client2->GetPlayer()->GetPet())); @@ -8284,29 +8545,16 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int restoreSpells.insert(make_pair(lua_spell, client2->GetPlayer()->GetCharmedPet())); // target added via restoreSpells } - - lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__); } } // end of pet clause else if(client != client2) // maintained type must be 0, so client restoreSpells.insert(make_pair(lua_spell, client2->GetPlayer())); - lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__); - multimap::iterator entries; - for(entries = lua_spell->char_id_targets.begin(); entries != lua_spell->char_id_targets.end(); entries++) - { - int32 ent_char_id = entries->first; - int8 ent_target_type = entries->second; - if(ent_char_id == target_char && ent_target_type == maintained_target_type) - entries = lua_spell->char_id_targets.erase(entries); - } - lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__); + lua_spell->RemoveCharIDTargetAndType(target_char, maintained_target_type); } else { - lua_spell->MSpellTargets.writelock(__FUNCTION__, __LINE__); - lua_spell->char_id_targets.insert(make_pair(target_char,maintained_target_type)); - lua_spell->MSpellTargets.releasewritelock(__FUNCTION__, __LINE__); + lua_spell->AddCharIDTarget(target_char,maintained_target_type); } } } @@ -8335,7 +8583,8 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int info->maintained_effects[effect_slot].expire_timestamp = Timer::GetCurrentTime2() + expire_timestamp; info->maintained_effects[effect_slot].icon = icon; info->maintained_effects[effect_slot].icon_backdrop = icon_backdrop; - info->maintained_effects[effect_slot].spell_id = spell_id; + info->maintained_effects[effect_slot].spell_id = lua_spell->spell->GetSpellID(); + info->maintained_effects[effect_slot].inherited_spell_id = lua_spell->spell->GetSpellData()->inherited_spell_id; info->maintained_effects[effect_slot].tier = tier; info->maintained_effects[effect_slot].total_time = total_time; info->maintained_effects[effect_slot].spell = lua_spell; @@ -8407,7 +8656,7 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int Client* tmpCaster = nullptr; MaintainedEffects* effect = nullptr; if (caster_char_id != player->GetCharacterID() && (tmpCaster = zone_list.GetClientByCharID(caster_char_id)) != nullptr && (cross_zone_target_buff || - tmpCaster->GetCurrentZone() == player->GetZone()) && tmpCaster->GetPlayer() && (effect = tmpCaster->GetPlayer()->GetMaintainedSpell(in_spell_id)) != nullptr) + tmpCaster->GetCurrentZone() == player->GetZone()) && tmpCaster->GetPlayer() && (effect = tmpCaster->GetPlayer()->GetMaintainedSpell(in_spell_id, true)) != nullptr) { if(prev_target_type > 0) { @@ -8419,7 +8668,7 @@ void WorldDatabase::LoadCharacterSpellEffects(int32 char_id, Client* client, int restoreSpells.insert(make_pair(effect->spell, player->GetCharmedPet())); } } - else if(!player->GetSpellEffect(effect->spell_id, tmpCaster->GetPlayer())) + else if(!player->GetSpellEffect(effect->spell_id, tmpCaster->GetPlayer(), true)) { if(effect->spell->initial_target_char_id == player->GetCharacterID()) effect->spell->initial_target = player->GetID(); @@ -8772,7 +9021,6 @@ void WorldDatabase::SaveSignMark(int32 char_id, int32 sign_id, char* char_name, LogWrite(SIGN__DEBUG, 0, "Sign", "ERROR in WorldDatabase::SaveSignMark"); return; } - } string WorldDatabase::GetSignMark(int32 char_id, int32 sign_id, char* char_name) { @@ -8805,4 +9053,121 @@ int32 WorldDatabase::GetMysqlExpCurve(int level) { } //return 1 so we dont break shit later divide by 0 and all that. return 1; +} + +bool WorldDatabase::RemoveBrokerItem(int32 cid, int64 uid, int32 quantity) { + if (quantity <= 0) + return false; + + string update = string("UPDATE broker_items SET count = count - %u WHERE character_id = %u AND unique_id = %llu AND count >= %u"); + Query q; + q.RunQuery2( + Q_UPDATE, + update.c_str(), quantity, cid, uid, quantity + ); + + if (q.GetErrorNumber() && q.GetError() && q.GetErrorNumber() < 0xFFFFFFFF) { + LogWrite(WORLD__ERROR, 0, "Broker", + "DB error removing %u from item %llu for char %u: %s", + quantity, uid, cid, q.GetError()); + return false; + } + + // exactly one row updated ⇒ we had enough stock (and trigger will + // delete it if count hit zero) + return (q.GetAffectedRows() == 1); +} + +int32 WorldDatabase::LoadBrokerSellers(BrokerManager &broker) { + Query query; + MYSQL_ROW row; + int32 count = 0; + + MYSQL_RES * result = query.RunQuery2( + Q_SELECT, + "SELECT character_id, seller_name, house_id, sale_enabled, sell_from_inventory, coin_session, coin_total FROM broker_sellers" + ); + + while (result && (row = mysql_fetch_row(result))) { + int32 cid = atoul(row[0]); + std::string name = row[1] ? row[1] : ""; + int32 hid = strtoull(row[2], NULL, 0); + bool en = atoi(row[3]) != 0; + bool inv = atoi(row[4]) != 0; + int64 coin_session = strtoull(row[5], NULL, 0); + int64 coin_total = strtoull(row[6], NULL, 0); + + broker.LoadSeller(cid, name, hid, true, inv, coin_session, coin_total); + ++count; + + LogWrite(PLAYER__DEBUG, 5, "Broker", + "--Loaded broker player: %u (%s), house=%u, sale=%u, inv=%u", + cid, name.c_str(), hid, en, inv + ); + } + + return count; +} + +int32 WorldDatabase::LoadBrokerItems(BrokerManager &broker) { + Query query; + MYSQL_ROW row; + int32 count = 0; + + MYSQL_RES * result = query.RunQuery2( + Q_SELECT, + "SELECT unique_id, character_id, house_id, item_id," + " cost_copper, for_sale, inv_slot_id, slot_id, count, from_inventory, creator " + " FROM broker_items" + ); + + while (result && (row = mysql_fetch_row(result))) { + SaleItem it{}; + it.unique_id = strtoull(row[0], NULL, 0); + it.character_id = atoi(row[1]); + it.house_id = atoi(row[2]); + it.item_id = strtoull(row[3], NULL, 0); + it.cost_copper = atoi(row[4]); + it.for_sale = atoi(row[5]) != 0; + it.inv_slot_id = atoi(row[6]); + it.slot_id = atoi(row[7]); + it.count = atoi(row[8]); + it.from_inventory = atoul(row[9]); + if(row[10]) + it.creator = std::string(row[10]); + + broker.LoadItem(it); + ++count; + + LogWrite(PLAYER__DEBUG, 5, "Broker", + "--Loaded broker item: uid=%llu, char=%u, item=%llu," + " price=%u, count=%u", + it.unique_id, it.character_id, + it.item_id, it.cost_copper, + it.count + ); + } + + return count; +} + +int32 WorldDatabase::LoadBrokerData(BrokerManager &broker) { + int32 p = LoadBrokerSellers(broker); + int32 i = LoadBrokerItems(broker); + return p + i; +} + +bool WorldDatabase::UpdateHouseSpawnScript(int32 dbid, std::string scriptName) { + string update = string("UPDATE spawn_houses SET lua_script='%s' where id=%u"); + Query q; + q.RunQuery2( + Q_UPDATE, + update.c_str(), getSafeEscapeString(scriptName.c_str()).c_str(), dbid + ); + + if (q.GetErrorNumber() && q.GetError() && q.GetErrorNumber() < 0xFFFFFFFF) { + return false; + } + + return (q.GetAffectedRows() == 1); } \ No newline at end of file diff --git a/old/WorldServer/WorldDatabase.h b/old/WorldServer/WorldDatabase.h index 09653bb..0ef5067 100644 --- a/old/WorldServer/WorldDatabase.h +++ b/old/WorldServer/WorldDatabase.h @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #ifndef EQ2WORLD_EMU_DATABASE_H #define EQ2WORLD_EMU_DATABASE_H @@ -30,11 +31,11 @@ #include #include -#include "../common/database/database.hpp" -#include "../common/types.hpp" -#include "../common/misc_functions.hpp" - -#include "../common/database_new.hpp" +#include "../common/database.h" +#include "../common/types.h" +#include "../common/MiscFunctions.h" +#include "../common/Mutex.h" +#include "../common/DatabaseNew.h" #include "client.h" #include "Object.h" #include "Widget.h" @@ -50,6 +51,7 @@ #include "Rules/Rules.h" #include "Languages.h" #include "World.h" +#include "Broker/BrokerManager.h" using namespace std; @@ -175,13 +177,13 @@ public: void SaveWorldTime(WorldTime* time); bool SaveSpawnInfo(Spawn* spawn); - int32 GetNextSpawnIDInZone(int32 zone_id); + int32 GetNextSpawnIDInZone(int32 zone_id, bool isInstanceType = false); bool SaveSpawnEntry(Spawn* spawn, const char* spawn_location_name, int8 percent, float x_offset, float y_offset, float z_offset, bool save_zonespawn = true, bool create_spawnlocation = true); float GetSpawnLocationPlacementOffsetX(int32 location_id); float GetSpawnLocationPlacementOffsetY(int32 location_id); float GetSpawnLocationPlacementOffsetZ(int32 location_id); - int32 GetNextSpawnLocation(); - bool CreateNewSpawnLocation(int32 id, const char* name); + int32 GetNextSpawnLocation(bool isInstanceType = false); + bool CreateNewSpawnLocation(int32 id, const char* name, bool isHouseType = false); bool RemoveSpawnFromSpawnLocation(Spawn* spawn); int32 GetSpawnLocationCount(int32 location, Spawn* spawn = 0); vector* GetSpawnNameList(const char* in_name); @@ -208,7 +210,7 @@ public: int32 SaveCharacter(PacketStruct* create, int32 loginID); int32 LoadNPCAppearanceEquipmentData(ZoneServer* zone); void SaveNPCAppearanceEquipment(int32 spawn_id, int8 slot_id, int16 type, int8 red=0, int8 green=0, int8 blue=0, int8 hred=0, int8 hgreen=0, int8 hblue=0); - void LoadSpecialZones(); + void LoadSpecialZones(bool silent=false); void SaveCharacterSkills(Client* client); void SaveCharacterQuests(Client* client); void SaveCharacterQuestProgress(Client* client, Quest* quest); @@ -240,7 +242,7 @@ public: void UpdateStartingSkillbar(int32 char_id, int8 class_id, int8 race_id); void UpdateStartingTitles(int32 char_id, int8 class_id, int8 race_id, int8 gender_id); bool UpdateSpawnLocationSpawns(Spawn* spawn); - bool UpdateSpawnWidget(int32 widget_id, char* query); + bool UpdateSpawnWidget(int32 widget_id, char* query, bool is_house = false); bool CheckVersionTable(); void LoadFactionAlliances(); void LoadFactionList(); @@ -275,16 +277,16 @@ public: void LoadGroundSpawnItems(ZoneServer* zone); void LoadSpawns(ZoneServer* zone); int8 GetAppearanceType(string type); - void LoadNPCs(ZoneServer* zone); + void LoadNPCs(ZoneServer* zone, bool isInstanceType = false); void LoadSpiritShards(ZoneServer* zone); int32 LoadAppearances(ZoneServer* zone, Client* client = 0); int32 LoadNPCSpells(); int32 LoadNPCSkills(ZoneServer* zone); int32 LoadNPCEquipment(ZoneServer* zone); - void LoadObjects(ZoneServer* zone); - void LoadGroundSpawns(ZoneServer* zone); - void LoadWidgets(ZoneServer* zone); - void LoadSigns(ZoneServer* zone); + void LoadObjects(ZoneServer* zone, bool isInstanceType = false); + void LoadGroundSpawns(ZoneServer* zone, bool isInstanceType = false); + void LoadWidgets(ZoneServer* zone, bool isInstanceType = false); + void LoadSigns(ZoneServer* zone, bool isInstanceType = false); void ReloadItemList(int32 item_id = 0); void LoadItemList(int32 item_id = 0); int32 LoadItemStats(int32 item_id = 0); @@ -293,7 +295,8 @@ public: int32 LoadItemLevelOverride(int32 item_id = 0); int32 LoadItemEffects(int32 item_id = 0); int32 LoadBookPages(int32 item_id = 0); - int32 LoadNextUniqueItemID(); + int64 LoadNextUniqueItemID(); + void ResetNextUniqueItemID(); int32 LoadSkillItems(int32 item_id = 0); int32 LoadRangeWeapons(int32 item_id = 0); int32 LoadThrownWeapons(int32 item_id = 0); @@ -330,7 +333,8 @@ public: bool InsertCharacterStats(int32 character_id, int8 class_id, int8 race_id); bool UpdateCharacterTimeStamp(int32 account_id, int32 character_id, int32 timestamp); bool insertCharacterProperty(Client* client, char* propName, char* propValue); - bool loadCharacterProperties(Client* client); + bool insertCharacterProperty(int32 charID, char* propName, char* propValue); + bool loadCharacterProperties(Client* client, bool preload = false); string GetPlayerName(char* name); int32 GetCharacterTimeStamp(int32 character_id, int32 account_id,bool* char_exists); int32 GetCharacterTimeStamp(int32 character_id); @@ -656,9 +660,23 @@ public: float x, float y, float z, float heading, int32 gridid, int32 charid, int32 zoneid, int32 instanceid); bool DeleteSpiritShard(int32 id); + void LoadCustomSpellData(Client* client, LuaSpell* luaspell); + void LoadCustomSpellDataIndex(Client* client, LuaSpell* luaspell); + void LoadCustomSpellDisplayEffects(Client* client, LuaSpell* luaspell); void LoadCharacterSpellEffects(int32 char_id, Client *client, int8 db_spell_type); int32 GetMysqlExpCurve(int level); + + bool RemoveBrokerItem(int32 cid, int64 uid, int32 quantity); + int32 LoadBrokerSellers(BrokerManager &broker); + int32 LoadBrokerItems(BrokerManager &broker); + int32 LoadBrokerData(BrokerManager &broker); + + void ClearSellerSession(int32 character_id); + void AddToSellerSession(int32 character_id, int64 amount); + int64 GetSellerSession(int32 character_id); + + bool UpdateHouseSpawnScript(int32 dbid, std::string scriptName); private: DatabaseNew database_new; std::map zone_names; diff --git a/old/WorldServer/Zone/ChestTrap.h b/old/WorldServer/Zone/ChestTrap.h index 1735d2c..9d4c9d3 100644 --- a/old/WorldServer/Zone/ChestTrap.h +++ b/old/WorldServer/Zone/ChestTrap.h @@ -1,8 +1,8 @@ #include #include #include - -#include "../../common/types.hpp" +#include "../../common/Mutex.h" +#include "../../common/types.h" #pragma once using namespace std; diff --git a/old/WorldServer/Zone/map.h b/old/WorldServer/Zone/map.h index 3b42d17..b6cdd92 100644 --- a/old/WorldServer/Zone/map.h +++ b/old/WorldServer/Zone/map.h @@ -22,8 +22,9 @@ #ifndef ZONE_MAP_H #define ZONE_MAP_H -#include "../../common/types.hpp" -#include "../../common/misc_functions.hpp" +#include "../../common/types.h" +#include "../../common/MiscFunctions.h" +#include "../../common/Mutex.h" #include "position.h" #include diff --git a/old/WorldServer/Zone/mob_movement_manager.h b/old/WorldServer/Zone/mob_movement_manager.h index 517b296..9520261 100644 --- a/old/WorldServer/Zone/mob_movement_manager.h +++ b/old/WorldServer/Zone/mob_movement_manager.h @@ -1,7 +1,7 @@ #pragma once #include #include "../Entity.h" - +#include "../../common/Mutex.h" class Mob; class Client; diff --git a/old/WorldServer/Zone/region_map.h b/old/WorldServer/Zone/region_map.h index 4593bc0..e2414f9 100644 --- a/old/WorldServer/Zone/region_map.h +++ b/old/WorldServer/Zone/region_map.h @@ -1,8 +1,8 @@ #ifndef EQ2EMU_REGION_MAP_H #define EQ2EMU_REGION_MAP_H -#include "../../common/types.hpp" -#include "../../common/misc_functions.hpp" +#include "../../common/types.h" +#include "../../common/MiscFunctions.h" #include "position.h" #include diff --git a/old/WorldServer/achievements/achievements.hpp b/old/WorldServer/achievements/achievements.hpp deleted file mode 100644 index 7fb625e..0000000 --- a/old/WorldServer/achievements/achievements.hpp +++ /dev/null @@ -1,575 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License - -#pragma once - -#include -#include -#include -#include -#include -#include -#include "../../common/types.hpp" -#include "../../common/log.hpp" -#include "../../common/config_reader.hpp" -#include "../Items/Items.h" - -using namespace std; - -// Forward declarations -extern ConfigReader configReader; -extern class MasterAchievementList master_achievement_list; - -// Structure representing achievement reward data -struct AchievementRewards -{ - int32 achievement_id; // ID of the associated achievement - string reward; // Description of the reward -}; - -// Structure representing achievement requirement data -struct AchievementRequirements -{ - int32 achievement_id; // ID of the associated achievement - string name; // Name/description of the requirement - int32 qty_req; // Quantity required to fulfill this requirement -}; - -// Structure representing achievement update item data -struct AchievementUpdateItems -{ - int32 achievement_id; // ID of the associated achievement - int32 item_update; // Update value for the item -}; - -// Main achievement class representing a single achievement with all its data -class Achievement -{ -public: - // Default constructor - initializes all members to default values - Achievement(); - - // Copy constructor - creates deep copy of another achievement - Achievement(Achievement *in); - - // Destructor - cleans up dynamically allocated requirement and reward structures - virtual ~Achievement(); - - // Setters for achievement properties - void SetID(int32 id) { this->id = id; } - void SetTitle(const char *title) { strncpy(this->title, title, sizeof(this->title)); } - void SetUncompletedText(const char *uncompleted_text) { strncpy(this->uncompleted_text, uncompleted_text, sizeof(this->uncompleted_text)); } - void SetCompletedText(const char *completed_text) { strncpy(this->completed_text, completed_text, sizeof(this->completed_text)); } - void SetCategory(const char *category) { strncpy(this->category, category, sizeof(this->category)); } - void SetExpansion(const char *expansion) { strncpy(this->expansion, expansion, sizeof(this->expansion)); } - void SetIcon(int16 icon) { this->icon = icon; } - void SetPointValue(int32 point_value) { this->point_value = point_value; } - void SetQtyReq(int32 qty_req) { this->qty_req = qty_req; } - void SetHide(bool hide) { this->hide = hide; } - void SetUnknown3a(int32 unknown3a) { this->unknown3a = unknown3a; } - void SetUnknown3b(int32 unknown3b) { this->unknown3b = unknown3b; } - - // Adds a requirement structure to this achievement - void AddAchievementRequirement(struct AchievementRequirements *requirement); - - // Adds a reward structure to this achievement - void AddAchievementReward(struct AchievementRewards *reward); - - // Getters for achievement properties - int32 GetID() { return id; } - const char * GetTitle() { return title; } - const char * GetUncompletedText() { return uncompleted_text; } - const char * GetCompletedText() { return completed_text; } - const char * GetCategory() { return category; } - const char * GetExpansion() { return expansion; } - int16 GetIcon() { return icon; } - int32 GetPointValue() { return point_value; } - int32 GetQtyReq() { return qty_req; } - bool GetHide() { return hide; } - int32 GetUnknown3a() { return unknown3a; } - int32 GetUnknown3b() { return unknown3b; } - vector * GetRequirements() { return &requirements; } - vector * GetRewards() { return &rewards; } - -private: - int32 id; // Unique identifier for this achievement - char title[512]; // Display title of the achievement - char uncompleted_text[512]; // Text shown when achievement is not completed - char completed_text[512]; // Text shown when achievement is completed - char category[32]; // Category this achievement belongs to - char expansion[32]; // Expansion pack this achievement is from - int16 icon; // Icon ID for display - int32 point_value; // Point value awarded for completion - int32 qty_req; // Base quantity requirement - bool hide; // Whether this achievement should be hidden - int32 unknown3a; // Unknown field for future use - int32 unknown3b; // Unknown field for future use - vector requirements; // List of requirements - vector rewards; // List of rewards -}; - -// Class representing an achievement update with completion data -class AchievementUpdate -{ -public: - // Default constructor - initializes members to default values - AchievementUpdate(); - - // Copy constructor - creates deep copy of another achievement update - AchievementUpdate(AchievementUpdate *in); - - // Destructor - cleans up dynamically allocated update item structures - virtual ~AchievementUpdate(); - - // Setters for update properties - void SetID(int32 id) { this->id = id; } - void SetCompletedDate(int32 completed_date) { this->completed_date = completed_date; } - - // Adds an update item structure to this achievement update - void AddAchievementUpdateItems(struct AchievementUpdateItems *update_item); - - // Getters for update properties - int32 GetID() { return id; } - int32 GetCompletedDate() { return completed_date; } - vector * GetUpdateItems() { return &update_items; } - -private: - int32 id; // Achievement ID this update refers to - int32 completed_date; // Timestamp when achievement was completed - vector update_items; // List of update items -}; - -// Master list managing all achievements in the system with thread safety -class MasterAchievementList -{ -public: - // Constructor - initializes member variables and mutex - MasterAchievementList(); - - // Destructor - cleans up all achievements and resources - virtual ~MasterAchievementList(); - - // Adds an achievement to the master list (thread-safe) - bool AddAchievement(Achievement *achievement); - - // Retrieves an achievement by ID (thread-safe) - Achievement * GetAchievement(int32 achievement_id); - - // Removes all achievements from the list (thread-safe) - void ClearAchievements(); - - // Returns the number of achievements in the list (thread-safe) - int32 Size(); - - // Creates the master achievement list packet for client communication - void CreateMasterAchievementListPacket(); - - // Returns the serialized achievement packet, null if not created - EQ2Packet * GetAchievementPacket() { return m_packetsCreated ? masterPacket : nullptr; } - - EQ2Packet *masterPacket; // Serialized packet data for client - -private: - std::shared_mutex mutex_achievements; // Thread safety for achievement operations - map achievements; // Map of achievement ID to achievement object - bool m_packetsCreated; // Flag indicating if packet has been created -}; - -// Player-specific achievement list without thread safety requirements -class PlayerAchievementList -{ -public: - // Constructor - no special initialization needed - PlayerAchievementList(); - - // Destructor - cleans up all player achievements - virtual ~PlayerAchievementList(); - - // Adds an achievement to the player's list - bool AddAchievement(Achievement *achievement); - - // Retrieves an achievement by ID from player's list - Achievement * GetAchievement(int32 achievement_id); - - // Removes all achievements from player's list - void ClearAchievements(); - - // Returns the number of achievements in player's list - int32 Size(); - - // Returns direct access to the achievements map - map * GetAchievements() { return &achievements; } - -private: - map achievements; // Map of player's achievements -}; - -// Player-specific achievement update list for tracking completion progress -class PlayerAchievementUpdateList -{ -public: - // Constructor - no special initialization needed - PlayerAchievementUpdateList(); - - // Destructor - cleans up all achievement updates - virtual ~PlayerAchievementUpdateList(); - - // Adds an achievement update to the player's list - bool AddAchievementUpdate(AchievementUpdate *achievement_update); - - // Removes all achievement updates from player's list - void ClearAchievementUpdates(); - - // Returns the number of achievement updates in player's list - int32 Size(); - - // Returns direct access to the achievement updates map - map * GetAchievementUpdates() { return &achievement_updates; } - -private: - map achievement_updates; // Map of player's achievement updates -}; - -// Implementation of Achievement class methods - -Achievement::Achievement() -{ - id = 0; - memset(title, 0, sizeof(title)); - memset(uncompleted_text, 0, sizeof(uncompleted_text)); - memset(completed_text, 0, sizeof(completed_text)); - memset(category, 0, sizeof(category)); - memset(expansion, 0, sizeof(expansion)); - icon = 0; - point_value = 0; - qty_req = 0; - hide = false; - unknown3a = 0; - unknown3b = 0; -} - -Achievement::Achievement(Achievement *in) -{ - vector *requirements_in; - vector *rewards_in; - vector::iterator itr; - vector::iterator itr2; - struct AchievementRequirements *achievement_requirement; - struct AchievementRewards *achievement_reward; - - assert(in); - - id = in->GetID(); - strncpy(title, in->GetTitle(), sizeof(title)); - strncpy(uncompleted_text, in->GetUncompletedText(), sizeof(uncompleted_text)); - strncpy(completed_text, in->GetCompletedText(), sizeof(completed_text)); - strncpy(category, in->GetCategory(), sizeof(category)); - strncpy(expansion, in->GetExpansion(), sizeof(expansion)); - icon = in->GetIcon(); - point_value = in->GetPointValue(); - qty_req = in->GetQtyReq(); - hide = in->GetHide(); - unknown3a = in->GetUnknown3a(); - unknown3b = in->GetUnknown3b(); - - // Deep copy requirements - requirements_in = in->GetRequirements(); - for (itr = requirements_in->begin(); itr != requirements_in->end(); itr++) { - achievement_requirement = new struct AchievementRequirements; - achievement_requirement->achievement_id = (*itr)->achievement_id; - achievement_requirement->name = (*itr)->name; - achievement_requirement->qty_req = (*itr)->qty_req; - requirements.push_back(achievement_requirement); - } - - // Deep copy rewards - rewards_in = in->GetRewards(); - for (itr2 = rewards_in->begin(); itr2 != rewards_in->end(); itr2++) { - achievement_reward = new struct AchievementRewards; - achievement_reward->achievement_id = (*itr2)->achievement_id; - achievement_reward->reward = (*itr2)->reward; - rewards.push_back(achievement_reward); - } -} - -Achievement::~Achievement() -{ - vector::iterator itr; - vector::iterator itr2; - - // Clean up all requirements - for (itr = requirements.begin(); itr != requirements.end(); itr++) - delete *itr; - - // Clean up all rewards - for (itr2 = rewards.begin(); itr2 != rewards.end(); itr2++) - delete *itr2; -} - -void Achievement::AddAchievementRequirement(struct AchievementRequirements *requirement) -{ - assert(requirement); - requirements.push_back(requirement); -} - -void Achievement::AddAchievementReward(struct AchievementRewards *reward) -{ - assert(reward); - rewards.push_back(reward); -} - -// Implementation of AchievementUpdate class methods - -AchievementUpdate::AchievementUpdate() -{ - id = 0; - completed_date = 0; -} - -AchievementUpdate::AchievementUpdate(AchievementUpdate *in) -{ - vector *items_in; - vector::iterator itr; - struct AchievementUpdateItems *items; - - assert(in); - - id = in->GetID(); - completed_date = in->GetCompletedDate(); - - // Deep copy update items - items_in = in->GetUpdateItems(); - for (itr = items_in->begin(); itr != items_in->end(); itr++) { - items = new struct AchievementUpdateItems; - items->achievement_id = (*itr)->achievement_id; - items->item_update = (*itr)->item_update; - update_items.push_back(items); - } -} - -AchievementUpdate::~AchievementUpdate() -{ - vector::iterator itr; - - // Clean up all update items - for (itr = update_items.begin(); itr != update_items.end(); itr++) - delete *itr; -} - -void AchievementUpdate::AddAchievementUpdateItems(struct AchievementUpdateItems *update_item) -{ - assert(update_item); - update_items.push_back(update_item); -} - -// Implementation of MasterAchievementList class methods - -MasterAchievementList::MasterAchievementList() -{ - m_packetsCreated = false; - masterPacket = nullptr; -} - -MasterAchievementList::~MasterAchievementList() -{ - ClearAchievements(); -} - -bool MasterAchievementList::AddAchievement(Achievement *achievement) -{ - bool ret = false; - - assert(achievement); - - // Use unique_lock for write operations - std::unique_lock lock(mutex_achievements); - if (achievements.count(achievement->GetID()) == 0) { - achievements[achievement->GetID()] = achievement; - ret = true; - } - - return ret; -} - -Achievement * MasterAchievementList::GetAchievement(int32 achievement_id) -{ - Achievement *achievement = nullptr; - - // Use shared_lock for read operations - std::shared_lock lock(mutex_achievements); - if (achievements.count(achievement_id) > 0) - achievement = achievements[achievement_id]; - - return achievement; -} - -void MasterAchievementList::ClearAchievements() -{ - map::iterator itr; - - // Use unique_lock for write operations - std::unique_lock lock(mutex_achievements); - for (itr = achievements.begin(); itr != achievements.end(); itr++) - delete itr->second; - achievements.clear(); -} - -int32 MasterAchievementList::Size() -{ - // Use shared_lock for read operations - std::shared_lock lock(mutex_achievements); - return achievements.size(); -} - -void MasterAchievementList::CreateMasterAchievementListPacket() -{ - map::iterator itr; - Achievement *achievement; - vector *requirements = nullptr; - vector::iterator itr2; - AchievementRequirements *requirement; - vector *rewards = nullptr; - vector::iterator itr3; - AchievementRewards *reward; - PacketStruct *packet; - int16 i = 0; - int16 j = 0; - int16 k = 0; - int16 version = 1096; - - // Get packet structure for achievement data - if (!(packet = configReader.getStruct("WS_CharacterAchievements", version))) { - return; - } - - // Set up packet array for all achievements - packet->setArrayLengthByName("num_achievements", achievements.size()); - for (itr = achievements.begin(); itr != achievements.end(); itr++) { - achievement = itr->second; - - // Set basic achievement data - packet->setArrayDataByName("achievement_id", achievement->GetID(), i); - packet->setArrayDataByName("title", achievement->GetTitle(), i); - packet->setArrayDataByName("uncompleted_text", achievement->GetUncompletedText(), i); - packet->setArrayDataByName("completed_text", achievement->GetCompletedText(), i); - packet->setArrayDataByName("category", achievement->GetCategory(), i); - packet->setArrayDataByName("expansion", achievement->GetExpansion(), i); - packet->setArrayDataByName("icon", achievement->GetIcon(), i); - packet->setArrayDataByName("point_value", achievement->GetPointValue(), i); - packet->setArrayDataByName("qty_req", achievement->GetQtyReq(), i); - packet->setArrayDataByName("hide_achievement", achievement->GetHide(), i); - packet->setArrayDataByName("unknown3", achievement->GetUnknown3a(), i); - packet->setArrayDataByName("unknown3", achievement->GetUnknown3b(), i); - - requirements = achievement->GetRequirements(); - rewards = achievement->GetRewards(); - j = 0; - k = 0; - - // Set achievement requirements - packet->setSubArrayLengthByName("num_items", requirements->size(), i, j); - for (itr2 = requirements->begin(); itr2 != requirements->end(); itr2++) { - requirement = *itr2; - packet->setSubArrayDataByName("item_name", requirement->name.c_str(), i, j); - packet->setSubArrayDataByName("item_qty_req", requirement->qty_req, i, j); - j++; - } - - // Set achievement rewards - packet->setSubArrayLengthByName("num_rewards", achievement->GetRewards()->size(), i, k); - for (itr3 = rewards->begin(); itr3 != rewards->end(); itr3++) { - reward = *itr3; - packet->setSubArrayDataByName("reward_item", reward->reward.c_str(), i, k); - k++; - } - i++; - } - - // Serialize and create final packet - EQ2Packet* data = packet->serialize(); - masterPacket = new EQ2Packet(OP_ClientCmdMsg, data->pBuffer, data->size); - delete packet; - delete data; - - m_packetsCreated = true; -} - -// Implementation of PlayerAchievementList class methods - -PlayerAchievementList::PlayerAchievementList() -{ -} - -PlayerAchievementList::~PlayerAchievementList() -{ - ClearAchievements(); -} - -bool PlayerAchievementList::AddAchievement(Achievement *achievement) -{ - assert(achievement); - - if (achievements.count(achievement->GetID()) == 0) { - achievements[achievement->GetID()] = achievement; - return true; - } - - return false; -} - -Achievement * PlayerAchievementList::GetAchievement(int32 achievement_id) -{ - if (achievements.count(achievement_id) > 0) - return achievements[achievement_id]; - - return nullptr; -} - -void PlayerAchievementList::ClearAchievements() -{ - map::iterator itr; - - for (itr = achievements.begin(); itr != achievements.end(); itr++) - delete itr->second; - achievements.clear(); -} - -int32 PlayerAchievementList::Size() -{ - return achievements.size(); -} - -// Implementation of PlayerAchievementUpdateList class methods - -PlayerAchievementUpdateList::PlayerAchievementUpdateList() -{ -} - -PlayerAchievementUpdateList::~PlayerAchievementUpdateList() -{ - ClearAchievementUpdates(); -} - -bool PlayerAchievementUpdateList::AddAchievementUpdate(AchievementUpdate *update) -{ - assert(update); - - if (achievement_updates.count(update->GetID()) == 0) { - achievement_updates[update->GetID()] = update; - return true; - } - return false; -} - -void PlayerAchievementUpdateList::ClearAchievementUpdates() -{ - map::iterator itr; - - for (itr = achievement_updates.begin(); itr != achievement_updates.end(); itr++) - delete itr->second; - achievement_updates.clear(); -} - -int32 PlayerAchievementUpdateList::Size() -{ - return achievement_updates.size(); -} \ No newline at end of file diff --git a/old/WorldServer/classes.cpp b/old/WorldServer/classes.cpp index e8779dc..ba9d7fd 100644 --- a/old/WorldServer/classes.cpp +++ b/old/WorldServer/classes.cpp @@ -17,10 +17,10 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ -#include "../common/debug.hpp" -#include "../common/log.hpp" +#include "../common/debug.h" +#include "../common/Log.h" #include "classes.h" -#include "../common/misc_functions.hpp" +#include "../common/MiscFunctions.h" #include Classes::Classes(){ diff --git a/old/WorldServer/classes.h b/old/WorldServer/classes.h index e858434..58beefe 100644 --- a/old/WorldServer/classes.h +++ b/old/WorldServer/classes.h @@ -19,7 +19,7 @@ */ #ifndef CLASSES_CH #define CLASSES_CH -#include "../common/types.hpp" +#include "../common/types.h" #include using namespace std; diff --git a/old/WorldServer/client.cpp b/old/WorldServer/client.cpp index 107e011..e069ac9 100644 --- a/old/WorldServer/client.cpp +++ b/old/WorldServer/client.cpp @@ -1,24 +1,25 @@ -/* -EQ2Emulator: Everquest II Server Emulator -Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) -This file is part of EQ2Emulator. + This file is part of EQ2Emulator. -EQ2Emulator 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. + EQ2Emulator 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. -EQ2Emulator 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. + EQ2Emulator 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 EQ2Emulator. If not, see . + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . */ -#include "../common/debug.hpp" -#include "../common/log.hpp" + +#include "../common/debug.h" +#include "../common/Log.h" #include #include #include @@ -34,7 +35,7 @@ along with EQ2Emulator. If not, see . #include "PlayerGroups.h" #include "Commands/Commands.h" #include "ClientPacketFunctions.h" -#include "../common/config_reader.hpp" +#include "../common/ConfigReader.h" #include "Guilds/Guild.h" #include "Variables.h" #include "Rules/Rules.h" @@ -73,15 +74,15 @@ along with EQ2Emulator. If not, see . #include "client.h" #include "../common/emu_opcodes.h" -#include "../common/packet/packet_dump.hpp" -#include "WorldDatabase.hpp" +#include "../common/packet_dump.h" +#include "WorldDatabase.h" #include "races.h" #include "classes.h" #include "LoginServer.h" #include "World.h" -#include "../common/eq_common_structs.hpp" +#include "../common/EQ2_Common_Structs.h" #include "net.h" -#include "../common/misc_functions.hpp" +#include "../common/MiscFunctions.h" #include "Skills.h" #include "LuaInterface.h" #include "Quests.h" @@ -93,6 +94,7 @@ along with EQ2Emulator. If not, see . #include "AltAdvancement/AltAdvancement.h" #include "Bots/Bot.h" #include "VisualStates.h" +#include "Broker/BrokerManager.h" extern WorldDatabase database; extern const char* ZONE_NAME; @@ -129,14 +131,15 @@ extern MasterRecipeBookList master_recipebook_list; extern VisualStates visual_states; extern PeerManager peer_manager; extern HTTPSClientPool peer_https_pool; +extern BrokerManager broker; using namespace std; -Client::Client(EQStream* ieqs) : underworld_cooldown_timer(5000), pos_update(125), quest_pos_timer(2000), lua_debug_timer(30000), delayTimer(500), transmuteID(0), temp_placement_timer(10), spawn_removal_timer(250) { +Client::Client(EQStream* ieqs) : underworld_cooldown_timer(5000), zone_enter_timer(500), pos_update(125), quest_pos_timer(2000), lua_debug_timer(30000), delayTimer(500), transmuteID(0), temp_placement_timer(10), spawn_removal_timer(250) { eqs = ieqs; ip = eqs->GetrIP(); port = ntohs(eqs->GetrPort()); - merchant_transaction = nullptr; + merchant_transaction_id = 0; mail_window.item = nullptr; // don't want this to be set(loose ptr) when using ResetSendMail to provide rest of the defaults ResetSendMail(false); timestamp_flag = 0; @@ -190,7 +193,7 @@ Client::Client(EQStream* ieqs) : underworld_cooldown_timer(5000), pos_update(125 dead_z = 0.0f; dead_h = 0.0f; lua_debug_timer.Disable(); - transport_spawn = 0; + transport_spawn_id = 0; MBuyBack.SetName("Client::MBuyBack"); MDeletePlayer.SetName("Client::MDeletePlayer"); MQuestPendingUpdates.SetName("Client::MQuestPendingUpdates"); @@ -241,6 +244,11 @@ Client::Client(EQStream* ieqs) : underworld_cooldown_timer(5000), pos_update(125 recipe_xor_packet = nullptr; recipe_packet_count = 0; recipe_orig_packet_size = 0; + gm_store_search = false; + SetShopWindowStatus(false); + search_page = 0; + firstlogin_transmit = false; + zone_enter_timer.Disable(); } Client::~Client() { @@ -305,7 +313,7 @@ void Client::RemoveClientFromZone() { zone_list.RemoveClientFromMap(player->GetName(), this); safe_delete(camp_timer); - safe_delete(search_items); + ClearItemSearch(); safe_delete(current_rez.expire_timer); safe_delete(pending_last_name); safe_delete_array(incoming_paperdoll.image_bytes); @@ -419,7 +427,7 @@ void Client::SendLoginInfo() { database.LoadCharacterQuestRewards(this); database.LoadPlayerMail(this); } - LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal..."); + LogWrite(CCLIENT__DEBUG, 0, "Client", "Client::SendLoginInfo %s Send Quest Journal...", GetPlayer()->GetName()); SendQuestJournal(true, 0, false); if (version > 561) // right version? possibly not! @@ -597,7 +605,7 @@ void Client::HandlePlayerRevive(int32 point_id) player->SetResurrecting(true); player->SetHP(player->GetTotalHP()); player->SetPower(player->GetTotalPower()); - + //revive at zone safe coords if (!revive_point) { @@ -728,9 +736,25 @@ void Client::SendControlGhost(int32 send_id, int8 unknown2) { } } -void Client::SendCharInfo() { - EQ2Packet* app; - +void Client::BeginPreCharInfo() { + if (!IsReadyForSpawns()) { + if (GetPlayer()->GetMap()) { + if(!firstlogin) { // find the appropriate grid id + auto loc = glm::vec3(GetPlayer()->GetX(), GetPlayer()->GetZ(), GetPlayer()->GetY()); + uint32 GridID = 0; + float new_z = GetPlayer()->FindBestZ(loc, nullptr, &GridID); + GetPlayer()->SetLocation(GridID); + } + } + SetReadyForSpawns(true); + } + else { + LogWrite(PLAYER__ERROR, 0, "Player", "ERROR! Player %s is already past stage BeginPreCharInfo, zone enter timer running: %u..", GetPlayer()->GetName(), zone_enter_timer.Enabled()); + return; + } + + player->CalculateApplyWeight(); + player->SetEquippedItemAppearances(); ClientPacketFunctions::SendCharacterData(this); @@ -758,6 +782,13 @@ void Client::SendCharInfo() { } GetCurrentZone()->AddSpawn(player); + + zone_enter_timer.Start(); +} + +void Client::SendCharInfo() { + zone_enter_timer.Disable(); + if (IsReloadingZone() && (zoning_x || zoning_y || zoning_z)) { GetPlayer()->SetX(zoning_x); GetPlayer()->SetY(zoning_y); @@ -772,7 +803,7 @@ void Client::SendCharInfo() { if (guild) guild->GuildMemberLogin(this, firstlogin); - app = player->GetPlayerItemList()->serialize(GetPlayer(), GetVersion()); + EQ2Packet* app = player->GetPlayerItemList()->serialize(GetPlayer(), GetVersion()); if (app) { LogWrite(CCLIENT__PACKET, 0, "Client", "Dump/Print Packet in func: %s, line: %i", __FUNCTION__, __LINE__); //DumpPacket(app); @@ -837,8 +868,10 @@ void Client::SendCharInfo() { if (player->GetHP() < player->GetTotalHP() || player->GetPower() < player->GetTotalPower()) GetCurrentZone()->AddDamagedSpawn(player); - if (firstlogin) + if (firstlogin) { + firstlogin_transmit = true; firstlogin = false; + } player->ClearProcs(); items = player->GetEquippedItemList(); @@ -909,6 +942,12 @@ void Client::SendCharInfo() { GetPlayer()->SetSaveSpellEffects(false); GetPlayer()->SetCharSheetChanged(true); GetPlayer()->SetReturningFromLD(false); + + broker.LockActiveItemsForClient(this); + + GetPlayer()->GetZone()->GetSpellProcess()->SendSpellBookUpdate(this); + pos_update.Start(); + quest_pos_timer.Start(); } void Client::SendZoneSpawns() { @@ -1425,7 +1464,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) { } case OP_CancelMoveObjectModeMsg: { SetSpawnPlacementMode(Client::ServerSpawnPlacementMode::DEFAULT); - if (GetTempPlacementSpawn()) + if (GetTempPlacementSpawn() && GetCurrentZone()) { Spawn* tmp = GetTempPlacementSpawn(); SetTempPlacementSpawn(nullptr); @@ -1440,7 +1479,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) { case OP_PositionMoveableObject: { LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_PositionMoveableObject", opcode, opcode); PacketStruct* place_object = configReader.getStruct("WS_PlaceMoveableObject", GetVersion()); - if (place_object && place_object->LoadPacketData(app->pBuffer, app->size)) { + if (place_object && place_object->LoadPacketData(app->pBuffer, app->size) && GetCurrentZone()) { Spawn* spawn = 0; bool was_temp_placement = false; if (GetTempPlacementSpawn()) { @@ -1490,7 +1529,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) { spawn->position_changed = true; _snprintf(query, 256, "open_heading=%f,include_heading=1", newHeading); - if (database.UpdateSpawnWidget(widget->GetWidgetID(), query)) + if (database.UpdateSpawnWidget(widget->GetWidgetID(), query, GetCurrentZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE)) SimpleMessage(CHANNEL_COLOR_YELLOW, "Successfully saved widget open heading information."); } else @@ -1507,7 +1546,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) { spawn->position_changed = true; _snprintf(query, 256, "closed_heading=%f,include_heading=1", newHeading); - if (database.UpdateSpawnWidget(widget->GetWidgetID(), query)) + if (database.UpdateSpawnWidget(widget->GetWidgetID(), query, GetCurrentZone()->GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE)) SimpleMessage(CHANNEL_COLOR_YELLOW, "Successfully saved widget close heading information."); if (spawn->GetSpawnLocationID()) @@ -1596,20 +1635,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) { } case OP_DoneLoadingEntityResourcesMsg: { LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_DoneLoadingEntityResourcesMsg", opcode, opcode); - if (!IsReadyForSpawns()) { - if (GetPlayer()->GetMap()) { - auto loc = glm::vec3(GetPlayer()->GetX(), GetPlayer()->GetZ(), GetPlayer()->GetY()); - uint32 GridID = 0; - float new_z = GetPlayer()->FindBestZ(loc, nullptr, &GridID); - GetPlayer()->SetLocation(GridID); - } - SetReadyForSpawns(true); - } - player->CalculateApplyWeight(); - SendCharInfo(); - GetPlayer()->GetZone()->GetSpellProcess()->SendSpellBookUpdate(this); - pos_update.Start(); - quest_pos_timer.Start(); + BeginPreCharInfo(); break; } case OP_LootItemsRequestMsg: { @@ -1914,12 +1940,32 @@ bool Client::HandlePacket(EQApplicationPacket* app) { if (packet) { if (packet->LoadPacketData(app->pBuffer, app->size) && player->GetZone()) { EQ2_16BitString str = packet->getType_EQ2_16BitString_ByName("signal"); - if (strcmp(str.data.c_str(), "sys_client_avatar_ready") == 0) { + if (GetCurrentZone() && strcmp(str.data.c_str(), "sys_client_avatar_ready") == 0) { LogWrite(CCLIENT__DEBUG, 0, "Client", "Client '%s' (%u) is ready for spawn updates.", GetPlayer()->GetName(), GetPlayer()->GetCharacterID()); SetReloadingZone(false); if (GetPlayer()->IsDeletedSpawn()) { GetPlayer()->SetDeletedSpawn(false); } + + if(firstlogin_transmit) { + if (auto info = broker.GetSellerInfo(GetPlayer()->GetCharacterID())) { + auto logs = broker.GetSellerLog(GetPlayer()->GetCharacterID()); + for (auto const & log : logs) { + SendHouseSaleLog(log.message, + 0, + 0, + 0); + } + } + firstlogin_transmit = false; + } + if(HasOwnerOrEditAccess()) { // we are in their own house + int64 coin_session = database.GetSellerSession(GetPlayer()->GetCharacterID()); + if(coin_session) { + OpenShopWindow(nullptr, false, 1); + } + } + ResetZoningCoords(); SetReadyForUpdates(); GetPlayer()->SendSpawnChanges(true); @@ -2071,7 +2117,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) { LogWrite(PLAYER__ERROR, 1, "Player", "Player %s tried to target %u index, but that index was not valid.", GetPlayer()->GetName(), index); } } - if (GetPlayer()->GetTarget()) + if (GetPlayer()->GetTarget() && GetCurrentZone()) GetCurrentZone()->CallSpawnScript(GetPlayer()->GetTarget(), SPAWN_SCRIPT_TARGETED, GetPlayer()); //player->SetTarget((int16*)app->pBuffer); break; @@ -2129,9 +2175,7 @@ bool Client::HandlePacket(EQApplicationPacket* app) { EQ2_CommandString remote(app->pBuffer, app->size); LogWrite(PACKET__DEBUG, 1, "Packet", "RemoteCmdMsg Packet dump:"); -#if EQDEBUG >= 9 - DumpPacket(app); -#endif + //DumpPacket(app); commands.Process(remote.handler, &remote.command, this); } else //bad client, disconnect @@ -2274,7 +2318,8 @@ bool Client::HandlePacket(EQApplicationPacket* app) { string name = packet->getType_EQ2_16BitString_ByName("pet_name").data; if (strlen(name.c_str()) != 0 && SetPetName(name.c_str())) { target->SetName(name.c_str()); - GetCurrentZone()->SendUpdateTitles(target); + if(GetCurrentZone()) + GetCurrentZone()->SendUpdateTitles(target); change = true; } @@ -2447,10 +2492,12 @@ bool Client::HandlePacket(EQApplicationPacket* app) { int32 spawn_index = 0; if (GetVersion() <= 561) { - spawn_index = packet->getType_int32_ByName("house_id"); + house_id = packet->getType_int32_ByName("house_id"); + spawn_index = GetPlayer()->GetTarget() ? GetPlayer()->GetTarget()->GetID() : 0; } else { house_id = packet->getType_int64_ByName("house_id"); + spawn_index = packet->getType_int32_ByName("spawn_id"); } ZoneChangeDetails zone_details; if (GetHouseZoneServer(&zone_details, spawn_index, house_id)) { @@ -2603,6 +2650,8 @@ bool Client::HandlePacket(EQApplicationPacket* app) { } case OP_ExitHouseMsg: { LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_ExitHouseMsg", opcode, opcode); + if(!GetCurrentZone()) + break; int32 instance_id = GetCurrentZone()->GetInstanceID(); if (instance_id > 0) { PlayerHouse* ph = world.GetPlayerHouseByInstanceID(instance_id); @@ -2903,8 +2952,8 @@ bool Client::HandleLootItem(Spawn* entity, Item* item, Spawn* target, bool overr return false; } - if (lootingPlayer->item_list.HasFreeSlot() || lootingPlayer->item_list.CanStack(item)) { - if (lootingPlayer->item_list.AssignItemToFreeSlot(item)) { + if (lootingPlayer->item_list.HasFreeSlot() || lootingPlayer->item_list.CanStack(item, false)) { + if (lootingPlayer->item_list.AssignItemToFreeSlot(item, true)) { if (item->CheckFlag2(HEIRLOOM)) { // TODO: RAID Support GroupMemberInfo* gmi = lootingClient->GetPlayer()->GetGroupMemberInfo(); @@ -2966,12 +3015,12 @@ bool Client::HandleLootItem(Spawn* entity, Item* item, Spawn* target, bool overr guild->AddNewGuildEvent(type, "%s has looted the %s %s", Timer::GetUnixTimeStamp(), true, lootingPlayer->GetName(), adjective, item->CreateItemLink(GetVersion()).c_str()); guild->SendMessageToGuild(type, "%s has looted the %s %s", lootingPlayer->GetName(), adjective, item->CreateItemLink(GetVersion()).c_str()); } - + + lootingClient->CheckPlayerQuestsItemUpdate(item); + if (item->GetItemScript() && lua_interface) lua_interface->RunItemScript(item->GetItemScript(), "obtained", item, lootingPlayer); - lootingClient->CheckPlayerQuestsItemUpdate(item); - if (GetVersion() <= 561) { EQ2Packet* outapp = lootingPlayer->SendInventoryUpdate(GetVersion()); if (outapp) @@ -3009,6 +3058,9 @@ void Client::HandleLootItemRequestPacket(EQApplicationPacket* app) { bool loot_all = (packet->getType_int8_ByName("loot_all") == 1); int32 target_id = packet->getType_int32_ByName("target_id"); int8 button_clicked = packet->getType_int8_ByName("button_clicked"); + if(!GetCurrentZone()) + return; + Spawn* spawn = GetCurrentZone()->GetSpawnByID(loot_id); if (!spawn) { safe_delete(packet); @@ -3373,7 +3425,10 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) { Item* item = 0; // translate from unique id to spawn id for houses - Spawn* spawn = this->GetCurrentZone()->GetSpawnFromUniqueItemID(id); + Spawn* spawn = nullptr; + + if(GetCurrentZone()) + spawn = GetCurrentZone()->GetSpawnFromUniqueItemID(id); bool wasSpawn = false; if (spawn) @@ -3395,6 +3450,16 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) { item = GetPlayer()->GetAppearanceEquipmentList()->GetItemFromUniqueID(id); if (!item) item = master_item_list.GetItem(id); + if (!item && HasOwnerOrEditAccess()) + item = GetPlayer()->item_list.GetVaultItemFromUniqueID(id, true); + if(!item && GetMerchantTransactionID()) { + Spawn* merchant = GetPlayer()->GetZone()->GetSpawnByID(GetMerchantTransactionID()); + if(merchant && merchant->GetHouseCharacterID() && merchant->GetPickupUniqueItemID()) { + if(auto itemInfo = broker.GetActiveItem(merchant->GetHouseCharacterID(), id)) { + item = master_item_list.GetItem(itemInfo->item_id); + } + } + } if (item) {// && sent_item_details.count(id) == 0){ MItemDetails.writelock(__FUNCTION__, __LINE__); sent_item_details[id] = true; @@ -3406,8 +3471,8 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) { delete item; } else { - LogWrite(WORLD__ERROR, 0, "World", "HandleExamineInfoRequest: Unknown Item ID = %u", id); - DumpPacket(app); + LogWrite(WORLD__ERROR, 0, "World", "HandleExamineInfoRequest#0: Unknown Item ID = %u", id); + //DumpPacket(app); } } else if (type == 1) { @@ -3429,6 +3494,16 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) { item = GetPlayer()->GetAppearanceEquipmentList()->GetItemFromUniqueID(unique_id); if (!item) item = master_item_list.GetItem(id); + + if(!item && GetMerchantTransactionID()) { + Spawn* merchant = GetPlayer()->GetZone()->GetSpawnByID(GetMerchantTransactionID()); + if(merchant && merchant->GetHouseCharacterID() && merchant->GetPickupUniqueItemID()) { + if(auto itemInfo = broker.GetActiveItem(merchant->GetHouseCharacterID(), id)) { + item = master_item_list.GetItem(itemInfo->item_id); + } + } + } + if (item) { MItemDetails.writelock(__FUNCTION__, __LINE__); sent_item_details[id] = true; @@ -3437,8 +3512,8 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) { QueuePacket(app); } else { - LogWrite(WORLD__ERROR, 0, "World", "HandleExamineInfoRequest: Unknown Item ID = %u", id); - DumpPacket(app); + LogWrite(WORLD__ERROR, 0, "World", "HandleExamineInfoRequest#1: Unknown Item ID = %u", id); + //DumpPacket(app); } } else if (type == 2) { @@ -3461,6 +3536,16 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) { //int16 unknown5 = request->getType_sint16_ByName("unknown5"); //printf("Type: (%i) Unknown_0: (%u) Unknown_1: (%u) Unknown2: (%i) Unique ID: (%u) Unknown5: (%i) Item ID: (%u)\n",type,unknown_0,unknown_1,unknown2,unique_id,unknown5,id); Item* item = master_item_list.GetItem(id); + + if(!item && GetMerchantTransactionID()) { + Spawn* merchant = GetPlayer()->GetZone()->GetSpawnByID(GetMerchantTransactionID()); + if(merchant && merchant->GetHouseCharacterID() && merchant->GetPickupUniqueItemID()) { + if(auto itemInfo = broker.GetActiveItem(merchant->GetHouseCharacterID(), id)) { + item = master_item_list.GetItem(itemInfo->item_id); + } + } + } + if (item) { //only display popup for non merchant links EQ2Packet* app = item->serialize(GetVersion(), (request->getType_int8_ByName("show_popup") != 0), GetPlayer(), true, 0, 0, GetVersion() > 561 ? true : false); @@ -3468,8 +3553,8 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) { QueuePacket(app); } else { - LogWrite(WORLD__ERROR, 0, "World", "HandleExamineInfoRequest: Unknown Item ID = %u", id); - DumpPacket(app); + LogWrite(WORLD__ERROR, 0, "World", "HandleExamineInfoRequest#2: Unknown Item ID = %u", id); + //DumpPacket(app); } } else if (type == 4) { //spell effect @@ -3595,7 +3680,7 @@ void Client::HandleExamineInfoRequest(EQApplicationPacket* app) { } else { LogWrite(CCLIENT__ERROR, 0, "World", "Client::HandleExamineInfoRequest from %s: Unknown examine request: %i", GetPlayer()->GetName(), (int)type); - DumpPacket(app); + //DumpPacket(app); } safe_delete(request); @@ -3714,10 +3799,10 @@ bool Client::Process(bool zone_process) { } case NewLoginState::LOGIN_DELAYED: { if (!delay_msg_timer.Enabled() || delay_msg_timer.Check()) { - LogWrite(CCLIENT__INFO, 0, "Client", "Wait for zone %s to load for new client %s...", GetCurrentZone()->GetZoneName(), GetPlayer()->GetName()); + LogWrite(CCLIENT__INFO, 0, "Client", "Wait for zone %s to load for new client %s...", GetCurrentZone() ? GetCurrentZone()->GetZoneName() : "Unknown", GetPlayer()->GetName()); delay_msg_timer.Start(1000, true); } - if (!GetCurrentZone()->IsLoading()) { + if (GetCurrentZone() && !GetCurrentZone()->IsLoading()) { new_client_login = NewLoginState::LOGIN_ALLOWED; } @@ -3797,7 +3882,7 @@ bool Client::Process(bool zone_process) { if (temp_placement_timer.Check()) { if (GetTempPlacementSpawn() && GetPlayer()->WasSentSpawn(GetTempPlacementSpawn()->GetID()) && !hasSentTempPlacementSpawn) { int8 placement = 0; - int32 uniqueID = GetPlacementUniqueItemID(); + int64 uniqueID = GetPlacementUniqueItemID(); Item* uniqueItem = GetPlayer()->item_list.GetItemFromUniqueID(uniqueID); if (uniqueItem && uniqueItem->houseitem_info) placement = uniqueItem->houseitem_info->house_location; @@ -3806,6 +3891,16 @@ bool Client::Process(bool zone_process) { temp_placement_timer.Disable(); } } + if(zone_enter_timer.Check() && GetCurrentZone()) { + Spawn* spawn_ready = GetCurrentZone()->GetSpawnByID(GetPlayer()->GetID()); + if(spawn_ready) { + SendCharInfo(); + zone_enter_timer.Disable(); // no longer need to run this timer + } + else { + LogWrite(PLAYER__INFO, 0, "Player", "Player %s pending to zone..", GetPlayer()->GetName()); + } + } if (GetCurrentZone() && pos_update.Check()) { ProcessStateCommands(); @@ -3861,7 +3956,9 @@ bool Client::Process(bool zone_process) { player_pos_changed = false; - GetCurrentZone()->CheckTransporters(this); + // avoid a glitch where we keep triggering this over and over again in succession after transporter is selected + if(!IsReloadingZone() && !IsZoning()) + GetCurrentZone()->CheckTransporters(this); if (GetPlayer()->GetRegionMap()) { @@ -4097,6 +4194,8 @@ void ClientList::Process() { struct in_addr in; in.s_addr = client->GetIP(); LogWrite(WORLD__INFO, 0, "World", "Removing client from ip: %s port: %i", inet_ntoa(in), client->GetPort()); + if(client->GetPlayer()->GetClient() == client) + client->GetPlayer()->SetClient(nullptr); safe_delete(client); } } @@ -4494,6 +4593,10 @@ void Client::SimpleMessage(int8 color, const char* message) { } void Client::SendSpellUpdate(Spell* spell, bool add_silently, bool add_to_hotbar) { + // disable potentially putting a spell on the hotbar (like archetypes) that are not to be shown to player or book + if(spell && spell->GetSpellData() && spell->GetSpellData()->type == SPELL_BOOK_TYPE_NOT_SHOWN) + add_to_hotbar = false; + PacketStruct* packet = configReader.getStruct("WS_SpellGainedMsg", GetVersion()); if (packet) { int8 xxx = spell->GetSpellData()->is_aa; @@ -4861,7 +4964,7 @@ bool Client::GotoSpawn(const char* search_name, bool forceTarget) { } if (target && target != GetPlayer()) { GetPlayer()->SetX(target->GetX()); - GetPlayer()->SetY(y); + GetPlayer()->SetY(y, false, true); GetPlayer()->SetZ(target->GetZ()); GetPlayer()->SetHeading(target->GetHeading()); GetPlayer()->SetLocation(target->GetLocation()); @@ -5052,7 +5155,7 @@ void Client::Zone(ZoneChangeDetails* new_zone, ZoneServer* opt_zone, bool set_co } // block out the member info for the group - TempRemoveGroup(); + TempRemoveGroup(zoning_id, zoning_instance_id); UpdateTimeStampFlag(ZONE_UPDATE_FLAG); @@ -5238,7 +5341,7 @@ void Client::DetermineCharacterUpdates() { } void Client::Save() { - if (GetCharacterID() == 0 || IsSaveDisabled()) + if (GetCharacterID() == 0 || IsSaveDisabled() || !IsReadyForUpdates()) return; if (current_zone) { @@ -5428,12 +5531,29 @@ void Client::SendPopupMessage(int8 unknown, const char* text, const char* type, } -void Client::ChangeLevel(int16 old_level, int16 new_level) { +bool Client::ChangeLevel(int16 old_level, int16 new_level, int32 xp_earned) { if (new_level < 1) { SimpleMessage(CHANNEL_COLOR_RED, "You cannot be lower than level 1!"); - return; + return false; + } + const char* playerScript = world.GetPlayerScript(0); // 0 = global script + const char* playerZoneScript = world.GetPlayerScript(GetCurrentZoneID()); // zone script + sint32 returnValue = 0; + if(playerScript || playerZoneScript) { + std::vector args = { + LuaArg(GetPlayer()->GetZone()), + LuaArg(GetPlayer()), + LuaArg(old_level), + LuaArg(new_level), + LuaArg(xp_earned) + }; + if(playerScript && lua_interface->RunPlayerScriptWithReturn(playerScript, "on_level_up", args, &returnValue) && returnValue == -1) { + return false; + } + if(playerZoneScript && lua_interface->RunPlayerScriptWithReturn(playerZoneScript, "on_level_up", args, &returnValue) && returnValue == -1) { + return false; + } } - if (player->GetLevel() != new_level) { player->SetLevel(new_level); if (player->GetGroupMemberInfo()) { @@ -5625,17 +5745,54 @@ void Client::ChangeLevel(int16 old_level, int16 new_level) { if (GetPlayer()->GetHP() < GetPlayer()->GetTotalHP() || GetPlayer()->GetPower() < GetPlayer()->GetTotalPower()) GetPlayer()->GetZone()->AddDamagedSpawn(GetPlayer()); + + if(playerScript || playerZoneScript) { + std::vector args = { + LuaArg(GetPlayer()->GetZone()), + LuaArg(GetPlayer()), + LuaArg(old_level), + LuaArg(new_level), + LuaArg(xp_earned) + }; + if(playerScript && lua_interface->RunPlayerScriptWithReturn(playerScript, "on_level_up_complete", args, &returnValue) && returnValue == -1) { + return true; + } + if(playerZoneScript && lua_interface->RunPlayerScriptWithReturn(playerZoneScript, "on_level_up_complete", args, &returnValue) && returnValue == -1) { + return true; + } + } + + return true; } -void Client::ChangeTSLevel(int16 old_level, int16 new_level) { +bool Client::ChangeTSLevel(int16 old_level, int16 new_level, int32 xp_earned) { if (new_level < 1) { SimpleMessage(CHANNEL_COLOR_RED, "You cannot be lower than level 1!"); - return; + return false; } if ((player->GetTSLevel() >= 9 && player->GetTradeskillClass() == 1) || (player->GetTSLevel() >= 19 && (player->GetTradeskillClass() == 1 || player->GetTradeskillClass() == 2 || player->GetTradeskillClass() == 6 || player->GetTradeskillClass() == 10))) { SimpleMessage(CHANNEL_COLOR_YELLOW, "You can not gain levels until you select your next class!"); - return; + return false; } + const char* playerScript = world.GetPlayerScript(0); // 0 = global script + const char* playerZoneScript = world.GetPlayerScript(GetCurrentZoneID()); // zone script + sint32 returnValue = 0; + if(playerScript || playerZoneScript) { + std::vector args = { + LuaArg(GetPlayer()->GetZone()), + LuaArg(GetPlayer()), + LuaArg(old_level), + LuaArg(new_level), + LuaArg(xp_earned) + }; + if(playerScript && lua_interface->RunPlayerScriptWithReturn(playerScript, "on_tradeskill_level_up", args, &returnValue) && returnValue == -1) { + return false; + } + if(playerZoneScript && lua_interface->RunPlayerScriptWithReturn(playerZoneScript, "on_tradeskill_level_up", args, &returnValue) && returnValue == -1) { + return false; + } + } + if (new_level > old_level) player->UpdatePlayerHistory(HISTORY_TYPE_XP, HISTORY_SUBTYPE_TRADESKILL, new_level, player->GetTradeskillClass()); @@ -5820,6 +5977,7 @@ void Client::ChangeTSLevel(int16 old_level, int16 new_level) { // to when you are actually able to select traits. QueuePacket(GetPlayer()->GetPlayerInfo()->serialize(GetVersion())); QueuePacket(master_trait_list.GetTraitListPacket(this)); + return true; } void Client::CloseLoot(int32 spawn_id) { @@ -6381,19 +6539,18 @@ void Client::CastGroupOrSelf(Entity* source, uint32 spellID, uint32 spellTier, f } } -Spawn* Client::GetBanker() { - return banker; +int32 Client::GetBanker() { + return banker_id; } -void Client::SetBanker(Spawn* in_banker) { - banker = in_banker; - +void Client::SetBanker(int32 in_banker) { + banker_id = in_banker; } void Client::Bank(Spawn* banker, bool cancel) { if (banker && banker->primary_command_list.size() > 0 && banker->primary_command_list[0]->command == "bank") { if (!cancel) - SetBanker(banker); + SetBanker(banker->GetID()); else SetBanker(0); PacketStruct* packet = configReader.getStruct("WS_UpdateBank", GetVersion()); @@ -6550,7 +6707,8 @@ bool Client::BankWithdrawalNoBanker(int64 amount) { void Client::BankWithdrawal(int64 amount) { bool cheater = false; - if (GetBanker() && amount > 0) { + Spawn* banker = GetCurrentZone()->GetSpawnByID(GetBanker()); + if (banker && amount > 0) { string withdrawal = ""; char withdrawal_data[512] = { 0 }; int32 tmp = 0; @@ -6638,7 +6796,8 @@ void Client::BankWithdrawal(int64 amount) { void Client::BankDeposit(int64 amount) { bool cheater = false; - if (GetBanker() && amount > 0) { + Spawn* banker = GetCurrentZone()->GetSpawnByID(GetBanker()); + if (banker && amount > 0) { int32 tmp = 0; char deposit_data[512] = { 0 }; string deposit = ""; @@ -7083,6 +7242,38 @@ void Client::RemovePendingQuest(int32 quest_id) { } } +void Client::DeleteQuest(int32 quest_id, bool override_deny_delete) { + if(quest_id > 0) { + GetPlayer()->MPlayerQuests.readlock(__FUNCTION__, __LINE__); + if(lua_interface && GetPlayer()->player_quests.count(quest_id) > 0) { + Quest* quest = GetPlayer()->player_quests[quest_id]; + GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__); + if (quest && (override_deny_delete || quest->CanDeleteQuest())) { + lua_interface->CallQuestFunction(quest, "Deleted", GetPlayer()); + RemovePlayerQuest(quest_id); + GetCurrentZone()->SendQuestUpdates(this); + } + } + else { + GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__); + } + } +} + +void Client::DeleteAllQuests(bool override_deny_delete) { + std::vector quest_ids; + map::iterator itr; + GetPlayer()->MPlayerQuests.readlock(__FUNCTION__, __LINE__); + for (std::map::iterator itr = player->player_quests.begin(); itr != player->player_quests.end(); ++itr) { + quest_ids.push_back(itr->first); + } + GetPlayer()->MPlayerQuests.releasereadlock(__FUNCTION__, __LINE__); + + for (int32 id : quest_ids) { + DeleteQuest(id, override_deny_delete); + } +} + void Client::SetPlayerQuest(Quest* quest, map* progress) { if (!quest || !progress) { return; @@ -7123,14 +7314,15 @@ void Client::AddPlayerQuest(Quest* quest, bool call_accepted, bool send_packets) quest->SetPlayer(player); quest->SetSaveNeeded(true); + LogWrite(CCLIENT__DEBUG, 0, "Client", "Client::AddPlayerQuest %s added quest %s id %u...", GetPlayer()->GetName(), quest->GetName(), quest->GetQuestID()); + current_quest_id = quest->GetQuestID(); if (send_packets && quest->GetQuestGiver() > 0) GetCurrentZone()->SendSpawnChangesByDBID(quest->GetQuestGiver(), this, false, true); if (lua_interface && call_accepted) lua_interface->CallQuestFunction(quest, "Accepted", player); + if (send_packets) { - LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal..."); - //SendQuestJournal(); SendQuestJournalUpdate(quest); // sent twice to match live @@ -7159,15 +7351,15 @@ void Client::RemovePlayerQuest(int32 id, bool send_update, bool delete_quest) { int32 quest_giver = player->player_quests[id]->GetQuestGiver(); GetPlayer()->MPlayerQuests.releasewritelock(__FUNCTION__, __LINE__); + LogWrite(CCLIENT__DEBUG, 0, "Client", "Client::RemovePlayerQuest %s remove quest id %u...", GetPlayer()->GetName(), id); + if (send_update && quest_giver > 0) GetCurrentZone()->SendSpawnChangesByDBID(quest_giver, this, false, true); if (send_update) { - LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal..."); SendQuestJournal(false, 0, true); } player->RemoveQuest(id, delete_quest); if (send_update) { - LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal..."); SendQuestJournal(false, 0, true); GetCurrentZone()->SendAllSpawnsForVisChange(this); } @@ -7209,7 +7401,7 @@ void Client::SendQuestFailure(Quest* quest) { for (int32 i = 0; i < failures->size(); i++) { step = failures->at(i); QueuePacket(quest->QuestJournalReply(GetVersion(), GetNameCRC(), player, step, 1, false, true)); - LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal..."); + LogWrite(CCLIENT__DEBUG, 0, "Client", "Client::SendQuestFailure %s quest failure for %s id %u...", GetPlayer()->GetName(), quest->GetName(), quest->GetQuestID()); SendQuestJournal(false, 0, true); } failures->clear(); @@ -7237,8 +7429,9 @@ void Client::SendQuestUpdate(Quest* quest) { QueuePacket(quest->QuestJournalReply(GetVersion(), GetNameCRC(), player, step)); updated = true; } - LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal..."); - + if(updated) { + LogWrite(CCLIENT__DEBUG, 0, "Client", "Client::SendQuestUpdate %s step %u updated %s id %u...", GetPlayer()->GetName(), step->GetStepID(), quest->GetName(), quest->GetQuestID()); + } } if (lua_interface && quest->GetCompleted() && quest->GetCompleteAction()) { lua_interface->CallQuestFunction(quest, quest->GetCompleteAction(), player); @@ -7533,18 +7726,20 @@ void Client::PopulateQuestRewardItems(vector * items, PacketStruct* packe if (items) { int32 total_item_count = 0; - for (int s = 0; s < items->size(); s++) { - Item* tmpItem = items->at(s); - if (tmpItem) { - if (tmpItem->details.count > 1) { - total_item_count += tmpItem->details.count; - } - else { - total_item_count += 1; + if(GetVersion() < 546 || GetVersion() > 561) { + for (int s = 0; s < items->size(); s++) { + Item* tmpItem = items->at(s); + if (tmpItem) { + if (tmpItem->details.count > 1) { + total_item_count += tmpItem->details.count; + } + else { + total_item_count += 1; + } } } } - packet->setArrayLengthByName(num_rewards_str.c_str(), total_item_count); + packet->setArrayLengthByName(num_rewards_str.c_str(), (GetVersion() < 546 || GetVersion() > 561) ? total_item_count : items->size()); int16 count = 0; int16 pos = 0; for (int32 i = 0; i < items->size();) { @@ -7557,7 +7752,10 @@ void Client::PopulateQuestRewardItems(vector * items, PacketStruct* packe packet->setItemArrayDataByName(item_str.c_str(), items->at(i), player, pos, 0, 2); pos++; - + if(GetVersion() >= 546 && GetVersion() <= 561) { + i++; + continue; + } if (count >= items->at(i)->details.count - 1) { count = 0; } @@ -7724,8 +7922,10 @@ void Client::GiveQuestReward(Quest* quest, bool has_displayed) { AddPendingQuestAcceptReward(quest); + LogWrite(CCLIENT__DEBUG, 0, "Client", "Client::GiveQuestReward %s completed quest, displaying reward for %s id %u...", GetPlayer()->GetName(), quest->GetName(), quest->GetQuestID()); + DisplayQuestComplete(quest, quest->GetQuestTemporaryState(), quest->GetQuestTemporaryDescription()); - LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal..."); + SendQuestJournal(); if (quest->GetQuestTemporaryState()) { @@ -8002,8 +8202,13 @@ bool Client::AddItem(Item* item, bool* item_deleted, AddItemType type) { */ } CheckPlayerQuestsItemUpdate(item); + bool skipItemDeleted = true; if (item->GetItemScript() && lua_interface) - lua_interface->RunItemScript(item->GetItemScript(), "obtained", item, player); + if(lua_interface->RunItemScript(item->GetItemScript(), "obtained", item, player)) + skipItemDeleted = false; // we called "obtained" properly, check ptr to make sure it is valid + + if(!skipItemDeleted && !lua_interface->IsLuaUserDataValid(item) && item_deleted) + *item_deleted = true; } else { lua_interface->SetLuaUserDataStale(item); @@ -8014,7 +8219,9 @@ bool Client::AddItem(Item* item, bool* item_deleted, AddItemType type) { return false; } - + + OpenShopWindow(nullptr); + return true; } @@ -8129,12 +8336,17 @@ void Client::SetLuaDebugClient(bool val) { } void Client::SetMerchantTransaction(Spawn* spawn) { - merchant_transaction = spawn; + if(spawn) { + merchant_transaction_id = spawn->GetID(); + } + else { + merchant_transaction_id = 0; + } } -Spawn* Client::GetMerchantTransaction() { - return merchant_transaction; +int32 Client::GetMerchantTransactionID() { + return merchant_transaction_id; } void Client::SetMailTransaction(Spawn* spawn) { @@ -8199,10 +8411,13 @@ float Client::CalculateSellMultiplier(int32 merchant_id) { } void Client::SellItem(int32 item_id, int16 quantity, int32 unique_id) { - Spawn* spawn = GetMerchantTransaction(); + Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID()); Guild* guild = GetPlayer()->GetGuild(); if (spawn && spawn->GetMerchantID() > 0 && (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY)) && spawn->IsClientInMerchantLevelRange(this)) { + if((spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO)) + return; + int32 total_sell_price = 0; int32 total_status_sell_price = 0; //for status float multiplier = CalculateBuyMultiplier(spawn->GetMerchantID()); @@ -8218,11 +8433,15 @@ void Client::SellItem(int32 item_id, int16 quantity, int32 unique_id) { if (!item) item = player->item_list.GetItemFromID(item_id); if (item && master_item) { - if(item->details.inv_slot_id == -3 || item->details.inv_slot_id == -4) { + if(item->details.inv_slot_id == InventorySlotType::BANK || item->details.inv_slot_id == InventorySlotType::SHARED_BANK) { SimpleMessage(CHANNEL_COLOR_RED, "You cannot sell an item in the bank."); return; } - if (item->details.item_locked || item->details.equip_slot_id) + if(GetPlayer()->item_list.IsItemInSlotType(item, InventorySlotType::HOUSE_VAULT)) { + SimpleMessage(CHANNEL_COLOR_RED, "You cannot sell an item in the house vault."); + return; + } + if (item->IsItemLocked() || item->details.equip_slot_id) { SimpleMessage(CHANNEL_COLOR_RED, "You cannot sell the item in use."); return; @@ -8317,9 +8536,12 @@ void Client::SellItem(int32 item_id, int16 quantity, int32 unique_id) { } void Client::BuyBack(int32 item_id, int16 quantity) { - Spawn* spawn = GetMerchantTransaction(); + Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID()); if (spawn && spawn->GetMerchantID() > 0 && (!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY_BACK)) && spawn->IsClientInMerchantLevelRange(this)) { + if((spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO)) + return; + deque::iterator itr; BuyBackItem* buyback = 0; BuyBackItem* closest = 0; @@ -8348,7 +8570,7 @@ void Client::BuyBack(int32 item_id, int16 quantity) { sint64 dispFlags = 0; if (item && item->GetItemScript() && lua_interface && lua_interface->RunItemScript(item->GetItemScript(), "buyback_display_flags", item, player, nullptr, &dispFlags) && (dispFlags & DISPLAY_FLAG_NO_BUY)) SimpleMessage(CHANNEL_NARRATIVE, "You do not meet all the requirements to buy this item."); - else if (!player->item_list.HasFreeSlot() && !player->item_list.CanStack(item)) + else if (!player->item_list.HasFreeSlot() && !player->item_list.CanStack(item, false)) SimpleMessage(CHANNEL_COLOR_RED, "You do not have any slots available for this item."); else if (player->RemoveCoins(closest->quantity * closest->price)) { bool removed = false; @@ -8391,9 +8613,15 @@ void Client::BuyBack(int32 item_id, int16 quantity) { void Client::BuyItem(int32 item_id, int16 quantity) { // Get the merchant we are buying from - Spawn* spawn = GetMerchantTransaction(); + LogWrite(CCLIENT__ERROR, 0, "Client", "buy item %u quantity %u", item_id, quantity); + + Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID()); // Make sure the spawn has a merchant list - if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) { + if(spawn && spawn->GetHouseCharacterID()) { + broker.BuyItem(this, spawn->GetHouseCharacterID(), item_id, quantity); + SendMerchantWindow(spawn, false); + } + else if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) { int32 total_buy_price = 0; float multiplier = CalculateBuyMultiplier(spawn->GetMerchantID()); int32 sell_price = 0; @@ -8440,7 +8668,7 @@ void Client::BuyItem(int32 item_id, int16 quantity) { total_buy_price = sell_price * quantity; item = new Item(master_item); item->details.count = quantity; - if (!player->item_list.HasFreeSlot() && !player->item_list.CanStack(item)) { + if (!player->item_list.HasFreeSlot() && !player->item_list.CanStack(item, false)) { SimpleMessage(CHANNEL_COLOR_RED, "You do not have any slots available for this item."); lua_interface->SetLuaUserDataStale(item); safe_delete(item); @@ -8457,7 +8685,6 @@ void Client::BuyItem(int32 item_id, int16 quantity) { bool itemDeleted = false; AddItem(item, &itemDeleted); if (!itemDeleted) { - CheckPlayerQuestsItemUpdate(item); if (item && total_available < 0xFF) { world.DecreaseMerchantQuantity(spawn->GetMerchantID(), item_id, quantity); SendBuyMerchantList(); @@ -8554,7 +8781,6 @@ void Client::BuyItem(int32 item_id, int16 quantity) { bool itemDeleted = false; AddItem(item, &itemDeleted); if (!itemDeleted) { - CheckPlayerQuestsItemUpdate(item); if (item && total_available < 0xFF) { world.DecreaseMerchantQuantity(spawn->GetMerchantID(), item_id, quantity); SendBuyMerchantList(); @@ -8585,8 +8811,11 @@ void Client::BuyItem(int32 item_id, int16 quantity) { } void Client::RepairItem(int32 item_id) { - Spawn* spawn = GetMerchantTransaction(); + Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID()); if (spawn && (spawn->GetMerchantType() & MERCHANT_TYPE_REPAIR)) { + if((spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO)) + return; + Item* item = player->item_list.GetItemFromID(item_id); if (!item) item = player->GetEquipmentList()->GetItemFromItemID(item_id); @@ -8623,8 +8852,11 @@ void Client::RepairItem(int32 item_id) { } void Client::RepairAllItems() { - Spawn* spawn = GetMerchantTransaction(); + Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID()); if (spawn && (spawn->GetMerchantType() & MERCHANT_TYPE_REPAIR)) { + if((spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO)) + return; + vector* repairable_items = GetRepairableItems(); if (repairable_items && repairable_items->size() > 0) { vector::iterator itr; @@ -8771,8 +9003,11 @@ void Client::SendAchievementUpdate(bool first_login) { } void Client::SendBuyMerchantList(bool sell) { - Spawn* spawn = GetMerchantTransaction(); + Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID()); if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) { + if((spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO)) + return; + vector* items = world.GetMerchantItemList(spawn->GetMerchantID(), spawn->GetMerchantType(), player); if (items) { PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion()); @@ -8918,7 +9153,7 @@ void Client::SendBuyMerchantList(bool sell) { } void Client::SendSellMerchantList(bool sell) { - Spawn* spawn = GetMerchantTransaction(); + Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID()); if (!spawn || (spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY) || (spawn->GetMerchantType() & MERCHANT_TYPE_LOTTO)) return; @@ -8932,8 +9167,8 @@ void Client::SendSellMerchantList(bool sell) { for (test_itr = items->begin(); test_itr != items->end(); test_itr++) { bool isbagwithitems = false; - if(test_itr->second && (test_itr->second->details.inv_slot_id == -3 || test_itr->second->details.inv_slot_id == -4)) - continue; // omit bank/shared-bank + if(test_itr->second && (test_itr->second->details.inv_slot_id < 0)) + continue; // omit bank/shared-bank/vault/anything with negative inventory slot id if (test_itr->second && test_itr->second->IsBag() && (test_itr->second->details.num_slots - test_itr->second->details.num_free_slots != test_itr->second->details.num_slots)) isbagwithitems = true; @@ -9054,7 +9289,7 @@ void Client::SendSellMerchantList(bool sell) { void Client::SendBuyBackList(bool sell) { if (GetVersion() <= 561) //this wasn't added until LU37 on July 31st 2007, well after the DoF client return; - Spawn* spawn = GetMerchantTransaction(); + Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID()); if (spawn && spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)) { deque::iterator itr; int i = 0; @@ -9127,7 +9362,7 @@ void Client::SendBuyBackList(bool sell) { } void Client::SendRepairList() { - Spawn* spawn = GetMerchantTransaction(); + Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID()); if (spawn) { vector* repairable_items = GetRepairableItems(); PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion()); @@ -9202,7 +9437,7 @@ void Client::ShowLottoWindow() { SimpleMessage(CHANNEL_COLOR_RED, "This client does not support the gambler UI, only Desert of Flames or later client."); return; } - Spawn* spawn = GetMerchantTransaction(); + Spawn* spawn = GetCurrentZone()->GetSpawnByID(GetMerchantTransactionID()); if (spawn) { int32 item_id = rule_manager.GetZoneRule(GetCurrentZoneID(), R_World, GamblingTokenItemID)->GetInt32(); @@ -9383,7 +9618,7 @@ void Client::SendGuildCreateWindow() { } } -void Client::AddBuyBack(int32 unique_id, int32 item_id, int16 quantity, int32 price, bool save_needed) { +void Client::AddBuyBack(int64 unique_id, int32 item_id, int16 quantity, int32 price, bool save_needed) { BuyBackItem* item = new BuyBackItem; item->item_id = item_id; item->unique_id = unique_id; @@ -9554,6 +9789,25 @@ void Client::DisplayMailMessage(int32 mail_id) { safe_delete(update); } if (!mail->already_read) { + const char* playerScript = world.GetPlayerScript(0); // 0 = global script + const char* playerZoneScript = world.GetPlayerScript(GetCurrentZoneID()); // zone script + if(playerScript || playerZoneScript) { + std::vector args = { + LuaArg(GetCurrentZone()), + LuaArg(GetPlayer()), + LuaArg(GetMailTransaction()), + LuaArg(mail->player_from), + LuaArg(mail->subject), + LuaArg(mail->mail_body), + LuaArg(mail->char_item_id) + }; + if(playerScript) { + lua_interface->RunPlayerScriptWithReturn(playerScript, "on_mail_first_read", args); + } + if(playerZoneScript) { + lua_interface->RunPlayerScriptWithReturn(playerZoneScript, "on_mail_first_read", args); + } + } mail->already_read = true; SendMailList(); } @@ -9577,13 +9831,15 @@ void Client::DisplayMailMessage(int32 mail_id) { if (mail->stack || mail->char_item_id) { Item* item = master_item_list.GetItem(mail->char_item_id); - item->stack_count = mail->stack > 1 ? mail->stack : 0; - if (version < 860) - packet->setItemByName("item", item, player, 0, version <= 373 ? -2 : -1); - else if (version < 1193) - packet->setItemByName("item", item, player, 0, 0); - else - packet->setItemByName("item", item, player, 0, 2); + if(item) { + item->stack_count = mail->stack > 1 ? mail->stack : 0; + if (version < 860) + packet->setItemByName("item", item, player, 0, version <= 373 ? -2 : -1); + else if (version < 1193) + packet->setItemByName("item", item, player, 0, 0); + else + packet->setItemByName("item", item, player, 0, 2); + } } else { @@ -10033,7 +10289,7 @@ void Client::ProcessTeleport(Spawn* spawn, vector* destin if (transport_id > 0) has_map = GetCurrentZone()->TransportHasMap(transport_id); - transport_spawn = spawn; + transport_spawn_id = spawn->GetID(); vector transport_list; vector::iterator itr; TransportDestination* destination = 0; @@ -10043,7 +10299,7 @@ void Client::ProcessTeleport(Spawn* spawn, vector* destin transport_list.push_back(destination); } if (transport_list.size() == 0 && destination) { - if (destination->destination_zone_id == 0 || destination->destination_zone_id == GetCurrentZone()->GetZoneID()) { + if (destination->destination_zone_id == 0 || (destination->destination_zone_id == GetCurrentZone()->GetZoneID() && destination->type != TRANSPORT_TYPE_FORCEZONE)) { if (destination->type == TRANSPORT_TYPE_FLIGHT) SendFlightAutoMount(destination->flight_path_id, destination->mount_id, destination->mount_red_color, destination->mount_green_color, destination->mount_blue_color); @@ -10055,7 +10311,6 @@ void Client::ProcessTeleport(Spawn* spawn, vector* destin } } else { - // determine if this is an instanced zone that already exists ZoneChangeDetails zone_details; bool foundZone = world.GetGroupManager()->IdentifyMemberInGroupOrRaid(&zone_details, this, destination->destination_zone_id); @@ -10190,7 +10445,7 @@ void Client::ProcessTeleportLocation(EQApplicationPacket* app) { zone_name = zone_name.substr(0, lastSpacePos); } } - if (this->GetTemporaryTransportID() || (spawn && spawn == transport_spawn && spawn->GetTransporterID())) + if (this->GetTemporaryTransportID() || (spawn && spawn->GetID() == transport_spawn_id && spawn->GetTransporterID())) GetCurrentZone()->GetTransporters(&destinations, this, this->GetTemporaryTransportID() ? this->GetTemporaryTransportID() : spawn->GetTransporterID()); vector::iterator itr; for (itr = destinations.begin(); itr != destinations.end(); itr++) { @@ -10206,7 +10461,7 @@ void Client::ProcessTeleportLocation(EQApplicationPacket* app) { SimpleMessage(CHANNEL_COLOR_RED, "Error processing transport."); else { if (cost == 0 || player->RemoveCoins(cost)) { - if (destination->destination_zone_id == 0 || destination->destination_zone_id == GetCurrentZone()->GetZoneID()) { + if (destination->destination_zone_id == 0 || (destination->destination_zone_id == GetCurrentZone()->GetZoneID() && destination->type != TRANSPORT_TYPE_FORCEZONE)) { if (destination->type == TRANSPORT_TYPE_FLIGHT) SendFlightAutoMount(destination->flight_path_id, destination->mount_id, destination->mount_red_color, destination->mount_green_color, destination->mount_blue_color); @@ -10290,19 +10545,88 @@ void Client::AddSendNewSpells(vector* spells) { } } -void Client::SetItemSearch(vector* items) { +void Client::SetItemSearch(vector* items, map values) { if (items) { - safe_delete(search_items); - search_items = items; + ClearItemSearch(); + { + std::lock_guard L(item_search_mtx_); + search_items = items; + search_values = values; + } } } -vector* Client::GetSearchItems() { - return search_items; +void Client::ClearItemSearch() { + std::lock_guard L(item_search_mtx_); + if(search_items) { + for (auto it = search_items->begin(); it != search_items->end(); /* no increment here */) { + Item* item = *it; + if (item->is_search_store_item) { + safe_delete(item); + // erase returns the next iterator + it = search_items->erase(it); + } else { + ++it; + } + } + } + safe_delete(search_items); +} + +void Client::SendSellerItemByItemUniqueId(int64 unique_id) { + std::lock_guard L(item_search_mtx_); + if (!search_items) return; + + for (Item* item : *search_items) { + if (item && item->details.unique_id == unique_id) { + EQ2Packet* app = item->serialize(GetVersion(), true, GetPlayer()); + QueuePacket(app); + break; + } + } +} + + +void Client::BuySellerItemByItemUniqueId(int64 unique_id, int16 quantity) { + int32 seller_id = 0; + + { + std::lock_guard L(item_search_mtx_); + if (!search_items) return; + + for (Item* item : *search_items) { + if (item && item->details.unique_id == unique_id) { + seller_id = item->seller_char_id; + break; + } + } + } + + if(seller_id) { + if(broker.BuyItem(this, seller_id, unique_id, quantity)) { + vector* items = master_item_list.GetItems(search_values, this); + if(items){ + SetItemSearch(items, search_values); + SearchStore(search_page); + } + } + } + else { + Message(CHANNEL_COLOR_RED, "Could not find seller %u when attempting to buy unique id %u.", seller_id, unique_id); + } +} + +void Client::SetSellerStatus() { + bool sellInv = broker.CanSellFromInventory(GetPlayer()->GetCharacterID()); + bool itemsSelling = broker.IsSellingItems(GetPlayer()->GetCharacterID()); + broker.AddSeller(GetPlayer()->GetCharacterID(), std::string(GetPlayer()->GetName()), + GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), true, sellInv); } void Client::SearchStore(int32 page) { + std::lock_guard L(item_search_mtx_); + SetSearchPage(page); if (search_items) { PacketStruct* packet = configReader.getStruct("WS_BrokerItems", GetVersion()); if (packet) { @@ -10331,27 +10655,49 @@ void Client::SearchStore(int32 page) { std::string teststr("test "); teststr.append(std::to_string(i)); packet->setArrayDataByName("string_one", teststr.c_str(), i); - packet->setArrayDataByName("string_two", "testtwo", i); - packet->setArrayDataByName("seller_name", "EQ2EMuDev", i); - packet->setArrayDataByName("item_id", item->details.item_id, i); - packet->setArrayDataByName("item_id2", item->details.item_id, i); + if(IsGMStoreSearch()) { + packet->setArrayDataByName("seller_name", "EQ2EMuDev", i); + packet->setArrayDataByName("item_id", item->details.item_id, i); + packet->setArrayDataByName("item_id2", item->details.item_id, i); + packet->setArrayDataByName("sell_price", item->sell_price, i); + if (item->stack_count == 0) + packet->setArrayDataByName("quantity", 1, i); + else + packet->setArrayDataByName("quantity", item->stack_count, i); + packet->setArrayDataByName("string_two", "testtwo", i); + } + else { + packet->setArrayDataByName("seller_name", item->seller_name.c_str(), i); + packet->setArrayDataByName("item_id", item->details.unique_id, i); + packet->setArrayDataByName("item_id2", item->details.unique_id, i); + packet->setArrayDataByName("sell_price", item->broker_price, i); + packet->setArrayDataByName("quantity", item->details.count, i); + if(item->seller_house_id && !item->is_search_in_inventory) { + HouseZone* hz = world.GetHouseZone(item->seller_house_id); + if(hz && item->seller_name.size() > 0) { + string name; + name = item->seller_name; + name.append("'s "); + name.append(hz->name); + packet->setArrayDataByName("string_two", name.c_str(), i); + } + } + } + packet->setArrayDataByName("icon", item->GetIcon(GetVersion()), i); //packet->setArrayDataByName("unknown2b", i, i); packet->setArrayDataByName("item_seller_id", 1, i); - if (item->stack_count == 0) - packet->setArrayDataByName("quantity", 1, i); - else - packet->setArrayDataByName("quantity", item->stack_count, i); packet->setArrayDataByName("stack_size", item->stack_count, i); - packet->setArrayDataByName("sell_price", item->sell_price, i); std::string tmpStr(""); tmpStr.append(item->name.c_str()); - tmpStr.append(" ("); - tmpStr.append(std::to_string(item->details.item_id)); - tmpStr.append(")"); + if(IsGMStoreSearch()) { + tmpStr.append(" ("); + tmpStr.append(std::to_string(item->details.item_id)); + tmpStr.append(")"); + } packet->setArrayDataByName("item_name", tmpStr.c_str(), i); packet->setArrayDataByName("req_level", item->generic_info.adventure_default_level, i); @@ -10406,7 +10752,7 @@ void Client::SendChatRelationship(int8 type, const char* name) { if (type == 0) { Client* client = zone_list.GetClientByCharName(name); if (client) { - packet->setArrayDataByName("location", client->GetCurrentZone()->GetZoneName()); + packet->setArrayDataByName("location", client->GetCurrentZone() ? client->GetCurrentZone()->GetZoneName() : ""); packet->setArrayDataByName("class_name", classes.GetClassName(client->GetPlayer()->GetAdventureClass())); } } @@ -10435,7 +10781,7 @@ void Client::SendFriendList() { client = zone_list.GetClientByCharName(names[i]); packet->setArrayDataByName("name", names[i].c_str(), i); if (client) { - packet->setArrayDataByName("location", client->GetCurrentZone()->GetZoneName(), i); + packet->setArrayDataByName("location", client->GetCurrentZone() ? client->GetCurrentZone()->GetZoneName() : "", i); packet->setArrayDataByName("class_name", classes.GetClassName(client->GetPlayer()->GetAdventureClass()), i); } } @@ -11725,9 +12071,8 @@ void Client::AttemptStartAutoMount() { packet->setDataByName("speed", GetCurrentZone()->GetFlightPathSpeed(GetPendingFlightPath())); QueuePacket(packet->serialize()); safe_delete(packet); - - on_auto_mount = true; } + on_auto_mount = true; } else { LogWrite(CCLIENT__ERROR, 0, "Client", "OP_ReadyForTakeOffMsg recieved but unable to get an index for path (%u) in zone (%u)", GetPendingFlightPath(), GetCurrentZone()->GetZoneID()); @@ -11735,7 +12080,7 @@ void Client::AttemptStartAutoMount() { EndAutoMount(); } - SetPendingFlightPath(0); + //SetPendingFlightPath(0); } else @@ -11786,6 +12131,7 @@ void Client::EndAutoMount() { player->SetMountColor(&mount_color); player->SetMountSaddleColor(&saddle_color); player->SetTempMount(0); + SetPendingFlightPath(0); } bool Client::EntityCommandPrecheck(Spawn* spawn, const char* command) { @@ -11919,6 +12265,10 @@ bool Client::HandleNewLogin(int32 account_id, int32 access_code) new_client_login = NewLoginState::LOGIN_ALLOWED; } + database.loadCharacterProperties(this, true); + // vault slots + RefreshVaultSlotCount(); + const char* zone_script = world.GetZoneScript(GetCurrentZone()->GetZoneID()); if (zone_script && lua_interface) lua_interface->RunZoneScript(zone_script, "new_client", GetCurrentZone(), GetPlayer()); @@ -12221,9 +12571,128 @@ void Client::SendDefaultCommand(Spawn* spawn, const char* command, float distanc } } +void Client::RefreshVaultSlotCount() { + std::vector houses = world.GetAllPlayerHouses(GetCharacterID()); + if (!houses.empty()) { + int8 bestSlots = 0; + int32 bestZoneID = 0; + + for (PlayerHouse* ph : houses) { + if (!ph) + continue; + HouseZone* hz = world.GetHouseZone(ph->house_id); + if (!hz) + continue; + + if (hz->vault_slots > bestSlots) { + bestSlots = hz->vault_slots; + bestZoneID = ph->house_id; + } + } + + if (bestZoneID != 0) { + GetPlayer()->GetPlayerInfo()->SetHouseZone(bestZoneID); + GetPlayer()->SetHouseVaultSlots(bestSlots); + } + } +} + + +/* +** + October 18, 2005, 07:17:51 PM Rocawne buys Elemental Vestment (Adept I) for 1 Platinum, 0 Gold, 0 Silver, 0 Copper +October 19, 2005, 02:57:20 AM Radian buys A Rusting Gear for 5 Gold, 75 Silver, 0 Copper + +but then they stop outputting coin that equals 0 + +October 20, 2005, 05:29:33 AM Kilea buys Rules of the Sandscrawler Clan - Page 7 for 9 Gold, 75 Silver +October 21, 2005, 12:29:41 AM Meldo buys Oozing Wound (Apprentice IV) for 10 Gold + +February 14, 2006, 02:59:32 PM Tom buys 13 pu-erh tea leafes for 4 Gold, 55 Silver +**/ +void Client::SendHouseSaleLog(std::string message, int64 coin_session, int64 coin_total, int8 flag) { + PacketStruct* packet = configReader.getStruct("WS_HouseStoreLog", GetVersion()); + if (packet) { + packet->setDataByName("data", message.c_str()); + packet->setDataByName("coin_gain_session", coin_session); + packet->setDataByName("coin_gain_alltime", coin_total); + packet->setDataByName("sales_log_open", flag); + EQ2Packet* outapp = packet->serialize(); + QueuePacket(outapp); + safe_delete(packet); + } +} + +void Client::SetItemSaleStatus(int64 unique_id, bool status) { + broker.SetSaleStatus(GetPlayer()->GetCharacterID(), unique_id, status); + OpenShopWindow(nullptr); +} + +void Client::OpenShopWindow(Spawn* interaction, bool sendAlways, int8 saleLogOnly) { + if (GetCurrentZone()->GetInstanceType() == PERSONAL_HOUSE_INSTANCE && !HasOwnerOrEditAccess()) { + return; + } + + auto info = broker.GetSellerInfo(GetPlayer()->GetCharacterID()); + bool wasOpen = false; + + if(!saleLogOnly) { + wasOpen = GetShopWindowStatus(); + + if(sendAlways) + SetShopWindowStatus(true); + + if(!GetShopWindowStatus() && !sendAlways) + return; // don't send window is not open + + if(!info) + broker.AddSeller(GetPlayer()->GetCharacterID(), std::string(GetPlayer()->GetName()), GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), true, false); + + GetPlayer()->item_list.GetVaultItems(this, interaction ? interaction->GetID() : GetPlayer()->GetID(), GetPlayer()->GetHouseVaultSlots(), info ? info->sell_from_inventory : false); + + broker.LockActiveItemsForClient(this); + } + + if(!wasOpen && info) { + int64 coin_session = database.GetSellerSession(GetPlayer()->GetCharacterID()); + if(coin_session) { + broker.ResetSellerSessionCoins(GetPlayer()->GetCharacterID()); + std::string msg = FormatCoinReceiveMessage(coin_session, "consigned sales"); + broker.LogSaleMessage(GetPlayer()->GetCharacterID(), msg); + GetPlayer()->AddCoins(coin_session); + SimpleMessage(CHANNEL_NARRATIVE, msg.c_str()); + SendHouseSaleLog(msg, info ? coin_session : 0, info ? info->coin_total : 0, saleLogOnly); + } + else { + SendHouseSaleLog("", 0, info ? info->coin_total : 0, saleLogOnly); + } + } +} + +void Client::SetItemSaleCost(int64 unique_id, int32 platinum, int32 gold, int32 silver, int32 copper) { + int64 cost = platinum * 1000000 + gold * 10000 + silver * 100 + copper; + broker.SetSalePrice(GetPlayer()->GetCharacterID(), unique_id, cost); + OpenShopWindow(nullptr); +} + +void Client::AddItemSale(int64 unique_id, int32 item_id, int64 price, int32 inv_slot_id, int16 slot_id, int16 count, bool inInventory, bool forSale, std::string itemCreator) { + SaleItem it{}; + it.unique_id = unique_id; + it.character_id = GetPlayer()->GetCharacterID(); + it.house_id = GetPlayer()->GetPlayerInfo()->GetHouseZoneID(); + it.item_id = item_id; + it.cost_copper = price; + it.for_sale = forSale; + it.inv_slot_id = inv_slot_id; + it.slot_id = slot_id; + it.count = count; + it.from_inventory = inInventory; + it.creator = itemCreator; + broker.AddItem(it); +} bool Client::HandleHouseEntityCommands(Spawn* spawn, int32 spawnid, string command) { - if (GetCurrentZone()->GetInstanceType() != PERSONAL_HOUSE_INSTANCE) + if (!GetCurrentZone() || GetCurrentZone()->GetInstanceType() != PERSONAL_HOUSE_INSTANCE) return false; if (command == "house_spawn_examine") @@ -12253,26 +12722,12 @@ bool Client::PopulateHouseSpawn(PacketStruct* place_object) { Spawn* tmp = GetTempPlacementSpawn(); - int32 spawn_group_id = database.GetNextSpawnLocation(); + int32 spawn_group_id = database.GetNextSpawnLocation(true); tmp->SetSpawnLocationID(spawn_group_id); float newHeading = place_object->getType_float_ByName("heading") + 180; int32 spawnDBID = 0; - if (GetCurrentZone()->house_object_database_lookup.count(tmp->GetModelType()) > 0) - { - spawnDBID = GetCurrentZone()->house_object_database_lookup.Get(tmp->GetModelType()); - tmp->SetDatabaseID(spawnDBID); - } - else - { - spawnDBID = database.FindHouseInstanceSpawn(tmp); - if (spawnDBID) - { - GetCurrentZone()->house_object_database_lookup.Put(tmp->GetModelType(), spawnDBID); - tmp->SetDatabaseID(spawnDBID); - } - } tmp->SetX(place_object->getType_float_ByName("x")); tmp->SetY(place_object->getType_float_ByName("y")); @@ -12288,7 +12743,6 @@ bool Client::PopulateHouseSpawn(PacketStruct* place_object) if (!spawnDBID) { - GetCurrentZone()->house_object_database_lookup.Put(tmp->GetModelType(), tmp->GetDatabaseID()); // we need to copy as to not delete the ZoneServer object_list entry this on house item pickup GetCurrentZone()->AddObject(tmp->GetDatabaseID(), ((Object*)tmp)->Copy()); } @@ -12307,33 +12761,41 @@ bool Client::PopulateHouseSpawnFinalize() GetCurrentZone()->AddSpawn(tmp); GetCurrentZone()->SendSpawnChanges(tmp, this); SetTempPlacementSpawn(nullptr); - int32 uniqueID = GetPlacementUniqueItemID(); - Item* uniqueItem = GetPlayer()->item_list.GetItemFromUniqueID(uniqueID); + int64 uniqueID = GetPlacementUniqueItemID(); + Item* uniqueItem = GetPlayer()->item_list.GetVaultItemFromUniqueID(uniqueID); + if(!uniqueItem) { + Message(CHANNEL_COLOR_RED, "Missing unique item!"); + return false; + } + if(!uniqueItem->TryLockItem(LockReason::LockReason_House)) { + Message(CHANNEL_COLOR_RED, "Item could not be locked for house placement!"); + return false; + } + else { + QueuePacket(GetPlayer()->SendInventoryUpdate(GetVersion())); + } + tmp->SetPickupItemID(uniqueItem->details.item_id); tmp->SetPickupUniqueItemID(uniqueID); - - if (uniqueItem) + if (GetCurrentZone()->GetInstanceType() == PERSONAL_HOUSE_INSTANCE) { - if (GetCurrentZone()->GetInstanceType() == PERSONAL_HOUSE_INSTANCE) - { - Query query; - query.RunQuery2(Q_INSERT, "insert into spawn_instance_data set spawn_id = %u, spawn_location_id = %u, pickup_item_id = %u, pickup_unique_item_id = %u", tmp->GetDatabaseID(), tmp->GetSpawnLocationID(), tmp->GetPickupItemID(), uniqueID); - } - - if (uniqueItem->GetItemScript() && - lua_interface->RunItemScript(uniqueItem->GetItemScript(), "placed", uniqueItem, GetPlayer(), tmp)) - { - uniqueItem = GetPlayer()->item_list.GetItemFromUniqueID(uniqueID); - } - - if (uniqueItem) { - database.DeleteItem(GetCharacterID(), uniqueItem, 0); - GetPlayer()->item_list.RemoveItem(uniqueItem, true); - QueuePacket(GetPlayer()->SendInventoryUpdate(GetVersion())); - } - - SetPlacementUniqueItemID(0); + Query query; + query.RunQuery2(Q_INSERT, "insert into spawn_instance_data set spawn_id = %u, spawn_location_id = %u, pickup_item_id = %u, pickup_unique_item_id = %u", tmp->GetDatabaseID(), tmp->GetSpawnLocationID(), tmp->GetPickupItemID(), uniqueID); } + + if (uniqueItem->GetItemScript() && + lua_interface->RunItemScript(uniqueItem->GetItemScript(), "placed", uniqueItem, GetPlayer(), tmp)) + { + uniqueItem = GetPlayer()->item_list.GetItemFromUniqueID(uniqueID); + } + + if (uniqueItem && uniqueItem->generic_info.item_type != ITEM_TYPE_HOUSE_CONTAINER) { + database.DeleteItem(GetCharacterID(), uniqueItem, 0); + GetPlayer()->item_list.RemoveItem(uniqueItem, true); + QueuePacket(GetPlayer()->SendInventoryUpdate(GetVersion())); + } + + SetPlacementUniqueItemID(0); return true; } @@ -12364,6 +12826,10 @@ void Client::SendFlightAutoMount(int32 path_id, int16 mount_id, int8 mount_red_c ((Player*)player)->SetTempMountColor(((Entity*)player)->GetMountColor()); ((Player*)player)->SetTempMountSaddleColor(((Entity*)player)->GetMountSaddleColor()); + int32 index = GetCurrentZone()->GetFlightPathIndex(GetPendingFlightPath()); + if(GetVersion() <= 561) { + GetCurrentZone()->SendFlightPathsPackets(this, index); + } PacketStruct* packet = configReader.getStruct("WS_ReadyForTakeOff", GetVersion()); if (packet) { QueuePacket(packet->serialize()); @@ -12373,14 +12839,14 @@ void Client::SendFlightAutoMount(int32 path_id, int16 mount_id, int8 mount_red_c if (mount_id) ((Entity*)GetPlayer())->SetMount(mount_id, mount_red_color, mount_green_color, mount_blue_color); + if(GetVersion() <= 561) { PacketStruct* packet = configReader.getStruct("WS_CreateBoatTransportMsg", GetVersion()); if (!packet) { LogWrite(CCLIENT__ERROR, 0, "Client", "WS_CreateBoatTransportMsg missing for version %u", GetVersion()); return; } - int8 index = (int8)GetCurrentZone()->GetFlightPathIndex(GetPendingFlightPath()); - packet->setDataByName("path_id", index); + packet->setDataByName("path_id", 1); // workaround we send the SendFlightPathsPackets based on the index since the structure seems to only honor the first index as every index+1 // packet->PrintPacket(); QueuePacket(packet->serialize()); safe_delete(packet); @@ -12555,7 +13021,7 @@ void Client::ReplaceGroupClient(Client* new_client) } } -void Client::TempRemoveGroup() +void Client::TempRemoveGroup(int32 zone_id, int32 instance_id) { if (this->GetPlayer()->GetGroupMemberInfo()) { @@ -12567,6 +13033,10 @@ void Client::TempRemoveGroup() rejoin_group_id = this->GetPlayer()->GetGroupMemberInfo()->group_id; this->GetPlayer()->GetGroupMemberInfo()->client = 0; this->GetPlayer()->GetGroupMemberInfo()->member = 0; + if(zone_id) { + this->GetPlayer()->GetGroupMemberInfo()->zone_id = zone_id; + this->GetPlayer()->GetGroupMemberInfo()->instance_id = instance_id; + } this->GetPlayer()->SetGroupMemberInfo(0); group->MGroupMembers.releasewritelock(); group->RemoveClientReference(this); @@ -12716,6 +13186,11 @@ void Client::ConsumeFoodDrink(Item* item, int32 slot) } } else { + + char msg[512]; + snprintf(msg, 512, "ConsumeFoodDrink missing proper item script set %s ID %i", item->name.c_str(), item->details.item_id); + if (!world.CheckTempBugCRC(msg)) + commands.Command_ReportBug(this, new Seperator(msg)); Message(CHANNEL_NARRATIVE, "SERVER BUG! Item Script not assigned for consuming '%s'.", item->name.c_str()); return; } @@ -13523,6 +13998,18 @@ bool Client::GetHouseZoneServer(ZoneChangeDetails* zone_details, int32 spawn_id, hz = world.GetHouseZone(ph->house_id); } } + if(!ph) { + Spawn* houseWidget = GetPlayer()->GetSpawnByIndex(house_id); + if (houseWidget && houseWidget->IsWidget() && ((Widget*)houseWidget)->GetHouseID()) { + hz = world.GetHouseZone(((Widget*)houseWidget)->GetHouseID()); + if (hz) { + ph = world.GetPlayerHouseByHouseID(GetPlayer()->GetCharacterID(), hz->id); + } + else { + Message(CHANNEL_COLOR_YELLOW, "HouseWidget#2 spawn index %u house zone could not be found.", spawn_id); + } + } + } if (ph && hz) { if (zone_list.GetZoneByInstance(zone_details, ph->instance_id, hz->zone_id)) { @@ -13645,4 +14132,144 @@ bool Client::SendDialogChoice(int32 spawnID, const std::string& windowTextPrompt safe_delete(p); return true; +} + +void Client::SendMerchantWindow(Spawn* spawn, bool sell) { + if(GetVersion() < 561) { + sell = false; // doesn't support in the same way as AoM just open the normal buy/sell window + } + if(spawn) { + SetMerchantTransaction(spawn); + if (spawn->GetHouseCharacterID() > 0) { + if (auto info = broker.GetSellerInfo(spawn->GetHouseCharacterID())) { + PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion()); + if (packet) { + packet->setDataByName("spawn_id", GetPlayer()->GetIDWithPlayerSpawn(spawn)); + int32 i = 0; + int tmp_level = 0; + sint8 item_difficulty = 0; + auto items = broker.GetActiveForSaleItems(spawn->GetHouseCharacterID()); + int32 itemCount = 0; + for (auto const& itm : items) { + if(itm.inv_slot_id != spawn->GetPickupUniqueItemID()) + continue; + if (!itm.for_sale) { + continue; + } + itemCount++; + } + packet->setArrayLengthByName("num_items", itemCount); + + for (auto const& itm : items) { + if(itm.inv_slot_id != spawn->GetPickupUniqueItemID()) + continue; + if (!itm.for_sale) { + continue; + } + + Item* item = master_item_list.GetItem(itm.item_id); + if (!item) + continue; + + packet->setArrayDataByName("item_name", item->name.c_str(), i); + packet->setArrayDataByName("item_id", itm.unique_id, i); + //packet->setArrayDataByName("unique_item_id", item->details.item_id, i); + packet->setArrayDataByName("stack_size", itm.count, i); + packet->setArrayDataByName("icon", item->GetIcon(GetVersion()), i); + if (item->generic_info.adventure_default_level > 0) + tmp_level = item->generic_info.adventure_default_level; + else + tmp_level = item->generic_info.tradeskill_default_level; + packet->setArrayDataByName("level", tmp_level, i); + if (rule_manager.GetZoneRule(GetCurrentZoneID(), R_World, DisplayItemTiers)->GetBool()) { + packet->setArrayDataByName("tier", item->details.tier, i); + } + packet->setArrayDataByName("item_id2", item->details.item_id, i); + item_difficulty = player->GetArrowColor(tmp_level); + if (item_difficulty != ARROW_COLOR_WHITE && item_difficulty != ARROW_COLOR_RED && item_difficulty != ARROW_COLOR_GRAY) + item_difficulty = ARROW_COLOR_WHITE; + + sint64 overrideValue = 0; + if (item->GetItemScript() && lua_interface && lua_interface->RunItemScript(item->GetItemScript(), "item_difficulty", item, player, nullptr, &overrideValue)) + item_difficulty = (sint8)overrideValue; + + item_difficulty -= 6; + if (item_difficulty < 0) + item_difficulty *= -1; + + packet->setArrayDataByName("item_difficulty", item_difficulty, i); + packet->setArrayDataByName("quantity", 1, i); + packet->setArrayDataByName("unknown5", 255, i); + packet->setArrayDataByName("stack_size2", itm.count, i); + + sint64 dispFlags = 0; + if (item->GetItemScript() && lua_interface && lua_interface->RunItemScript(item->GetItemScript(), "buy_display_flags", item, player, nullptr, &dispFlags)) + packet->setArrayDataByName("display_flags", (int8)dispFlags, i); + + std::string overrideValueStr; + // classic client isn't properly tracking this field, DoF we don't have it identified yet, but no field to cause any issues (can add later if identified) + if (GetVersion() >= 546 && item->GetItemScript() && lua_interface && lua_interface->RunItemScriptWithReturnString(item->GetItemScript(), "item_description", item, player, &overrideValueStr)) + packet->setArrayDataByName("description", overrideValueStr.c_str(), i); + + packet->setArrayDataByName("price", itm.cost_copper, i); + + i++; + } + } + + if (GetVersion() < 561) { + //buy is 0 so dont need to set it + if (sell) + packet->setDataByName("type", 1); + } + else if (GetVersion() == 561) { + packet->setDataByName("type", 2); + } + else { + if (sell) + packet->setDataByName("type", 130); + else + packet->setDataByName("type", 2); + } + EQ2Packet* outapp = packet->serialize(); + QueuePacket(outapp); + safe_delete(packet); + if(GetVersion() > 561) { + PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion()); + if (packet) { + packet->setDataByName("spawn_id", 0xFFFFFFFF); + packet->setDataByName("type", 16); + EQ2Packet* outapp = packet->serialize(); + if (outapp) + QueuePacket(outapp); + safe_delete(packet); + } + } + } + } + else if (spawn->GetMerchantID() > 0 && spawn->IsClientInMerchantLevelRange(this)){ + SendHailCommand(spawn); + //MerchantFactionMultiplier* multiplier = world.GetMerchantMultiplier(spawn->GetMerchantID()); + //if(!multiplier || (multiplier && GetPlayer()->GetFactions()->GetFactionValue(multiplier->faction_id) >= multiplier->faction_min)){ + SendBuyMerchantList(sell); + if(!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY)) + SendSellMerchantList(sell); + if(!(spawn->GetMerchantType() & MERCHANT_TYPE_NO_BUY_BACK)) + SendBuyBackList(sell); + + if(GetVersion() > 561) { + PacketStruct* packet = configReader.getStruct("WS_UpdateMerchant", GetVersion()); + if (packet) { + packet->setDataByName("spawn_id", 0xFFFFFFFF); + packet->setDataByName("type", 16); + EQ2Packet* outapp = packet->serialize(); + if (outapp) + QueuePacket(outapp); + safe_delete(packet); + } + } + } + if (spawn->GetMerchantType() & MERCHANT_TYPE_REPAIR) + SendRepairList(); + } } \ No newline at end of file diff --git a/old/WorldServer/client.h b/old/WorldServer/client.h index 253e605..55de68b 100644 --- a/old/WorldServer/client.h +++ b/old/WorldServer/client.h @@ -1,22 +1,23 @@ -/* - EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) - This file is part of EQ2Emulator. + This file is part of EQ2Emulator. - EQ2Emulator 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. + EQ2Emulator 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. - EQ2Emulator 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. + EQ2Emulator 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 EQ2Emulator. If not, see . + You should have received a copy of the GNU General Public License + along with EQ2Emulator. If not, see . */ + #ifndef CLIENT_H #define CLIENT_H @@ -33,8 +34,8 @@ #include #include -#include "../common/stream/eq_stream.hpp" -#include "../common/timer.hpp" +#include "../common/EQStream.h" +#include "../common/timer.h" #include "Items/Items.h" #include "zoneserver.h" #include "Player.h" @@ -77,7 +78,7 @@ struct QueuedQuest { struct BuyBackItem { int32 item_id; - int32 unique_id; + int64 unique_id; int16 quantity; int32 price; bool save_needed; @@ -271,6 +272,7 @@ public: void SendZoneSpawns(); void HandleVerbRequest(EQApplicationPacket* app); void SendControlGhost(int32 send_id = 0xFFFFFFFF, int8 unknown2 = 0); + void BeginPreCharInfo(); void SendCharInfo(); void SendLoginDeniedBadVersion(); void SendCharPOVGhost(); @@ -285,13 +287,14 @@ public: void HandleQuickbarUpdateRequest(EQApplicationPacket* app); void SendPopupMessage(int8 unknown, const char* text, const char* type, float size, int8 red, int8 green, int8 blue); void PopulateSkillMap(); - void ChangeLevel(int16 old_level, int16 new_level); - void ChangeTSLevel(int16 old_level, int16 new_level); + bool ChangeLevel(int16 old_level, int16 new_level, int32 xp_earned = 0); + bool ChangeTSLevel(int16 old_level, int16 new_level, int32 xp_earned = 0); bool Summon(const char* search_name); std::string IdentifyInstanceLockout(int32 zoneID, bool displayClient = true); bool IdentifyInstance(ZoneChangeDetails* zone_details, int32 zoneID); bool TryZoneInstance(int32 zoneID, bool zone_coords_valid = false); bool GotoSpawn(const char* search_name, bool forceTarget = false); + void DisplayDeadWindow(); void HandlePlayerRevive(int32 point_id); void Bank(Spawn* banker, bool cancel = false); @@ -299,8 +302,8 @@ public: bool BankWithdrawalNoBanker(int64 amount); bool BankHasCoin(int64 amount); void BankDeposit(int64 amount); - Spawn* GetBanker(); - void SetBanker(Spawn* in_banker); + int32 GetBanker(); + void SetBanker(int32 in_banker); bool AddItem(int32 item_id, int16 quantity = 0, AddItemType type = AddItemType::NOT_SET); bool AddItem(Item* item, bool* item_deleted = 0, AddItemType type = AddItemType::NOT_SET); bool AddItemToBank(int32 item_id, int16 quantity = 0); @@ -339,6 +342,7 @@ public: inline int32 GetAccountID() { return account_id; } inline const char* GetAccountName() { return account_name; } inline sint16 GetAdminStatus() { return admin_status; } + inline bool IsGMStoreSearch() { return gm_store_search; } inline int16 GetVersion() { return version; } void SetNameCRC(int32 val) { name_crc = val; } int32 GetNameCRC() { return name_crc; } @@ -347,8 +351,8 @@ public: void SetVersion(int16 new_version) { version = new_version; } void SetAccountID(int32 in_accountid) { account_id = in_accountid; } void SetCharacterID(int32 in_characterid) { character_id = in_characterid; } - void SetAdminStatus(sint16 in_status) { admin_status = in_status; } - + void SetAdminStatus(sint16 in_status) { admin_status = in_status; SetGMStoreSearch(false); } + void SetGMStoreSearch(bool setting_val) { gm_store_search = setting_val; } void DetermineCharacterUpdates(); @@ -377,6 +381,8 @@ public: void AcceptQuest(int32 quest_id); bool HasPendingQuest(int32 quest_id); void RemovePendingQuest(int32 quest_id); + void DeleteQuest(int32 quest_id, bool override_deny_delete = false); + void DeleteAllQuests(bool override_deny_delete = false); void SetPlayerQuest(Quest* quest, map* progress); void AddPlayerQuest(Quest* quest, bool call_accepted = true, bool send_packets = true); void RemovePlayerQuest(int32 id, bool send_update = true, bool delete_quest = true); @@ -401,13 +407,14 @@ public: void RemoveCombineSpawn(Spawn* spawn); void SaveCombineSpawns(const char* name = 0); Spawn* GetCombineSpawn(); + void SetCombineSpawn(Spawn* spawn) { combine_spawn = spawn; } bool ShouldTarget(); void TargetSpawn(Spawn* spawn); void ReloadQuests(); int32 GetCurrentQuestID() { return current_quest_id; } void SetLuaDebugClient(bool val); void SetMerchantTransaction(Spawn* spawn); - Spawn* GetMerchantTransaction(); + int32 GetMerchantTransactionID(); void SetMailTransaction(Spawn* spawn); Spawn* GetMailTransaction(); void PlaySound(const char* name); @@ -425,7 +432,7 @@ public: void BuyBack(int32 item_id, int16 quantity); void RepairItem(int32 item_id); void RepairAllItems(); - void AddBuyBack(int32 unique_id, int32 item_id, int16 quantity, int32 price, bool save_needed = true); + void AddBuyBack(int64 unique_id, int32 item_id, int16 quantity, int32 price, bool save_needed = true); deque* GetBuyBacks(); vector* GetRepairableItems(); vector* GetItemsByEffectType(ItemEffectType type, ItemEffectType secondary_effect = NO_EFFECT_TYPE); @@ -448,9 +455,13 @@ public: void SendNewAdventureSpells(); void SendNewTradeskillSpells(); string GetCoinMessage(int32 total_coins); - void SetItemSearch(vector* items); - vector* GetSearchItems(); + void SetItemSearch(vector* items, map values); + void ClearItemSearch(); + void SearchStore(int32 page); + void SendSellerItemByItemUniqueId(int64 unique_id); + void BuySellerItemByItemUniqueId(int64 unique_id, int16 quantity); + void SetSellerStatus(); void SetPlayer(Player* new_player); void AddPendingQuestAcceptReward(Quest* quest); @@ -560,12 +571,20 @@ public: Spawn* GetTempPlacementSpawn() { return tempPlacementSpawn; } - void SetPlacementUniqueItemID(int32 id) { placement_unique_item_id = id; } - int32 GetPlacementUniqueItemID() { return placement_unique_item_id; } + void SetPlacementUniqueItemID(int64 id) { placement_unique_item_id = id; } + int64 GetPlacementUniqueItemID() { return placement_unique_item_id; } void SetHasOwnerOrEditAccess(bool val) { hasOwnerOrEditAccess = val; } bool HasOwnerOrEditAccess() { return hasOwnerOrEditAccess; } - + void RefreshVaultSlotCount(); + void SendHouseSaleLog(std::string message, int64 coin_session, int64 coin_total, int8 flag); + void SetItemSaleStatus(int64 unique_id, bool status); + void OpenShopWindow(Spawn* interaction, bool sendAlways = false, int8 saleLogOnly = 0); + void SetShopWindowStatus(bool status) { shop_window_open = status; } + bool GetShopWindowStatus() { return shop_window_open; }// false for disabled , true for enabled + void SetItemSaleCost(int64 unique_id, int32 platinum, int32 gold, int32 silver, int32 copper); + void AddItemSale(int64 unique_id, int32 item_id, int64 price, int32 inv_slot_id, int16 slot_id, int16 count, bool inInventory, bool forSale, std::string itemCreator); + bool HandleHouseEntityCommands(Spawn* spawn, int32 spawnid, string command); // find an appropriate spawn to use for the house object, save spawn location/entry data to DB bool PopulateHouseSpawn(PacketStruct* place_object); @@ -585,7 +604,7 @@ public: void SetRejoinGroupID(int32 id) { rejoin_group_id = id; } - void TempRemoveGroup(); + void TempRemoveGroup(int32 zone_id = 0, int32 instance_id = 0); void ReplaceGroupClient(Client* new_client); void SendWaypoints(); @@ -715,6 +734,16 @@ public: void SendReceiveOffer(Client* client_target, int8 type, std::string name, int8 unknown2); bool SendDialogChoice(int32 spawnID, const std::string& windowTextPrompt, const std::string& acceptText, const std::string& acceptCommand, const std::string& declineText, const std::string& declineCommand, int32 time, int8 textBox, int8 textBoxRequired, int32 maxLength); + + void SetTransportSpawnID(int32 id) { transport_spawn_id = id; } + int32 GetTransportSpawnID() { return transport_spawn_id; } + + void SetLastTellName(std::string tellName) { last_tell_name = tellName; } + std::string GetLastTellName() { return last_tell_name; } + + void SetSearchPage(int32 page) { search_page = page; } + + void SendMerchantWindow(Spawn* spawn, bool sell); DialogManager dialog_manager; private: void AddRecipeToPlayerPack(Recipe* recipe, PacketStruct* packet, int16* i); @@ -735,12 +764,14 @@ private: Mutex MQuestQueue; Mutex MDeletePlayer; vector* search_items; + map search_values; + int32 search_page; int32 waypoint_id = 0; map waypoints; - Spawn* transport_spawn; + int32 transport_spawn_id; Mutex MBuyBack; deque buy_back_items; - Spawn* merchant_transaction; + int32 merchant_transaction_id; Spawn* mail_transaction; mutable std::shared_mutex MPendingQuestAccept; vector pending_quest_accept; @@ -754,7 +785,7 @@ private: mutable std::shared_mutex MConversation; map > conversation_map; int32 current_quest_id; - Spawn* banker; + int32 banker_id; map sent_spell_details; map sent_item_details; Player* player; @@ -765,6 +796,7 @@ private: int32 account_id; int32 character_id; sint16 admin_status; // -2 Banned, -1 Suspended, 0 User, etc. + bool gm_store_search; char account_name[64]; char zone_name[64]; int32 zoneID; @@ -801,11 +833,13 @@ private: float zoning_y; float zoning_z; float zoning_h; - bool firstlogin; - + std::atomic firstlogin; + std::atomic firstlogin_transmit; + enum NewLoginState { LOGIN_NONE, LOGIN_DELAYED, LOGIN_ALLOWED, LOGIN_INITIAL_LOAD, LOGIN_SEND }; NewLoginState new_client_login; // 1 = delayed state, 2 = let client in Timer underworld_cooldown_timer; + Timer zone_enter_timer; Timer pos_update; Timer quest_pos_timer; Timer lua_debug_timer; @@ -847,7 +881,7 @@ private: int32 delayedAccessKey; Timer delayTimer; Spawn* tempPlacementSpawn; - int32 placement_unique_item_id; + int64 placement_unique_item_id; bool hasOwnerOrEditAccess; bool hasSentTempPlacementSpawn; @@ -879,6 +913,12 @@ private: uchar* recipe_xor_packet; int recipe_packet_count; int recipe_orig_packet_size; + + std::string last_tell_name; + + mutable std::mutex item_search_mtx_; + std::atomic shop_window_open; + map str_values; }; class ClientList { diff --git a/old/WorldServer/net.cpp b/old/WorldServer/net.cpp index d10b19b..a43ce61 100644 --- a/old/WorldServer/net.cpp +++ b/old/WorldServer/net.cpp @@ -1,6 +1,6 @@ /* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,8 +17,9 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ -#include "../common/debug.hpp" -#include "../common/log.hpp" + +#include "../common/debug.h" +#include "../common/Log.h" #include #include @@ -33,24 +34,25 @@ using namespace std; #include "../common/queue.h" #include "../common/timer.h" -#include "../common/stream/eq_stream_factory.h" -#include "../common/stream/eq_stream.h" +#include "../common/EQStreamFactory.h" +#include "../common/EQStream.h" #include "net.h" #include "Variables.h" -#include "WorldDatabase.hpp" -#include "../common/separator.hpp" +#include "WorldDatabase.h" +#include "../common/seperator.h" #include "../common/version.h" #include "../common/EQEMuError.h" -#include "../common/opcodes/opcode_manager.hpp" +#include "../common/opcodemgr.h" +#include "../common/Common_Defines.h" #include "../common/JsonParser.h" -#include "../common/common_defines.hpp" +#include "../common/Common_Defines.h" #include "LoginServer.h" #include "Commands/Commands.h" #include "Factions.h" #include "World.h" -#include "../common/config_reader.hpp" +#include "../common/ConfigReader.h" #include "Skills.h" #include "LuaInterface.h" #include "Guilds/Guild.h" @@ -100,6 +102,7 @@ LuaInterface* lua_interface = new LuaInterface(); #include "Titles.h" #include "Languages.h" #include "Achievements/Achievements.h" +#include "./Broker/BrokerManager.h" volatile bool RunLoops = true; sint32 numclients = 0; @@ -117,7 +120,6 @@ extern Variables variables; extern PeerManager peer_manager; extern HTTPSClientPool peer_https_pool; ConfigReader configReader; -int32 MasterItemList::next_unique_id = 0; int last_signal = 0; RuleManager rule_manager; MasterTitlesList master_titles_list; @@ -125,7 +127,7 @@ MasterLanguagesList master_languages_list; ChestTrapList chest_trap_list; extern MasterAchievementList master_achievement_list; extern map EQOpcodeVersions; - +extern BrokerManager broker; ThreadReturnType ItemLoad (void* tmp); ThreadReturnType AchievmentLoad (void* tmp); @@ -151,7 +153,6 @@ int main(int argc, char** argv) { net.WelcomeHeader(); LogWrite(INIT__INFO, 0, "Init", "Starting EQ2Emulator WorldServer..."); - //int32 server_startup = time(NULL); //remove this when all database calls are using the new database class if (!database.Init()) { @@ -274,45 +275,30 @@ int main(int argc, char** argv) { #endif #endif } - - // JA temp logger - LogWrite(MISC__TODO, 0, "Reformat", "JA: This is as far as I got reformatting the console logs."); - + if (!threadedLoad) { - // JA: Load all Item info LogWrite(ITEM__INFO, 0, "Items", "Loading Items..."); database.LoadItemList(); - MasterItemList::ResetUniqueID(database.LoadNextUniqueItemID()); LogWrite(SPELL__INFO, 0, "Spells", "Loading Spells..."); database.LoadSpells(); LogWrite(SPELL__INFO, 0, "Spells", "Loading Spell Errors..."); database.LoadSpellErrors(); - - // Jabantiz: Load traits + LogWrite(WORLD__INFO, 0, "Traits", "Loading Traits..."); database.LoadTraits(); - // JA: Load all Quest info LogWrite(QUEST__INFO, 0, "Quests", "Loading Quests..."); database.LoadQuests(); LogWrite(COLLECTION__INFO, 0, "Collect", "Loading Collections..."); database.LoadCollections(); - LogWrite(MISC__TODO, 1, "TODO", "TODO loading achievements\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__); - //LogWrite(ACHIEVEMENT__INFO, 0, "Achievements", "Loading Achievements..."); - //database.LoadAchievements(); - //master_achievement_list.CreateMasterAchievementListPacket(); - LogWrite(MERCHANT__INFO, 0, "Merchants", "Loading Merchants..."); database.LoadMerchantInformation(); } - LogWrite(GUILD__INFO, 0, "Guilds", "Loading Guilds..."); - database.LoadGuilds(); - LogWrite(TRADESKILL__INFO, 0, "Recipes", "Loading Recipe Books..."); database.LoadRecipeBooks(); LogWrite(TRADESKILL__INFO, 0, "Recipes", "Loading Recipes..."); @@ -338,9 +324,11 @@ int main(int argc, char** argv) { LogWrite(LUA__INFO, 0, "LUA", "Loading Zone Scripts..."); database.LoadZoneScriptData(); + LogWrite(LUA__INFO, 0, "LUA", "Loading Player Scripts..."); + world.LoadPlayerScripts(); + LogWrite(WORLD__INFO, 0, "World", "Loading House Zone Data..."); database.LoadHouseZones(); - database.LoadPlayerHouses(); LogWrite(WORLD__INFO, 0, "World", "Loading Heroic OP Data..."); database.LoadHOStarters(); @@ -360,14 +348,25 @@ int main(int argc, char** argv) { if (threadedLoad) { LogWrite(WORLD__INFO, 0, "World", "Waiting for load threads to finish."); - while (!world.items_loaded || !world.spells_loaded /*|| !world.achievments_loaded*/) + while (!world.items_loaded || !world.spells_loaded) Sleep(10); LogWrite(WORLD__INFO, 0, "World", "Load threads finished."); } + + LogWrite(GUILD__INFO, 0, "Guilds", "Loading Guilds..."); + database.LoadGuilds(); + + LogWrite(WORLD__INFO, 0, "World", "Loading Player House Data..."); + database.LoadPlayerHouses(); LogWrite(WORLD__INFO, 0, "World", "Total World startup time: %u seconds.", Timer::GetUnixTimeStamp() - t_total); int ret_code = 0; if (eqsf.Open(net.GetWorldPort())) { + world.world_loaded = true; // need this set ahead so peering starts sending data also + + LogWrite(WORLD__INFO, 0, "World", "Loading Broker Data..."); + database.LoadBrokerData(broker); + if (strlen(net.GetWorldAddress()) == 0) LogWrite(NET__INFO, 0, "Net", "World server listening on port %i", net.GetWorldPort()); else @@ -376,7 +375,6 @@ int main(int argc, char** argv) { if(strlen(net.GetInternalWorldAddress())>0) LogWrite(NET__INFO, 0, "Net", "World server listening on: %s:%i", net.GetInternalWorldAddress(), net.GetWorldPort()); - world.world_loaded = true; world.world_uptime = getCurrentTimestamp(); #ifdef WIN32 _beginthread(StartPeerPoll, 0, NULL); @@ -554,8 +552,8 @@ ThreadReturnType ItemLoad (void* tmp) LogWrite(ITEM__INFO, 0, "Items", "Loading Items..."); db.LoadItemList(); - MasterItemList::ResetUniqueID(db.LoadNextUniqueItemID()); - + db.ResetNextUniqueItemID(); + // Relies on the item list so needs to be in the item thread LogWrite(COLLECTION__INFO, 0, "Collect", "Loading Collections..."); db.LoadCollections(); @@ -641,7 +639,7 @@ ThreadReturnType StartPeerPoll (void* tmp) std::this_thread::sleep_for(std::chrono::milliseconds(1000)); if(check_zone > 60) { check_zone = 0; - database.LoadSpecialZones(); + database.LoadSpecialZones(true); } check_zone++; } diff --git a/old/WorldServer/net.h b/old/WorldServer/net.h index 4b32aae..99739a4 100644 --- a/old/WorldServer/net.h +++ b/old/WorldServer/net.h @@ -37,8 +37,8 @@ #include #include -#include "../common/linked_list.hpp" -#include "../common/types.hpp" +#include "../common/linked_list.h" +#include "../common/types.h" ThreadReturnType EQ2ConsoleListener(void *tmp); void CatchSignal(int sig_num); diff --git a/old/WorldServer/races.cpp b/old/WorldServer/races.cpp index eea8792..d2c7eb3 100644 --- a/old/WorldServer/races.cpp +++ b/old/WorldServer/races.cpp @@ -17,9 +17,9 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ -#include "../common/debug.hpp" +#include "../common/debug.h" #include "races.h" -#include "../common/misc_functions.hpp" +#include "../common/MiscFunctions.h" Races::Races(){ race_map["BARBARIAN"] = 0; diff --git a/old/WorldServer/races.h b/old/WorldServer/races.h index 0d3331c..1764959 100644 --- a/old/WorldServer/races.h +++ b/old/WorldServer/races.h @@ -19,7 +19,7 @@ */ #ifndef RACES_H #define RACES_H -#include "../common/types.hpp" +#include "../common/types.h" #include using namespace std; diff --git a/old/WorldServer/zoneserver.cpp b/old/WorldServer/zoneserver.cpp index 8ec42d4..4aadeb9 100644 --- a/old/WorldServer/zoneserver.cpp +++ b/old/WorldServer/zoneserver.cpp @@ -1,27 +1,28 @@ /* -EQ2Emulator: Everquest II Server Emulator -Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) -This file is part of EQ2Emulator. -EQ2Emulator 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. + This file is part of EQ2Emulator. -EQ2Emulator 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. + EQ2Emulator 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. -You should have received a copy of the GNU General Public License -along with EQ2Emulator. If not, see . + EQ2Emulator 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 EQ2Emulator. If not, see . */ -#include "../common/debug.hpp" +#include "../common/debug.h" #include using namespace std; #include -#include "../common/misc.hpp" +#include "../common/misc.h" #include #include #include @@ -30,6 +31,7 @@ using namespace std; #include "Commands/Commands.h" #include "Zone/pathfinder_interface.h" #include "NPC_AI.h" +#include "Broker/BrokerManager.h" #ifdef WIN32 #include @@ -48,7 +50,7 @@ using namespace std; #include #include -#include "../common/unix.hpp" +#include "../common/unix.h" #define SOCKET_ERROR -1 #define INVALID_SOCKET -1 @@ -56,15 +58,15 @@ using namespace std; extern int errno; #endif -#include "../common/servertalk.hpp" -#include "../common/packet/packet_dump.hpp" -#include "WorldDatabase.hpp" +#include "../common/servertalk.h" +#include "../common/packet_dump.h" +#include "WorldDatabase.h" #include "races.h" #include "classes.h" -#include "../common/separator.hpp" -#include "../common/stream/eq_stream.h" -#include "../common/stream/eq_stream_factory.h" -#include "../common/opcodes/opcode_manager.hpp" +#include "../common/seperator.h" +#include "../common/EQStream.h" +#include "../common/EQStreamFactory.h" +#include "../common/opcodemgr.h" #include "zoneserver.h" #include "client.h" #include "LoginServer.h" @@ -76,7 +78,7 @@ extern int errno; #include "VisualStates.h" #include "ClientPacketFunctions.h" #include "SpellProcess.h" -#include "../common/log.hpp" +#include "../common/Log.h" #include "Rules/Rules.h" #include "Chat/Chat.h" #include "Tradeskills/Tradeskills.h" @@ -115,7 +117,7 @@ extern Chat chat; extern MasterRaceTypeList race_types_list; extern MasterSpellList master_spell_list; // temp - remove later extern MasterSkillList master_skill_list; - +extern BrokerManager broker; int32 MinInstanceID = 1000; @@ -173,8 +175,6 @@ ZoneServer::ZoneServer(const char* name) { tradeskillMgr = nullptr; watchdogTimestamp = Timer::GetCurrentTime2(); - MPendingSpawnRemoval.SetName("ZoneServer::MPendingSpawnRemoval"); - lifetime_client_count = 0; groupraidMinLevel = 0; @@ -190,11 +190,16 @@ ZoneServer::ZoneServer(const char* name) { typedef map ChangedSpawnMapType; ZoneServer::~ZoneServer() { + lua_interface->DeletePendingSpells(true, this); + + MMasterSpawnLock.writelock(__FUNCTION__, __LINE__); zoneShuttingDown = true; //ensure other threads shut down too //allow other threads to properly shut down + if(is_initialized) { LogWrite(ZONE__INFO, 0, "Zone", "Initiating zone shutdown of '%s'", zone_name); } + MMasterSpawnLock.releasewritelock(__FUNCTION__, __LINE__); int32 disp_count = 0; int32 next_disp_count = 100; while (spawnthread_active || initial_spawn_threads_active > 0){ @@ -309,6 +314,7 @@ void ZoneServer::Init() shutdownTimer.Disable(); spawn_range.Start(rule_manager.GetGlobalRule(R_Zone, CheckAttackPlayer)->GetInt32()); aggro_timer.Start(rule_manager.GetGlobalRule(R_Zone, CheckAttackNPC)->GetInt32()); + delete_timer.Start(1000); /* Weather stuff */ InitWeather(); @@ -663,19 +669,6 @@ void ZoneServer::DeleteData(bool boot_clients){ MWidgetTimers.writelock(__FUNCTION__, __LINE__); widget_timers.clear(); MWidgetTimers.releasewritelock(__FUNCTION__, __LINE__); - - map::iterator struct_itr; - for (struct_itr = versioned_info_structs.begin(); struct_itr != versioned_info_structs.end(); struct_itr++) - safe_delete(struct_itr->second); - versioned_info_structs.clear(); - - for (struct_itr = versioned_pos_structs.begin(); struct_itr != versioned_pos_structs.end(); struct_itr++) - safe_delete(struct_itr->second); - versioned_pos_structs.clear(); - - for (struct_itr = versioned_vis_structs.begin(); struct_itr != versioned_vis_structs.end(); struct_itr++) - safe_delete(struct_itr->second); - versioned_vis_structs.clear(); } void ZoneServer::RemoveLocationProximities() { @@ -810,6 +803,7 @@ void ZoneServer::ProcessDepop(bool respawns_allowed, bool repop) { if(!client) continue; client->GetPlayer()->SetTarget(0); + client->SetMailTransaction(0); if(repop) client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Zone Repop in progress..."); else{ @@ -1515,70 +1509,78 @@ void ZoneServer::AddPendingDelete(Spawn* spawn) { } void ZoneServer::DeleteSpawns(bool delete_all) { + // 1) Snapshot & clear delete‐list MSpawnDeleteList.writelock(__FUNCTION__, __LINE__); - MPendingSpawnRemoval.readlock(__FUNCTION__, __LINE__); - if(spawn_delete_list.size() > 0){ - map::iterator itr; - map::iterator erase_itr; + std::vector> to_process( + spawn_delete_list.begin(), spawn_delete_list.end() + ); + spawn_delete_list.clear(); + MSpawnDeleteList.releasewritelock(__FUNCTION__, __LINE__); + + // Prepare a list of entries we’ll need to keep around + std::vector> to_keep; + if(to_process.size() > 0) { + to_keep.reserve(to_process.size()); int32 current_time = Timer::GetCurrentTime2(); - Spawn* spawn = 0; - for (itr = spawn_delete_list.begin(); itr != spawn_delete_list.end(); ) { - if (delete_all || current_time >= itr->second){ - // we haven't removed it from the spawn list yet.. - if(!delete_all && m_pendingSpawnRemove.count(itr->first->GetID())) - continue; - - spawn = itr->first; - - lua_interface->SetLuaUserDataStale(spawn); - - if (spellProcess) { - spellProcess->RemoveCaster(spawn, true); - } + for (auto &entry : to_process) { + Spawn* spawn = entry.first; + int32 when = entry.second; - if(movementMgr != nullptr) { - movementMgr->RemoveMob((Entity*)spawn); - } - - // delete brain if it has one - if(spawn->IsNPC()) { - NPC* tmpNPC = (NPC*)spawn; - if(tmpNPC->Brain()) - tmpNPC->SetBrain(nullptr); - } - - erase_itr = itr++; - spawn_delete_list.erase(erase_itr); - - MSpawnList.writelock(__FUNCTION__, __LINE__); - std::map::iterator sitr = spawn_list.find(spawn->GetID()); - if(sitr != spawn_list.end()) { - spawn_list.erase(sitr); - } - - if(spawn->IsCollector()) { - std::map::iterator subitr = subspawn_list[SUBSPAWN_TYPES::COLLECTOR].find(spawn->GetID()); - if(subitr != subspawn_list[SUBSPAWN_TYPES::COLLECTOR].end()) { - subspawn_list[SUBSPAWN_TYPES::COLLECTOR].erase(subitr); - } - } - - if(spawn->GetPickupItemID()) { - std::map::iterator subitr = subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].find(spawn->GetPickupItemID()); - if(subitr != subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].end() && subitr->second == spawn) { - subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].erase(subitr); - } - housing_spawn_map.erase(spawn->GetID()); - } - MSpawnList.releasewritelock(__FUNCTION__, __LINE__); - safe_delete(spawn); + if (!delete_all && current_time < when) { + to_keep.emplace_back(entry); + continue; } - else - itr++; + + lua_interface->SetLuaUserDataStale(spawn); + + if (spellProcess) { + spellProcess->RemoveCaster(spawn, true); + } + + MSpawnList.writelock(__FUNCTION__, __LINE__); + if(spawn->IsEntity() && movementMgr != nullptr) { + movementMgr->RemoveMob((Entity*)spawn); + } + + // delete brain if it has one + if(spawn->IsNPC()) { + NPC* tmpNPC = (NPC*)spawn; + if(tmpNPC->Brain()) + tmpNPC->SetBrain(nullptr); + } + + std::map::iterator sitr = spawn_list.find(spawn->GetID()); + if(sitr != spawn_list.end()) { + spawn_list.erase(sitr); + } + + if(spawn->IsCollector()) { + std::map::iterator subitr = subspawn_list[SUBSPAWN_TYPES::COLLECTOR].find(spawn->GetID()); + if(subitr != subspawn_list[SUBSPAWN_TYPES::COLLECTOR].end()) { + subspawn_list[SUBSPAWN_TYPES::COLLECTOR].erase(subitr); + } + } + + if(spawn->GetPickupItemID()) { + std::map::iterator subitr = subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].find(spawn->GetPickupItemID()); + if(subitr != subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].end() && subitr->second == spawn) { + subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].erase(subitr); + } + housing_spawn_map.erase(spawn->GetID()); + } + MSpawnList.releasewritelock(__FUNCTION__, __LINE__); + safe_delete(spawn); } } - MPendingSpawnRemoval.releasereadlock(__FUNCTION__, __LINE__); - MSpawnDeleteList.releasewritelock(__FUNCTION__, __LINE__); + + // Add all the kept entries + if (!to_keep.empty()) { + MSpawnDeleteList.writelock(__FUNCTION__, __LINE__); + for (auto &kv : to_keep) { + spawn_delete_list.emplace(kv.first, kv.second); + } + MSpawnDeleteList.releasewritelock(__FUNCTION__, __LINE__); + } } void ZoneServer::AddDamagedSpawn(Spawn* spawn){ @@ -1637,9 +1639,30 @@ bool ZoneServer::Process() LogWrite(WIDGET__INFO, 0, "Widget", "-Loading Widget data..."); database.LoadWidgets(this); LogWrite(WIDGET__INFO, 0, "Widget", "-Load Widget data complete!"); - + LogWrite(GROUNDSPAWN__INFO, 0, "GSpawn", "-Loading Groundspawn data..."); database.LoadGroundSpawns(this); + LogWrite(GROUNDSPAWN__INFO, 0, "GSpawn", "-Load Groundspawn data complete!"); + + if(GetInstanceType() == Instance_Type::PERSONAL_HOUSE_INSTANCE) { + LogWrite(NPC__INFO, 0, "NPC", "-Loading House NPC data..."); + database.LoadNPCs(this, true); + LogWrite(NPC__INFO, 0, "NPC", "-Load House NPC data complete!"); + + LogWrite(OBJECT__INFO, 0, "Object", "-Loading House Object data..."); + database.LoadObjects(this, true); + LogWrite(OBJECT__INFO, 0, "Object", "-Load House Object data complete!"); + + LogWrite(SIGN__INFO, 0, "Sign", "-Loading House Sign data..."); + database.LoadSigns(this, true); + LogWrite(SIGN__INFO, 0, "Sign", "-Load House Sign data complete!"); + + LogWrite(GROUNDSPAWN__INFO, 0, "GSpawn", "-Loading House Groundspawn data..."); + database.LoadGroundSpawns(this, true); + LogWrite(GROUNDSPAWN__INFO, 0, "GSpawn", "-Load House Groundspawn data complete!"); + } + + LogWrite(GROUNDSPAWN__INFO, 0, "GSpawn", "-Load Groundspawn entries..."); database.LoadGroundSpawnEntries(this); LogWrite(GROUNDSPAWN__INFO, 0, "GSpawn", "-Load Groundspawn data complete!"); @@ -1863,11 +1886,8 @@ bool ZoneServer::SpawnProcess(){ bool spawnRange = spawn_range.Check(); bool checkRemove = spawn_check_remove.Check(); bool aggroCheck = aggro_timer.Check(); - vector pending_spawn_list_remove; + bool deleteTimer = delete_timer.Check(); - // Check to see if there are any spawn id's that need to be removed from the spawn list, if so remove them all - ProcessSpawnRemovals(); - map::iterator itr; if (spawnRange || checkRemove) { @@ -1933,40 +1953,16 @@ bool ZoneServer::SpawnProcess(){ CombatProcess(spawn); } } - else { - // unable to get a valid spawn, lets add the id to another list to remove from the spawn list after this loop is finished - pending_spawn_list_remove.push_back(itr->first); - } } MSpawnList.releasereadlock(__FUNCTION__, __LINE__); } - // Check to see if there are any spawn id's that need to be removed from the spawn list, if so remove them all - if (pending_spawn_list_remove.size() > 0) { - MSpawnList.writelock(__FUNCTION__, __LINE__); - vector::iterator itr2; - for (itr2 = pending_spawn_list_remove.begin(); itr2 != pending_spawn_list_remove.end(); itr2++) { - spawn_list.erase(*itr2); - subspawn_list[SUBSPAWN_TYPES::COLLECTOR].erase(*itr2); - - std::map::iterator hsmitr = housing_spawn_map.find(*itr2); - if(hsmitr != housing_spawn_map.end()) { - subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].erase(hsmitr->second); - housing_spawn_map.erase(hsmitr); - } - } - - pending_spawn_list_remove.clear(); - MSpawnList.releasewritelock(__FUNCTION__, __LINE__); - } - - // Double Check to see if there are any spawn id's that need to be removed from the spawn list, if so remove them before we replace with pending spawns - // and also potentially further down when we delete the Spawn* in DeleteSpawns(false) - ProcessSpawnRemovals(); - // Check to see if there are spawns waiting to be added to the spawn list, if so add them all - if (pending_spawn_list_add.size() > 0) { + MPendingSpawnListAdd.readlock(__FUNCTION__, __LINE__); + int32 pending_count = pending_spawn_list_add.size(); + MPendingSpawnListAdd.releasereadlock(__FUNCTION__, __LINE__); + if (pending_count > 0) { ProcessPendingSpawns(); } @@ -1984,7 +1980,7 @@ bool ZoneServer::SpawnProcess(){ // Delete unused spawns, do this last - if(!zoneShuttingDown) + if(!zoneShuttingDown && deleteTimer) DeleteSpawns(false); // Nothing should come after this @@ -2538,23 +2534,25 @@ Spawn* ZoneServer::ProcessSpawnLocation(SpawnLocation* spawnlocation, mapentities[i]->spawn_type == SPAWN_ENTRY_TYPE_NPC) - spawnTime = database.CheckSpawnRemoveInfo(instNPCs,spawnlocation->entities[i]->spawn_location_id); - else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_OBJECT) - spawnTime = database.CheckSpawnRemoveInfo(instObjSpawns,spawnlocation->entities[i]->spawn_location_id); - else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_WIDGET) - spawnTime = database.CheckSpawnRemoveInfo(instWidgetSpawns,spawnlocation->entities[i]->spawn_location_id); - else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_SIGN) - spawnTime = database.CheckSpawnRemoveInfo(instSignSpawns,spawnlocation->entities[i]->spawn_location_id); - else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_GROUNDSPAWN) - spawnTime = database.CheckSpawnRemoveInfo(instGroundSpawns,spawnlocation->entities[i]->spawn_location_id); - - if(spawnTime == 0) { // don't respawn - return nullptr; - } - else if(spawnTime > 1) { // if not 1, respawn after time - AddRespawn(spawnlocation->entities[i]->spawn_location_id, spawnTime); - return nullptr; + if(GetInstanceType() != PERSONAL_HOUSE_INSTANCE) { + if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_NPC) + spawnTime = database.CheckSpawnRemoveInfo(instNPCs,spawnlocation->entities[i]->spawn_location_id); + else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_OBJECT) + spawnTime = database.CheckSpawnRemoveInfo(instObjSpawns,spawnlocation->entities[i]->spawn_location_id); + else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_WIDGET) + spawnTime = database.CheckSpawnRemoveInfo(instWidgetSpawns,spawnlocation->entities[i]->spawn_location_id); + else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_SIGN) + spawnTime = database.CheckSpawnRemoveInfo(instSignSpawns,spawnlocation->entities[i]->spawn_location_id); + else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_GROUNDSPAWN) + spawnTime = database.CheckSpawnRemoveInfo(instGroundSpawns,spawnlocation->entities[i]->spawn_location_id); + + if(spawnTime == 0) { // don't respawn + return nullptr; + } + else if(spawnTime > 1) { // if not 1, respawn after time + AddRespawn(spawnlocation->entities[i]->spawn_location_id, spawnTime); + return nullptr; + } } if (spawnlocation->conditional > 0) { @@ -2638,19 +2636,19 @@ Spawn* ZoneServer::ProcessInstanceSpawnLocation(SpawnLocation* spawnlocation, ma if(spawnlocation->entities[i]->spawn_percentage >= rand_number) { if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_NPC && - (spawnTime = database.CheckSpawnRemoveInfo(instNPCs,spawnlocation->entities[i]->spawn_location_id)) > 0) + (GetInstanceType() == PERSONAL_HOUSE_INSTANCE || (spawnTime = database.CheckSpawnRemoveInfo(instNPCs,spawnlocation->entities[i]->spawn_location_id)) > 0)) spawn = AddNPCSpawn(spawnlocation, spawnlocation->entities[i]); else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_GROUNDSPAWN && - (spawnTime = database.CheckSpawnRemoveInfo(instGroundSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0) + (GetInstanceType() == PERSONAL_HOUSE_INSTANCE || (spawnTime = database.CheckSpawnRemoveInfo(instGroundSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0)) spawn = AddGroundSpawn(spawnlocation, spawnlocation->entities[i]); else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_OBJECT && - (spawnTime = database.CheckSpawnRemoveInfo(instObjSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0) + (GetInstanceType() == PERSONAL_HOUSE_INSTANCE || (spawnTime = database.CheckSpawnRemoveInfo(instObjSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0)) spawn = AddObjectSpawn(spawnlocation, spawnlocation->entities[i]); else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_WIDGET && - (spawnTime = database.CheckSpawnRemoveInfo(instWidgetSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0) + (GetInstanceType() == PERSONAL_HOUSE_INSTANCE || (spawnTime = database.CheckSpawnRemoveInfo(instWidgetSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0)) spawn = AddWidgetSpawn(spawnlocation, spawnlocation->entities[i]); else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_SIGN && - (spawnTime = database.CheckSpawnRemoveInfo(instSignSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0) + (GetInstanceType() == PERSONAL_HOUSE_INSTANCE || (spawnTime = database.CheckSpawnRemoveInfo(instSignSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0)) spawn = AddSignSpawn(spawnlocation, spawnlocation->entities[i]); if(spawn && spawn->IsOmittedByDBFlag()) @@ -2665,26 +2663,28 @@ Spawn* ZoneServer::ProcessInstanceSpawnLocation(SpawnLocation* spawnlocation, ma database.GetHouseSpawnInstanceData(this, spawn); const char* script = 0; - - for(int x=0;x<3;x++) - { - switch(x) + + if(spawn && !spawn->GetSpawnScript()) { + for(int x=0;x<3;x++) { - case 0: - script = world.GetSpawnEntryScript(spawnlocation->entities[i]->spawn_entry_id); - break; - case 1: - script = world.GetSpawnLocationScript(spawnlocation->entities[i]->spawn_location_id); - break; - case 2: - script = world.GetSpawnScript(spawnlocation->entities[i]->spawn_id); - break; - } + switch(x) + { + case 0: + script = world.GetSpawnEntryScript(spawnlocation->entities[i]->spawn_entry_id); + break; + case 1: + script = world.GetSpawnLocationScript(spawnlocation->entities[i]->spawn_location_id); + break; + case 2: + script = world.GetSpawnScript(spawnlocation->entities[i]->spawn_id); + break; + } - if(spawn && script && lua_interface->GetSpawnScript(script) != 0) - { - spawn->SetSpawnScript(string(script)); - break; + if(script && lua_interface->GetSpawnScript(script) != 0) + { + spawn->SetSpawnScript(string(script)); + break; + } } } @@ -3254,7 +3254,7 @@ bool ZoneServer::CallSpawnScript(Spawn* npc, int8 type, Spawn* spawn, const char bool fileExists = (stat(tmpScript.c_str(), &buffer) == 0); if (fileExists) { - LogWrite(SPAWN__WARNING, 0, "Spawn", "No script file described in the database, overriding with SpawnScript at %s", (char*)tmpScript.c_str()); + LogWrite(SPAWN__WARNING, 0, "Spawn", "No script file described in the database for spawn %s with database spawn id %u, overriding with SpawnScript at %s", npc->GetName(), npc->GetDatabaseID(), (char*)tmpScript.c_str()); npc->SetSpawnScript(tmpScript); script = npc->GetSpawnScript(); } @@ -3387,7 +3387,7 @@ void ZoneServer::CheckTransporters(Client* client) { for (itr = transporter_locations.begin(); itr != transporter_locations.end(); itr++) { loc = *itr; if(client->GetPlayer()->GetDistance(loc->trigger_x, loc->trigger_y, loc->trigger_z) <= loc->trigger_radius){ - if(loc->destination_zone_id == 0 || loc->destination_zone_id == GetZoneID()){ + if(loc->destination_zone_id == 0 || (loc->destination_zone_id == GetZoneID() && !loc->force_zone)){ EQ2Packet* packet = client->GetPlayer()->Move(loc->destination_x, loc->destination_y, loc->destination_z, client->GetVersion()); if(packet) client->QueuePacket(packet); @@ -3397,10 +3397,10 @@ void ZoneServer::CheckTransporters(Client* client) { bool foundZone = zone_list.GetZone(&zone_details, loc->destination_zone_id); if(foundZone){ client->GetPlayer()->SetX(loc->destination_x); - client->GetPlayer()->SetY(loc->destination_y); + client->GetPlayer()->SetY(loc->destination_y, false, true); client->GetPlayer()->SetZ(loc->destination_z); client->GetPlayer()->SetHeading(loc->destination_heading); - client->Zone(&zone_details, (ZoneServer*)zone_details.zonePtr); + client->Zone(&zone_details, (ZoneServer*)zone_details.zonePtr, false); } } break; @@ -3606,7 +3606,10 @@ void ZoneServer::RemoveClient(Client* client) bool dismissPets = false; if(client) - { + { + if(client->GetPlayer() && client->GetPlayer()->GetCharacterID()) + broker.AddSeller(client->GetPlayer()->GetCharacterID(), std::string(client->GetPlayer()->GetName()), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), true, false); + if (client->GetPlayer()) client_list.RemovePlayerFromInvisHistory(client->GetPlayer()->GetID()); @@ -3688,6 +3691,10 @@ void ZoneServer::RemoveClient(Client* client) RemoveSpawn(client->GetPlayer(), false, true, true, true, true); int32 DisconnectClientTimer = rule_manager.GetGlobalRule(R_World, RemoveDisconnectedClientsTimer)->GetInt32(); + + if(client->GetPlayer()->GetClient() == client) + client->GetPlayer()->SetClient(nullptr); + connected_clients.Remove(client, true, DisconnectClientTimer); // changed from a hardcoded 30000 (30 sec) to the DisconnectClientTimer rule } } @@ -3697,6 +3704,8 @@ void ZoneServer::RemoveClientImmediately(Client* client) { if(client) { + if(client->GetPlayer() && client->GetPlayer()->GetCharacterID()) + broker.AddSeller(client->GetPlayer()->GetCharacterID(), std::string(client->GetPlayer()->GetName()), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), true, false); if(client->GetPlayer()) { if((client->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) > 0) { client->GetPlayer()->SetActivityStatus(client->GetPlayer()->GetActivityStatus() - ACTIVITY_STATUS_LINKDEAD); @@ -3910,12 +3919,14 @@ void ZoneServer::HandleChatMessage(Client* client, std::string fromName, const c void ZoneServer::HandleChatMessage(Spawn* from, const char* to, int16 channel, const char* message, float distance, const char* channel_name, bool show_bubble, int32 language){ vector::iterator client_itr; Client* client = 0; + std::string tokenedMsg = std::string(message); + SpellProcess::ReplaceEffectTokens(tokenedMsg, from, from->GetTarget()); MClientList.readlock(__FUNCTION__, __LINE__); for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) { client = *client_itr; if(client && client->IsConnected()) - HandleChatMessage(client, from, to, channel, message, distance, channel_name, show_bubble, language); + HandleChatMessage(client, from, to, channel, tokenedMsg.c_str(), distance, channel_name, show_bubble, language); } MClientList.releasereadlock(__FUNCTION__, __LINE__); } @@ -4607,7 +4618,33 @@ void ZoneServer::CheckSpawnScriptTimers(){ vector::iterator itr; for(itr = call_timers.begin(); itr != call_timers.end(); itr++){ SpawnScriptTimer tmpTimer = (SpawnScriptTimer)*itr; - CallSpawnScript(GetSpawnByID(tmpTimer.spawn), SPAWN_SCRIPT_TIMER, tmpTimer.player > 0 ? GetSpawnByID(tmpTimer.player) : 0, tmpTimer.function.c_str()); + Spawn* callSpawn = GetSpawnByID(tmpTimer.spawn); + Spawn* target = nullptr; + if(tmpTimer.player) { + target = GetSpawnByID(tmpTimer.player); + } + if(callSpawn) { + if(!callSpawn->IsPlayer()) { + CallSpawnScript(GetSpawnByID(tmpTimer.spawn), SPAWN_SCRIPT_TIMER, target, tmpTimer.function.c_str()); + } + else { + const char* playerScript = world.GetPlayerScript(0); // 0 = global script + const char* playerZoneScript = world.GetPlayerScript(GetZoneID()); // zone script + if(playerScript || playerZoneScript) { + std::vector args = { + LuaArg(this), + LuaArg(callSpawn), + LuaArg(target) + }; + if(playerScript) { + lua_interface->RunPlayerScriptWithReturn(playerScript, tmpTimer.function.c_str(), args); + } + if(playerZoneScript) { + lua_interface->RunPlayerScriptWithReturn(playerZoneScript, tmpTimer.function.c_str(), args); + } + } + } + } } } } @@ -4638,7 +4675,7 @@ void ZoneServer::KillSpawnByDistance(Spawn* spawn, float max_distance, bool incl if(test_spawn && test_spawn->Alive() && test_spawn->GetID() > 0 && test_spawn->GetID() != spawn->GetID() && test_spawn->IsEntity() && (!test_spawn->IsPlayer() || include_players)){ if(test_spawn->GetDistance(spawn) < max_distance) - KillSpawn(true, test_spawn, spawn, send_packet); + KillSpawn(false, test_spawn, spawn, send_packet); } } grids->second->MSpawns.unlock_shared(); @@ -4714,11 +4751,24 @@ void ZoneServer::RemoveSpawn(Spawn* spawn, bool delete_spawn, bool respawn, bool for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) { client = *client_itr; - if (client && (client->GetVersion() > 373 || !client->IsZoning() || client->GetPlayer() != spawn)) { //don't send destroy ghost of 283 client when zoning + if (client && (!client->IsZoning() || client->GetPlayer() != spawn)) { if (client->GetPlayer()->HasTarget() && client->GetPlayer()->GetTarget() == spawn) client->GetPlayer()->SetTarget(0); - if(client->GetPlayer()->WasSentSpawn(spawn->GetID()) || client->GetPlayer()->IsSendingSpawn(spawn->GetID())) + if(client->GetMailTransaction() == spawn) + client->SetMailTransaction(0); + if(client->GetBanker() == spawn->GetID()) + client->SetBanker(0); + if(client->GetTransportSpawnID() == spawn->GetID()) + client->SetTransportSpawnID(0); + if(client->GetTempPlacementSpawn() == spawn) + client->SetTempPlacementSpawn(nullptr); + if(client->GetCombineSpawn() == spawn) + client->SetCombineSpawn(nullptr); + + //don't send destroy ghost of 283 client when zoning + if((client->GetVersion() > 373 || !client->IsZoning()) && (client->GetPlayer()->WasSentSpawn(spawn->GetID()) || client->GetPlayer()->IsSendingSpawn(spawn->GetID()))) SendRemoveSpawn(client, spawn, packet, delete_spawn); + if (spawn_range_map.count(client) > 0) spawn_range_map.Get(client)->erase(spawn->GetID()); } @@ -4751,10 +4801,6 @@ void ZoneServer::RemoveSpawn(Spawn* spawn, bool delete_spawn, bool respawn, bool spawn->SetDeletedSpawn(true); - // we will remove the spawn ptr and entry in the spawn_list later.. it is not safe right now (lua? client process? spawn process? etc? too many factors) - if(erase_from_spawn_list) - AddPendingSpawnRemove(spawn->GetID()); - if(respawn && !spawn->IsPlayer() && spawn->GetSpawnLocationID() > 0) { LogWrite(ZONE__DEBUG, 3, "Zone", "Handle NPC Respawn for '%s'.", spawn->GetName()); AddRespawn(spawn); @@ -5050,7 +5096,7 @@ void ZoneServer::ProcessFaction(Spawn* spawn, Client* client) } void ZoneServer::Despawn(Spawn* spawn, int32 timer){ - if (spawn && movementMgr != nullptr) { + if (spawn && spawn->IsEntity() && movementMgr != nullptr) { movementMgr->RemoveMob((Entity*)spawn); } if(!spawn || spawn->IsPlayer()) @@ -5129,7 +5175,7 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo //Check if caster is alive after death proc called, incase of deathsave if (dead->Alive()) return; - + RemoveSpellTimersFromSpawn(dead, true, !dead->IsPlayer(), true, !isSpell); ((Entity*)dead)->IsCasting(false); @@ -5362,19 +5408,42 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo // the corpse has loot or not if the timer value (pop_timer) is 0xFFFFFFFF } } - - // If the dead spawns was not a player add it to the dead spawn list - if (!dead->IsPlayer() && !dead->IsBot()) - AddDeadSpawn(dead, pop_timer); // if dead was a player clear hostile spells from its spell queue if (dead->IsPlayer()) spellProcess->RemoveSpellFromQueue((Player*)dead, true); if (dead->IsNPC()) - ((NPC*)dead)->Brain()->ClearHate(); + ((NPC*)dead)->Brain()->ClearHate(spawnListLocked); safe_delete(encounter); + + const char* functionToCall = "on_player_death"; + bool isPlayerDead = true; + if(dead->IsPlayer() || (killer && killer->IsPlayer() && (isPlayerDead = false))) { + if(!isPlayerDead) { + functionToCall = "on_player_kill"; + } + const char* playerScript = world.GetPlayerScript(0); // 0 = global script + const char* playerZoneScript = world.GetPlayerScript(GetZoneID()); // zone script + if(playerScript || playerZoneScript) { + std::vector args = { + LuaArg(this), + LuaArg(dead), + LuaArg(killer) + }; + if(playerScript) { + lua_interface->RunPlayerScriptWithReturn(playerScript, functionToCall, args); + } + if(playerZoneScript) { + lua_interface->RunPlayerScriptWithReturn(playerZoneScript, functionToCall, args); + } + } + } + + // If the dead spawns was not a player add it to the dead spawn list + if (!dead->IsPlayer() && !dead->IsBot()) + AddDeadSpawn(dead, pop_timer); } void ZoneServer::SendDamagePacket(Spawn* attacker, Spawn* victim, int8 type1, int8 type2, int8 damage_type, int16 damage, const char* spell_name) { @@ -5657,9 +5726,13 @@ void ZoneServer::SendInterruptPacket(Spawn* interrupted, LuaSpell* spell, bool f packet = configReader.getStruct(fizzle ? "WS_SpellFizzle" : "WS_Interrupt", client->GetVersion()); if(packet){ packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(interrupted)); - packet->setArrayLengthByName("num_targets", spell->targets.size()); - for (int32 i = 0; i < spell->targets.size(); i++) - packet->setArrayDataByName("target_id", client->GetPlayer()->GetIDWithPlayerSpawn(client->GetPlayer()->GetZone()->GetSpawnByID(spell->targets[i])), i); + std::vector targets = spell->GetTargets(); // snapshot under lock + int i = 0; + packet->setArrayLengthByName("num_targets", targets.size()); + for (int32 id : targets) { + packet->setArrayDataByName("target_id", client->GetPlayer()->GetIDWithPlayerSpawn(client->GetPlayer()->GetZone()->GetSpawnByID(id)), i); + i++; + } packet->setDataByName("spell_id", spell->spell->GetSpellID()); outapp = packet->serialize(); client->QueuePacket(outapp); @@ -5700,15 +5773,18 @@ void ZoneServer::SendCastSpellPacket(LuaSpell* spell, Entity* caster, int32 spel } packet->setDataByName("spawn_id", caster_id); - packet->setArrayLengthByName("num_targets", spell->targets.size()); - for (int32 i = 0; i < spell->targets.size(); i++) { - int32 target_id = client->GetPlayer()->GetIDWithPlayerSpawn(spell->caster->GetZone()->GetSpawnByID(spell->targets[i])); + std::vector targets = spell->GetTargets(); // snapshot under lock + int i = 0; + packet->setArrayLengthByName("num_targets", targets.size()); + for (int32 id : targets) { + int32 target_id = client->GetPlayer()->GetIDWithPlayerSpawn(spell->caster->GetZone()->GetSpawnByID(id)); if(target_id) { packet->setArrayDataByName("target", target_id, i); } else { packet->setArrayDataByName("target", 0xFFFFFFFF, i); } + i++; } int32 visual_to_use = spell_visual_override > 0 ? spell_visual_override : spell->spell->GetSpellData()->spell_visual; @@ -7883,7 +7959,9 @@ void ZoneServer::SendDispellPacket(Entity* caster, Spawn* target, string dispell continue; if(target && target->GetDistance(player) > 50) continue; - + if(client->GetVersion() <= 561) + continue; // does not support packet + packet = configReader.getStruct("WS_HearDispell", client->GetVersion()); if(packet){ packet->setDataByName("spell_name", spell_name.c_str()); @@ -8019,18 +8097,12 @@ void ZoneServer::SetSpawnStructs(Client* client) { //Save a copy of the correct spawn substructs for the client's player, save here a copy if we don't have one PacketStruct* pos = configReader.getStruct("Substruct_SpawnPositionStruct", client_ver); player->SetSpawnPosStruct(pos); - if (versioned_pos_structs.count(pos->GetVersion()) == 0) - versioned_pos_structs[pos->GetVersion()] = new PacketStruct(pos, true); PacketStruct* vis = configReader.getStruct("Substruct_SpawnVisualizationInfoStruct", client_ver); player->SetSpawnVisStruct(vis); - if (versioned_vis_structs.count(vis->GetVersion()) == 0) - versioned_vis_structs[vis->GetVersion()] = new PacketStruct(vis, true); PacketStruct* info = configReader.getStruct("Substruct_SpawnInfoStruct", client_ver); player->SetSpawnInfoStruct(info); - if (versioned_info_structs.count(info->GetVersion()) == 0) - versioned_info_structs[info->GetVersion()] = new PacketStruct(info, true); PacketStruct* header = configReader.getStruct("WS_SpawnStruct_Header", client_ver); player->SetSpawnHeaderStruct(header); @@ -8504,7 +8576,7 @@ LootTable* ZoneServer::GetLootTable(int32 table_id){ return loot_tables[table_id]; } -void ZoneServer::AddLocationTransporter(int32 zone_id, string message, float trigger_x, float trigger_y, float trigger_z, float trigger_radius, int32 destination_zone_id, float destination_x, float destination_y, float destination_z, float destination_heading, int32 cost, int32 unique_id){ +void ZoneServer::AddLocationTransporter(int32 zone_id, string message, float trigger_x, float trigger_y, float trigger_z, float trigger_radius, int32 destination_zone_id, float destination_x, float destination_y, float destination_z, float destination_heading, int32 cost, int32 unique_id, bool force_zone){ LocationTransportDestination* loc = new LocationTransportDestination; loc->message = message; loc->trigger_x = trigger_x; @@ -8518,6 +8590,7 @@ void ZoneServer::AddLocationTransporter(int32 zone_id, string message, float tri loc->destination_heading = destination_heading; loc->cost = cost; loc->unique_id = unique_id; + loc->force_zone = force_zone; MTransporters.lock(); if(location_transporters.count(zone_id) == 0) location_transporters[zone_id] = new MutexList(); @@ -8658,6 +8731,7 @@ void ZoneServer::DeleteGlobalSpawns() { ClearEntityCommands(); DeleteGroundSpawnItems(); + DeleteTransporters(); DeleteGlobalTransporters(); DeleteTransporterMaps(); } @@ -8759,19 +8833,37 @@ void ZoneServer::DeleteFlightPaths() { m_flightPaths.clear(); } -void ZoneServer::SendFlightPathsPackets(Client* client) { +void ZoneServer::SendFlightPathsPackets(Client* client, int32 index) { + if(client->GetVersion() <= 561 && index == 0xFFFFFFFF) + return; + // Only send a packet if there are flight paths if (m_flightPathRoutes.size() > 0) { PacketStruct* packet = configReader.getStruct("WS_FlightPathsMsg", client->GetVersion()); if (packet) { int32 num_routes = m_flightPaths.size(); + if(index != 0xFFFFFFFF) { + num_routes = 1; + } packet->setArrayLengthByName("number_of_routes", num_routes); packet->setArrayLengthByName("number_of_routes2", num_routes); packet->setArrayLengthByName("number_of_routes3", num_routes); packet->setArrayLengthByName("number_of_routes4", num_routes); map::iterator itr; int32 i = 0; + bool breakout = false; for (itr = m_flightPaths.begin(); itr != m_flightPaths.end(); itr++, i++) { + + if(index != 0xFFFFFFFF) { + if(i != index) { + continue; + } + else { + i = 0; + breakout = true; + } + } + packet->setArrayDataByName("route_length", m_flightPathRoutes[itr->first].size(), i); packet->setArrayDataByName("ground_mount", itr->second->flying ? 0 : 1, i); packet->setArrayDataByName("allow_dismount", itr->second->dismount ? 1 : 0, i); @@ -8783,6 +8875,8 @@ void ZoneServer::SendFlightPathsPackets(Client* client) { packet->setSubArrayDataByName("y", (*itr2)->Y, i, j); packet->setSubArrayDataByName("z", (*itr2)->Z, i, j); } + if(breakout) + break; } client->QueuePacket(packet->serialize()); safe_delete(packet); @@ -8901,25 +8995,27 @@ void ZoneServer::SetSpawnScript(SpawnEntry* entry, Spawn* spawn) const char* script = 0; - for (int x = 0; x < 3; x++) - { - switch (x) + if(spawn && !spawn->GetSpawnScript()) { + for (int x = 0; x < 3; x++) { - case 0: - script = world.GetSpawnEntryScript(entry->spawn_entry_id); - break; - case 1: - script = world.GetSpawnLocationScript(entry->spawn_location_id); - break; - case 2: - script = world.GetSpawnScript(entry->spawn_id); - break; - } + switch (x) + { + case 0: + script = world.GetSpawnEntryScript(entry->spawn_entry_id); + break; + case 1: + script = world.GetSpawnLocationScript(entry->spawn_location_id); + break; + case 2: + script = world.GetSpawnScript(entry->spawn_id); + break; + } - if (script && lua_interface && lua_interface->GetSpawnScript(script) != 0) - { - spawn->SetSpawnScript(string(script)); - break; + if (script && lua_interface && lua_interface->GetSpawnScript(script) != 0) + { + spawn->SetSpawnScript(string(script)); + break; + } } } } @@ -9035,36 +9131,6 @@ Spawn* ZoneServer::GetSpawnFromUniqueItemID(int32 unique_id) return nullptr; } -void ZoneServer::AddPendingSpawnRemove(int32 id) -{ - MPendingSpawnRemoval.writelock(__FUNCTION__, __LINE__); - m_pendingSpawnRemove.insert(make_pair(id,true)); - MPendingSpawnRemoval.releasewritelock(__FUNCTION__, __LINE__); -} - -void ZoneServer::ProcessSpawnRemovals() -{ - MSpawnList.writelock(__FUNCTION__, __LINE__); - MPendingSpawnRemoval.writelock(__FUNCTION__, __LINE__); - if (m_pendingSpawnRemove.size() > 0) { - map::iterator itr2; - for (itr2 = m_pendingSpawnRemove.begin(); itr2 != m_pendingSpawnRemove.end(); itr2++) { - spawn_list.erase(itr2->first); - subspawn_list[SUBSPAWN_TYPES::COLLECTOR].erase(itr2->first); - - std::map::iterator hsmitr = housing_spawn_map.find(itr2->first); - if(hsmitr != housing_spawn_map.end()) { - subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].erase(hsmitr->second); - housing_spawn_map.erase(hsmitr); - } - } - - m_pendingSpawnRemove.clear(); - } - MPendingSpawnRemoval.releasewritelock(__FUNCTION__, __LINE__); - MSpawnList.releasewritelock(__FUNCTION__, __LINE__); -} - void ZoneServer::AddSpawnToGroup(Spawn* spawn, int32 group_id) { if( spawn->GetSpawnGroupID() > 0 ) @@ -9164,6 +9230,8 @@ void ZoneServer::RemoveClientsFromZone(ZoneServer* zone) { MClientList.readlock(__FUNCTION__, __LINE__); for (itr = clients.begin(); itr != clients.end(); itr++) { Client* client = *itr; + if(client->GetPlayer() && client->GetPlayer()->GetCharacterID()) + broker.AddSeller(client->GetPlayer()->GetCharacterID(), std::string(client->GetPlayer()->GetName()), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), true, false); if(client->GetCurrentZone() == zone) { client->SetCurrentZone(nullptr); } @@ -9177,7 +9245,7 @@ void ZoneServer::RemoveClientsFromZone(ZoneServer* zone) { void ZoneServer::SendSubSpawnUpdates(SUBSPAWN_TYPES subtype) { std::map::iterator subitr; MSpawnList.readlock(__FUNCTION__, __LINE__); - for(subitr = subspawn_list[subtype].begin(); subitr != subspawn_list[subtype].end(); subitr++) { + for(subitr = subspawn_list[subtype].begin(); subitr != subspawn_list[subtype].end(); subitr++) { subitr->second->changed = true; subitr->second->info_changed = true; AddChangedSpawn(subitr->second); @@ -9198,30 +9266,38 @@ bool ZoneServer::HouseItemSpawnExists(int32 item_id) { } void ZoneServer::ProcessPendingSpawns() { - MPendingSpawnListAdd.writelock(__FUNCTION__, __LINE__); - list::iterator itr2; - for (itr2 = pending_spawn_list_add.begin(); itr2 != pending_spawn_list_add.end(); itr2++) { - Spawn* spawn = *itr2; - - MSpawnList.writelock(__FUNCTION__, __LINE__); - if (spawn) - spawn_list[spawn->GetID()] = spawn; - - if(spawn->IsCollector()) { - subspawn_list[SUBSPAWN_TYPES::COLLECTOR].insert(make_pair(spawn->GetID(),spawn)); - } - if(spawn->GetPickupItemID()) { - subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].insert(make_pair(spawn->GetPickupItemID(),spawn)); - housing_spawn_map.insert(make_pair(spawn->GetID(), spawn->GetPickupItemID())); - } - MSpawnList.releasewritelock(__FUNCTION__, __LINE__); - - CheckSpawnRange(spawn); + std::vector toAdd; + { + MPendingSpawnListAdd.writelock(__FUNCTION__, __LINE__); + toAdd.reserve(pending_spawn_list_add.size()); + for (auto *p : pending_spawn_list_add) + toAdd.push_back(p); + pending_spawn_list_add.clear(); + MPendingSpawnListAdd.releasewritelock(__FUNCTION__, __LINE__); } - - pending_spawn_list_add.clear(); - MPendingSpawnListAdd.releasewritelock(__FUNCTION__, __LINE__); - spawn_check_add.Trigger(); + + bool spawnsAdded = false; + + for (auto *p : toAdd) + { + MSpawnList.writelock(__FUNCTION__, __LINE__); + spawn_list[p->GetID()] = p; + + if(p->IsCollector()) { + subspawn_list[SUBSPAWN_TYPES::COLLECTOR].insert(make_pair(p->GetID(),p)); + } + if(p->GetPickupItemID()) { + subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].insert(make_pair(p->GetPickupItemID(),p)); + housing_spawn_map.insert(make_pair(p->GetID(), p->GetPickupItemID())); + } + + MSpawnList.releasewritelock(__FUNCTION__, __LINE__); + CheckSpawnRange(p); + spawnsAdded = true; + } + + if(spawnsAdded) + spawn_check_add.Trigger(); } void ZoneServer::AddSpawnToGrid(Spawn* spawn, int32 grid_id) { diff --git a/old/WorldServer/zoneserver.h b/old/WorldServer/zoneserver.h index 3039e80..f82fb1d 100644 --- a/old/WorldServer/zoneserver.h +++ b/old/WorldServer/zoneserver.h @@ -1,6 +1,6 @@ -/* +/* EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) This file is part of EQ2Emulator. @@ -17,19 +17,21 @@ You should have received a copy of the GNU General Public License along with EQ2Emulator. If not, see . */ + #ifndef ZONESERVER_H #define ZONESERVER_H #include #include -#include "../common/linked_list.hpp" -#include "../common/timer.hpp" -#include "../common/queue.hpp" -#include "../common/servertalk.hpp" -#include "../common/tcp.hpp" +#include "../common/linked_list.h" +#include "../common/timer.h" +#include "../common/queue.h" +#include "../common/servertalk.h" +#include "../common/TCPConnection.h" #include "WorldTCPConnection.h" -#include "../common/data_buffer.hpp" +#include "../common/Mutex.h" +#include "../common/DataBuffer.h" #include "net.h" #include "Player.h" #include "Combat.h" @@ -169,7 +171,7 @@ struct TrackedSpawn { struct HouseItem { int32 spawn_id; int32 item_id; - int32 unique_id; + int64 unique_id; Item* item; }; @@ -678,7 +680,7 @@ public: void AddFlightPath(int32 id, FlightPathInfo* info); void AddFlightPathLocation(int32 id, FlightPathLocation* location); void DeleteFlightPaths(); - void SendFlightPathsPackets(Client* client); + void SendFlightPathsPackets(Client* client, int32 index = 0xFFFFFFFF); int32 GetFlightPathIndex(int32 id); float GetFlightPathSpeed(int32 id); @@ -700,15 +702,10 @@ public: Spawn* GetSpawnFromUniqueItemID(int32 unique_id); void SendHouseItems(Client* client); - MutexMap house_object_database_lookup; // 1st int32 = model type, 2nd int32 = spawn id - int32 GetWatchdogTime() { return watchdogTimestamp; } void SetWatchdogTime(int32 time) { watchdogTimestamp = time; } void CancelThreads(); - void AddPendingSpawnRemove(int32 id); - void ProcessSpawnRemovals(); - bool SendRemoveSpawn(Client* client, Spawn* spawn, PacketStruct* packet = 0, bool delete_spawn = false); void AddSpawnToGroup(Spawn* spawn, int32 group_id); @@ -924,6 +921,7 @@ private: Timer widget_timer; Timer queue_updates; Timer shutdownDelayTimer; + Timer delete_timer; /* Enums */ Instance_Type InstanceType; @@ -980,10 +978,6 @@ private: bool can_gate; bool can_evac; - map versioned_pos_structs; - map versioned_info_structs; - map versioned_vis_structs; - /* Weather Stuff */ bool weather_enabled; // false = disabled, true = enabled int8 weather_type; // 0 = normal, 1 = dynamic, 2 = random, 3 = chaotic @@ -1033,9 +1027,6 @@ private: int32 watchdogTimestamp; - std::map m_pendingSpawnRemove; - Mutex MPendingSpawnRemoval; - std::map lua_queued_state_commands; std::map> lua_spawn_update_command; std::mutex MLuaQueueStateCmd; @@ -1148,7 +1139,7 @@ public: LootTable* GetLootTable(int32 table_id); /* Transporters */ - void AddLocationTransporter(int32 zone_id, string message, float trigger_x, float trigger_y, float trigger_z, float trigger_radius, int32 destination_zone_id, float destination_x, float destination_y, float destination_z, float destination_heading, int32 cost, int32 unique_id); + void AddLocationTransporter(int32 zone_id, string message, float trigger_x, float trigger_y, float trigger_z, float trigger_radius, int32 destination_zone_id, float destination_x, float destination_y, float destination_z, float destination_heading, int32 cost, int32 unique_id, bool force_zone); void AddTransporter(int32 transport_id, int8 type, string name, string message, int32 destination_zone_id, float destination_x, float destination_y, float destination_z, float destination_heading, int32 cost, int32 unique_id, int8 min_level, int8 max_level, int32 quest_req, int16 quest_step_req, int32 quest_complete, int32 map_x, int32 map_y, int32 expansion_flag, int32 holiday_flag, int32 min_client_version, int32 max_client_version, int32 flight_path_id, int16 mount_id, int8 mount_red_color, int8 mount_green_color, int8 mount_blue_color); diff --git a/old/common/CRC16.cpp b/old/common/CRC16.cpp new file mode 100644 index 0000000..f7b43ad --- /dev/null +++ b/old/common/CRC16.cpp @@ -0,0 +1,328 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include + +unsigned long IntArray[]={ +0x00000000, +0x77073096, +0xEE0E612C, +0x990951BA, +0x076DC419, +0x706AF48F, +0xE963A535, +0x9E6495A3, +0x0EDB8832, +0x79DCB8A4, +0xE0D5E91E, +0x97D2D988, +0x09B64C2B, +0x7EB17CBD, +0xE7B82D07, +0x90BF1D91, +0x1DB71064, +0x6AB020F2, +0xF3B97148, +0x84BE41DE, +0x1ADAD47D, +0x6DDDE4EB, +0xF4D4B551, +0x83D385C7, +0x136C9856, +0x646BA8C0, +0xFD62F97A, +0x8A65C9EC, +0x14015C4F, +0x63066CD9, +0xFA0F3D63, +0x8D080DF5, +0x3B6E20C8, +0x4C69105E, +0xD56041E4, +0xA2677172, +0x3C03E4D1, +0x4B04D447, +0xD20D85FD, +0xA50AB56B, +0x35B5A8FA, +0x42B2986C, +0xDBBBC9D6, +0xACBCF940, +0x32D86CE3, +0x45DF5C75, +0xDCD60DCF, +0xABD13D59, +0x26D930AC, +0x51DE003A, +0xC8D75180, +0xBFD06116, +0x21B4F4B5, +0x56B3C423, +0xCFBA9599, +0xB8BDA50F, +0x2802B89E, +0x5F058808, +0xC60CD9B2, +0xB10BE924, +0x2F6F7C87, +0x58684C11, +0xC1611DAB, +0xB6662D3D, +0x76DC4190, +0x01DB7106, +0x98D220BC, +0xEFD5102A, +0x71B18589, +0x06B6B51F, +0x9FBFE4A5, +0xE8B8D433, +0x7807C9A2, +0x0F00F934, +0x9609A88E, +0xE10E9818, +0x7F6A0DBB, +0x086D3D2D, +0x91646C97, +0xE6635C01, +0x6B6B51F4, +0x1C6C6162, +0x856530D8, +0xF262004E, +0x6C0695ED, +0x1B01A57B, +0x8208F4C1, +0xF50FC457, +0x65B0D9C6, +0x12B7E950, +0x8BBEB8EA, +0xFCB9887C, +0x62DD1DDF, +0x15DA2D49, +0x8CD37CF3, +0xFBD44C65, +0x4DB26158, +0x3AB551CE, +0xA3BC0074, +0xD4BB30E2, +0x4ADFA541, +0x3DD895D7, +0xA4D1C46D, +0xD3D6F4FB, +0x4369E96A, +0x346ED9FC, +0xAD678846, +0xDA60B8D0, +0x44042D73, +0x33031DE5, +0xAA0A4C5F, +0xDD0D7CC9, +0x5005713C, +0x270241AA, +0xBE0B1010, +0xC90C2086, +0x5768B525, +0x206F85B3, +0xB966D409, +0xCE61E49F, +0x5EDEF90E, +0x29D9C998, +0xB0D09822, +0xC7D7A8B4, +0x59B33D17, +0x2EB40D81, +0xB7BD5C3B, +0xC0BA6CAD, +0xEDB88320, +0x9ABFB3B6, +0x03B6E20C, +0x74B1D29A, +0xEAD54739, +0x9DD277AF, +0x04DB2615, +0x73DC1683, +0xE3630B12, +0x94643B84, +0x0D6D6A3E, +0x7A6A5AA8, +0xE40ECF0B, +0x9309FF9D, +0x0A00AE27, +0x7D079EB1, +0xF00F9344, +0x8708A3D2, +0x1E01F268, +0x6906C2FE, +0xF762575D, +0x806567CB, +0x196C3671, +0x6E6B06E7, +0xFED41B76, +0x89D32BE0, +0x10DA7A5A, +0x67DD4ACC, +0xF9B9DF6F, +0x8EBEEFF9, +0x17B7BE43, +0x60B08ED5, +0xD6D6A3E8, +0xA1D1937E, +0x38D8C2C4, +0x4FDFF252, +0xD1BB67F1, +0xA6BC5767, +0x3FB506DD, +0x48B2364B, +0xD80D2BDA, +0xAF0A1B4C, +0x36034AF6, +0x41047A60, +0xDF60EFC3, +0xA867DF55, +0x316E8EEF, +0x4669BE79, +0xCB61B38C, +0xBC66831A, +0x256FD2A0, +0x5268E236, +0xCC0C7795, +0xBB0B4703, +0x220216B9, +0x5505262F, +0xC5BA3BBE, +0xB2BD0B28, +0x2BB45A92, +0x5CB36A04, +0xC2D7FFA7, +0xB5D0CF31, +0x2CD99E8B, +0x5BDEAE1D, +0x9B64C2B0, +0xEC63F226, +0x756AA39C, +0x026D930A, +0x9C0906A9, +0xEB0E363F, +0x72076785, +0x05005713, +0x95BF4A82, +0xE2B87A14, +0x7BB12BAE, +0x0CB61B38, +0x92D28E9B, +0xE5D5BE0D, +0x7CDCEFB7, +0x0BDBDF21, +0x86D3D2D4, +0xF1D4E242, +0x68DDB3F8, +0x1FDA836E, +0x81BE16CD, +0xF6B9265B, +0x6FB077E1, +0x18B74777, +0x88085AE6, +0xFF0F6A70, +0x66063BCA, +0x11010B5C, +0x8F659EFF, +0xF862AE69, +0x616BFFD3, +0x166CCF45, +0xA00AE278, +0xD70DD2EE, +0x4E048354, +0x3903B3C2, +0xA7672661, +0xD06016F7, +0x4969474D, +0x3E6E77DB, +0xAED16A4A, +0xD9D65ADC, +0x40DF0B66, +0x37D83BF0, +0xA9BCAE53, +0xDEBB9EC5, +0x47B2CF7F, +0x30B5FFE9, +0xBDBDF21C, +0xCABAC28A, +0x53B39330, +0x24B4A3A6, +0xBAD03605, +0xCDD70693, +0x54DE5729, +0x23D967BF, +0xB3667A2E, +0xC4614AB8, +0x5D681B02, +0x2A6F2B94, +0xB40BBE37, +0xC30C8EA1, +0x5A05DF1B, +0x2D02EF8D, +}; + +unsigned long CRC16(const unsigned char *buf, int size, int key) +{ + unsigned long ecx = key; //mov ecx, [esp+arg_8] + unsigned long eax = ecx; //mov eax, ecx + unsigned long edi; + + eax = ~ eax; //not eax + eax&=0xFF; //and eax, 0FFh + eax=IntArray[eax]; //mov eax, dword_0_10115D38[eax*4] IntArray + eax ^= 0x00FFFFFF; //xor eax, 0FFFFFFh + int edx = ecx; //mov edx, ecx + edx = edx >> 8; //sar edx, 8 + edx = edx ^ eax; //xor edx, eax + eax = eax >> 8; //sar eax, 8 + edx &= 0xFF; //and edx, 0FFh + eax &= 0x00FFFFFF; //and eax, 0FFFFFFh + eax ^= IntArray[edx]; //xor eax, dword_0_10115D38[edx*4] + edx = ecx; //mov edx, ecx + edx = edx >> 0x10; //sar edx, 10h + edx ^= eax; //xor edx, eax + eax = eax >> 8; //sar eax, 8 + edx &= 0xFF; //and edx, 0FFh + int esi = IntArray[edx]; //mov esi, dword_0_10115D38[edx*4] + edx = size; //mov edx, [esp+4+arg_4] + eax &= 0x00FFFFFF; //and eax, 0FFFFFFh + eax ^= esi; //xor eax, esi + ecx = ecx >> 0x18; //sar ecx, 18h + ecx ^= eax; //xor ecx, eax + ecx &= 0xFF; //and ecx, 0FFh + esi = IntArray[ecx]; //mov esi, dword_0_10115D38[ecx*4] + ecx = (int)*buf; //mov ecx, [esp+4+arg_0] + eax = eax >> 8; //sar eax, 8 + eax &= 0x00FFFFFF; //and eax, 0FFFFFFh + eax ^= esi; //xor eax, esi + for(int x = 0; x < size; x++) + { //eax is the crc, ecx is the current part of the buffer + int edx = 0; //xor edx, edx + edx = buf[x] & 0x00FF; //mov dl, [ecx] + + edx ^= eax; //xor edx, eax + eax = eax >> 8; //sar eax, 8 + edx &= 0xFF; //and edx, 0FFh + edi = IntArray[edx]; //mov edi, dword_0_10115D38[edx*4] + eax &= 0x00FFFFFF; //and eax, 0FFFFFFh + eax ^= edi; //xor eax, edi + } + return ~eax; +} diff --git a/old/common/CRC16.h b/old/common/CRC16.h new file mode 100644 index 0000000..7aacd36 --- /dev/null +++ b/old/common/CRC16.h @@ -0,0 +1,25 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef _CRC16_H +#define _CRC16_H + +unsigned long CRC16(const unsigned char *buf, int size, int key); + +#endif diff --git a/old/common/Common_Defines.h b/old/common/Common_Defines.h new file mode 100644 index 0000000..4600d97 --- /dev/null +++ b/old/common/Common_Defines.h @@ -0,0 +1,32 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#define BASEDIR "./" + +#ifndef DB_INI_FILE + #ifdef LOGIN + #define DB_INI_FILE BASEDIR "login_db.ini" + #else + #define DB_INI_FILE BASEDIR "world_db.ini" + #endif +#endif + +#ifndef MAIN_CONFIG_FILE + #define MAIN_CONFIG_FILE BASEDIR "server_config.json" +#endif \ No newline at end of file diff --git a/old/common/Condition.cpp b/old/common/Condition.cpp new file mode 100644 index 0000000..348c90a --- /dev/null +++ b/old/common/Condition.cpp @@ -0,0 +1,133 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include "debug.h" +#include "Condition.h" + +#ifdef WIN32 +#else +#include +#include +#include +#endif + +#ifdef WIN32 +/* + + Windows does not support condition variables by default. + So we use a simple hack of sleeping in wait() and doing + nothing anywhere else. + + some possible places to look for ways to do this: + http://www.cs.wustl.edu/~schmidt/win32-cv-1.html + + http://sources.redhat.com/pthreads-win32/ + http://sources.redhat.com/cgi-bin/cvsweb.cgi/pthreads/pthread_cond_signal.c?rev=1.7&content-type=text/x-cvsweb-markup&cvsroot=pthreads-win32 + +*/ + +#define CONDITION_HACK_GRANULARITY 4 + + +Condition::Condition() +{ +} + +void Condition::Signal() +{ +} + +void Condition::SignalAll() +{ +} + +void Condition::Wait() +{ + Sleep(CONDITION_HACK_GRANULARITY); +} + +Condition::~Condition() +{ +} + + +#else //!WIN32 + +Condition::Condition() +{ + pthread_cond_init(&cond,NULL); + pthread_mutex_init(&mutex,NULL); +} + +void Condition::Signal() +{ + pthread_mutex_lock(&mutex); + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); +} + +void Condition::SignalAll() +{ + pthread_mutex_lock(&mutex); + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&mutex); +} + +void Condition::Wait() +{ + pthread_mutex_lock(&mutex); + pthread_cond_wait(&cond,&mutex); + pthread_mutex_unlock(&mutex); +} + +/* +I commented this specifically because I think it might be very +difficult to write a windows counterpart to it, so I would like +to discourage its use until we can confirm that it can be reasonably +implemented on windows. + +bool Condition::TimedWait(unsigned long usec) +{ +struct timeval now; +struct timespec timeout; +int retcode=0; + pthread_mutex_lock(&mutex); + gettimeofday(&now,NULL); + now.tv_usec+=usec; + timeout.tv_sec = now.tv_sec + (now.tv_usec/1000000); + timeout.tv_nsec = (now.tv_usec%1000000) *1000; + //cout << "now=" << now.tv_sec << "."<. +*/ +#ifndef __CONDITION_H +#define __CONDITION_H + +#ifndef WIN32 +#include +#endif + +//Sombody, someday needs to figure out how to implement a condition +//system on windows... + + +class Condition { + private: +#ifndef WIN32 + pthread_cond_t cond; + pthread_mutex_t mutex; +#endif + public: + Condition(); + void Signal(); + void SignalAll(); + void Wait(); +// bool TimedWait(unsigned long usec); + ~Condition(); +}; + +#endif + + diff --git a/old/common/ConfigReader.cpp b/old/common/ConfigReader.cpp new file mode 100644 index 0000000..6264d59 --- /dev/null +++ b/old/common/ConfigReader.cpp @@ -0,0 +1,304 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include "ConfigReader.h" +#include "Log.h" + +ConfigReader::~ConfigReader(){ + MStructs.lock(); + DestroyStructs(); + MStructs.unlock(); +} +PacketStruct* ConfigReader::getStructByVersion(const char* name, int16 version){ + PacketStruct* packet = 0; + PacketStruct* newpacket = 0; + MStructs.lock(); + vector* struct_versions = structs[string(name)]; + if(struct_versions){ + vector::iterator iter; + for(iter = struct_versions->begin(); iter != struct_versions->end(); iter++){ + packet = *iter; + if(packet && packet->GetVersion() == version){ + newpacket = new PacketStruct(packet, version); + break; + } + } + } + MStructs.unlock(); + if(!newpacket) + LogWrite(PACKET__ERROR, 0, "Packet", "Could not find struct named '%s' with version: %i", name, version); + return newpacket; +} +void ConfigReader::ReloadStructs(){ + MStructs.lock(); + DestroyStructs(); + for(int32 i=0;i*>::iterator struct_iterator; + for(struct_iterator=structs.begin();struct_iterator!=structs.end();struct_iterator++) { + vector* versions = struct_iterator->second; + vector::iterator version_iter; + if(versions){ + for(version_iter = versions->begin(); version_iter != versions->end(); version_iter++){ + safe_delete(*version_iter); + } + } + safe_delete(versions); + } + structs.clear(); +} +PacketStruct* ConfigReader::getStruct(const char* name, int16 version){ + PacketStruct* latest_version = 0; + PacketStruct* new_latest_version = 0; + MStructs.lock(); + vector* struct_versions = structs[string(name)]; + if(struct_versions){ + vector::iterator iter; + for(iter = struct_versions->begin(); iter != struct_versions->end(); iter++){ + if((*iter)->GetVersion() <= version && (!latest_version || (*iter)->GetVersion() > latest_version->GetVersion())) + latest_version = *iter; + } + if (latest_version) { + // TODO: Need to revisit this, if we have a packet struct with a version of 1, but no opcodes, we still go to the else if statement + // which results in sending corrupt data to client + if (latest_version->GetOpcode() != OP_Unknown && (latest_version->GetOpcodeValue(version) == 0xFFFF || latest_version->GetOpcodeValue(version)==0xCDCD)) { + LogWrite(PACKET__ERROR, 0, "Packet", "Could not find valid opcode for Packet Struct '%s' and client version %d", latest_version->GetName(), version); + } + else if(strlen(latest_version->GetOpcodeType()) == 0 || latest_version->GetOpcode() != OP_Unknown) + new_latest_version = new PacketStruct(latest_version, version); + } + + } + MStructs.unlock(); + if(!new_latest_version && !latest_version) + LogWrite(PACKET__ERROR, 0, "Packet", "Could not find struct named '%s' with version: %i", name, version); + return new_latest_version; +} +int16 ConfigReader::GetStructVersion(const char* name, int16 version){ + MStructs.lock(); + vector* struct_versions = structs[string(name)]; + int16 ret = 0; + if(struct_versions){ + vector::iterator iter; + PacketStruct* latest_version = 0; + for(iter = struct_versions->begin(); iter != struct_versions->end(); iter++){ + if(!latest_version || ( (*iter)->GetVersion() > latest_version->GetVersion() && (*iter)->GetVersion() <= version) ) + latest_version = *iter; + } + if(latest_version) + ret = latest_version->GetVersion(); + } + MStructs.unlock(); + return ret; +} +void ConfigReader::addStruct(const char* name, int16 version, PacketStruct* new_struct){ + string strname(name); + vector* struct_versions = structs[strname]; + if(struct_versions) + struct_versions->push_back(new_struct); + else{ + struct_versions = new vector; + struct_versions->push_back(new_struct); + structs[strname] = struct_versions; + } +} +bool ConfigReader::LoadFile(const char* name){ + load_files.push_back(name); + return processXML_Elements(name); +} +bool ConfigReader::processXML_Elements(const char* fileName){ + XMLNode xMainNode=XMLNode::openFileHelper(fileName,"EQ2Emulator"); + if(xMainNode.isEmpty()) + return false; + for(int i=0;iSetName(struct_name); + if(opcode_type) + new_struct->SetOpcodeType(opcode_type); + if(opcode_name){ + if(!new_struct->SetOpcode(opcode_name)){ + safe_delete(new_struct); + continue; + } + } + new_struct->SetVersion(version); + loadDataStruct(new_struct, xMainNode.getChildNode("Struct", i)); + addStruct(struct_name, version, new_struct); + } + return true; +} +void ConfigReader::loadDataStruct(PacketStruct* packet, XMLNode parentNode, bool array_packet){ + for(int x=0;xGetVersion()); + if(substruct_packet){ + vector::iterator itr; + vector* structs = substruct_packet->getStructs(); + DataStruct* ds = 0; + int i = 0; + char tmp[12] = {0}; + for(i=0;ibegin();itr!=structs->end();itr++) { + ds = *itr; + string new_name; + if(array_packet) + new_name = string(name).append("_").append(ds->GetStringName()); + else + new_name = string(name).append("_").append(ds->GetStringName()).append("_").append(tmp); + + DataStruct* ds2 = new DataStruct(new_name.c_str(), ds->GetType(),ds->GetLength(), ds->GetType2()); + + if(!array_packet && strlen(ds->GetArraySizeVariable()) > 1) + ds2->SetArraySizeVariable(string(name).append("_").append(ds->GetArraySizeVariable()).append("_").append(tmp).c_str()); + ds2->SetOversized(ds->GetOversized()); + ds2->SetOversizedByte(ds->GetOversizedByte()); + ds2->SetDefaultValue(ds->GetDefaultValue()); + ds2->SetMaxArraySize(ds->GetMaxArraySize()); + ds2->SetIfSetVariable(ds->GetIfSetVariable() ? ds->GetIfSetVariable() : if_variable); + ds2->SetIfNotSetVariable(ds->GetIfSetVariable() ? ds->GetIfNotSetVariable() : if_not_variable); + ds2->SetIfNotEqualsVariable(ds->GetIfNotEqualsVariable()); + ds2->SetIfFlagNotSetVariable(ds->GetIfFlagNotSetVariable()); + ds2->SetIfFlagSetVariable(ds->GetIfFlagSetVariable()); + ds2->SetIsOptional(ds->IsOptional()); + ds2->AddIfSetVariable(if_variable); //add this if the modifier is on the piece that is including the substruct + ds2->AddIfNotSetVariable(if_not_variable); //add this if the modifier is on the piece that is including the substruct + packet->add(ds2); + } + } + if(!array_packet){ + i--; + substruct_packet->renameSubstructArray(name, i); + //ds2->SetArraySizeVariable((char*)string(name).append("_").append(ds->GetArraySizeVariable()).append("_").append(tmp).c_str()); + packet->addPacketArrays(substruct_packet); + } + + safe_delete(substruct_packet); + } + continue; + } + else if(type && strncasecmp(type,"Array", 5)==0 && array_size){ + PacketStruct* new_packet = new PacketStruct; + new_packet->SetName(name); + new_packet->IsSubPacket(true); + new_packet->SetVersion(packet->GetVersion()); + loadDataStruct(new_packet, parentNode.getChildNode("Data", x), true); + packet->add(new_packet); + } + if(!name || !type) + { + LogWrite(MISC__WARNING, 0, "Misc", "Ignoring invalid Data Element, all elements must include at least an ElementName and Type!"); + LogWrite(MISC__WARNING, 0, "Misc", "\tStruct: '%s', version: %i", parentNode.getAttribute("Name"), parentNode.getAttribute("ClientVersion")); + continue; + } + DataStruct* ds = new DataStruct(name, type, num_size, type2); + int8 oversized_value = 0; + int8 oversized_byte_value = 255; + if(oversized){ + try{ + oversized_value = atoi(oversized); + } + catch(...){} + } + if(oversized_byte){ + try{ + oversized_byte_value = atoi(oversized_byte); + } + catch(...){} + } + ds->SetOversizedByte(oversized_byte_value); + ds->SetOversized(oversized_value); + ds->SetMaxArraySize(max_array_size); + if(array_size) + ds->SetArraySizeVariable(array_size); + ds->SetDefaultValue(byte_val); + ds->SetIfSetVariable(if_variable); + ds->SetIfNotSetVariable(if_not_variable); + ds->SetIfEqualsVariable(if_equals_variable); + ds->SetIfNotEqualsVariable(if_not_equals_variable); + ds->SetIfFlagNotSetVariable(if_flag_not_set_variable); + ds->SetIfFlagSetVariable(if_flag_set_variable); + if (optional && strlen(optional) > 0 && (strcmp("true", optional) == 0 || strcmp("TRUE", optional) == 0 || strcmp("True", optional) == 0)) + ds->SetIsOptional(true); + packet->add(ds); + } +} + diff --git a/old/common/ConfigReader.h b/old/common/ConfigReader.h new file mode 100644 index 0000000..cd34ae5 --- /dev/null +++ b/old/common/ConfigReader.h @@ -0,0 +1,52 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef __CONFIG_READER__ +#define __CONFIG_READER__ +#include +#include "PacketStruct.h" +#include +#include +#include +#include "xmlParser.h" +#include "Mutex.h" + +using namespace std; + +class ConfigReader{ +public: + ~ConfigReader(); + + void addStruct(const char* name, int16 version, PacketStruct* new_struct); + PacketStruct* getStruct(const char* name, int16 version); + PacketStruct* getStructByVersion(const char* name, int16 version); + void loadDataStruct(PacketStruct* packet, XMLNode parentNode, bool array_packet = false); + bool processXML_Elements(const char* fileName); + int16 GetStructVersion(const char* name, int16 version); + void DestroyStructs(); + void ReloadStructs(); + bool LoadFile(const char* name); +private: + Mutex MStructs; + vector load_files; + map*> structs; + //vector structs; +}; +#endif + diff --git a/old/common/Crypto.cpp b/old/common/Crypto.cpp new file mode 100644 index 0000000..369390e --- /dev/null +++ b/old/common/Crypto.cpp @@ -0,0 +1,47 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include "Crypto.h" +#include +#include "../common/packet_dump.h" + +using namespace std; +void test(); +int64 Crypto::RSADecrypt(uchar* text, int16 size){ + int64 ret = 0; + uchar* buffer = new uchar[8]; + for(int i=7;i>=0;i--) + buffer[7-i] = text[i]; + memcpy(&ret, buffer, 8); + safe_delete_array(buffer); + return ret; +} + +void Crypto::RC4Decrypt(uchar* text, int32 size){ + MCrypto.lock(); + client->Cypher(text, size); + MCrypto.unlock(); +} + +void Crypto::RC4Encrypt(uchar* text, int32 size){ + MCrypto.lock(); + server->Cypher(text, size); + MCrypto.unlock(); +} + diff --git a/old/common/Crypto.h b/old/common/Crypto.h new file mode 100644 index 0000000..d2c478b --- /dev/null +++ b/old/common/Crypto.h @@ -0,0 +1,66 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef _CRYPTO_H +#define _CRYPTO_H +#include +#include +#include "RC4.h" +#include "../common/types.h" + +using namespace std; +class Crypto { +public: + ~Crypto(){ safe_delete(client); safe_delete(server); } + Crypto() { rc4_key = 0; encrypted = false; client = 0; server = 0; }; + + static int64 RSADecrypt(uchar* text, int16 size); + void RC4Encrypt(uchar* text, int32 size); + void RC4Decrypt(uchar* text, int32 size); + int64 getRC4Key() { return rc4_key; } + void setRC4Key(int64 key) { + rc4_key = key; + if(key > 0){ + encrypted = true; + client = new RC4(~key); + server = new RC4(key); + uchar temp[20]; + client->Cypher(temp, 20); + server->Cypher(temp, 20); + } + else{ + encrypted = false; + safe_delete(client); + safe_delete(server); + } + } + bool isEncrypted(){ return encrypted; } + void setEncrypted(bool in_val){ encrypted = in_val; } + + +private: + RC4* server; + RC4* client; + bool encrypted; + int64 rc4_key; + mutex MCrypto; +}; + +#endif + diff --git a/old/common/DataBuffer.h b/old/common/DataBuffer.h new file mode 100644 index 0000000..f40486d --- /dev/null +++ b/old/common/DataBuffer.h @@ -0,0 +1,207 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef __EQ2_DATABUFFER_ +#define __EQ2_DATABUFFER_ +#include +#include "../common/types.h" +#include "../common/EQPacket.h" +#include "../common/EQ2_Common_Structs.h" + +#ifdef WORLD + #include "../WorldServer/SpawnLists.h" +#endif + +using namespace std; + +class DataBuffer{ +public: + bool changed; + uchar* getData(){ return (uchar*)buffer.c_str(); } + int32 getDataSize(){ return buffer.length(); } + string* getDataString(){ return &buffer; } + void CreateEQ2Color(EQ2_Color& color){ + CreateEQ2Color(&color); + } + uchar* GetLoadBuffer(){ + return load_buffer; + } + int32 GetLoadPos(){ + return load_pos; + } + int32 GetLoadLen(){ + return load_len; + } + void SetLoadPos(int32 new_pos){ + load_pos = new_pos; + } + void CreateEQ2Color(EQ2_Color* color){ + int8 rgb[3]; + float* tmp = 0; + for(int i=0;i<3;i++){ + tmp = (float*)(load_buffer + load_pos); + rgb[i] = (int8)((*tmp)*255); + load_pos += sizeof(float); + } + color->red = rgb[0]; + color->green = rgb[1]; + color->blue = rgb[2]; + } + + template void MakeEQ2_Int8(Type& output){ + MakeEQ2_Int8(&output); + } + template void MakeEQ2_Int8(Type* output){ + float* tmp = (float*)(load_buffer + load_pos); + if(*tmp < 0) + *tmp *= -1; + sint8 result = (sint8)((*tmp)*100); + memcpy(output, &result, sizeof(sint8)); + load_pos += sizeof(float); + } + void InitializeGetData(){ + get_buffer = (uchar*)buffer.c_str(); + get_len = buffer.length(); + get_pos = 0; + } + void InitializeLoadData(uchar* input, int32 size){ + buffer = string((char*)input, size); + load_buffer = (uchar*)buffer.c_str(); + load_len = size; + load_pos = 0; + } + template void LoadDataString(String& output){ + LoadDataString(&output); + } + template void LoadDataString(String* output){ + if((sizeof(output->size) + load_pos) <= load_len){ + memcpy(&output->size, load_buffer + load_pos, sizeof(output->size)); + load_pos += sizeof(output->size); + } + if((output->size + load_pos) <= load_len){ + output->data = string((char*)(load_buffer + load_pos), output->size); + load_pos += output->size; + } + } + template void LoadData(Type& output){ + LoadData(&output); + } + template void LoadData(Type* output, int32 array_size){ + if(array_size<=1){ + LoadData(output); + } + else{ + for(int32 i=0;i void LoadData(Type* output){ + if((sizeof(Type) + load_pos) <= load_len){ + memcpy(output, load_buffer + load_pos, sizeof(Type)); + load_pos += sizeof(Type); + } + } + template void LoadData(Type& output, int32 array_size){ + LoadData(&output, array_size); + } + void LoadSkip(int8 bytes){ + load_pos += bytes; + } + template void LoadSkip(Type& skip){ + LoadSkip(&skip); + } + template void LoadSkip(Type* skip){ + load_pos += sizeof(Type); + } + template void GetData(Type* output){ + if((sizeof(Type) + get_pos) <= get_len){ + *output = (Type*)get_buffer; + get_pos += sizeof(output); + } + } + void AddZeros(int16 num){ + int8* data = new int8[num]; + memset(data, 0, num); + AddData(*data); + safe_delete_array(data); + } + template void StructAddData(Type input, int16 size, string* datastring){ + if(datastring) + datastring->append((char*)&input, size); + else + buffer.append((char*)&input, size); + } + template void StructAddData(Type input, int32 array_size, int16 size, string* datastring){ + if(array_size>0){ + for(int32 i=0;i void AddData(Type input, string* datastring = 0){ + if(!datastring) + datastring = &buffer; + datastring->append((char*)&input, sizeof(input)); + } + template void AddData(Type input, int32 array_size, string* datastring = 0){ + if(array_size>0){ + for(int32 i=0;i void AddDataString(String* input, string* datastring = 0){ + AddDataString(*input, datastring); + } + template void AddDataString(String input, string* datastring = 0){ + input.size = input.data.length(); + if(!datastring) + datastring = &buffer; + datastring->append((char*)&input.size, sizeof(input.size)); + datastring->append(input.data); + } + void AddCharArray(char* array, string* datastring = 0){ + if(!datastring) + datastring = &buffer; + datastring->append(array); + } + void AddCharArray(char* array, int16 size, string* datastring = 0){ + if(!datastring) + datastring = &buffer; + datastring->append(array, size); + } + void AddData(string data, string* datastring = 0){ + if(!datastring) + datastring = &buffer; + datastring->append(data); + } + void Clear() { buffer.clear(); } +private: + string buffer; + uchar* get_buffer; + uchar* load_buffer; + int32 get_len; + int32 get_pos; + int32 load_len; + int32 load_pos; +}; +#endif + diff --git a/old/common/DatabaseNew.cpp b/old/common/DatabaseNew.cpp new file mode 100644 index 0000000..e1e2450 --- /dev/null +++ b/old/common/DatabaseNew.cpp @@ -0,0 +1,422 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include +#include +#include +#include +#include "Log.h" +#include "DatabaseNew.h" +#include + +//increase this if large queries are being run frequently to make less calls to malloc() +#define QUERY_INITIAL_SIZE 512 + +#if defined WORLD +#define DB_INI "world_db.ini" +#elif defined LOGIN +#define DB_INI "login_db.ini" +#elif defined PARSER +#define DB_INI "parser_db.ini" +#endif + +DatabaseNew::DatabaseNew() { + mysql_init(&mysql); + int timeout = 10; + mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); + MMysql.SetName("DatabaseNew::mysql"); +} + +DatabaseNew::~DatabaseNew() { + mysql_close(&mysql); +#if MYSQL_VERSION_ID >= 50003 + mysql_library_end(); +#else + mysql_server_end(); +#endif +} + +bool DatabaseNew::Connect() { + char line[256], *key, *val; + char host[256], user[64], password[64], database[64], port[64]; + bool found_section = false; + FILE *f; + + if ((f = fopen(DB_INI, "r")) == NULL) { + LogWrite(DATABASE__ERROR, 0, "Database", "Unable to read %s\n", DB_INI); + return false; + } + + memset(host, 0, sizeof(host)); + memset(user, 0, sizeof(user)); + memset(password, 0, sizeof(password)); + memset(database, 0, sizeof(database)); + memset(port, 0, sizeof(port)); + + while (fgets(line, sizeof(line), f) != NULL) { + if (line[0] == '#' || line[0] == '\n' || line[0] == '\r') + continue; + + if (!found_section) { + if (strncasecmp(line, "[Database]", 10) == 0) + found_section = true; + } + else { + if ((key = strtok(line, "=")) != NULL) { + if ((val = strtok(NULL, "\r\n")) != NULL) { + if (strncasecmp(line, "host", 4) == 0) + strncpy(host, val, sizeof(host) - 1); + else if (strncasecmp(line, "user", 4) == 0) + strncpy(user, val, sizeof(user) - 1); + else if (strncasecmp(line, "password", 8) == 0) + strncpy(password, val, sizeof(password) - 1); + else if (strncasecmp(line, "database", 8) == 0) + strncpy(database, val, sizeof(database) - 1); + else if (strncasecmp(line, "port", 4) == 0) + strncpy(port, val, sizeof(port) - 1); + } + } + } + } + + fclose(f); + + if (host[0] == '\0') { + LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'host' in '%s'\n", DB_INI); + return false; + } + if (user[0] == '\0') { + LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'user' in '%s'\n", DB_INI); + return false; + } + if (password[0] == '\0') { + LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'password' in '%s'\n", DB_INI); + return false; + } + if (database[0] == '\0') { + LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'database' in '%s'\n", DB_INI); + return false; + } + + unsigned int portnum = atoul(port); + return Connect(host, user, password, database, portnum); +} + +bool DatabaseNew::Connect(const char *host, const char *user, const char *password, const char *database, unsigned int port) { + if (mysql_real_connect(&mysql, host, user, password, database, port, NULL, 0) == NULL) { + LogWrite(DATABASE__ERROR, 0, "Database", "Unable to connect to MySQL server at %s:%u: %s\n", host, port, mysql_error(&mysql)); + return false; + } + + return true; +} + +bool DatabaseNew::Query(const char *query, ...) { + char *buf; + size_t size = QUERY_INITIAL_SIZE; + int num_chars; + va_list args; + bool ret = true; + + MMysql.writelock(__FUNCTION__, __LINE__); + while (true) { + if ((buf = (char *)malloc(size)) == NULL) { + LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate database query of %u bytes\n", size); + MMysql.releasewritelock(__FUNCTION__, __LINE__); + return false; + } + + va_start(args, query); + num_chars = vsnprintf(buf, size, query, args); + va_end(args); + + if (num_chars > -1 && (size_t)num_chars < size) + break; + + if (num_chars > -1) + size = num_chars + 1; + else + size *= 2; + + free(buf); + } + + if (mysql_real_query(&mysql, buf, num_chars) != 0) { + + if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) { + LogWrite(DATABASE__ERROR, 0, "Database", "Lost connection, attempting to recover and retry query..."); + Connect(); + + // retry attempt of previous query (1 try and we give up) + if (mysql_real_query(&mysql, buf, num_chars) != 0) { + ret = false; + } + } + else if (!IsIgnoredErrno(mysql_errno(&mysql))) { + LogWrite(DATABASE__ERROR, 0, "Database", "Error %i running MySQL query: %s\n%s\n", mysql_errno(&mysql), mysql_error(&mysql), buf); + ret = false; + } + } + free(buf); + + MMysql.releasewritelock(__FUNCTION__, __LINE__); + return ret; +} + +bool DatabaseNew::Select(DatabaseResult *result, const char *query, ...) { + char *buf; + size_t size = QUERY_INITIAL_SIZE; + int num_chars; + va_list args; + MYSQL_RES *res; + bool ret = true; + + MMysql.writelock(__FUNCTION__, __LINE__); + while (true) { + if ((buf = (char *)malloc(size)) == NULL) { + LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate database query of %u bytes\n", size); + MMysql.releasewritelock(__FUNCTION__, __LINE__); + return false; + } + + va_start(args, query); + num_chars = vsnprintf(buf, size, query, args); + va_end(args); + + if (num_chars > -1 && (size_t)num_chars < size) + break; + + if (num_chars > -1) + size = num_chars + 1; + else + size *= 2; + + free(buf); + } + + if (mysql_real_query(&mysql, buf, (unsigned long)num_chars) != 0) { + + if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) { + LogWrite(DATABASE__ERROR, 0, "Database", "Lost connection, attempting to recover and retry query..."); + + mysql_close(&mysql); + Connect(); + + // retry attempt of previous query (1 try and we give up) + if (mysql_real_query(&mysql, buf, (unsigned long)num_chars) != 0) { + ret = false; + } + } + else if (!IsIgnoredErrno(mysql_errno(&mysql))) { + LogWrite(DATABASE__ERROR, 0, "Database", "Error %i running MySQL query: %s\n%s\n", mysql_errno(&mysql), mysql_error(&mysql), buf); + ret = false; + } + } + + if (ret && !IsIgnoredErrno(mysql_errno(&mysql))) { + res = mysql_store_result(&mysql); + + if (res != NULL) + { + // Grab number of rows and number of fields from the query + uint8 num_rows = mysql_affected_rows(&mysql); + uint8 num_fields = mysql_field_count(&mysql); + + ret = result->StoreResult(res, num_fields, num_rows); + } + else { + LogWrite(DATABASE__ERROR, 0, "Database", "Error storing MySql query result (%d): %s\n%s", mysql_errno(&mysql), mysql_error(&mysql), buf); + ret = false; + } + } + + free(buf); + + MMysql.releasewritelock(__FUNCTION__, __LINE__); + return ret; +} + +int32 DatabaseNew::LastInsertID() +{ + return (int32)mysql_insert_id(&mysql); +} + +long DatabaseNew::AffectedRows() +{ + return mysql_affected_rows(&mysql); +} + +char * DatabaseNew::Escape(const char *str, size_t len) { + char *buf = (char *)malloc(len * 2 + 1); + + if (buf == NULL) { + LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", len * 2 + 1, __FUNCTION__, __LINE__); + return NULL; + } + + mysql_real_escape_string(&mysql, buf, str, len); + return buf; +} + +char * DatabaseNew::Escape(const char *str) { + return Escape(str, strlen(str)); +} + +string DatabaseNew::EscapeStr(const char *str, size_t len) { + char *buf = (char *)malloc(len * 2 + 1); + string ret; + + if (buf == NULL) { + LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", len * 2 + 1, __FUNCTION__, __LINE__); + return NULL; + } + + mysql_real_escape_string(&mysql, buf, str, len); + ret.append(buf); + free(buf); + + return ret; +} + +string DatabaseNew::EscapeStr(const char *str) { + return EscapeStr(str, strlen(str)); +} + +string DatabaseNew::EscapeStr(string str) { + return EscapeStr(str.c_str(), str.length()); +} + +bool DatabaseNew::QueriesFromFile(const char * file) { + bool success = true; + long size; + char *buf; + int ret; + MYSQL_RES *res; + FILE *f; + + f = fopen(file, "rb"); + if (f == NULL) { + LogWrite(DATABASE__ERROR, 0, "Database", "Unable to open '%s' for reading: %s", file, strerror(errno)); + return false; + } + + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + + buf = (char *)malloc(size + 1); + if (buf == NULL) { + fclose(f); + LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", size + 1, __FUNCTION__, __LINE__); + return false; + } + + if (fread(buf, sizeof(*buf), size, f) != (size_t)size) { + LogWrite(DATABASE__ERROR, 0, "Database", "Failed to read from '%s': %s", file, strerror(errno)); + fclose(f); + free(buf); + return false; + } + + buf[size] = '\0'; + fclose(f); + + mysql_set_server_option(&mysql, MYSQL_OPTION_MULTI_STATEMENTS_ON); + ret = mysql_real_query(&mysql, buf, size); + free(buf); + + if (ret != 0) { + LogWrite(DATABASE__ERROR, 0, "Database", "Error running MySQL queries from file '%s' (%d): %s", file, mysql_errno(&mysql), mysql_error(&mysql)); + success = false; + } + else { + //all results must be processed + do { + res = mysql_store_result(&mysql); + if (res != NULL) + mysql_free_result(res); + ret = mysql_next_result(&mysql); + + if (ret > 0) { + LogWrite(DATABASE__ERROR, 0, "Database", "Error running MySQL queries from file '%s' (%d): %s", file, mysql_errno(&mysql), mysql_error(&mysql)); + success = false; + } + + } while (ret == 0); + } + mysql_set_server_option(&mysql, MYSQL_OPTION_MULTI_STATEMENTS_OFF); + + return success; +} + +void DatabaseNew::SetIgnoredErrno(unsigned int db_errno) { + vector::iterator itr; + + for (itr = ignored_errnos.begin(); itr != ignored_errnos.end(); itr++) { + if ((*itr) == db_errno) + return; + } + + ignored_errnos.push_back(db_errno); +} + +void DatabaseNew::RemoveIgnoredErrno(unsigned int db_errno) { + vector::iterator itr; + + for (itr = ignored_errnos.begin(); itr != ignored_errnos.end(); itr++) { + if ((*itr) == db_errno) { + ignored_errnos.erase(itr); + break; + } + } +} + +bool DatabaseNew::IsIgnoredErrno(unsigned int db_errno) { + vector::iterator itr; + + for (itr = ignored_errnos.begin(); itr != ignored_errnos.end(); itr++) { + if ((*itr) == db_errno) + return true; + } + + return false; +} + +// Sends the MySQL server a keepalive +void DatabaseNew::PingNewDB() { + MMysql.writelock(__FUNCTION__, __LINE__); + mysql_ping(&mysql); + + int32* errnum = new int32; + *errnum = mysql_errno(&mysql); + + switch (*errnum) + { + case CR_COMMANDS_OUT_OF_SYNC: + case CR_SERVER_GONE_ERROR: + case CR_UNKNOWN_ERROR: + { + LogWrite(DATABASE__ERROR, 0, "Database", "[Database] We lost connection to the database., errno: %i", errno); + break; + } + } + + safe_delete(errnum); + MMysql.releasewritelock(__FUNCTION__, __LINE__); +} \ No newline at end of file diff --git a/old/common/DatabaseNew.h b/old/common/DatabaseNew.h new file mode 100644 index 0000000..2e2e002 --- /dev/null +++ b/old/common/DatabaseNew.h @@ -0,0 +1,48 @@ +#ifndef COMMON_DATABASE_H_ +#define COMMON_DATABASE_H_ + +#include +#include "DatabaseResult.h" + +using namespace std; + +class DatabaseNew { +public: + DatabaseNew(); + virtual ~DatabaseNew(); + + unsigned int GetError() {return mysql_errno(&mysql);} + const char * GetErrorMsg() {return mysql_error(&mysql);} + + bool Connect(); + bool Connect(const char *host, const char *user, const char *password, const char *database, unsigned int port = 3306); + + bool Query(const char *query, ...); + bool Select(DatabaseResult *result, const char *query, ...); + + int32 LastInsertID(); + long AffectedRows(); + + //these two must free() the return char* after it's used in a query + char * Escape(const char *str, size_t len); + char * Escape(const char *str); + + //does not need free() + string EscapeStr(const char *str, size_t len); + string EscapeStr(const char *str); + string EscapeStr(string str); + + bool QueriesFromFile(const char *file); + void SetIgnoredErrno(unsigned int db_errno); + void RemoveIgnoredErrno(unsigned int db_errno); + bool IsIgnoredErrno(unsigned int db_errno); + + void PingNewDB(); +private: + MYSQL mysql; + Mutex MMysql; + + vector ignored_errnos; +}; + +#endif diff --git a/old/common/DatabaseResult.cpp b/old/common/DatabaseResult.cpp new file mode 100644 index 0000000..05df1a8 --- /dev/null +++ b/old/common/DatabaseResult.cpp @@ -0,0 +1,234 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include +#include +#include "Log.h" +#include "DatabaseResult.h" + +//enforced by MySQL...couldn't find a #define in their headers though +#define FIELD_NAME_MAX 64 + +//return this instead of NULL for certain functions to prevent crashes from coding errors +static const char *empty_str = ""; + +DatabaseResult::DatabaseResult(): field_map(), result(0), num_fields(0), row(0) { +} + +DatabaseResult::~DatabaseResult() { + unsigned int i; + + if (result != NULL) + mysql_free_result(result); + + if (field_map.size()) { + field_map.clear(); + } +} + +bool DatabaseResult::StoreResult(MYSQL_RES* res, uint8 field_count, uint8 row_count) { + + //clear any previously stored result + if (result != NULL) + mysql_free_result(result); + + //clear any field names from a previous result + if (field_map.size()) { + field_map.clear(); + } + + result = res; + num_rows = row_count; + num_fields = field_count; + + // No rows or fields then we don't care + if (!num_rows || !num_fields) { + mysql_free_result(res); + result = NULL; + return false; + } + + + const MYSQL_FIELD* fields = mysql_fetch_fields(result); + + for (uint8 i = 0; i < num_fields; ++i) { + field_map.emplace(std::make_pair(std::string_view(fields[i].name), i)); + } + + return true; +} + +const char * DatabaseResult::GetFieldValue(unsigned int index) { + if (index >= num_fields) { + LogWrite(DATABASE__ERROR, 0, "Database Result", "Attempt to access field at index %u but there %s only %u field%s", index, num_fields == 1 ? "is" : "are", num_fields, num_fields == 1 ? "" : "s"); + return NULL; + } + + return row[index]; +} + +const char * DatabaseResult::GetFieldValueStr(const char *field_name) { + const auto& map_iterator = field_map.find(std::string_view(field_name)); + if (map_iterator != field_map.end()) { + return row[map_iterator->second]; + } + + LogWrite(DATABASE__ERROR, 0, "Database Result", "Unknown field name '%s'", field_name); + return NULL; +} + +bool DatabaseResult::Next() { + return (result != NULL && (row = mysql_fetch_row(result)) != NULL); +} + +bool DatabaseResult::IsNull(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL; +} + +bool DatabaseResult::IsNullStr(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL; +} + +int8 DatabaseResult::GetInt8(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? 0 : atoi(value); +} + +int8 DatabaseResult::GetInt8Str(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? 0 : atoi(value); +} + +sint8 DatabaseResult::GetSInt8(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? 0 : atoi(value); +} + +sint8 DatabaseResult::GetSInt8Str(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? 0 : atoi(value); +} + +int16 DatabaseResult::GetInt16(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? 0 : atoi(value); +} + +int16 DatabaseResult::GetInt16Str(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? 0 : atoi(value); +} + +sint16 DatabaseResult::GetSInt16(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? 0 : atoi(value); +} + +sint16 DatabaseResult::GetSInt16Str(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? 0 : atoi(value); +} + +int32 DatabaseResult::GetInt32(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? 0U : strtoul(value, NULL, 10); +} + +int32 DatabaseResult::GetInt32Str(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? 0U : strtoul(value, NULL, 10); +} + +sint32 DatabaseResult::GetSInt32(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? 0 : atoi(value); +} + +sint32 DatabaseResult::GetSInt32Str(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? 0 : atoi(value); +} + +uint64 DatabaseResult::GetInt64(unsigned int index) { + const char *value = GetFieldValue(index); +#ifdef _WIN32 + return value == NULL ? 0UL : _strtoui64(value, NULL, 10); +#else + return value == NULL ? 0UL : strtoull(value, NULL, 10); +#endif +} + +uint64 DatabaseResult::GetInt64Str(const char *field_name) { + const char *value = GetFieldValueStr(field_name); +#ifdef _WIN32 + return value == NULL ? 0UL : _strtoui64(value, NULL, 10); +#else + return value == NULL ? 0UL : strtoull(value, NULL, 10); +#endif +} + +sint64 DatabaseResult::GetSInt64(unsigned int index) { + const char *value = GetFieldValue(index); +#ifdef _WIN32 + return value == NULL ? 0L : _strtoi64(value, NULL, 10); +#else + return value == NULL ? 0L : strtoll(value, NULL, 10); +#endif +} + +sint64 DatabaseResult::GetSInt64Str(const char *field_name) { + const char *value = GetFieldValueStr(field_name); +#ifdef _WIN32 + return value == NULL ? 0L : _strtoi64(value, NULL, 10); +#else + return value == NULL ? 0L : strtoll(value, NULL, 10); +#endif +} + +float DatabaseResult::GetFloat(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? 0.0F : atof(value); +} + +float DatabaseResult::GetFloatStr(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? 0.0F : atof(value); +} + +char DatabaseResult::GetChar(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? '\0' : value[0]; +} + +char DatabaseResult::GetCharStr(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? '\0' : value[0]; +} + +const char * DatabaseResult::GetString(unsigned int index) { + const char *value = GetFieldValue(index); + return value == NULL ? empty_str : value; +} + +const char * DatabaseResult::GetStringStr(const char *field_name) { + const char *value = GetFieldValueStr(field_name); + return value == NULL ? empty_str : value; +} diff --git a/old/common/DatabaseResult.h b/old/common/DatabaseResult.h new file mode 100644 index 0000000..36b3c3e --- /dev/null +++ b/old/common/DatabaseResult.h @@ -0,0 +1,57 @@ +#ifndef COMMON_DATABASERESULT_H_ +#define COMMON_DATABASERESULT_H_ + +#include "types.h" +#ifdef _WIN32 +#include //#include when we/if we go to winsock2 :/ +#endif +#include +#include + +class DatabaseResult { +public: + DatabaseResult(); + virtual ~DatabaseResult(); + + bool StoreResult(MYSQL_RES* result, uint8 field_count, uint8 row_count); + bool Next(); + + bool IsNull(unsigned int index); + bool IsNullStr(const char *field_name); + int8 GetInt8(unsigned int index); + int8 GetInt8Str(const char *field_name); + sint8 GetSInt8(unsigned int index); + sint8 GetSInt8Str(const char *field_name); + int16 GetInt16(unsigned int index); + int16 GetInt16Str(const char *field_name); + sint16 GetSInt16(unsigned int index); + sint16 GetSInt16Str(const char *field_name); + int32 GetInt32(unsigned int index); + int32 GetInt32Str(const char *field_name); + sint32 GetSInt32(unsigned int index); + sint32 GetSInt32Str(const char *field_name); + int64 GetInt64(unsigned int index); + int64 GetInt64Str(const char *field_name); + sint64 GetSInt64(unsigned int index); + sint64 GetSInt64Str(const char *field_name); + float GetFloat(unsigned int index); + float GetFloatStr(const char *field_name); + char GetChar(unsigned int index); + char GetCharStr(const char *field_name); + const char * GetString(unsigned int index); + const char * GetStringStr(const char *field_name); + + const unsigned int GetNumRows() { return num_rows; } + + const char * GetFieldValue(unsigned int index); + const char * GetFieldValueStr(const char *field_name); +private: + MYSQL_RES *result; + MYSQL_ROW row; + unsigned int num_rows; + unsigned int num_fields; + + std::map field_map; +}; + +#endif diff --git a/old/common/EQ2_Common_Structs.h b/old/common/EQ2_Common_Structs.h new file mode 100644 index 0000000..e6db286 --- /dev/null +++ b/old/common/EQ2_Common_Structs.h @@ -0,0 +1,361 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef _EQ2COMMON_STRUCTS_ +#define _EQ2COMMON_STRUCTS_ + +#define SPAWN_PACKET_SIZE 895 +#define EQUIPMENT_L_WEAPON_INDEX 0 //chars left hand weapon +#define EQUIPMENT_R_WEAPON_INDEX 1 //chars right hand weapon +#define EQUIPMENT_HELMET 2 + +#pragma pack(1) +struct KeyGen_Struct{ + int32 size; +}; +struct KeyGen_End_Struct{ + int32 exponent_len; + int8 exponent; +}; +struct LoginByNumRequest_Struct{ + int32 account_id; + int32 access_code; + int16 version; + int32 unknown2[5]; +}; +struct LS_LoginResponse{ + int8 reply_code; // 0 granted, 1 denied + int16 unknown01; + int8 unknown02; + sint32 unknown03; // -1 denied, 0 granted + sint32 unknown04; + sint32 unknown05; + sint32 unknown06; + int8 unknown07; + int8 unknown08; + int8 unknown09; + int8 unknown10; + sint32 unknown11; + int32 accountid; + int16 unknown12; +}; +#pragma pack() +enum EQ2_EquipmentSlot { + slot_primary=0, + slot_secondary=1, + slot_head=2, + slot_chest=3, + slot_shoulders=4, + slot_forearms=5, + slot_hands=6, + slot_legs=7, + slot_feet=8, + slot_left_ring=9, + slot_right_ring=10, + slot_ears=11, + slot_neck=12, + slot_left_wrist=13, + slot_right_wrist=14, + slot_ranged=15, + slot_ammo=16, + slot_waist=17, + slot_activate1=18, + slot_activate2=19, + slot_textures=20, + slot_hair=21, + slot_beard=22, + slot_naked_chest=23, + slot_naked_legs=24 +}; +struct EQ2_EquipmentItem{ + int16 type; + EQ2_Color color; + EQ2_Color highlight; +}; +struct EQ2_Equipment{ + int16 equip_id[25]; + EQ2_Color color[25]; + EQ2_Color highlight[25]; +}; +#pragma pack(1) +struct CharFeatures{ + int16 hair_type; + int16 hair_face_type; + int16 wing_type; + int16 chest_type; + int16 legs_type; + sint8 eye_type[3]; + sint8 ear_type[3]; + sint8 eye_brow_type[3]; + sint8 cheek_type[3]; + sint8 lip_type[3]; + sint8 chin_type[3]; + sint8 nose_type[3]; + sint8 body_size; + sint8 body_age; + sint8 soga_eye_type[3]; + sint8 soga_ear_type[3]; + sint8 soga_eye_brow_type[3]; + sint8 soga_cheek_type[3]; + int16 soga_chest_type; + int16 soga_legs_type; + sint8 soga_lip_type[3]; + sint8 soga_chin_type[3]; + sint8 soga_nose_type[3]; + sint8 soga_body_size; + sint8 soga_body_age; + int16 soga_hair_type; + int16 soga_hair_face_type; + int16 combat_voice; + int16 emote_voice; + int16 mount_model_type; + + EQ2_Color mount_saddle_color; + EQ2_Color mount_color; + EQ2_Color skin_color; + EQ2_Color eye_color; + EQ2_Color hair_type_color; + EQ2_Color hair_type_highlight_color; + EQ2_Color hair_face_color; + EQ2_Color hair_face_highlight_color; + EQ2_Color hair_highlight_color; + EQ2_Color wing_color1; + EQ2_Color wing_color2; + EQ2_Color shirt_color; + EQ2_Color pants_color; + EQ2_Color hair_color1; + EQ2_Color hair_color2; + EQ2_Color soga_skin_color; + EQ2_Color soga_eye_color; + EQ2_Color soga_hair_color1; + EQ2_Color soga_hair_color2; + EQ2_Color soga_hair_type_color; + EQ2_Color soga_hair_type_highlight_color; + EQ2_Color soga_hair_face_color; + EQ2_Color soga_hair_face_highlight_color; + EQ2_Color soga_hair_highlight_color; + + EQ2_Color model_color; + EQ2_Color soga_model_color; +}; +struct PositionData{ + int32 grid_id; + int32 bad_grid_id; + sint8 Speed1; + sint8 Speed2; + sint16 Dir1; + sint16 Dir2; + sint16 Pitch1; + sint16 Pitch2; + sint16 Roll; + float X; + float Y; + float Z; + float X2; + float Y2; + float Z2; + float X3; + float Y3; + float Z3; + float SpawnOrigX; + float SpawnOrigY; + float SpawnOrigZ; + float SpawnOrigHeading; + float SpawnOrigPitch; + float SpawnOrigRoll; + float SpeedX; + float SpeedY; + float SpeedZ; + float SideSpeed; + float VertSpeed; + float ClientHeading1; + float ClientHeading2; + float ClientPitch; + int16 collision_radius; + int16 state; +}; +struct AppearanceData { + PositionData pos; + int16 model_type; + int16 soga_model_type; + int16 activity_status; + int16 visual_state; + int16 action_state; + int16 mood_state; + int16 emote_state; + int8 attackable; + int8 icon; + int8 hide_hood; + int8 show_level; + + int8 locked_no_loot; + int8 quest_flag; + int8 heroic_flag; + int8 show_command_icon; + int8 display_hand_icon; + int8 player_flag; + int8 targetable; + int8 display_name; + char sub_title[255]; //Guild + int32 display_hp;//0 = 100 percent + int32 power_left; //bar not shown if >=100 + int8 adventure_class; + int8 tradeskill_class; + int8 level; + int8 tradeskill_level; + int8 min_level; + int8 max_level; + int8 difficulty; + int16 visible; // 02 = normal, 15 = shadow + char name[128]; //size around here somewhere + char last_name[64]; + char prefix_title[128]; + char suffix_title[128]; + int8 race; + int8 gender; + int32 randomize; + int8 lua_race_id; +}; +struct Player_Update{ +/*0000*/ int32 activity; +/*0004*/ float unknown2; // 1 +/*0008*/ float direction1; +/*0012*/ float unknown3[8]; +/*0044*/ float speed; +/*0048*/ float side_speed; +/*0052*/ float vert_speed; +/*0056*/ float orig_x; +/*0060*/ float orig_y; +/*0064*/ float orig_z; +/*0068*/ float orig_x2; +/*0072*/ float orig_y2; +/*0076*/ float orig_z2; +/*0080*/ float unknown5[3]; +/*0092*/ int32 unknown6; +/*0096*/ float unknown7[3]; +/*0108*/ int32 unknown8; +/*0112*/ int32 grid_location; +/*0116*/ float x; +/*0120*/ float y; +/*0124*/ float z; +/*0128*/ float direction2; +/*0132*/ float pitch; +/*0136*/ float unknown10; +/*0140*/ float speed_x; +/*0144*/ float speed_y; +/*0148*/ float speed_z; +}; +struct Player_Update283 { + /*0000*/ int32 activity; + /*0004*/ int32 movement_mode; // 1 + /*0008*/ float direction1; + /*0012*/ float desiredpitch; + /*0016*/ float desired_heading_speed; + /*0020*/ float desired_pitch_speed; + /*0024*/ float collision_radius; + /*0028*/ float collision_scale; + /*0032*/ float temp_scale; + /*0036*/ float speed_modifier; + /*0040*/ float swim_speed_modifier; + /*0044*/ float speed; + /*0048*/ float side_speed; + /*0052*/ float vert_speed; + /*0056*/ float orig_x; + /*0060*/ float orig_y; + /*0064*/ float orig_z; + /*0068*/ float orig_x2; + /*0072*/ float orig_y2; + /*0076*/ float orig_z2; + /*0080*/ int32 face_actor_id; + /*0084*/ int32 face_actor_range; + /*0088*/ int32 grid_location; + /*0092*/ float x; + /*0096*/ float y; + /*0100*/ float z; + /*0104*/ float direction2; + /*0108*/ float pitch; + /*0112*/ float roll; + /*0116*/ float speed_x; + /*0120*/ float speed_y; + /*0124*/ float speed_z; +};//0128 +struct Player_Update1096{ +/*0000*/ int32 activity; +/*0004*/ float unknown2; // 1 +/*0008*/ float direction1; +/*0012*/ float unknown3[8]; +/*0044*/ float unk_speed; +/*0048*/ float speed; +/*0052*/ float side_speed; +/*0056*/ float vert_speed; +/*0060*/ float orig_x; +/*0064*/ float orig_y; +/*0068*/ float orig_z; +/*0072*/ float orig_x2; +/*0076*/ float orig_y2; +/*0080*/ float orig_z2; +/*0092*/ float unknown5[3]; +/*0096*/ int32 unknown6; +/*0108*/ float unknown7[3]; +/*0112*/ int32 unknown8; +/*0116*/ int32 grid_location; +/*0120*/ float x; +/*0124*/ float y; +/*0128*/ float z; +/*0132*/ float direction2; +/*0136*/ float pitch; +/*0140*/ float unknown10; +/*0144*/ float speed_x; +/*0148*/ float speed_y; +/*0152*/ float speed_z; +}; + +struct Player_Update1144{ +/*0000*/ int32 activity; +/*0004*/ float unknown2; // 1 +/*0008*/ float direction1; +/*0012*/ float unknown3[12]; +/*0044*/ float unk_speed; +/*0048*/ float speed; +/*0052*/ float side_speed; +/*0056*/ float vert_speed; +/*0060*/ float orig_x; +/*0064*/ float orig_y; +/*0068*/ float orig_z; +/*0072*/ float orig_x2; +/*0076*/ float orig_y2; +/*0080*/ float orig_z2; +/*0092*/ float unknown5[3]; +/*0096*/ int32 unknown6; +/*0108*/ float unknown7[3]; +/*0112*/ int32 unknown8; +/*0116*/ int32 grid_location; +/*0120*/ float x; +/*0124*/ float y; +/*0128*/ float z; +/*0132*/ float direction2; +/*0136*/ float pitch; +/*0140*/ float unknown10; +/*0144*/ float speed_x; +/*0148*/ float speed_y; +/*0152*/ float speed_z; +}; +#pragma pack() +#endif + diff --git a/old/common/EQEMuError.cpp b/old/common/EQEMuError.cpp new file mode 100644 index 0000000..323463a --- /dev/null +++ b/old/common/EQEMuError.cpp @@ -0,0 +1,131 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifdef WIN32 +#include +#include +#endif +#include "EQEMuError.h" +#include "linked_list.h" +#include "Mutex.h" +#include "MiscFunctions.h" +#include +#include +#ifdef WIN32 + #include +#endif + +void CatchSignal(int sig_num); + +const char* EQEMuErrorText[EQEMuError_MaxErrorID] = { "ErrorID# 0, No Error", + "MySQL Error #1405 or #2001 means your mysql server rejected the username and password you presented it.", + "MySQL Error #2003 means you were unable to connect to the mysql server.", + "MySQL Error #2005 means you there are too many connections on the mysql server. The server is overloaded.", + "MySQL Error #2007 means you the server is out of memory. The server is overloaded.", + }; + +LinkedList* EQEMuErrorList; +Mutex* MEQEMuErrorList; +AutoDelete< LinkedList > ADEQEMuErrorList(&EQEMuErrorList); +AutoDelete ADMEQEMuErrorList(&MEQEMuErrorList); + +const char* GetErrorText(int32 iError) { + if (iError >= EQEMuError_MaxErrorID) + return "ErrorID# out of range"; + else + return EQEMuErrorText[iError]; +} + +void AddEQEMuError(eEQEMuError iError, bool iExitNow) { + if (!iError) + return; + if (!EQEMuErrorList) { + EQEMuErrorList = new LinkedList; + MEQEMuErrorList = new Mutex; + } + LockMutex lock(MEQEMuErrorList); + + LinkedListIterator iterator(*EQEMuErrorList); + iterator.Reset(); + while (iterator.MoreElements()) { + if (iterator.GetData()[0] == 1) { + if (*((eEQEMuError*) &(iterator.GetData()[1])) == iError) + return; + } + iterator.Advance(); + } + + char* tmp = new char[6]; + tmp[0] = 1; + tmp[5] = 0; + *((int32*) &tmp[1]) = iError; + EQEMuErrorList->Append(tmp); + + if (iExitNow) + CatchSignal(2); +} + +void AddEQEMuError(char* iError, bool iExitNow) { + if (!iError) + return; + if (!EQEMuErrorList) { + EQEMuErrorList = new LinkedList; + MEQEMuErrorList = new Mutex; + } + LockMutex lock(MEQEMuErrorList); + char* tmp = strcpy(new char[strlen(iError) + 1], iError); + EQEMuErrorList->Append(tmp); + + if (iExitNow) + CatchSignal(2); +} + +int32 CheckEQEMuError() { + if (!EQEMuErrorList) + return 0; + int32 ret = 0; + char* tmp = 0; + bool HeaderPrinted = false; + LockMutex lock(MEQEMuErrorList); + + while ((tmp = EQEMuErrorList->Pop() )) { + if (!HeaderPrinted) { + fprintf(stdout, "===============================\nRuntime errors:\n\n"); + HeaderPrinted = true; + } + if (tmp[0] == 1) { + fprintf(stdout, "%s\n", GetErrorText(*((int32*) &tmp[1]))); + } + else { + fprintf(stdout, "%s\n\n", tmp); + } + safe_delete(tmp); + ret++; + } + return ret; +} + +void CheckEQEMuErrorAndPause() { + if (CheckEQEMuError()) { + fprintf(stdout, "Hit any key to exit\n"); + getchar(); + } +} + + diff --git a/old/common/EQEMuError.h b/old/common/EQEMuError.h new file mode 100644 index 0000000..03b364c --- /dev/null +++ b/old/common/EQEMuError.h @@ -0,0 +1,39 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef EQEMuError_H +#define EQEMuError_H + +#include "../common/types.h" + +enum eEQEMuError { EQEMuError_NoError, + EQEMuError_Mysql_1405, + EQEMuError_Mysql_2003, + EQEMuError_Mysql_2005, + EQEMuError_Mysql_2007, + EQEMuError_MaxErrorID }; + +void AddEQEMuError(eEQEMuError iError, bool iExitNow = false); +void AddEQEMuError(char* iError, bool iExitNow = false); +int32 CheckEQEMuError(); +void CheckEQEMuErrorAndPause(); + +#endif + + diff --git a/old/common/EQPacket.cpp b/old/common/EQPacket.cpp new file mode 100644 index 0000000..c2f9e1f --- /dev/null +++ b/old/common/EQPacket.cpp @@ -0,0 +1,661 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include "debug.h" +#include +#include +#include +#include +#include "EQPacket.h" +#include "misc.h" +#include "op_codes.h" +#include "CRC16.h" +#include "opcodemgr.h" +#include "packet_dump.h" +#include +#include "Log.h" +#include + +using namespace std; +extern mapEQOpcodeManager; + +uint8 EQApplicationPacket::default_opcode_size=2; + +EQPacket::EQPacket(const uint16 op, const unsigned char *buf, uint32 len) +{ + this->opcode=op; + this->pBuffer=NULL; + this->size=0; + version = 0; + setTimeInfo(0,0); + if (len>0) { + this->size=len; + pBuffer= new unsigned char[this->size]; + if (buf) { + memcpy(this->pBuffer,buf,this->size); + } else { + memset(this->pBuffer,0,this->size); + } + } +} + +const char* EQ2Packet::GetOpcodeName() { + int16 OpcodeVersion = GetOpcodeVersion(version); + if (EQOpcodeManager.count(OpcodeVersion) > 0) + return EQOpcodeManager[OpcodeVersion]->EmuToName(login_op); + else + return NULL; +} + +int8 EQ2Packet::PreparePacket(int16 MaxLen) { + int16 OpcodeVersion = GetOpcodeVersion(version); + + // stops a crash for incorrect version + if (EQOpcodeManager.count(OpcodeVersion) == 0) + { + LogWrite(PACKET__ERROR, 0, "Packet", "Version %i is not listed in the opcodes table.", version); + return -1; + } + + packet_prepared = true; + + int16 login_opcode = EQOpcodeManager[OpcodeVersion]->EmuToEQ(login_op); + if (login_opcode == 0xcdcd) + { + LogWrite(PACKET__ERROR, 0, "Packet", "Version %i is not listed in the opcodes table for opcode %s", version, EQOpcodeManager[OpcodeVersion]->EmuToName(login_op)); + return -1; + } + + int16 orig_opcode = login_opcode; + int8 offset = 0; + //one of the int16s is for the seq, other is for the EQ2 opcode and compressed flag (OP_Packet is the header, not the opcode) + int32 new_size = size + sizeof(int16) + sizeof(int8); + bool oversized = false; + if (login_opcode != 2) { + new_size += sizeof(int8); //for opcode + if (login_opcode >= 255) { + new_size += sizeof(int16); + oversized = true; + } + else + login_opcode = ntohs(login_opcode); + } + uchar* new_buffer = new uchar[new_size]; + memset(new_buffer, 0, new_size); + uchar* ptr = new_buffer + sizeof(int16); // sequence is first + if (login_opcode != 2) { + if (oversized) { + ptr += sizeof(int8); //compressed flag + int8 addon = 0xff; + memcpy(ptr, &addon, sizeof(int8)); + ptr += sizeof(int8); + } + memcpy(ptr, &login_opcode, sizeof(int16)); + ptr += sizeof(int16); + } + else { + memcpy(ptr, &login_opcode, sizeof(int8)); + ptr += sizeof(int8); + } + memcpy(ptr, pBuffer, size); + + safe_delete_array(pBuffer); + pBuffer = new_buffer; + offset = new_size - size - 1; + size = new_size; + + return offset; +} + +uint32 EQProtocolPacket::serialize(unsigned char *dest, int8 offset) const +{ + if (opcode>0xff) { + *(uint16 *)dest=opcode; + } else { + *(dest)=0; + *(dest+1)=opcode; + } + memcpy(dest+2,pBuffer+offset,size-offset); + + return size+2; +} + +uint32 EQApplicationPacket::serialize(unsigned char *dest) const +{ + uint8 OpCodeBytes = app_opcode_size; + + if (app_opcode_size==1) + *(unsigned char *)dest=opcode; + else + { + // Application opcodes with a low order byte of 0x00 require an extra 0x00 byte inserting prior to the opcode. + if ((opcode & 0x00ff) == 0) + { + *(uint8*)dest = 0; + *(uint16*)(dest + 1) = opcode; + ++OpCodeBytes; + } + else + *(uint16*)dest = opcode; + } + + memcpy(dest+app_opcode_size,pBuffer,size); + + return size+ OpCodeBytes; +} + +EQPacket::~EQPacket() +{ + safe_delete_array(pBuffer); + pBuffer=NULL; +} + + +void EQPacket::DumpRawHeader(uint16 seq, FILE* to) const +{ + /*if (timestamp.tv_sec) { + char temp[20]; + tm t; + const time_t sec = timestamp.tv_sec; + localtime_s(&t, &sec); + strftime(temp, 20, "%F %T", &t); + fprintf(to, "%s.%06lu ", temp, timestamp.tv_usec); + }*/ + + DumpRawHeaderNoTime(seq, to); +} + +const char* EQPacket::GetOpcodeName(){ + int16 OpcodeVersion = GetOpcodeVersion(version); + if(EQOpcodeManager.count(OpcodeVersion) > 0) + return EQOpcodeManager[OpcodeVersion]->EQToName(opcode); + else + return NULL; +} +void EQPacket::DumpRawHeaderNoTime(uint16 seq, FILE *to) const +{ + if (src_ip) { + string sIP,dIP;; + sIP=long2ip(src_ip); + dIP=long2ip(dst_ip); + fprintf(to, "[%s:%d->%s:%d] ",sIP.c_str(),src_port,dIP.c_str(),dst_port); + } + if (seq != 0xffff) + fprintf(to, "[Seq=%u] ",seq); + + string name; + int16 OpcodeVersion = GetOpcodeVersion(version); + if(EQOpcodeManager.count(OpcodeVersion) > 0) + name = EQOpcodeManager[OpcodeVersion]->EQToName(opcode); + + fprintf(to, "[OpCode 0x%04x (%s) Size=%u]\n",opcode,name.c_str(),size); +} + +void EQPacket::DumpRaw(FILE *to) const +{ + DumpRawHeader(); + if (pBuffer && size) + dump_message_column(pBuffer, size, " ", to); + fprintf(to, "\n"); +} + +EQProtocolPacket::EQProtocolPacket(const unsigned char *buf, uint32 len, int in_opcode) +{ + uint32 offset = 0; + if(in_opcode>=0) { + opcode = in_opcode; + } + else { + // Ensure there are at least 2 bytes for the opcode + if (len < 2 || buf == nullptr) { + // Not enough data to read opcode; set defaults or handle error appropriately + opcode = 0; // or set to a designated invalid opcode + offset = len; // no payload available + } else { + offset = 2; + opcode = ntohs(*(const uint16 *)buf); + } + } + + // Check that there is payload data after the header + if (len > offset) { + size = len - offset; + pBuffer = new unsigned char[size]; + if(buf) + memcpy(pBuffer, buf + offset, size); + else + memset(pBuffer, 0, size); + } else { + pBuffer = nullptr; + size = 0; + } + + version = 0; + eq2_compressed = false; + packet_prepared = false; + packet_encrypted = false; + sent_time = 0; + attempt_count = 0; + sequence = 0; +} + +bool EQ2Packet::AppCombine(EQ2Packet* rhs){ + bool result = false; + uchar* tmpbuffer = 0; + bool over_sized_packet = false; + int32 new_size = 0; + //bool whee = false; +// DumpPacket(this); +// DumpPacket(rhs); + /*if(rhs->size >= 255){ + DumpPacket(this); + DumpPacket(rhs); + whee = true; + }*/ + if (opcode==OP_AppCombined && ((size + rhs->size + 3) < 255)){ + int16 tmp_size = rhs->size - 2; + if(tmp_size >= 255){ + new_size = size+tmp_size+3; + over_sized_packet = true; + } + else + new_size = size+tmp_size+1; + tmpbuffer = new uchar[new_size]; + uchar* ptr = tmpbuffer; + memcpy(ptr, pBuffer, size); + ptr += size; + if(over_sized_packet){ + memset(ptr, 255, sizeof(int8)); + ptr += sizeof(int8); + tmp_size = htons(tmp_size); + memcpy(ptr, &tmp_size, sizeof(int16)); + ptr += sizeof(int16); + } + else{ + memcpy(ptr, &tmp_size, sizeof(int8)); + ptr += sizeof(int8); + } + memcpy(ptr, rhs->pBuffer+2, rhs->size-2); + delete[] pBuffer; + size = new_size; + pBuffer=tmpbuffer; + safe_delete(rhs); + result=true; + } + else if (rhs->size > 2 && size > 2 && (size + rhs->size + 6) < 255) { + int32 tmp_size = size - 2; + int32 tmp_size2 = rhs->size - 2; + opcode=OP_AppCombined; + bool over_sized_packet2 = false; + new_size = size; + if(tmp_size >= 255){ + new_size += 5; + over_sized_packet = true; + } + else + new_size += 3; + if(tmp_size2 >= 255){ + new_size += tmp_size2+3; + over_sized_packet2 = true; + } + else + new_size += tmp_size2+1; + tmpbuffer = new uchar[new_size]; + tmpbuffer[2]=0; + tmpbuffer[3]=0x19; + uchar* ptr = tmpbuffer+4; + if(over_sized_packet){ + memset(ptr, 255, sizeof(int8)); + ptr += sizeof(int8); + tmp_size = htons(tmp_size); + memcpy(ptr, &tmp_size, sizeof(int16)); + ptr += sizeof(int16); + } + else{ + memcpy(ptr, &tmp_size, sizeof(int8)); + ptr += sizeof(int8); + } + memcpy(ptr, pBuffer+2, size-2); + ptr += (size-2); + if(over_sized_packet2){ + memset(ptr, 255, sizeof(int8)); + ptr += sizeof(int8); + tmp_size2 = htons(tmp_size2); + memcpy(ptr, &tmp_size2, sizeof(int16)); + ptr += sizeof(int16); + } + else{ + memcpy(ptr, &tmp_size2, sizeof(int8)); + ptr += sizeof(int8); + } + memcpy(ptr, rhs->pBuffer+2, rhs->size-2); + size = new_size; + delete[] pBuffer; + pBuffer=tmpbuffer; + safe_delete(rhs); + result=true; + } + /*if(whee){ + DumpPacket(this); + cout << "fsdfsdf"; + }*/ + //DumpPacket(this); + return result; +} + +bool EQProtocolPacket::combine(const EQProtocolPacket *rhs) +{ + bool result=false; + //if(dont_combine) + // return false; + //if (opcode==OP_Combined && size+rhs->size+5<256) { + if (opcode == OP_Combined && size + rhs->size + 5 < 256) { + auto tmpbuffer = new unsigned char[size + rhs->size + 3]; + memcpy(tmpbuffer, pBuffer, size); + uint32 offset = size; + tmpbuffer[offset++] = rhs->Size(); + offset += rhs->serialize(tmpbuffer + offset); + size = offset; + delete[] pBuffer; + pBuffer = tmpbuffer; + result = true; + } + else if (size + rhs->size + 7 < 256) { + auto tmpbuffer = new unsigned char[size + rhs->size + 6]; + uint32 offset = 0; + tmpbuffer[offset++] = Size(); + offset += serialize(tmpbuffer + offset); + tmpbuffer[offset++] = rhs->Size(); + offset += rhs->serialize(tmpbuffer + offset); + size = offset; + delete[] pBuffer; + pBuffer = tmpbuffer; + opcode = OP_Combined; + result = true; + } + return result; +} + +EQApplicationPacket::EQApplicationPacket(const unsigned char *buf, uint32 len, uint8 opcode_size) +{ +uint32 offset=0; + app_opcode_size=(opcode_size==0) ? EQApplicationPacket::default_opcode_size : opcode_size; + + if (app_opcode_size==1) { + opcode=*(const unsigned char *)buf; + offset++; + } else { + opcode=*(const uint16 *)buf; + offset+=2; + } + + if ((len-offset)>0) { + pBuffer=new unsigned char[len-offset]; + memcpy(pBuffer,buf+offset,len-offset); + size=len-offset; + } else { + pBuffer=NULL; + size=0; + } + + emu_opcode = OP_Unknown; +} + +bool EQApplicationPacket::combine(const EQApplicationPacket *rhs) +{ +cout << "CALLED AP COMBINE!!!!\n"; + return false; +} + +void EQApplicationPacket::SetOpcode(EmuOpcode emu_op) { + if(emu_op == OP_Unknown) { + opcode = 0; + emu_opcode = OP_Unknown; + return; + } + + opcode = EQOpcodeManager[GetOpcodeVersion(version)]->EmuToEQ(emu_op); + + if(opcode == OP_Unknown) { + LogWrite(PACKET__DEBUG, 0, "Packet", "Unable to convert Emu opcode %s (%d) into an EQ opcode.", OpcodeNames[emu_op], emu_op); + } + + //save the emu opcode we just set. + emu_opcode = emu_op; +} + +const EmuOpcode EQApplicationPacket::GetOpcodeConst() const { + if(emu_opcode != OP_Unknown) { + return(emu_opcode); + } + if(opcode == 10000) { + return(OP_Unknown); + } + + EmuOpcode emu_op; + emu_op = EQOpcodeManager[GetOpcodeVersion(version)]->EQToEmu(opcode); + if(emu_op == OP_Unknown) { + LogWrite(PACKET__DEBUG, 1, "Packet", "Unable to convert EQ opcode 0x%.4X (%i) to an emu opcode (%s)", opcode, opcode, __FUNCTION__); + } + + return(emu_op); +} + +EQApplicationPacket *EQProtocolPacket::MakeApplicationPacket(uint8 opcode_size) const { + EQApplicationPacket *res = new EQApplicationPacket; + res->app_opcode_size=(opcode_size==0) ? EQApplicationPacket::default_opcode_size : opcode_size; + if (res->app_opcode_size==1) { + res->pBuffer= new unsigned char[size+1]; + memcpy(res->pBuffer+1,pBuffer,size); + *(res->pBuffer)=htons(opcode)&0xff; + res->opcode=opcode&0xff; + res->size=size+1; + } else { + res->pBuffer= new unsigned char[size]; + memcpy(res->pBuffer,pBuffer,size); + res->opcode=opcode; + res->size=size; + } + res->copyInfo(this); + return(res); +} +bool EQProtocolPacket::ValidateCRC(const unsigned char *buffer, int length, uint32 Key) +{ +bool valid=false; + // OP_SessionRequest, OP_SessionResponse, OP_OutOfSession are not CRC'd + if (buffer[0]==0x00 && (buffer[1]==OP_SessionRequest || buffer[1]==OP_SessionResponse || buffer[1]==OP_OutOfSession)) { + valid=true; + } else if(buffer[2] == 0x00 && buffer[3] == 0x19){ + valid = true; + } + else { + uint16 comp_crc=CRC16(buffer,length-2,Key); + uint16 packet_crc=ntohs(*(const uint16 *)(buffer+length-2)); +#ifdef EQN_DEBUG + if (packet_crc && comp_crc != packet_crc) { + cout << "CRC mismatch: comp=" << hex << comp_crc << ", packet=" << packet_crc << dec << endl; + } +#endif + valid = (!packet_crc || comp_crc == packet_crc); + } + return valid; +} + +uint32 EQProtocolPacket::Decompress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize) +{ +uint32 newlen=0; +uint32 flag_offset=0; + newbuf[0]=buffer[0]; + if (buffer[0]==0x00) { + flag_offset=2; + newbuf[1]=buffer[1]; + } else + flag_offset=1; + + if (length>2 && buffer[flag_offset]==0x5a) { + LogWrite(PACKET__DEBUG, 0, "Packet", "In Decompress 1"); + newlen=Inflate(const_cast(buffer+flag_offset+1),length-(flag_offset+1)-2,newbuf+flag_offset,newbufsize-flag_offset)+2; + + // something went bad with zlib + if (newlen == -1) + { + LogWrite(PACKET__ERROR, 0, "Packet", "Debug Bad Inflate!"); + DumpPacket(buffer, length); + memcpy(newbuf, buffer, length); + return length; + } + + newbuf[newlen++]=buffer[length-2]; + newbuf[newlen++]=buffer[length-1]; + } else if (length>2 && buffer[flag_offset]==0xa5) { + LogWrite(PACKET__DEBUG, 0, "Packet", "In Decompress 2"); + memcpy(newbuf+flag_offset,buffer+flag_offset+1,length-(flag_offset+1)); + newlen=length-1; + } else { + memcpy(newbuf,buffer,length); + newlen=length; + } + + return newlen; +} + +uint32 EQProtocolPacket::Compress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize) { +uint32 flag_offset=1,newlength; + //dump_message_column(buffer,length,"Before: "); + newbuf[0]=buffer[0]; + if (buffer[0]==0) { + flag_offset=2; + newbuf[1]=buffer[1]; + } + if (length>30) { + newlength=Deflate(const_cast(buffer+flag_offset),length-flag_offset,newbuf+flag_offset+1,newbufsize); + *(newbuf+flag_offset)=0x5a; + newlength+=flag_offset+1; + } else { + memmove(newbuf+flag_offset+1,buffer+flag_offset,length-flag_offset); + *(newbuf+flag_offset)=0xa5; + newlength=length+1; + } + //dump_message_column(newbuf,length,"After: "); + + return newlength; +} + +void EQProtocolPacket::ChatDecode(unsigned char *buffer, int size, int DecodeKey) +{ + if (buffer[1]!=0x01 && buffer[0]!=0x02 && buffer[0]!=0x1d) { + int Key=DecodeKey; + unsigned char *test=(unsigned char *)malloc(size); + buffer+=2; + size-=2; + + int i; + for (i = 0 ; i+4 <= size ; i+=4) + { + int pt = (*(int*)&buffer[i])^(Key); + Key = (*(int*)&buffer[i]); + *(int*)&test[i]=pt; + } + unsigned char KC=Key&0xFF; + for ( ; i < size ; i++) + { + test[i]=buffer[i]^KC; + } + memcpy(buffer,test,size); + free(test); + } +} + +void EQProtocolPacket::ChatEncode(unsigned char *buffer, int size, int EncodeKey) +{ + if (buffer[1]!=0x01 && buffer[0]!=0x02 && buffer[0]!=0x1d) { + int Key=EncodeKey; + char *test=(char*)malloc(size); + int i; + buffer+=2; + size-=2; + for ( i = 0 ; i+4 <= size ; i+=4) + { + int pt = (*(int*)&buffer[i])^(Key); + Key = pt; + *(int*)&test[i]=pt; + } + unsigned char KC=Key&0xFF; + for ( ; i < size ; i++) + { + test[i]=buffer[i]^KC; + } + memcpy(buffer,test,size); + free(test); + } +} + +bool EQProtocolPacket::IsProtocolPacket(const unsigned char* in_buff, uint32_t len, bool bTrimCRC) { + bool ret = false; + uint16_t opcode = ntohs(*(uint16_t*)in_buff); + uint32_t offset = 2; + + switch (opcode) { + case OP_SessionRequest: + case OP_SessionDisconnect: + case OP_KeepAlive: + case OP_SessionStatResponse: + case OP_Packet: + case OP_Combined: + case OP_Fragment: + case OP_Ack: + case OP_OutOfOrderAck: + case OP_OutOfSession: + { + ret = true; + break; + } + } + + return ret; +} + + + +void DumpPacketHex(const EQApplicationPacket* app) +{ + DumpPacketHex(app->pBuffer, app->size); +} + +void DumpPacketAscii(const EQApplicationPacket* app) +{ + DumpPacketAscii(app->pBuffer, app->size); +} +void DumpPacket(const EQProtocolPacket* app) { + DumpPacketHex(app->pBuffer, app->size); +} +void DumpPacket(const EQApplicationPacket* app, bool iShowInfo) { + if (iShowInfo) { + cout << "Dumping Applayer: 0x" << hex << setfill('0') << setw(4) << app->GetOpcode() << dec; + cout << " size:" << app->size << endl; + } + DumpPacketHex(app->pBuffer, app->size); +// DumpPacketAscii(app->pBuffer, app->size); +} + +void DumpPacketBin(const EQApplicationPacket* app) { + DumpPacketBin(app->pBuffer, app->size); +} + + diff --git a/old/common/EQPacket.h b/old/common/EQPacket.h new file mode 100644 index 0000000..455a72c --- /dev/null +++ b/old/common/EQPacket.h @@ -0,0 +1,209 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef _EQPACKET_H +#define _EQPACKET_H + +#include "types.h" +#include +#include + +#ifdef WIN32 + #include + #include +#else + #include + #include +#endif + +#include "emu_opcodes.h" +#include "op_codes.h" +#include "packet_dump.h" + +class OpcodeManager; + +class EQStream; + +class EQPacket { + friend class EQStream; +public: + unsigned char *pBuffer; + uint32 size; + uint32 src_ip,dst_ip; + uint16 src_port,dst_port; + uint32 priority; + timeval timestamp; + int16 version; + ~EQPacket(); + void DumpRawHeader(uint16 seq=0xffff, FILE *to = stdout) const; + void DumpRawHeaderNoTime(uint16 seq=0xffff, FILE *to = stdout) const; + void DumpRaw(FILE *to = stdout) const; + const char* GetOpcodeName(); + + void setVersion(int16 new_version){ version = new_version; } + void setSrcInfo(uint32 sip, uint16 sport) { src_ip=sip; src_port=sport; } + void setDstInfo(uint32 dip, uint16 dport) { dst_ip=dip; dst_port=dport; } + void setTimeInfo(uint32 ts_sec, uint32 ts_usec) { timestamp.tv_sec=ts_sec; timestamp.tv_usec=ts_usec; } + void copyInfo(const EQPacket *p) { src_ip=p->src_ip; src_port=p->src_port; dst_ip=p->dst_ip; dst_port=p->dst_port; timestamp.tv_sec=p->timestamp.tv_sec; timestamp.tv_usec=p->timestamp.tv_usec; } + uint32 Size() const { return size+2; } + +//no reason to have this method in zone or world + + uint16 GetRawOpcode() const { return(opcode); } + + + inline bool operator<(const EQPacket &rhs) { + return (timestamp.tv_sec < rhs.timestamp.tv_sec || (timestamp.tv_sec==rhs.timestamp.tv_sec && timestamp.tv_usec < rhs.timestamp.tv_usec)); + } + void SetProtocolOpcode(int16 new_opcode){ + opcode = new_opcode; + } + +protected: + uint16 opcode; + + EQPacket(const uint16 op, const unsigned char *buf, const uint32 len); + EQPacket(const EQPacket &p) { version = 0; } + EQPacket() { opcode=0; pBuffer=NULL; size=0; version = 0; setTimeInfo(0, 0); } + +}; + +class EQApplicationPacket; + +class EQProtocolPacket : public EQPacket { +public: + EQProtocolPacket(uint16 op, const unsigned char *buf, uint32 len) : EQPacket(op,buf,len) { + eq2_compressed = false; + packet_prepared = false; + packet_encrypted = false; + sequence = 0; + sent_time = 0; + attempt_count = 0; + acked = false; + } + EQProtocolPacket(const unsigned char *buf, uint32 len, int in_opcode = -1); + bool combine(const EQProtocolPacket *rhs); + uint32 serialize (unsigned char *dest, int8 offset = 0) const; + static bool ValidateCRC(const unsigned char *buffer, int length, uint32 Key); + static uint32 Decompress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize); + static uint32 Compress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize); + static void ChatDecode(unsigned char *buffer, int size, int DecodeKey); + static void ChatEncode(unsigned char *buffer, int size, int EncodeKey); + static bool IsProtocolPacket(const unsigned char* in_buff, uint32_t len, bool bTrimCRC); + + EQProtocolPacket *Copy() { + EQProtocolPacket* new_packet = new EQProtocolPacket(opcode,pBuffer,size); + new_packet->eq2_compressed = this->eq2_compressed; + new_packet->packet_prepared = this->packet_prepared; + new_packet->packet_encrypted = this->packet_encrypted; + return new_packet; + } + EQApplicationPacket *MakeApplicationPacket(uint8 opcode_size=0) const; + bool eq2_compressed; + bool packet_prepared; + bool packet_encrypted; + bool acked; + int32 sent_time; + int8 attempt_count; + int32 sequence; + +private: + EQProtocolPacket(const EQProtocolPacket &p) { } + //bool dont_combine; +}; +class EQ2Packet : public EQProtocolPacket { +public: + EQ2Packet(const EmuOpcode in_login_op, const unsigned char *buf, uint32 len) : EQProtocolPacket(OP_Packet,buf,len){ + login_op = in_login_op; + eq2_compressed = false; + packet_prepared = false; + packet_encrypted = false; + } + bool AppCombine(EQ2Packet* rhs); + EQ2Packet* Copy() { + EQ2Packet* new_packet = new EQ2Packet(login_op,pBuffer,size); + new_packet->eq2_compressed = this->eq2_compressed; + new_packet->packet_prepared = this->packet_prepared; + new_packet->packet_encrypted = this->packet_encrypted; + return new_packet; + } + int8 PreparePacket(int16 MaxLen); + const char* GetOpcodeName(); + EmuOpcode login_op; +}; +class EQApplicationPacket : public EQPacket { + friend class EQProtocolPacket; + friend class EQStream; +public: + EQApplicationPacket() : EQPacket(0,NULL,0) { emu_opcode = OP_Unknown; app_opcode_size=default_opcode_size; } + EQApplicationPacket(const EmuOpcode op) : EQPacket(0,NULL,0) { SetOpcode(op); app_opcode_size=default_opcode_size; } + EQApplicationPacket(const EmuOpcode op, const uint32 len) : EQPacket(0,NULL,len) { SetOpcode(op); app_opcode_size=default_opcode_size; } + EQApplicationPacket(const EmuOpcode op, const unsigned char *buf, const uint32 len) : EQPacket(0,buf,len) { SetOpcode(op); app_opcode_size=default_opcode_size; } + bool combine(const EQApplicationPacket *rhs); + uint32 serialize (unsigned char *dest) const; + uint32 Size() const { return size+app_opcode_size; } + EQApplicationPacket *Copy() const { + EQApplicationPacket *it = new EQApplicationPacket; + try { + it->pBuffer= new unsigned char[size]; + memcpy(it->pBuffer,pBuffer,size); + it->size=size; + it->opcode = opcode; + it->emu_opcode = emu_opcode; + it->version = version; + return(it); + } + catch( bad_alloc &ba ) + { + cout << ba.what() << endl; + if( NULL != it ) + delete it; + } + return NULL; + } + + void SetOpcodeSize(uint8 s) { app_opcode_size=s; } + void SetOpcode(EmuOpcode op); + const EmuOpcode GetOpcodeConst() const; + inline const EmuOpcode GetOpcode() const { return(GetOpcodeConst()); } + //caching version of get + inline const EmuOpcode GetOpcode() { EmuOpcode r = GetOpcodeConst(); emu_opcode = r; return(r); } + + static uint8 default_opcode_size; + +protected: + //this is just a cache so we dont look it up several times on Get() + EmuOpcode emu_opcode; + +private: + //this constructor should only be used by EQProtocolPacket, as it + //assumes the first two bytes of buf are the opcode. + EQApplicationPacket(const unsigned char *buf, uint32 len, uint8 opcode_size=0); + EQApplicationPacket(const EQApplicationPacket &p) { emu_opcode = OP_Unknown; app_opcode_size=default_opcode_size; } + + uint8 app_opcode_size; +}; + +void DumpPacketHex(const EQApplicationPacket* app); +void DumpPacket(const EQProtocolPacket* app); +void DumpPacketAscii(const EQApplicationPacket* app); +void DumpPacket(const EQApplicationPacket* app, bool iShowInfo = false); +void DumpPacketBin(const EQApplicationPacket* app); + +#endif diff --git a/old/common/EQStream.cpp b/old/common/EQStream.cpp new file mode 100644 index 0000000..fef52ad --- /dev/null +++ b/old/common/EQStream.cpp @@ -0,0 +1,1921 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifdef WIN32 +#include + #include +#endif +#include "debug.h" +#include +#include +#include +#include +#include +#include +#ifdef WIN32 + #include +#else + #include + #include + #include + #include + #include + #include + #include +#endif +#include "EQPacket.h" +#include "EQStream.h" +#include "EQStreamFactory.h" +#include "misc.h" +#include "Mutex.h" +#include "op_codes.h" +#include "CRC16.h" +#include "packet_dump.h" +#ifdef LOGIN + #include "../LoginServer/login_structs.h" +#endif +#include "EQ2_Common_Structs.h" +#include "Log.h" + + +//#define DEBUG_EMBEDDED_PACKETS 1 +uint16 EQStream::MaxWindowSize=2048; + +void EQStream::init(bool resetSession) { + if (resetSession) + { + streamactive = false; + sessionAttempts = 0; + } + + timeout_delays = 0; + + MInUse.lock(); + active_users = 0; + MInUse.unlock(); + + Session=0; + Key=0; + MaxLen=0; + NextInSeq=0; + NextOutSeq=0; + CombinedAppPacket=NULL; + + MAcks.lock(); + MaxAckReceived = -1; + NextAckToSend = -1; + LastAckSent = -1; + MAcks.unlock(); + + LastSeqSent=-1; + MaxSends=5; + LastPacket=Timer::GetCurrentTime2(); + oversize_buffer=NULL; + oversize_length=0; + oversize_offset=0; + Factory = NULL; + + rogue_buffer=NULL; + roguebuf_offset=0; + roguebuf_size=0; + + MRate.lock(); + RateThreshold=RATEBASE/250; + DecayRate=DECAYBASE/250; + MRate.unlock(); + + BytesWritten=0; + SequencedBase = 0; + AverageDelta = 500; + + crypto->setRC4Key(0); + + retransmittimer = Timer::GetCurrentTime2(); + retransmittimeout = 500 * RETRANSMIT_TIMEOUT_MULT; + + reconnectAttempt = 0; + if (uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + LogWrite(PACKET__DEBUG, 9, "Packet", "init Invalid Sequenced queue: BS %u + SQ %u != NOS %u", SequencedBase, SequencedQueue.size(), NextOutSeq); + } +} + +EQStream::EQStream(sockaddr_in addr){ + crypto = new Crypto(); + resend_que_timer = new Timer(1000); + combine_timer = new Timer(250); //250 milliseconds + combine_timer->Start(); + resend_que_timer->Start(); + init(); + remote_ip=addr.sin_addr.s_addr; + remote_port=addr.sin_port; + State=CLOSED; + StreamType=UnknownStream; + compressed=true; + encoded=false; + app_opcode_size=2; + #ifdef WIN32 + ZeroMemory(&stream, sizeof(z_stream)); + #else + bzero(&stream, sizeof(z_stream)); + #endif + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + deflateInit2(&stream, 9, Z_DEFLATED, 13, 9, Z_DEFAULT_STRATEGY); + //deflateInit(&stream, 5); + compressed_offset = 0; + client_version = 0; + received_packets = 0; + sent_packets = 0; + +#ifdef WRITE_PACKETS + write_packets = 0; + char write_packets_filename[64]; + snprintf(write_packets_filename, sizeof(write_packets_filename), "PacketLog%i.log", Timer::GetCurrentTime2()); + write_packets = fopen(write_packets_filename, "w+"); +#endif +} + +EQProtocolPacket* EQStream::ProcessEncryptedData(uchar* data, int32 size, int16 opcode){ + //cout << "B4:\n"; + //DumpPacket(data, size); + /*if(size >= 2 && data[0] == 0 && data[1] == 0){ + cout << "Attempting to fix packet!\n"; + //Have to fix bad packet from client or it will screw up encryption :P + size--; + data++; + }*/ + crypto->RC4Decrypt(data,size); + int8 offset = 0; + if(data[0] == 0xFF && size > 2){ + offset = 3; + memcpy(&opcode, data+sizeof(int8), sizeof(int16)); + } + else{ + offset = 1; + memcpy(&opcode, data, sizeof(int8)); + } + //cout << "After:\n"; + //DumpPacket(data, size); + return new EQProtocolPacket(opcode, data+offset, size - offset); +} + +EQProtocolPacket* EQStream::ProcessEncryptedPacket(EQProtocolPacket *p){ + EQProtocolPacket* ret = NULL; + if(p->opcode == OP_Packet && p->size > 2) + ret = ProcessEncryptedData(p->pBuffer+2, p->size-2, p->opcode); + else + ret = ProcessEncryptedData(p->pBuffer, p->size, p->opcode); + return ret; +} + +bool EQStream::ProcessEmbeddedPacket(uchar* pBuffer, int16 length,int8 opcode) { + if(!pBuffer || !crypto->isEncrypted()) + return false; + + MCombineQueueLock.lock(); + EQProtocolPacket* newpacket = ProcessEncryptedData(pBuffer, length, opcode); + MCombineQueueLock.unlock(); + + if (newpacket) { +#ifdef DEBUG_EMBEDDED_PACKETS + printf("Opcode: %u\n", newpacket->opcode); + DumpPacket(newpacket->pBuffer, newpacket->size); +#endif + + EQApplicationPacket* ap = newpacket->MakeApplicationPacket(2); + if (ap->version == 0) + ap->version = client_version; + InboundQueuePush(ap); +#ifdef WRITE_PACKETS + WritePackets(ap->GetOpcodeName(), pBuffer, length, false); +#endif + safe_delete(newpacket); + return true; + } + + return false; +} + +bool EQStream::HandleEmbeddedPacket(EQProtocolPacket *p, int16 offset, int16 length){ + if(!p) + return false; + +#ifdef DEBUG_EMBEDDED_PACKETS + // printf works better with DumpPacket + printf( "Start Packet with offset %u, length %u, p->size %u\n", offset, length, p->size); +#endif + + if(p->size >= ((uint32)(offset+2))){ + if(p->pBuffer[offset] == 0 && p->pBuffer[offset+1] == 0x19){ + uint32 data_length = 0; + if(length == 0) { + // Ensure there are at least 2 bytes after offset. + if(p->size < offset + 2) { + return false; // Not enough data. + } + data_length = p->size - offset - 2; + } else { + // Ensure provided length is at least 2. + if(length < 2) { + return false; // Provided length too short. + } + data_length = length - 2; + } +#ifdef DEBUG_EMBEDDED_PACKETS + printf( "Creating OP_AppCombined Packet with offset %u, length %u, p->size %u\n", offset, length, p->size); + DumpPacket(p->pBuffer, p->size); +#endif + // Verify that offset + 2 + data_length does not exceed p->size. + if(offset + 2 + data_length > p->size) { + return false; // Out-of-bounds. + } + EQProtocolPacket *subp = new EQProtocolPacket(OP_AppCombined, p->pBuffer + offset + 2, data_length); + subp->copyInfo(p); + ProcessPacket(subp, p); + safe_delete(subp); + return true; + } + else if (p->pBuffer[offset] == 0 && p->pBuffer[offset + 1] == 0) { + if (length == 0) + length = p->size - 1 - offset; + else + length--; + +#ifdef DEBUG_EMBEDDED_PACKETS + printf( "Creating Opcode 0 Packet!"); + DumpPacket(p->pBuffer + 1 + offset, length); +#endif + uchar* buffer = (p->pBuffer + 1 + offset); + bool valid = ProcessEmbeddedPacket(buffer, length); + + if(valid) + return true; + } + else if(offset+4 < p->size && ntohl(*(uint32 *)(p->pBuffer+offset)) != 0xffffffff) { +#ifdef DEBUG_EMBEDDED_PACKETS + uint16 seq = NextInSeq-1; + sint8 check = 0; + + if(offset == 2) { + seq=ntohs(*(uint16 *)(p->pBuffer)); + check=CompareSequence(NextInSeq,seq); + } + printf( "Unhandled Packet with offset %u, length %u, p->size %u, check: %i, nextinseq: %u, seq: %u\n", offset, length, p->size, check, NextInSeq, seq); + DumpPacket(p->pBuffer, p->size); +#endif + + if(length == 0) + length = p->size - offset; + + + uchar* buffer = (p->pBuffer + offset); + + bool valid = ProcessEmbeddedPacket(buffer, length); + + if(valid) + return true; + } + else if(p->pBuffer[offset] != 0xff && p->pBuffer[offset+1] == 0xff && p->size >= offset + 3) { + // Read the first byte into a wider type to avoid underflow. + uint16 total_length = p->pBuffer[offset]; // promote to uint16 + // Check that there is enough data: we expect offset+2+total_length == p->size. + if(total_length + offset + 2 == p->size && total_length >= 2) { + uint32 data_length = total_length - 2; + // No additional bounds check needed because equality condition ensures it. + EQProtocolPacket *subp = new EQProtocolPacket(p->pBuffer + offset + 2, data_length, OP_Packet); + subp->copyInfo(p); + ProcessPacket(subp, p); + delete subp; + return true; + } + } + } + return false; +} + +void EQStream::ProcessPacket(EQProtocolPacket *p, EQProtocolPacket* lastp) +{ + uint32 processed=0,subpacket_length=0; + + if (p) { + + if (p->opcode!=OP_SessionRequest && p->opcode!=OP_SessionResponse && !Session) { +#ifdef EQN_DEBUG + LogWrite(PACKET__ERROR, 0, "Packet", "*** Session not initialized, packet ignored "); + //p->DumpRaw(); +#endif + return; + } + + //cout << "Received " << (int)p->opcode << ":\n"; + //DumpPacket(p->pBuffer, p->size); + switch (p->opcode) { + case OP_Combined: { + processed=0; + int8 offset = 0; + int count = 0; +#ifdef LE_DEBUG + printf( "OP_Combined:\n"); + DumpPacket(p); +#endif + while(processedsize) { + if ((subpacket_length=(unsigned char)*(p->pBuffer+processed))==0xff) { + subpacket_length = ntohs(*(uint16*)(p->pBuffer + processed + 1)); + //printf("OP_Combined subpacket_length %u\n",subpacket_length); + offset = 3; + } + else { + offset = 1; + } + + //printf("OP_Combined processed %u p->size %u subpacket length %u count %i\n",processed, p->size, subpacket_length, count); + count++; +#ifdef LE_DEBUG + printf( "OP_Combined Packet %i (%u) (%u):\n", count, subpacket_length, processed); +#endif + bool isSubPacket = EQProtocolPacket::IsProtocolPacket(p->pBuffer + processed + offset, subpacket_length, false); + if (isSubPacket) { + EQProtocolPacket* subp = new EQProtocolPacket(p->pBuffer + processed + offset, subpacket_length); + subp->copyInfo(p); +#ifdef LE_DEBUG + printf( "Opcode %i:\n", subp->opcode); + DumpPacket(subp); +#endif + ProcessPacket(subp, p); +#ifdef LE_DEBUG + DumpPacket(subp); +#endif + delete subp; + } + else { + offset = 1; // 0xFF in this case means it is actually 255 bytes of encrypted data after a 00 09 packet + //Garbage packet? + if(ntohs(*reinterpret_cast(p->pBuffer + processed + offset)) <= 0x1e) { + subpacket_length=(unsigned char)*(p->pBuffer+processed); + LogWrite(PACKET__ERROR, 0, "Packet", "!!!!!!!!!Garbage Packet Unknown Process as OP_Packet!!!!!!!!!!!!!\n"); + DumpPacket(p->pBuffer + processed + offset, subpacket_length); + uchar* newbuf = p->pBuffer; + newbuf += processed + offset; + EQProtocolPacket *subp=new EQProtocolPacket(newbuf,subpacket_length); + subp->copyInfo(p); + ProcessPacket(subp, p); + delete subp; + } + else { + crypto->RC4Decrypt(p->pBuffer + processed + offset, subpacket_length); + LogWrite(PACKET__ERROR, 0, "Packet", "!!!!!!!!!Garbage Packet!!!!!!!!!!!!! processed: %u, offset: %u, count: %i, subpacket_length: %u, offset_pos_1: %u, oversized_buffer_present: %u, offset size: %u, offset length: %u\n", + processed, offset, count, subpacket_length, p->pBuffer[processed + offset], oversize_buffer ? 1 : 0, oversize_offset, oversize_length); + if(p->pBuffer[processed + offset] == 0xff) + { + uchar* newbuf = p->pBuffer; + newbuf += processed + offset + 1; + + DumpPacket(p->pBuffer + processed + offset, subpacket_length); + EQProtocolPacket *subp=new EQProtocolPacket(newbuf, subpacket_length, OP_Packet); + subp->copyInfo(p); + ProcessPacket(subp, p); + delete subp; + } + else + break; // bad packet + } + } + processed+=subpacket_length+offset; + } + break; + } + case OP_AppCombined: { + processed=0; + EQProtocolPacket* newpacket = 0; + int8 offset = 0; +#ifdef DEBUG_EMBEDDED_PACKETS + printf( "OP_AppCombined: \n"); + DumpPacket(p); +#endif + int count = 0; + while(processedsize) { + count++; + if ((subpacket_length=(unsigned char)*(p->pBuffer+processed))==0xff) { + subpacket_length=ntohs(*(uint16 *)(p->pBuffer+processed+1)); + offset = 3; + } else + offset = 1; + + if(crypto->getRC4Key()==0 && p && subpacket_length > 8+offset){ + #ifdef DEBUG_EMBEDDED_PACKETS + DumpPacket(p->pBuffer, p->size); + #endif + p->pBuffer += offset; + processRSAKey(p, subpacket_length); + p->pBuffer -= offset; + } + else if(crypto->isEncrypted()){ +#ifdef DEBUG_EMBEDDED_PACKETS + printf( "OP_AppCombined Packet %i (%u) (%u): \n", count, subpacket_length, processed); + DumpPacket(p->pBuffer+processed+offset, subpacket_length); +#endif + if(!HandleEmbeddedPacket(p, processed + offset, subpacket_length)){ + uchar* buffer = (p->pBuffer + processed + offset); + if(!ProcessEmbeddedPacket(buffer, subpacket_length, OP_AppCombined)) { + LogWrite(PACKET__ERROR, 0, "Packet", "*** This is bad, ProcessEmbeddedPacket failed, report to Image!"); + } + } + } + processed+=subpacket_length+offset; + } + } + break; + case OP_Packet: { + if (!p->pBuffer || (p->Size() < 4)) + { + break; + } + + uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); + sint8 check=CompareSequence(NextInSeq,seq); + if (check == SeqFuture) { +#ifdef EQN_DEBUG + LogWrite(PACKET__DEBUG, 1, "Packet", "*** Future packet: Expecting Seq=%i, but got Seq=%i", NextInSeq, seq); + LogWrite(PACKET__DEBUG, 1, "Packet", "[Start]"); + p->DumpRawHeader(seq); + LogWrite(PACKET__DEBUG, 1, "Packet", "[End]"); +#endif + OutOfOrderpackets[seq] = p->Copy(); + + // Image (2020): Removed as this is bad contributes to infinite loop + //SendOutOfOrderAck(seq); + } else if (check == SeqPast) { +#ifdef EQN_DEBUG + LogWrite(PACKET__DEBUG, 1, "Packet", "*** Duplicate packet: Expecting Seq=%i, but got Seq=%i", NextInSeq, seq); + LogWrite(PACKET__DEBUG, 1, "Packet", "[Start]"); + p->DumpRawHeader(seq); + LogWrite(PACKET__DEBUG, 1, "Packet", "[End]"); +#endif + // Image (2020): Removed as this is bad contributes to infinite loop + //OutOfOrderpackets[seq] = p->Copy(); + SendOutOfOrderAck(seq); + } else { + EQProtocolPacket* qp = RemoveQueue(seq); + if (qp) { + LogWrite(PACKET__DEBUG, 1, "Packet", "OP_Fragment: Removing older queued packet with sequence %i", seq); + delete qp; + } + + SetNextAckToSend(seq); + NextInSeq++; + + if(HandleEmbeddedPacket(p)) + break; + if(crypto->getRC4Key()==0 && p && p->size >= 69){ + #ifdef DEBUG_EMBEDDED_PACKETS + DumpPacket(p->pBuffer, p->size); + #endif + processRSAKey(p); + } + else if(crypto->isEncrypted() && p){ + MCombineQueueLock.lock(); + EQProtocolPacket* newpacket = ProcessEncryptedPacket(p); + MCombineQueueLock.unlock(); + if(newpacket){ + EQApplicationPacket* ap = newpacket->MakeApplicationPacket(2); + if (ap->version == 0) + ap->version = client_version; +#ifdef WRITE_PACKETS + WritePackets(ap->GetOpcodeName(), p->pBuffer, p->size, false); +#endif + InboundQueuePush(ap); + safe_delete(newpacket); + } + } + } + } + break; + case OP_Fragment: { + if (!p->pBuffer || (p->Size() < 4)) + { + break; + } + + uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); + sint8 check=CompareSequence(NextInSeq,seq); + if (check == SeqFuture) { +#ifdef EQN_DEBUG + LogWrite(PACKET__DEBUG, 1, "Packet", "*** Future packet2: Expecting Seq=%i, but got Seq=%i", NextInSeq, seq); + LogWrite(PACKET__DEBUG, 1, "Packet", "[Start]"); + //p->DumpRawHeader(seq); + LogWrite(PACKET__DEBUG, 1, "Packet", "[End]"); +#endif + OutOfOrderpackets[seq] = p->Copy(); + //SendOutOfOrderAck(seq); + } else if (check == SeqPast) { +#ifdef EQN_DEBUG + LogWrite(PACKET__DEBUG, 1, "Packet", "*** Duplicate packet2: Expecting Seq=%i, but got Seq=%i", NextInSeq, seq); + LogWrite(PACKET__DEBUG, 1, "Packet", "[Start]"); + //p->DumpRawHeader(seq); + LogWrite(PACKET__DEBUG, 1, "Packet", "[End]"); +#endif + //OutOfOrderpackets[seq] = p->Copy(); + SendOutOfOrderAck(seq); + } else { + // In case we did queue one before as well. + EQProtocolPacket* qp = RemoveQueue(seq); + if (qp) { + LogWrite(PACKET__DEBUG, 1, "Packet", "OP_Fragment: Removing older queued packet with sequence %i", seq); + delete qp; + } + + SetNextAckToSend(seq); + NextInSeq++; + if (oversize_buffer) { + memcpy(oversize_buffer+oversize_offset,p->pBuffer+2,p->size-2); + oversize_offset+=p->size-2; + //cout << "Oversized is " << oversize_offset << "/" << oversize_length << " (" << (p->size-2) << ") Seq=" << seq << endl; + if (oversize_offset==oversize_length) { + if (*(p->pBuffer+2)==0x00 && *(p->pBuffer+3)==0x19) { + EQProtocolPacket *subp=new EQProtocolPacket(oversize_buffer,oversize_offset); + subp->copyInfo(p); + ProcessPacket(subp, p); + delete subp; + } else { + + if(crypto->isEncrypted() && p && p->size > 2){ + MCombineQueueLock.lock(); + EQProtocolPacket* p2 = ProcessEncryptedData(oversize_buffer, oversize_offset, p->opcode); + MCombineQueueLock.unlock(); + EQApplicationPacket* ap = p2->MakeApplicationPacket(2); + ap->copyInfo(p); + if (ap->version == 0) + ap->version = client_version; +#ifdef WRITE_PACKETS + WritePackets(ap->GetOpcodeName(), oversize_buffer, oversize_offset, false); +#endif + ap->copyInfo(p); + InboundQueuePush(ap); + safe_delete(p2); + } + } + delete[] oversize_buffer; + oversize_buffer=NULL; + oversize_offset=0; + } + } else if (!oversize_buffer) { + oversize_length=ntohl(*(uint32 *)(p->pBuffer+2)); + oversize_buffer=new unsigned char[oversize_length]; + memcpy(oversize_buffer,p->pBuffer+6,p->size-6); + oversize_offset=p->size-6; + //cout << "Oversized is " << oversize_offset << "/" << oversize_length << " (" << (p->size-6) << ") Seq=" << seq << endl; + } + } + } + break; + case OP_KeepAlive: { +#ifndef COLLECTOR + NonSequencedPush(new EQProtocolPacket(p->opcode,p->pBuffer,p->size)); +#endif + } + break; + case OP_Ack: { + if (!p->pBuffer || (p->Size() < 4)) + { + LogWrite(PACKET__DEBUG, 9, "Packet", "Received OP_Ack that was of malformed size"); + break; + } + uint16 seq = ntohs(*(uint16*)(p->pBuffer)); + AckPackets(seq); + retransmittimer = Timer::GetCurrentTime2(); + } + break; + case OP_SessionRequest: { + if (p->Size() < sizeof(SessionRequest)) + { + break; + } + + if (GetState() == ESTABLISHED) { + //_log(NET__ERROR, _L "Received OP_SessionRequest in ESTABLISHED state (%d) streamactive (%i) attempt (%i)" __L, GetState(), streamactive, sessionAttempts); + + // client seems to try a max of 4 times (initial +3 retries) then gives up, giving it a few more attempts just in case + // streamactive means we identified the opcode, we cannot re-establish this connection + if (streamactive || (sessionAttempts > 30)) + { + SendDisconnect(false); + SetState(CLOSED); + break; + } + } + + sessionAttempts++; + if(GetState() == WAIT_CLOSE) { + printf("WAIT_CLOSE Reconnect with streamactive %u, sessionAttempts %u\n", streamactive, sessionAttempts); + reconnectAttempt++; + } + init(GetState() != ESTABLISHED); + OutboundQueueClear(); + SessionRequest *Request=(SessionRequest *)p->pBuffer; + Session=ntohl(Request->Session); + SetMaxLen(ntohl(Request->MaxLength)); +#ifndef COLLECTOR + NextInSeq=0; + Key=0x33624702; + SendSessionResponse(); +#endif + SetState(ESTABLISHED); + } + break; + case OP_SessionResponse: { + if (p->Size() < sizeof(SessionResponse)) + { + break; + } + init(); + OutboundQueueClear(); + SetActive(true); + SessionResponse *Response=(SessionResponse *)p->pBuffer; + SetMaxLen(ntohl(Response->MaxLength)); + Key=ntohl(Response->Key); + NextInSeq=0; + SetState(ESTABLISHED); + if (!Session) + Session=ntohl(Response->Session); + compressed=(Response->Format&FLAG_COMPRESSED); + encoded=(Response->Format&FLAG_ENCODED); + + // Kinda kludgy, but trie for now + if (compressed) { + if (remote_port==9000 || (remote_port==0 && p->src_port==9000)) + SetStreamType(WorldStream); + else + SetStreamType(ZoneStream); + } else if (encoded) + SetStreamType(ChatOrMailStream); + else + SetStreamType(LoginStream); + } + break; + case OP_SessionDisconnect: { + //NextInSeq=0; + SendDisconnect(); + //SetState(CLOSED); + } + break; + case OP_OutOfOrderAck: { + if (!p->pBuffer || (p->Size() < 4)) + { + LogWrite(PACKET__DEBUG, 9, "Packet", "Received OP_OutOfOrderAck that was of malformed size"); + break; + } + uint16 seq = ntohs(*(uint16*)(p->pBuffer)); + MOutboundQueue.lock(); + + if (uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + LogWrite(PACKET__DEBUG, 9, "Packet", "Pre-OOA Invalid Sequenced queue: BS %u + SQ %u != NOS %u", SequencedBase, SequencedQueue.size(), NextOutSeq); + } + + //if the packet they got out of order is between our last acked packet and the last sent packet, then its valid. + if (CompareSequence(SequencedBase, seq) != SeqPast && CompareSequence(NextOutSeq, seq) == SeqPast) { + uint16 sqsize = SequencedQueue.size(); + uint16 index = seq - SequencedBase; + LogWrite(PACKET__DEBUG, 9, "Packet", "OP_OutOfOrderAck marking packet acked in queue (queue index = %u, queue size = %u)", index, sqsize); + if (index < sqsize) { + SequencedQueue[index]->acked = true; + // flag packets for a resend + uint16 count = 0; + uint32 timeout = AverageDelta * 2 + 100; + for (auto sitr = SequencedQueue.begin(); sitr != SequencedQueue.end() && count < index; ++sitr, ++count) { + if (!(*sitr)->acked && (*sitr)->sent_time > 0 && (((*sitr)->sent_time + timeout) < Timer::GetCurrentTime2())) { + (*sitr)->sent_time = 0; + LogWrite(PACKET__DEBUG, 9, "Packet", "OP_OutOfOrderAck Flagging packet %u for retransmission", SequencedBase + count); + } + } + } + + if (RETRANSMIT_TIMEOUT_MULT) { + retransmittimer = Timer::GetCurrentTime2(); + } + } + else { + LogWrite(PACKET__DEBUG, 9, "Packet", "Received OP_OutOfOrderAck for out-of-window %u. Window (%u->%u)", seq, SequencedBase, NextOutSeq); + } + + if (uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + LogWrite(PACKET__DEBUG, 9, "Packet", "Post-OOA Invalid Sequenced queue: BS %u + SQ %u != NOS %u", SequencedBase, SequencedQueue.size(), NextOutSeq); + } + + MOutboundQueue.unlock(); + } + break; + case OP_ServerKeyRequest:{ + if (p->Size() < sizeof(ClientSessionStats)) + { + //_log(NET__ERROR, _L "Received OP_SessionStatRequest that was of malformed size" __L); + break; + } + + ClientSessionStats* Stats = (ClientSessionStats*)p->pBuffer; + int16 request_id = Stats->RequestID; + AdjustRates(ntohl(Stats->average_delta)); + ServerSessionStats* stats=(ServerSessionStats*)p->pBuffer; + memset(stats, 0, sizeof(ServerSessionStats)); + stats->RequestID = request_id; + stats->current_time = ntohl(Timer::GetCurrentTime2()); + stats->sent_packets = ntohl(sent_packets); + stats->sent_packets2 = ntohl(sent_packets); + stats->received_packets = ntohl(received_packets); + stats->received_packets2 = ntohl(received_packets); + NonSequencedPush(new EQProtocolPacket(OP_SessionStatResponse,p->pBuffer,p->size)); + if(!crypto->isEncrypted()) + SendKeyRequest(); + else + SendSessionResponse(); + } + break; + case OP_SessionStatResponse: { + LogWrite(PACKET__INFO, 0, "Packet", "OP_SessionStatResponse"); + } + break; + case OP_OutOfSession: { + LogWrite(PACKET__INFO, 0, "Packet", "OP_OutOfSession"); + SendDisconnect(); + SetState(CLOSED); + } + break; + default: + //EQApplicationPacket *ap = p->MakeApplicationPacket(app_opcode_size); + //InboundQueuePush(ap); + + cout << "Orig Packet: " << p->opcode << endl; + DumpPacket(p->pBuffer, p->size); + if(p && p->size >= 69){ + processRSAKey(p); + } + MCombineQueueLock.lock(); + EQProtocolPacket* p2 = ProcessEncryptedData(p->pBuffer, p->size, OP_Packet); + MCombineQueueLock.unlock(); + cout << "Decrypted Packet: " << p2->opcode << endl; + DumpPacket(p2->pBuffer, p2->size); + + safe_delete(p2); + /* if(p2) + { + EQApplicationPacket* ap = p2->MakeApplicationPacket(2); + if (ap->version == 0) + ap->version = client_version; + InboundQueuePush(ap); + safe_delete(p2); + }*/ + + //EQProtocolPacket* puse = p2; + /* if (!rogue_buffer) { + roguebuf_size=puse->size; + rogue_buffer=new unsigned char[roguebuf_size]; + memcpy(rogue_buffer,puse->pBuffer,puse->size); + roguebuf_offset=puse->size; + cout << "RogueBuf is " << roguebuf_offset << "/" << roguebuf_size << " (" << (p->size-6) << ") NextInSeq=" << NextInSeq << endl; + } + else { + int32 new_size = roguebuf_size + puse->size; + uchar* tmp_buffer = new unsigned char[new_size]; + uchar* ptr = tmp_buffer; + + memcpy(ptr,rogue_buffer,roguebuf_size); + ptr += roguebuf_size; + memcpy(ptr,puse->pBuffer,puse->size); + roguebuf_offset=puse->size; + + safe_delete_array(rogue_buffer); + + rogue_buffer = tmp_buffer; + roguebuf_size = new_size; + roguebuf_offset = new_size; + cout << "RogueBuf is " << roguebuf_offset << "/" << roguebuf_size << " (" << (p->size-6) << ") NextInSeq=" << NextInSeq << endl; + }*/ +#ifdef WRITE_PACKETS + WritePackets(ap->GetOpcodeName(), p->pBuffer, p->size, false); +#endif + //InboundQueuePush(ap); + LogWrite(PACKET__INFO, 0, "Packet", "Received unknown packet type, not adding to inbound queue"); + //safe_delete(p2); + //SendDisconnect(); + break; + } + } +} + +int8 EQStream::EQ2_Compress(EQ2Packet* app, int8 offset){ + +#ifdef LE_DEBUG + printf( "Before Compress in %s, line %i:\n", __FUNCTION__, __LINE__); + DumpPacket(app); +#endif + + + uchar* pDataPtr = app->pBuffer + offset; + int xpandSize = app->size * 2; + uchar* deflate_buff = new uchar[xpandSize]; + MCompressData.lock(); + stream.next_in = pDataPtr; + stream.avail_in = app->size - offset; + stream.next_out = deflate_buff; + stream.avail_out = xpandSize; + + int ret = deflate(&stream, Z_SYNC_FLUSH); + + if (ret != Z_OK) + { + printf("ZLIB COMPRESSION RETFAIL: %i, %i (Ret: %i)\n", app->size, stream.avail_out, ret); + MCompressData.unlock(); + safe_delete_array(deflate_buff); + return 0; + } + + int32 newsize = xpandSize - stream.avail_out; + safe_delete_array(app->pBuffer); + app->size = newsize + offset; + app->pBuffer = new uchar[app->size]; + app->pBuffer[(offset - 1)] = 1; + memcpy(app->pBuffer + offset, deflate_buff, newsize); + MCompressData.unlock(); + safe_delete_array(deflate_buff); + +#ifdef LE_DEBUG + printf( "After Compress in %s, line %i:\n", __FUNCTION__, __LINE__); + DumpPacket(app); +#endif + + return offset - 1; +} + +int16 EQStream::processRSAKey(EQProtocolPacket *p, uint16 subpacket_length){ + /*int16 limit = 0; + int8 offset = 13; + int8 offset2 = 0; + if(p->pBuffer[2] == 0) + limit = p->pBuffer[9]; + else{ + limit = p->pBuffer[5]; + offset2 = 5; + offset-=1; + } + crypto->setRC4Key(Crypto::RSADecrypt(p->pBuffer + offset + (limit-8), 8)); + return (limit + offset +1) - offset2;*/ + if(subpacket_length) + crypto->setRC4Key(Crypto::RSADecrypt(p->pBuffer + subpacket_length - 8, 8)); + else + crypto->setRC4Key(Crypto::RSADecrypt(p->pBuffer + p->size - 8, 8)); + + return 0; +} + +void EQStream::SendKeyRequest(){ + int32 crypto_key_size = 60; + int16 size = sizeof(KeyGen_Struct) + sizeof(KeyGen_End_Struct) + crypto_key_size; + EQ2Packet *outapp=new EQ2Packet(OP_WSLoginRequestMsg,NULL,size); + memcpy(&outapp->pBuffer[0], &crypto_key_size, sizeof(int32)); + memset(&outapp->pBuffer[4], 0xFF, crypto_key_size); + memset(&outapp->pBuffer[size-5], 1, 1); + memset(&outapp->pBuffer[size-1], 1, 1); + EQ2QueuePacket(outapp, true); +} + +void EQStream::EncryptPacket(EQ2Packet* app, int8 compress_offset, int8 offset){ + if(app->size>2 && crypto->isEncrypted()){ + app->packet_encrypted = true; + uchar* crypt_buff = app->pBuffer; + if(app->eq2_compressed) + crypto->RC4Encrypt(crypt_buff + compress_offset, app->size - compress_offset); + else + crypto->RC4Encrypt(crypt_buff + 2 + offset, app->size - 2 - offset); + } +} + +void EQStream::EQ2QueuePacket(EQ2Packet* app, bool attempted_combine){ + if(CheckActive()){ + if(!attempted_combine){ + MCombineQueueLock.lock(); + combine_queue.push_back(app); + MCombineQueueLock.unlock(); + } + else{ + MCombineQueueLock.lock(); + PreparePacket(app); + MCombineQueueLock.unlock(); +#ifdef LE_DEBUG + printf( "After B in %s, line %i:\n", __FUNCTION__, __LINE__); + DumpPacket(app); +#endif + SendPacket(app); + } + } +} + +void EQStream::UnPreparePacket(EQ2Packet* app){ + if(app->pBuffer[2] == 0 && app->pBuffer[3] == 19){ + uchar* new_buffer = new uchar[app->size-3]; + memcpy(new_buffer+2, app->pBuffer+5, app->size-3); + delete[] app->pBuffer; + app->size-=3; + app->pBuffer = new_buffer; + } +} + +#ifdef WRITE_PACKETS +char EQStream::GetChar(uchar in) +{ + if (in < ' ' || in > '~') + return '.'; + return (char)in; +} +void EQStream::WriteToFile(char* pFormat, ...) { + va_list args; + va_start(args, pFormat); + vfprintf(write_packets, pFormat, args); + va_end(args); +} + +void EQStream::WritePackets(const char* opcodeName, uchar* data, int32 size, bool outgoing) { + MWritePackets.lock(); + struct in_addr ip_addr; + ip_addr.s_addr = remote_ip; + char timebuffer[80]; + time_t rawtime; + struct tm* timeinfo; + time(&rawtime); + timeinfo = localtime(&rawtime); + strftime(timebuffer, 80, "%m/%d/%Y %H:%M:%S", timeinfo); + if (outgoing) + WriteToFile("-- %s --\n%s\nSERVER -> %s\n", opcodeName, timebuffer, inet_ntoa(ip_addr)); + else + WriteToFile("-- %s --\n%s\n%s -> SERVER\n", opcodeName, timebuffer, inet_ntoa(ip_addr)); + int i; + int nLines = size / 16; + int nExtra = size % 16; + uchar* pPtr = data; + for (i = 0; i < nLines; i++) + { + WriteToFile("%4.4X:\t%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n", i * 16, pPtr[0], pPtr[1], pPtr[2], pPtr[3], pPtr[4], pPtr[5], pPtr[6], pPtr[7], pPtr[8], pPtr[9], pPtr[10], pPtr[11], pPtr[12], pPtr[13], pPtr[14], pPtr[15], GetChar(pPtr[0]), GetChar(pPtr[1]), GetChar(pPtr[2]), GetChar(pPtr[3]), GetChar(pPtr[4]), GetChar(pPtr[5]), GetChar(pPtr[6]), GetChar(pPtr[7]), GetChar(pPtr[8]), GetChar(pPtr[9]), GetChar(pPtr[10]), GetChar(pPtr[11]), GetChar(pPtr[12]), GetChar(pPtr[13]), GetChar(pPtr[14]), GetChar(pPtr[15])); + pPtr += 16; + } + if (nExtra) + { + WriteToFile("%4.4X\t", nLines * 16); + for (i = 0; i < nExtra; i++) + { + WriteToFile("%2.2X ", pPtr[i]); + } + for (i; i < 16; i++) + WriteToFile(" "); + for (i = 0; i < nExtra; i++) + { + WriteToFile("%c", GetChar(pPtr[i])); + } + WriteToFile("\n"); + } + WriteToFile("\n\n"); + fflush(write_packets); + MWritePackets.unlock(); +} + +void EQStream::WritePackets(EQ2Packet* app, bool outgoing) { + if (app->version == 0) + app->version = client_version; + WritePackets(app->GetOpcodeName(), app->pBuffer, app->size, outgoing); +} +#endif + +void EQStream::PreparePacket(EQ2Packet* app, int8 offset){ + app->setVersion(client_version); + compressed_offset = 0; + +#ifdef LE_DEBUG + printf( "Before A in %s, line %i:\n", __FUNCTION__, __LINE__); + DumpPacket(app); +#endif + if(!app->packet_prepared){ + if(app->PreparePacket(MaxLen) == 255) //invalid version + return; + } + +#ifdef LE_DEBUG + printf( "After Prepare in %s, line %i:\n", __FUNCTION__, __LINE__); + DumpPacket(app); +#endif +#ifdef WRITE_PACKETS + if (!app->eq2_compressed && !app->packet_encrypted) + WritePackets(app, true); +#endif + + if(!app->eq2_compressed && app->size>128){ + compressed_offset = EQ2_Compress(app); + if (compressed_offset) + app->eq2_compressed = true; + } + if(!app->packet_encrypted){ + EncryptPacket(app, compressed_offset, offset); + if(app->size > 2 && app->pBuffer[2] == 0){ + uchar* new_buffer = new uchar[app->size+1]; + new_buffer[2] = 0; + memcpy(new_buffer+3, app->pBuffer+2, app->size-2); + delete[] app->pBuffer; + app->pBuffer = new_buffer; + app->size++; + } + } + +#ifdef LE_DEBUG + printf( "After A in %s, line %i:\n", __FUNCTION__, __LINE__); + DumpPacket(app); +#endif + +} + +void EQStream::SendPacket(EQProtocolPacket *p) +{ + uint32 chunksize,used; + uint32 length; + + // Convert the EQApplicationPacket to 1 or more EQProtocolPackets + if (p->size>( MaxLen-8)) { // proto-op(2), seq(2), app-op(2) ... data ... crc(2) + uchar* tmpbuff=p->pBuffer; + length=p->size - 2; + + EQProtocolPacket *out=new EQProtocolPacket(OP_Fragment,NULL,MaxLen-4); + *(uint32 *)(out->pBuffer+2)=htonl(length); + used=MaxLen-10; + memcpy(out->pBuffer+6,tmpbuff+2,used); + +#ifdef LE_DEBUG + printf("(%s, %i) New Fragment:\n ", __FUNCTION__, __LINE__); + DumpPacket(out); +#endif + + SequencedPush(out); + + while (usedpBuffer+2,tmpbuff,1); + memcpy(out->pBuffer+2,tmpbuff+used+2,chunksize); +#ifdef LE_DEBUG + printf("Chunk: \n"); + DumpPacket(out); +#endif + SequencedPush(out); + used+=chunksize; + + } + +#ifdef LE_DEBUG + printf( "ChunkDelete: \n"); + DumpPacket(out); + //cerr << "1: Deleting 0x" << hex << (uint32)(p) << dec << endl; +#endif + + delete p; + } else { + SequencedPush(p); + } +} +void EQStream::SendPacket(EQApplicationPacket *p) +{ +uint32 chunksize,used; +uint32 length; + + // Convert the EQApplicationPacket to 1 or more EQProtocolPackets + if (p->size>(MaxLen-8)) { // proto-op(2), seq(2), app-op(2) ... data ... crc(2) + //cout << "Making oversized packet for: " << endl; + //cout << p->size << endl; + //p->DumpRawHeader(); + //dump_message(p->pBuffer,p->size,timestamp()); + //cout << p->size << endl; + unsigned char *tmpbuff=new unsigned char[p->size+2]; + //cout << hex << (int)tmpbuff << dec << endl; + length=p->serialize(tmpbuff); + + EQProtocolPacket *out=new EQProtocolPacket(OP_Fragment,NULL,MaxLen-4); + *(uint32 *)(out->pBuffer+2)=htonl(p->Size()); + memcpy(out->pBuffer+6,tmpbuff,MaxLen-10); + used=MaxLen-10; + SequencedPush(out); + //cout << "Chunk #" << ++i << " size=" << used << ", length-used=" << (length-used) << endl; + while (usedpBuffer+2,tmpbuff+used,chunksize); + out->size=chunksize+2; + SequencedPush(out); + used+=chunksize; + //cout << "Chunk #"<< ++i << " size=" << chunksize << ", length-used=" << (length-used) << endl; + } + //cerr << "1: Deleting 0x" << hex << (uint32)(p) << dec << endl; + delete p; + delete[] tmpbuff; + } else { + EQProtocolPacket *out=new EQProtocolPacket(OP_Packet,NULL,p->Size()+2); + p->serialize(out->pBuffer+2); + SequencedPush(out); + //cerr << "2: Deleting 0x" << hex << (uint32)(p) << dec << endl; + delete p; + } +} + +void EQStream::SequencedPush(EQProtocolPacket *p) +{ + p->setVersion(client_version); + MOutboundQueue.lock(); + *(uint16 *)(p->pBuffer)=htons(NextOutSeq); + SequencedQueue.push_back(p); + p->sequence = NextOutSeq; + NextOutSeq++; + MOutboundQueue.unlock(); +} + +void EQStream::NonSequencedPush(EQProtocolPacket *p) +{ + p->setVersion(client_version); + MOutboundQueue.lock(); + NonSequencedQueue.push(p); + MOutboundQueue.unlock(); +} + +void EQStream::SendAck(uint16 seq) +{ + uint16 Seq=htons(seq); + SetLastAckSent(seq); + NonSequencedPush(new EQProtocolPacket(OP_Ack,(unsigned char *)&Seq,sizeof(uint16))); +} + +void EQStream::SendOutOfOrderAck(uint16 seq) +{ + uint16 Seq=htons(seq); + NonSequencedPush(new EQProtocolPacket(OP_OutOfOrderAck,(unsigned char *)&Seq,sizeof(uint16))); +} + +bool EQStream::CheckCombineQueue(){ + bool ret = true; //processed all packets + MCombineQueueLock.lock(); + if(combine_queue.size() > 0){ + EQ2Packet* first = combine_queue.front(); + combine_queue.pop_front(); + if(combine_queue.size() == 0){ //nothing to combine this with + EQ2QueuePacket(first, true); + } + else{ + PreparePacket(first); + EQ2Packet* second = 0; + bool combine_worked = false; + int16 count = 0; + while(combine_queue.size()){ + count++; + second = combine_queue.front(); + combine_queue.pop_front(); + PreparePacket(second); + /*if(first->GetRawOpcode() != OP_AppCombined && first->pBuffer[2] == 0){ + EQ2Packet* tmp = second; + second = first; + first = tmp; + }*/ + if(!first->AppCombine(second)){ + first->SetProtocolOpcode(OP_Packet); + if(combine_worked){ + SequencedPush(first); + } + else{ + EQ2QueuePacket(first, true); + } + first = second; + combine_worked = false; + } + else{ + combine_worked = true; + //DumpPacket(first); + } + if(count >= 60 || first->size > 4000){ //other clients need packets too + ret = false; + break; + } + } + if(first){ + first->SetProtocolOpcode(OP_Packet); + if(combine_worked){ + SequencedPush(first); + } + else{ + EQ2QueuePacket(first, true); + } + } + } + } + MCombineQueueLock.unlock(); + return ret; +} + +void EQStream::CheckResend(int eq_fd){ + int32 curr = Timer::GetCurrentTime2(); + EQProtocolPacket* packet = 0; + deque::iterator itr; + MResendQue.lock(); + for(itr=resend_que.begin();itr!=resend_que.end();itr++){ + packet = *itr; + if(packet->attempt_count >= 5){//tried to resend this packet 5 times, client must already have it but didnt ack it + safe_delete(packet); + itr = resend_que.erase(itr); + if(itr == resend_que.end()) + break; + } + else{ + if((curr - packet->sent_time) < 1000) + continue; + packet->sent_time -=1000; + packet->attempt_count++; + WritePacket(eq_fd, packet); + } + } + MResendQue.unlock(); +} + + + +//returns SeqFuture if `seq` is later than `expected_seq` +EQStream::SeqOrder EQStream::CompareSequence(uint16 expected_seq, uint16 seq) +{ + if (expected_seq == seq) { + // Curent + return SeqInOrder; + } + else if ((seq > expected_seq && (uint32)seq < ((uint32)expected_seq + EQStream::MaxWindowSize)) || seq < (expected_seq - EQStream::MaxWindowSize)) { + // Future + return SeqFuture; + } + else { + // Past + return SeqPast; + } +} + +void EQStream::AckPackets(uint16 seq) +{ + std::deque::iterator itr, tmp; + + MOutboundQueue.lock(); + + SeqOrder ord = CompareSequence(SequencedBase, seq); + if (ord == SeqInOrder) { + //they are not acking anything new... + LogWrite(PACKET__DEBUG, 9, "Packet", "Received an ack with no window advancement (seq %u)", seq); + } + else if (ord == SeqPast) { + //they are nacking blocks going back before our buffer, wtf? + LogWrite(PACKET__DEBUG, 9, "Packet", "Received an ack with backward window advancement (they gave %u, our window starts at %u). This is bad" , seq, SequencedBase); + } + else { + LogWrite(PACKET__DEBUG, 9, "Packet", "Received an ack up through sequence %u. Our base is %u", seq, SequencedBase); + + + //this is a good ack, we get to ack some blocks. + seq++; //we stop at the block right after their ack, counting on the wrap of both numbers. + while (SequencedBase != seq) { + if (SequencedQueue.empty()) { + LogWrite(PACKET__DEBUG, 9, "Packet", "OUT OF PACKETS acked packet with sequence %u. Next send is %u before this", (unsigned long)SequencedBase, SequencedQueue.size()); + SequencedBase = NextOutSeq; + break; + } + LogWrite(PACKET__DEBUG, 9, "Packet", "Removing acked packet with sequence %u", (unsigned long)SequencedBase); + //clean out the acked packet + delete SequencedQueue.front(); + SequencedQueue.pop_front(); + //advance the base sequence number to the seq of the block after the one we just got rid of. + SequencedBase++; + } + if (uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + LogWrite(PACKET__DEBUG, 9, "Packet", "Post-Ack on %u Invalid Sequenced queue: BS %u + SQ %u != NOS %u", seq, SequencedBase, SequencedQueue.size(), NextOutSeq); + } + } + + MOutboundQueue.unlock(); +} + +void EQStream::Write(int eq_fd) +{ + queue ReadyToSend; + long maxack; + + // Check our rate to make sure we can send more + MRate.lock(); + sint32 threshold=RateThreshold; + MRate.unlock(); + if (BytesWritten > threshold) { + //cout << "Over threshold: " << BytesWritten << " > " << threshold << endl; + return; + } + + MCombinedAppPacket.lock(); + EQApplicationPacket *CombPack=CombinedAppPacket; + CombinedAppPacket=NULL; + MCombinedAppPacket.unlock(); + + if (CombPack) { + SendPacket(CombPack); + } + + // If we got more packets to we need to ack, send an ack on the highest one + MAcks.lock(); + maxack=MaxAckReceived; + // Added from peaks findings + if (NextAckToSend>LastAckSent || LastAckSent == 0x0000ffff) + SendAck(NextAckToSend); + MAcks.unlock(); + + // Lock the outbound queues while we process + MOutboundQueue.lock(); + + // Adjust where we start sending in case we get a late ack + //if (maxack>LastSeqSent) + // LastSeqSent=maxack; + + // Place to hold the base packet t combine into + EQProtocolPacket *p=NULL; + std::deque::iterator sitr; + + // Find the next sequenced packet to send from the "queue" + sitr = SequencedQueue.begin(); + + uint16 count = 0; + // get to start of packets + while (sitr != SequencedQueue.end() && (*sitr)->sent_time > 0) { + ++sitr; + ++count; + } + + bool SeqEmpty = false, NonSeqEmpty = false; + // Loop until both are empty or MaxSends is reached + while (!SeqEmpty || !NonSeqEmpty) { + + // See if there are more non-sequenced packets left + if (!NonSequencedQueue.empty()) { + if (!p) { + // If we don't have a packet to try to combine into, use this one as the base + // And remove it form the queue + p = NonSequencedQueue.front(); + LogWrite(PACKET__DEBUG, 9, "Packet", "Starting combined packet with non-seq packet of len %u",p->size); + NonSequencedQueue.pop(); + } + else if (!p->combine(NonSequencedQueue.front())) { + // Trying to combine this packet with the base didn't work (too big maybe) + // So just send the base packet (we'll try this packet again later) + LogWrite(PACKET__DEBUG, 9, "Packet", "Combined packet full at len %u, next non-seq packet is len %u", p->size, (NonSequencedQueue.front())->size); + ReadyToSend.push(p); + BytesWritten += p->size; + p = nullptr; + + if (BytesWritten > threshold) { + // Sent enough this round, lets stop to be fair + LogWrite(PACKET__DEBUG, 9, "Packet", "Exceeded write threshold in nonseq (%u > %u)", BytesWritten, threshold); + break; + } + } + else { + // Combine worked, so just remove this packet and it's spot in the queue + LogWrite(PACKET__DEBUG, 9, "Packet", "Combined non-seq packet of len %u, yeilding %u combined", (NonSequencedQueue.front())->size, p->size); + delete NonSequencedQueue.front(); + NonSequencedQueue.pop(); + } + } + else { + // No more non-sequenced packets + NonSeqEmpty = true; + } + + if (sitr != SequencedQueue.end()) { + uint16 seq_send = SequencedBase + count; //just for logging... + + if (SequencedQueue.empty()) { + LogWrite(PACKET__DEBUG, 9, "Packet", "Tried to write a packet with an empty queue (%u is past next out %u)", seq_send, NextOutSeq); + SeqEmpty = true; + continue; + } + + if ((*sitr)->acked || (*sitr)->sent_time != 0) { + ++sitr; + ++count; + if (p) { + LogWrite(PACKET__DEBUG, 9, "Packet", "Final combined packet not full, len %u", p->size); + ReadyToSend.push(p); + BytesWritten += p->size; + p = nullptr; + } + LogWrite(PACKET__DEBUG, 9, "Packet", "Not retransmitting seq packet %u because already marked as acked", seq_send); + } + else if (!p) { + // If we don't have a packet to try to combine into, use this one as the base + // Copy it first as it will still live until it is acked + p = (*sitr)->Copy(); + LogWrite(PACKET__DEBUG, 9, "Packet", "Starting combined packet with seq packet %u of len %u", seq_send, p->size); + (*sitr)->sent_time = Timer::GetCurrentTime2(); + ++sitr; + ++count; + } + else if (!p->combine(*sitr)) { + // Trying to combine this packet with the base didn't work (too big maybe) + // So just send the base packet (we'll try this packet again later) + LogWrite(PACKET__DEBUG, 9, "Packet", "Combined packet full at len %u, next seq packet %u is len %u", p->size, seq_send + 1, (*sitr)->size); + ReadyToSend.push(p); + BytesWritten += p->size; + p = nullptr; + if ((*sitr)->opcode != OP_Fragment && BytesWritten > threshold) { + // Sent enough this round, lets stop to be fair + LogWrite(PACKET__DEBUG, 9, "Packet", "Exceeded write threshold in seq (%u > %u)", BytesWritten, threshold); + break; + } + } + else { + // Combine worked + LogWrite(PACKET__DEBUG, 9, "Packet", "Combined seq packet %u of len %u, yeilding %u combined", seq_send, (*sitr)->size, p->size); + (*sitr)->sent_time = Timer::GetCurrentTime2(); + ++sitr; + ++count; + } + + if (uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + LogWrite(PACKET__DEBUG, 9, "Packet", "Post send Invalid Sequenced queue: BS %u + SQ %u != NOS %u", SequencedBase, SequencedQueue.size(), NextOutSeq); + } + } + else { + // No more sequenced packets + SeqEmpty = true; + } + } + MOutboundQueue.unlock(); // Unlock the queue + + // We have a packet still, must have run out of both seq and non-seq, so send it + if (p) { + LogWrite(PACKET__DEBUG, 9, "Packet", "Final combined packet not full, len %u", p->size); + ReadyToSend.push(p); + BytesWritten += p->size; + } + + // Send all the packets we "made" + while (!ReadyToSend.empty()) { + p = ReadyToSend.front(); + WritePacket(eq_fd, p); + delete p; + ReadyToSend.pop(); + } + + //see if we need to send our disconnect and finish our close + if (SeqEmpty && NonSeqEmpty) { + //no more data to send + if (GetState() == CLOSING) { + MOutboundQueue.lock(); + if (SequencedQueue.size() > 0 ) { + // retransmission attempts + } + else + { + LogWrite(PACKET__DEBUG, 9, "Packet", "All outgoing data flushed, disconnecting client."); + //we are waiting for the queues to empty, now we can do our disconnect. + //this packet will not actually go out until the next call to Write(). + SendDisconnect(); + //SetState(CLOSED); + } + MOutboundQueue.unlock(); + } + } +} + +void EQStream::WritePacket(int eq_fd, EQProtocolPacket *p) +{ +uint32 length = 0; +sockaddr_in address; + address.sin_family = AF_INET; + address.sin_addr.s_addr=remote_ip; + address.sin_port=remote_port; +#ifdef NOWAY + uint32 ip=address.sin_addr.s_addr; + cout << "Sending to: " + << (int)*(unsigned char *)&ip + << "." << (int)*((unsigned char *)&ip+1) + << "." << (int)*((unsigned char *)&ip+2) + << "." << (int)*((unsigned char *)&ip+3) + << "," << (int)ntohs(address.sin_port) << "(" << p->size << ")" << endl; + + p->DumpRaw(); + cout << "-------------" << endl; +#endif + length=p->serialize(buffer); + if (p->opcode!=OP_SessionRequest && p->opcode!=OP_SessionResponse) { + if (compressed) { + BytesWritten -= p->size; + uint32 newlen=EQProtocolPacket::Compress(buffer,length,write_buffer,2048); + memcpy(buffer,write_buffer,newlen); + length=newlen; + BytesWritten += newlen; + } + if (encoded) { + EQProtocolPacket::ChatEncode(buffer,length,Key); + } + *(uint16 *)(buffer+length)=htons(CRC16(buffer,length,Key)); + length+=2; + } + sent_packets++; + //dump_message_column(buffer,length,"Writer: "); + //cout << "Raw Data:\n"; + //DumpPacket(buffer, length); + sendto(eq_fd,(char *)buffer,length,0,(sockaddr *)&address,sizeof(address)); +} + +EQProtocolPacket *EQStream::Read(int eq_fd, sockaddr_in *from) +{ +int socklen; +int length=0; +unsigned char buffer[2048]; +EQProtocolPacket *p=NULL; +char temp[15]; + + socklen=sizeof(sockaddr); +#ifdef WIN32 + length=recvfrom(eq_fd, (char *)buffer, 2048, 0, (struct sockaddr*)from, (int *)&socklen); +#else + length=recvfrom(eq_fd, buffer, 2048, 0, (struct sockaddr*)from, (socklen_t *)&socklen); +#endif + if (length>=2) { + DumpPacket(buffer, length); + p=new EQProtocolPacket(buffer[1],&buffer[2],length-2); + //printf("Read packet: opcode %i length %u, expected-length: %u\n",buffer[1], length, p->size); + uint32 ip=from->sin_addr.s_addr; + sprintf(temp,"%d.%d.%d.%d:%d", + *(unsigned char *)&ip, + *((unsigned char *)&ip+1), + *((unsigned char *)&ip+2), + *((unsigned char *)&ip+3), + ntohs(from->sin_port)); + //cout << timestamp() << "Data from: " << temp << " OpCode 0x" << hex << setw(2) << setfill('0') << (int)p->opcode << dec << endl; + //dump_message(p->pBuffer,p->size,timestamp()); + + } + return p; +} + +void EQStream::SendSessionResponse() +{ +EQProtocolPacket *out=new EQProtocolPacket(OP_SessionResponse,NULL,sizeof(SessionResponse)); + SessionResponse *Response=(SessionResponse *)out->pBuffer; + Response->Session=htonl(Session); + Response->MaxLength=htonl(MaxLen); + Response->UnknownA=2; + Response->Format=0; + if (compressed) + Response->Format|=FLAG_COMPRESSED; + if (encoded) + Response->Format|=FLAG_ENCODED; + Response->Key=htonl(Key); + + out->size=sizeof(SessionResponse); + + NonSequencedPush(out); +} + +void EQStream::SendSessionRequest() +{ + EQProtocolPacket *out=new EQProtocolPacket(OP_SessionRequest,NULL,sizeof(SessionRequest)); + SessionRequest *Request=(SessionRequest *)out->pBuffer; + memset(Request,0,sizeof(SessionRequest)); + Request->Session=htonl(time(NULL)); + Request->MaxLength=htonl(512); + + NonSequencedPush(out); +} + +void EQStream::SendDisconnect(bool setstate) +{ + try{ + if(GetState() != ESTABLISHED && GetState() != WAIT_CLOSE) + return; + + EQProtocolPacket *out=new EQProtocolPacket(OP_SessionDisconnect,NULL,sizeof(uint32)+sizeof(int16)); + *(uint32 *)out->pBuffer=htonl(Session); + out->pBuffer[4] = 0; + out->pBuffer[5] = 6; + NonSequencedPush(out); + if(setstate) + SetState(CLOSING); + } + catch(...){} +} + +void EQStream::InboundQueuePush(EQApplicationPacket *p) +{ + MInboundQueue.lock(); + InboundQueue.push_back(p); + MInboundQueue.unlock(); +} + +EQApplicationPacket *EQStream::PopPacket() +{ +EQApplicationPacket *p=NULL; + + MInboundQueue.lock(); + if (InboundQueue.size()) { + p=InboundQueue.front(); + InboundQueue.pop_front(); + } + MInboundQueue.unlock(); + if(p) + p->setVersion(client_version); + return p; +} + +void EQStream::InboundQueueClear() +{ + MInboundQueue.lock(); + while(InboundQueue.size()){ + delete InboundQueue.front(); + InboundQueue.pop_front(); + } + MInboundQueue.unlock(); +} +void EQStream::EncryptPacket(uchar* data, int16 size){ + if(size>6){ + + } +} +bool EQStream::HasOutgoingData() +{ +bool flag; + + //once closed, we have nothing more to say + if(CheckClosed()) + return(false); + + MOutboundQueue.lock(); + flag=(!NonSequencedQueue.empty()); + if (!flag) { + flag = (!SequencedQueue.empty()); + } + MOutboundQueue.unlock(); + + if (!flag) { + MAcks.lock(); + flag= (NextAckToSend>LastAckSent); + MAcks.unlock(); + } + + if (!flag) { + MCombinedAppPacket.lock(); + flag=(CombinedAppPacket!=NULL); + MCombinedAppPacket.unlock(); + } + + return flag; +} + +void EQStream::OutboundQueueClear() +{ + MOutboundQueue.lock(); + while(NonSequencedQueue.size()) { + delete NonSequencedQueue.front(); + NonSequencedQueue.pop(); + } + while(SequencedQueue.size()) { + delete SequencedQueue.front(); + SequencedQueue.pop_front(); + } + MOutboundQueue.unlock(); +} + +void EQStream::Process(const unsigned char *buffer, const uint32 length) +{ + received_packets++; +static unsigned char newbuffer[2048]; +uint32 newlength=0; + +#ifdef LE_DEBUG +printf("ProcessBuffer:\n"); +DumpPacket(buffer, length); +#endif + + if (EQProtocolPacket::ValidateCRC(buffer,length,Key)) { + if (compressed) { + newlength=EQProtocolPacket::Decompress(buffer,length,newbuffer,2048); +#ifdef LE_DEBUG + printf("ProcessBufferDecompress:\n"); + DumpPacket(buffer, newlength); +#endif + } else { + memcpy(newbuffer,buffer,length); + newlength=length; + if (encoded) + EQProtocolPacket::ChatDecode(newbuffer,newlength-2,Key); + } + +#ifdef LE_DEBUG + printf("ResultProcessBuffer:\n"); + DumpPacket(buffer, newlength); +#endif + uint16 opcode=ntohs(*(const uint16 *)newbuffer); + //printf("Read packet: opcode %i newlength %u, newbuffer2len: %u, newbuffer3len: %u\n",opcode, newlength, newbuffer[2], newbuffer[3]); + if(opcode > 0 && opcode <= OP_OutOfSession) + { + if (buffer[1]!=0x01 && buffer[1]!=0x02 && buffer[1]!=0x1d) + newlength-=2; + + EQProtocolPacket p(newbuffer,newlength); + ProcessPacket(&p); + } + else + { + cout << "2Orig Packet: " << opcode << endl; + DumpPacket(newbuffer, newlength); + ProcessEmbeddedPacket(newbuffer, newlength, OP_Fragment); + } + ProcessQueue(); + } else { + cout << "Incoming packet failed checksum:" <(buffer),length,"CRC failed: "); + } +} + +long EQStream::GetMaxAckReceived() +{ + MAcks.lock(); + long l=MaxAckReceived; + MAcks.unlock(); + + return l; +} + +long EQStream::GetNextAckToSend() +{ + MAcks.lock(); + long l=NextAckToSend; + MAcks.unlock(); + + return l; +} + +long EQStream::GetLastAckSent() +{ + MAcks.lock(); + long l=LastAckSent; + MAcks.unlock(); + + return l; +} + +void EQStream::SetMaxAckReceived(uint32 seq) +{ + deque::iterator itr; + + MAcks.lock(); + MaxAckReceived=seq; + MAcks.unlock(); + MOutboundQueue.lock(); + if (long(seq) > LastSeqSent) + LastSeqSent=seq; + MResendQue.lock(); + EQProtocolPacket* packet = 0; + for(itr=resend_que.begin();itr!=resend_que.end();itr++){ + packet = *itr; + if(packet && packet->sequence <= seq){ + safe_delete(packet); + itr = resend_que.erase(itr); + if(itr == resend_que.end()) + break; + } + } + MResendQue.unlock(); + MOutboundQueue.unlock(); +} + +void EQStream::SetNextAckToSend(uint32 seq) +{ + MAcks.lock(); + NextAckToSend=seq; + MAcks.unlock(); +} + +void EQStream::SetLastAckSent(uint32 seq) +{ + MAcks.lock(); + LastAckSent=seq; + MAcks.unlock(); +} + +void EQStream::SetLastSeqSent(uint32 seq) +{ + MOutboundQueue.lock(); + LastSeqSent=seq; + MOutboundQueue.unlock(); +} + +void EQStream::SetStreamType(EQStreamType type) +{ + StreamType=type; + switch (StreamType) { + case LoginStream: + app_opcode_size=1; + compressed=false; + encoded=false; + break; + case EQ2Stream: + app_opcode_size=2; + compressed=false; + encoded=false; + break; + case ChatOrMailStream: + case ChatStream: + case MailStream: + app_opcode_size=1; + compressed=false; + encoded=true; + break; + case ZoneStream: + case WorldStream: + default: + app_opcode_size=2; + compressed=true; + encoded=false; + break; + } +} + +void EQStream::ProcessQueue() +{ + if (OutOfOrderpackets.empty()) { + return; + } + + EQProtocolPacket* qp = NULL; + while ((qp = RemoveQueue(NextInSeq)) != NULL) { + //_log(NET__DEBUG, _L "Processing Queued Packet: Seq=%d" __L, NextInSeq); + ProcessPacket(qp); + delete qp; + //_log(NET__APP_TRACE, _L "OP_Packet Queue size=%d" __L, PacketQueue.size()); + } +} + +EQProtocolPacket* EQStream::RemoveQueue(uint16 seq) +{ + map::iterator itr; + EQProtocolPacket* qp = NULL; + if ((itr = OutOfOrderpackets.find(seq)) != OutOfOrderpackets.end()) { + qp = itr->second; + OutOfOrderpackets.erase(itr); + //_log(NET__APP_TRACE, _L "OP_Packet Queue size=%d" __L, PacketQueue.size()); + } + return qp; +} + +void EQStream::Decay() +{ + MRate.lock(); + uint32 rate=DecayRate; + MRate.unlock(); + if (BytesWritten>0) { + BytesWritten-=rate; + if (BytesWritten<0) + BytesWritten=0; + } + + int count = 0; + MOutboundQueue.lock(); + for (auto sitr = SequencedQueue.begin(); sitr != SequencedQueue.end(); ++sitr, count++) { + if (!(*sitr)->acked && (*sitr)->sent_time > 0 && ((*sitr)->sent_time + retransmittimeout) < Timer::GetCurrentTime2()) { + (*sitr)->sent_time = 0; + LogWrite(PACKET__DEBUG, 9, "Packet", "Timeout exceeded for seq %u. Flagging packet for retransmission", SequencedBase + count); + } + } + MOutboundQueue.unlock(); +} + +void EQStream::AdjustRates(uint32 average_delta) +{ + if (average_delta && (average_delta <= AVERAGE_DELTA_MAX)) { + MRate.lock(); + AverageDelta = average_delta; + RateThreshold = RATEBASE / average_delta; + DecayRate = DECAYBASE / average_delta; + if (BytesWritten > RateThreshold) + BytesWritten = RateThreshold + DecayRate; + MRate.unlock(); + } + else { + AverageDelta = AVERAGE_DELTA_MAX; + } +} diff --git a/old/common/EQStream.h b/old/common/EQStream.h new file mode 100644 index 0000000..3ce45a8 --- /dev/null +++ b/old/common/EQStream.h @@ -0,0 +1,375 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef _EQPROTOCOL_H +#define _EQPROTOCOL_H + +#include +#include +#include +#include + +#include +#include +#ifndef WIN32 +#include +#endif +#include "EQPacket.h" +#include "Mutex.h" +#include "opcodemgr.h" +#include "misc.h" +#include "Condition.h" +#include "Crypto.h" +#include "zlib.h" +#include "timer.h" +#ifdef WRITE_PACKETS +#include +#endif + +using namespace std; + +typedef enum { + ESTABLISHED, + WAIT_CLOSE, + CLOSING, + DISCONNECTING, + CLOSED +} EQStreamState; + +#define FLAG_COMPRESSED 0x01 +#define FLAG_ENCODED 0x04 + +#define RATEBASE 1048576 // 1 MB +#define DECAYBASE 78642 // RATEBASE/10 + +#ifndef RETRANSMIT_TIMEOUT_MULT +#define RETRANSMIT_TIMEOUT_MULT 3.0 +#endif + +#ifndef RETRANSMIT_TIMEOUT_MAX +#define RETRANSMIT_TIMEOUT_MAX 5000 +#endif + +#ifndef AVERAGE_DELTA_MAX +#define AVERAGE_DELTA_MAX 2500 +#endif + +#pragma pack(1) +struct SessionRequest { + uint32 UnknownA; + uint32 Session; + uint32 MaxLength; +}; + +struct SessionResponse { + uint32 Session; + uint32 Key; + uint8 UnknownA; + uint8 Format; + uint8 UnknownB; + uint32 MaxLength; + uint32 UnknownD; +}; + +//Deltas are in ms, representing round trip times +struct ClientSessionStats { +/*000*/ uint16 RequestID; +/*002*/ uint32 last_local_delta; +/*006*/ uint32 average_delta; +/*010*/ uint32 low_delta; +/*014*/ uint32 high_delta; +/*018*/ uint32 last_remote_delta; +/*022*/ uint64 packets_sent; +/*030*/ uint64 packets_recieved; +/*038*/ +}; + +struct ServerSessionStats { + uint16 RequestID; + uint32 current_time; + uint32 unknown1; + uint32 received_packets; + uint32 unknown2; + uint32 sent_packets; + uint32 unknown3; + uint32 sent_packets2; + uint32 unknown4; + uint32 received_packets2; +}; + +#pragma pack() + +class OpcodeManager; +extern OpcodeManager *EQNetworkOpcodeManager; + +class EQStreamFactory; + +typedef enum { + UnknownStream=0, + LoginStream, + WorldStream, + ZoneStream, + ChatOrMailStream, + ChatStream, + MailStream, + EQ2Stream, +} EQStreamType; + +class EQStream { + protected: + typedef enum { + SeqPast, + SeqInOrder, + SeqFuture + } SeqOrder; + + uint32 received_packets; + uint32 sent_packets; + uint32 remote_ip; + uint16 remote_port; + uint8 buffer[8192]; + unsigned char *oversize_buffer; + uint32 oversize_offset,oversize_length; + unsigned char *rogue_buffer; + uint32 roguebuf_offset,roguebuf_size; + uint8 app_opcode_size; + EQStreamType StreamType; + bool compressed,encoded; + + unsigned char write_buffer[2048]; + + uint32 retransmittimer; + uint32 retransmittimeout; + //uint32 buffer_len; + + uint16 sessionAttempts; + uint16 reconnectAttempt; + bool streamactive; + + uint32 Session, Key; + uint16 NextInSeq; + uint16 NextOutSeq; + uint16 SequencedBase; //the sequence number of SequencedQueue[0] + uint32 MaxLen; + uint16 MaxSends; + int8 timeout_delays; + + uint8 active_users; //how many things are actively using this + Mutex MInUse; + +#ifdef WRITE_PACKETS + FILE* write_packets = NULL; + char GetChar(uchar in); + void WriteToFile(char* pFormat, ...); + void WritePackets(const char* opcodeName, uchar* data, int32 size, bool outgoing); + void WritePackets(EQ2Packet* app, bool outgoing); + Mutex MWritePackets; +#endif + + EQStreamState State; + Mutex MState; + + uint32 LastPacket; + Mutex MVarlock; + + EQApplicationPacket* CombinedAppPacket; + Mutex MCombinedAppPacket; + + long LastSeqSent; + Mutex MLastSeqSent; + void SetLastSeqSent(uint32); + + // Ack sequence tracking. + long MaxAckReceived,NextAckToSend,LastAckSent; + long GetMaxAckReceived(); + long GetNextAckToSend(); + long GetLastAckSent(); + void SetMaxAckReceived(uint32 seq); + void SetNextAckToSend(uint32); + void SetLastAckSent(uint32); + + Mutex MAcks; + + // Packets waiting to be sent + queue NonSequencedQueue; + deque SequencedQueue; + map OutOfOrderpackets; + Mutex MOutboundQueue; + + // Packes waiting to be processed + deque InboundQueue; + Mutex MInboundQueue; + + static uint16 MaxWindowSize; + + sint32 BytesWritten; + + Mutex MRate; + sint32 RateThreshold; + sint32 DecayRate; + uint32 AverageDelta; + + EQStreamFactory *Factory; + + public: + Mutex MCombineQueueLock; + bool CheckCombineQueue(); + deque combine_queue; + Timer* combine_timer; + + Crypto* crypto; + int8 EQ2_Compress(EQ2Packet* app, int8 offset = 3); + z_stream stream; + uchar* stream_buffer; + int32 stream_buffer_size; + bool eq2_compressed; + int8 compressed_offset; + int16 client_version; + int16 GetClientVersion(){ return client_version; } + void SetClientVersion(int16 version){ client_version = version; } + void ResetSessionAttempts() { reconnectAttempt = 0; } + bool HasSessionAttempts() { return reconnectAttempt>0; } + EQStream() { init(); remote_ip = 0; remote_port = 0; State = CLOSED; StreamType = UnknownStream; compressed = true; + encoded = false; app_opcode_size = 2;} + EQStream(sockaddr_in addr); + virtual ~EQStream() { + MOutboundQueue.lock(); + SetState(CLOSED); + MOutboundQueue.unlock(); + RemoveData(); + safe_delete(crypto); + safe_delete(combine_timer); + safe_delete(resend_que_timer); + safe_delete_array(oversize_buffer); + safe_delete_array(rogue_buffer); + deque::iterator cmb; + MCombineQueueLock.lock(); + for (cmb = combine_queue.begin(); cmb != combine_queue.end(); cmb++){ + safe_delete(*cmb); + } + MCombineQueueLock.unlock(); + deflateEnd(&stream); + map::iterator oop; + for (oop = OutOfOrderpackets.begin(); oop != OutOfOrderpackets.end(); oop++){ + safe_delete(oop->second); + } +#ifdef WRITE_PACKETS + if (write_packets) + fclose(write_packets); +#endif + } + inline void SetFactory(EQStreamFactory *f) { Factory=f; } + void init(bool resetSession = true); + void SetMaxLen(uint32 length) { MaxLen=length; } + int8 getTimeoutDelays(){ return timeout_delays; } + void addTimeoutDelay(){ timeout_delays++; } + void EQ2QueuePacket(EQ2Packet* app, bool attempted_combine = false); + void PreparePacket(EQ2Packet* app, int8 offset = 0); + void UnPreparePacket(EQ2Packet* app); + void EncryptPacket(EQ2Packet* app, int8 compress_offset, int8 offset); + void FlushCombinedPacket(); + void SendPacket(EQApplicationPacket *p); + void QueuePacket(EQProtocolPacket *p); + void SendPacket(EQProtocolPacket *p); + vector convert(EQApplicationPacket *p); + void NonSequencedPush(EQProtocolPacket *p); + void SequencedPush(EQProtocolPacket *p); + + Mutex MResendQue; + Mutex MCompressData; + dequeresend_que; + void CheckResend(int eq_fd); + + void AckPackets(uint16 seq); + void Write(int eq_fd); + + void SetActive(bool val) { streamactive = val; } + + void WritePacket(int fd,EQProtocolPacket *p); + + void EncryptPacket(uchar* data, int16 size); + uint32 GetKey() { return Key; } + void SetKey(uint32 k) { Key=k; } + void SetSession(uint32 s) { Session=s; } + void SetLastPacketTime(uint32 t) {LastPacket=t;} + + void Process(const unsigned char *data, const uint32 length); + void ProcessPacket(EQProtocolPacket *p, EQProtocolPacket* lastp=NULL); + + bool ProcessEmbeddedPacket(uchar* pBuffer, uint16 length, int8 opcode = OP_Packet); + bool HandleEmbeddedPacket(EQProtocolPacket *p, int16 offset = 2, int16 length = 0); + + EQProtocolPacket * ProcessEncryptedPacket(EQProtocolPacket *p); + EQProtocolPacket * ProcessEncryptedData(uchar* data, int32 size, int16 opcode); + + virtual void DispatchPacket(EQApplicationPacket *p) { p->DumpRaw(); } + + void SendSessionResponse(); + void SendSessionRequest(); + void SendDisconnect(bool setstate = true); + void SendAck(uint16 seq); + void SendOutOfOrderAck(uint16 seq); + + bool CheckTimeout(uint32 now, uint32 timeout=30) { return (LastPacket && (now-LastPacket) > timeout); } + bool Stale(uint32 now, uint32 timeout=30) { return (LastPacket && (now-LastPacket) > timeout); } + + void InboundQueuePush(EQApplicationPacket *p); + EQApplicationPacket *PopPacket(); // InboundQueuePop + void InboundQueueClear(); + + void OutboundQueueClear(); + bool HasOutgoingData(); + void SendKeyRequest(); + int16 processRSAKey(EQProtocolPacket *p, uint16 subpacket_length = 0); + void RemoveData() { InboundQueueClear(); OutboundQueueClear(); if (CombinedAppPacket) delete CombinedAppPacket; } + + // + inline bool IsInUse() { bool flag; MInUse.lock(); flag=(active_users>0); MInUse.unlock(); return flag; } + inline void PutInUse() { MInUse.lock(); active_users++; MInUse.unlock(); } + inline void ReleaseFromUse() { MInUse.lock(); if(active_users > 0) active_users--; MInUse.unlock(); } + + static SeqOrder CompareSequence(uint16 expected_seq, uint16 seq); + + inline EQStreamState GetState() { return State; } + inline void SetState(EQStreamState state) { MState.lock(); State = state; MState.unlock(); } + + inline uint32 GetRemoteIP() { return remote_ip; } + inline uint32 GetrIP() { return remote_ip; } + inline uint16 GetRemotePort() { return remote_port; } + inline uint16 GetrPort() { return remote_port; } + + + static EQProtocolPacket *Read(int eq_fd, sockaddr_in *from); + + void Close() { SendDisconnect(); } + bool CheckActive() { return (GetState()==ESTABLISHED); } + bool CheckClosed() { return GetState()==CLOSED; } + void SetOpcodeSize(uint8 s) { app_opcode_size = s; } + void SetStreamType(EQStreamType t); + inline const EQStreamType GetStreamType() const { return StreamType; } + + void ProcessQueue(); + EQProtocolPacket* RemoveQueue(uint16 seq); + + void Decay(); + void AdjustRates(uint32 average_delta); + Timer* resend_que_timer; +}; + +#endif diff --git a/old/common/EQStreamFactory.cpp b/old/common/EQStreamFactory.cpp new file mode 100644 index 0000000..965865e --- /dev/null +++ b/old/common/EQStreamFactory.cpp @@ -0,0 +1,444 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ + +#include "EQStreamFactory.h" +#include "Log.h" + +#ifdef WIN32 + #include + #include + #include + #include + #include +#else + #include + #include + #include + #include + #include + #include +#endif +#include +#include +#include +#include "op_codes.h" +#include "EQStream.h" +#include "packet_dump.h" +#ifdef WORLD + #include "../WorldServer/client.h" +#endif +using namespace std; + +#ifdef WORLD + extern ClientList client_list; +#endif +ThreadReturnType EQStreamFactoryReaderLoop(void *eqfs) +{ + if(eqfs){ + EQStreamFactory *fs=(EQStreamFactory *)eqfs; + fs->ReaderLoop(); + } + THREAD_RETURN(NULL); +} + +ThreadReturnType EQStreamFactoryWriterLoop(void *eqfs) +{ + if(eqfs){ + EQStreamFactory *fs=(EQStreamFactory *)eqfs; + fs->WriterLoop(); + } + THREAD_RETURN(NULL); +} + +ThreadReturnType EQStreamFactoryCombinePacketLoop(void *eqfs) +{ + if(eqfs){ + EQStreamFactory *fs=(EQStreamFactory *)eqfs; + fs->CombinePacketLoop(); + } + THREAD_RETURN(NULL); +} + +EQStreamFactory::EQStreamFactory(EQStreamType type, int port) +{ + StreamType=type; + Port=port; + listen_ip_address = 0; +} + +void EQStreamFactory::Close() +{ + CheckTimeout(true); + Stop(); + if (sock != -1) { +#ifdef WIN32 + closesocket(sock); +#else + close(sock); +#endif + sock = -1; + } +} +bool EQStreamFactory::Open() +{ +struct sockaddr_in address; +#ifndef WIN32 + pthread_t t1, t2, t3; +#endif + /* Setup internet address information. + This is used with the bind() call */ + memset((char *) &address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_port = htons(Port); +#if defined(LOGIN) || defined(MINILOGIN) + if(listen_ip_address) + address.sin_addr.s_addr = inet_addr(listen_ip_address); + else + address.sin_addr.s_addr = htonl(INADDR_ANY); +#else + address.sin_addr.s_addr = htonl(INADDR_ANY); +#endif + /* Setting up UDP port for new clients */ + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + return false; + } + + if (::bind(sock, (struct sockaddr *) &address, sizeof(address)) < 0) { + //close(sock); + sock=-1; + return false; + } + #ifdef WIN32 + unsigned long nonblock = 1; + ioctlsocket(sock, FIONBIO, &nonblock); + #else + fcntl(sock, F_SETFL, O_NONBLOCK); + #endif + //moved these because on windows the output was delayed and causing the console window to look bad +#ifdef LOGIN + LogWrite(LOGIN__DEBUG, 0, "Login", "Starting factory Reader"); + LogWrite(LOGIN__DEBUG, 0, "Login", "Starting factory Writer"); +#elif WORLD + LogWrite(WORLD__DEBUG, 0, "World", "Starting factory Reader"); + LogWrite(WORLD__DEBUG, 0, "World", "Starting factory Writer"); +#endif + #ifdef WIN32 + _beginthread(EQStreamFactoryReaderLoop,0, this); + _beginthread(EQStreamFactoryWriterLoop,0, this); + _beginthread(EQStreamFactoryCombinePacketLoop,0, this); + #else + pthread_create(&t1,NULL,EQStreamFactoryReaderLoop,this); + pthread_create(&t2,NULL,EQStreamFactoryWriterLoop,this); + pthread_create(&t3,NULL,EQStreamFactoryCombinePacketLoop,this); + pthread_detach(t1); + pthread_detach(t2); + pthread_detach(t3); + #endif + return true; +} + +EQStream *EQStreamFactory::Pop() +{ + if (!NewStreams.size()) + return NULL; + +EQStream *s=NULL; + //cout << "Pop():Locking MNewStreams" << endl; + MNewStreams.lock(); + if (NewStreams.size()) { + s=NewStreams.front(); + NewStreams.pop(); + s->PutInUse(); + } + MNewStreams.unlock(); + //cout << "Pop(): Unlocking MNewStreams" << endl; + + return s; +} + +void EQStreamFactory::Push(EQStream *s) +{ + //cout << "Push():Locking MNewStreams" << endl; + MNewStreams.lock(); + NewStreams.push(s); + MNewStreams.unlock(); + //cout << "Push(): Unlocking MNewStreams" << endl; +} + +void EQStreamFactory::ReaderLoop() +{ +fd_set readset; +map::iterator stream_itr; +int num; +int length; +unsigned char buffer[2048]; +sockaddr_in from; +int socklen=sizeof(sockaddr_in); +timeval sleep_time; + ReaderRunning=true; + while(sock!=-1) { + MReaderRunning.lock(); + if (!ReaderRunning) + break; + MReaderRunning.unlock(); + + FD_ZERO(&readset); + FD_SET(sock,&readset); + + sleep_time.tv_sec=30; + sleep_time.tv_usec=0; + if ((num=select(sock+1,&readset,NULL,NULL,&sleep_time))<0) { + // What do we wanna do? + } else if (num==0) + continue; + + if (FD_ISSET(sock,&readset)) { +#ifdef WIN32 + if ((length=recvfrom(sock,(char*)buffer,sizeof(buffer),0,(struct sockaddr*)&from,(int *)&socklen))<2) +#else + if ((length=recvfrom(sock,buffer,2048,0,(struct sockaddr *)&from,(socklen_t *)&socklen))<2) +#endif + { + // What do we wanna do? + } else { + char temp[25]; + sprintf(temp,"%u.%d",ntohl(from.sin_addr.s_addr),ntohs(from.sin_port)); + MStreams.lock(); + if ((stream_itr=Streams.find(temp))==Streams.end() || buffer[1]==OP_SessionRequest) { + MStreams.unlock(); + if (buffer[1]==OP_SessionRequest) { + if(stream_itr != Streams.end() && stream_itr->second) + stream_itr->second->SetState(CLOSED); + EQStream *s=new EQStream(from); + s->SetFactory(this); + s->SetStreamType(StreamType); + Streams[temp]=s; + WriterWork.Signal(); + Push(s); + s->Process(buffer,length); + s->SetLastPacketTime(Timer::GetCurrentTime2()); + } + } else { + EQStream *curstream = stream_itr->second; + //dont bother processing incoming packets for closed connections + if(curstream->CheckClosed()) + curstream = NULL; + else + curstream->PutInUse(); + MStreams.unlock(); + + if(curstream) { + curstream->Process(buffer,length); + curstream->SetLastPacketTime(Timer::GetCurrentTime2()); + curstream->ReleaseFromUse(); + } + } + } + } + } +} + +void EQStreamFactory::CheckTimeout(bool remove_all) +{ + //lock streams the entire time were checking timeouts, it should be fast. + MStreams.lock(); + + unsigned long now=Timer::GetCurrentTime2(); + map::iterator stream_itr; + + for(stream_itr=Streams.begin();stream_itr!=Streams.end();) { + EQStream *s = stream_itr->second; + EQStreamState state = s->GetState(); + + if (state==CLOSING && !s->HasOutgoingData()) { + stream_itr->second->SetState(CLOSED); + state = CLOSED; + } else if (s->CheckTimeout(now, STREAM_TIMEOUT)) { + const char* stateString; + switch (state){ + case ESTABLISHED: + stateString = "Established"; + break; + case CLOSING: + stateString = "Closing"; + break; + case CLOSED: + stateString = "Closed"; + break; + case WAIT_CLOSE: + stateString = "Wait-Close"; + break; + default: + stateString = "Unknown"; + break; + } + LogWrite(WORLD__DEBUG, 0, "World", "Timeout up!, state=%s (%u)", stateString, state); + if (state==ESTABLISHED) { + s->Close(); + } + else if (state == WAIT_CLOSE) { + s->SetState(CLOSING); + state = CLOSING; + } + else if (state == CLOSING) { + //if we time out in the closing state, just give up + s->SetState(CLOSED); + state = CLOSED; + } + } + //not part of the else so we check it right away on state change + if (remove_all || state==CLOSED) { + if (!remove_all && s->getTimeoutDelays()<2) { + s->addTimeoutDelay(); + //give it a little time for everybody to finish with it + } else { + //everybody is done, we can delete it now + +#ifdef LOGIN + LogWrite(LOGIN__DEBUG, 0, "Login", "Removing connection..."); +#else + LogWrite(WORLD__DEBUG, 0, "World", "Removing connection..."); +#endif + map::iterator temp=stream_itr; + stream_itr++; + //let whoever has the stream outside delete it + #ifdef WORLD + client_list.RemoveConnection(temp->second); + #endif + EQStream* stream = temp->second; + Streams.erase(temp); + delete stream; + continue; + } + } + + stream_itr++; + } + MStreams.unlock(); +} + +void EQStreamFactory::CombinePacketLoop(){ + deque combine_que; + CombinePacketRunning = true; + bool packets_waiting = false; + while(sock!=-1) { + if (!CombinePacketRunning) + break; + MStreams.lock(); + map::iterator stream_itr; + for(stream_itr=Streams.begin();stream_itr!=Streams.end();stream_itr++) { + if(!stream_itr->second){ + continue; + } + if(stream_itr->second->combine_timer && stream_itr->second->combine_timer->Check()) + combine_que.push_back(stream_itr->second); + } + EQStream* stream = 0; + packets_waiting = false; + while(combine_que.size()){ + stream = combine_que.front(); + if(stream->CheckActive()){ + if(!stream->CheckCombineQueue()) + packets_waiting = true; + } + combine_que.pop_front(); + } + MStreams.unlock(); + if(!packets_waiting) + Sleep(25); + + Sleep(1); + } +} + +void EQStreamFactory::WriterLoop() +{ +map::iterator stream_itr; +vector wants_write; +vector::iterator cur,end; +deque resend_que; +bool decay=false; +uint32 stream_count; + +Timer DecayTimer(20); + + WriterRunning=true; + DecayTimer.Enable(); + while(sock!=-1) { + Timer::SetCurrentTime(); + //if (!havework) { + //WriterWork.Wait(); + //} + MWriterRunning.lock(); + if (!WriterRunning) + break; + MWriterRunning.unlock(); + + wants_write.clear(); + + decay=DecayTimer.Check(); + + //copy streams into a seperate list so we dont have to keep + //MStreams locked while we are writting + MStreams.lock(); + for(stream_itr=Streams.begin();stream_itr!=Streams.end();stream_itr++) { + // If it's time to decay the bytes sent, then let's do it before we try to write + if(!stream_itr->second){ + Streams.erase(stream_itr); + break; + } + if (decay) + stream_itr->second->Decay(); + + if (stream_itr->second->HasOutgoingData()) { + stream_itr->second->PutInUse(); + wants_write.push_back(stream_itr->second); + } + if(stream_itr->second->resend_que_timer->Check()) + resend_que.push_back(stream_itr->second); + } + MStreams.unlock(); + + //do the actual writes + cur = wants_write.begin(); + end = wants_write.end(); + for(; cur != end; cur++) { + (*cur)->Write(sock); + (*cur)->ReleaseFromUse(); + } + while(resend_que.size()){ + resend_que.front()->CheckResend(sock); + resend_que.pop_front(); + } + Sleep(10); + + MStreams.lock(); + stream_count=Streams.size(); + MStreams.unlock(); + if (!stream_count) { + //cout << "No streams, waiting on condition" << endl; + WriterWork.Wait(); + //cout << "Awake from condition, must have a stream now" << endl; + } + } +} + + diff --git a/old/common/EQStreamFactory.h b/old/common/EQStreamFactory.h new file mode 100644 index 0000000..9dce3c7 --- /dev/null +++ b/old/common/EQStreamFactory.h @@ -0,0 +1,86 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef _EQSTREAMFACTORY_H + +#define _EQSTREAMFACTORY_H + +#include +#include +#include "../common/EQStream.h" +#include "../common/Condition.h" +#include "../common/opcodemgr.h" +#include "../common/timer.h" + +#define STREAM_TIMEOUT 45000 //in ms + +class EQStreamFactory { + private: + int sock; + int Port; + + bool ReaderRunning; + Mutex MReaderRunning; + bool WriterRunning; + Mutex MWriterRunning; + bool CombinePacketRunning; + Mutex MCombinePacketRunning; + + Condition WriterWork; + + EQStreamType StreamType; + + queue NewStreams; + Mutex MNewStreams; + + map Streams; + Mutex MStreams; + + + + Timer *DecayTimer; + + public: + char* listen_ip_address; + void CheckTimeout(bool remove_all = false); + EQStreamFactory(EQStreamType type) { ReaderRunning=false; WriterRunning=false; StreamType=type; } + EQStreamFactory(EQStreamType type, int port); + ~EQStreamFactory(){ + safe_delete_array(listen_ip_address); + } + + EQStream *Pop(); + void Push(EQStream *s); + + bool loadPublicKey(); + bool Open(); + bool Open(unsigned long port) { Port=port; return Open(); } + void Close(); + void ReaderLoop(); + void WriterLoop(); + void CombinePacketLoop(); + void Stop() { StopReader(); StopWriter(); StopCombinePacket(); } + void StopReader() { MReaderRunning.lock(); ReaderRunning=false; MReaderRunning.unlock(); } + void StopWriter() { MWriterRunning.lock(); WriterRunning=false; MWriterRunning.unlock(); WriterWork.Signal(); } + void StopCombinePacket() { MCombinePacketRunning.lock(); CombinePacketRunning=false; MCombinePacketRunning.unlock(); } + void SignalWriter() { WriterWork.Signal(); } + +}; + +#endif diff --git a/old/common/GlobalHeaders.h b/old/common/GlobalHeaders.h new file mode 100644 index 0000000..98b7458 --- /dev/null +++ b/old/common/GlobalHeaders.h @@ -0,0 +1,58 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ + +//Character Creation Replies, put in globals so name filter can return proper responses +#define UNKNOWNERROR_REPLY 0 +#define CREATESUCCESS_REPLY 1 +#define NOSERVERSAVAIL_REPLY 2 +#define CREATEPENDING_REPLY 3 +#define MAXCHARSALLOWED_REPLY 4 +#define INVALIDRACE_REPLY 5 +#define INVALIDCITY_REPLY 6 +#define INVALIDCLASS_REPLY 7 +#define INVALIDGENDER_REPLY 8 +#define INVALIDFIRST_LVL_REPLY 9 +#define BADNAMELENGTH_REPLY 10 +#define NAMEINVALID_REPLY 11 +#define NAMEFILTER_REPLY 12 // name_filter reply (bad word or blocked words) +#define NAMETAKEN_REPLY 13 +#define OVERLOADEDSERVER_REPLY 14 +#define UNKNOWNERROR_REPLY2 15 +#define INVALIDFEATURES1_REPLY 16 +#define INVALIDFEATURES2_REPLY 17 +#define INVALIDRACE_APPEARANCE_REPLY 18 + +#define PLAY_ERROR_PROBLEM 0 +#define PLAY_ERROR_ZONE_DOWN 4 +#define PLAY_ERROR_CHAR_NOT_LOADED 5 +#define PLAY_ERROR_CHAR_NOT_FOUND 6 +#define PLAY_ERROR_ACCOUNT_IN_USE 7 +#define PLAY_ERROR_SERVER_TIMEOUT 8 +#define PLAY_ERROR_SERVER_SHUTDOWN 9 +#define PLAY_ERROR_LOADING_ERROR 10 +#define PLAY_ERROR_EXCHANGE_SERVER 11 +#define PLAY_ERROR_REGION_SERVER 12 +#define PLAY_ERROR_CLASS_INVALID 13 +#define PLAY_ERROR_TOO_MANY_CHARACTERS 14 +#define PLAY_ERROR_EOF_EXP_NOT_FOUND 15 +#define PLAY_ERROR_UNKNOWN_RESPONSE 16 +#define PLAY_ERROR_UNKNOWN 17 +#define PLAY_ERROR_ACCOUNT_BANNED 18 +#define PLAY_ERROR_PROHIBITED 19 diff --git a/old/common/JsonParser.cpp b/old/common/JsonParser.cpp new file mode 100644 index 0000000..cca065a --- /dev/null +++ b/old/common/JsonParser.cpp @@ -0,0 +1,97 @@ +#include "JsonParser.h" + +JsonParser::JsonParser(const std::string &filename) { + is_loaded = false; + try { + boost::property_tree::read_json(filename, pt); + parseTree(pt, ""); + is_loaded = true; + } catch (const boost::property_tree::json_parser_error &e) { + std::cerr << "Error reading JSON file: " << e.what() << std::endl; + } +} + +bool JsonParser::convertStringToUnsignedChar(const std::string& str, unsigned char& result) { + unsigned long ul; + try { + ul = std::stoul(str); + } catch (const std::invalid_argument&) { + return false; // Not a valid number + } catch (const std::out_of_range&) { + return false; // Number is too large for unsigned long + } + + if (ul > std::numeric_limits::max()) { + return false; // Number is too large for unsigned short + } + + result = static_cast(ul); + return true; +} + +bool JsonParser::convertStringToUnsignedShort(const std::string& str, unsigned short& result) { + unsigned long ul; + try { + ul = std::stoul(str); + } catch (const std::invalid_argument&) { + return false; // Not a valid number + } catch (const std::out_of_range&) { + return false; // Number is too large for unsigned long + } + + if (ul > std::numeric_limits::max()) { + return false; // Number is too large for unsigned short + } + + result = static_cast(ul); + return true; +} + +bool JsonParser::convertStringToUnsignedInt(const std::string& str, unsigned int& result) { + unsigned long ul; + try { + ul = std::stoul(str); + } catch (const std::invalid_argument&) { + return false; // Not a valid number + } catch (const std::out_of_range&) { + return false; // Number is too large for unsigned long + } + + if (ul > std::numeric_limits::max()) { + return false; // Number is too large for unsigned short + } + + result = static_cast(ul); + return true; +} + +bool JsonParser::convertStringToUnsignedLong(const std::string& str, unsigned long& result) { + unsigned long ul; + try { + ul = std::stoul(str); + } catch (const std::invalid_argument&) { + return false; // Not a valid number + } catch (const std::out_of_range&) { + return false; // Number is too large for unsigned long + } + + if (ul > std::numeric_limits::max()) { + return false; // Number is too large for unsigned short + } + + result = ul; + return true; +} + +void JsonParser::parseTree(const boost::property_tree::ptree &tree, const std::string &path) { + for (const auto &node : tree) { + std::string currentPath = path.empty() ? node.first : path + "." + node.first; + if (node.second.empty()) { + std::string name = currentPath; + boost::algorithm::to_lower(name); + values[name] = node.second.get_value(); + } else { + parseTree(node.second, currentPath); + } + } +} \ No newline at end of file diff --git a/old/common/JsonParser.h b/old/common/JsonParser.h new file mode 100644 index 0000000..f92b07d --- /dev/null +++ b/old/common/JsonParser.h @@ -0,0 +1,33 @@ + +#include +#include +#include +#include +#include +#include +#include + +class JsonParser { +public: + JsonParser(const std::string &filename); + + std::string getValue(const std::string &path) const { + auto it = values.find(path); + if (it != values.end()) { + return it->second; + } + return ""; + } + + static bool convertStringToUnsignedChar(const std::string& str, unsigned char& result); + static bool convertStringToUnsignedShort(const std::string& str, unsigned short& result); + static bool convertStringToUnsignedInt(const std::string& str, unsigned int& result); + static bool convertStringToUnsignedLong(const std::string& str, unsigned long& result); + bool IsLoaded() { return is_loaded; } +private: + boost::property_tree::ptree pt; + std::map values; + + void parseTree(const boost::property_tree::ptree &tree, const std::string &path); + bool is_loaded; +}; diff --git a/old/common/Log.cpp b/old/common/Log.cpp new file mode 100644 index 0000000..b74be71 --- /dev/null +++ b/old/common/Log.cpp @@ -0,0 +1,615 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include "Log.h" +#include "xmlParser.h" +#include "types.h" +#include +#include +#include +#include +#include +#include +#include +#include "../WorldServer/World.h" +#include "../WorldServer/client.h" +#include "../WorldServer/zoneserver.h" + +extern ZoneList zone_list; + +#ifdef _WIN32 + #include + #ifndef snprintf + #define snprintf sprintf_s + #endif +#include + #include +#else +#endif + +#define LOG_CATEGORY(category) #category, +const char *log_category_names[NUMBER_OF_LOG_CATEGORIES] = { + #include "LogTypes.h" +}; + +#define LOG_TYPE(category, type, level, color, enabled, logfile, console, client, str) { level, color, enabled, logfile, console, client, LOG_ ##category, #category "__" #type, ( strlen(str)>0 ) ? str : #category "__" #type }, +static LogTypeStatus real_log_type_info[NUMBER_OF_LOG_TYPES+1] = +{ + #include "LogTypes.h" + { 0, 0, false, false, false, false, NUMBER_OF_LOG_CATEGORIES, "BAD TYPE", "Bad Name" } /* dummy trailing record */ +}; + +LogTypeStatus *log_type_info = real_log_type_info; + +//make these rules? +#define LOG_CYCLE 100 //milliseconds between each batch of log writes +#define LOGS_PER_CYCLE 50 //amount of logs to write per cycle + +#define LOG_DIR "logs" + +#if defined LOGIN +#define EXE_NAME "login" +#elif defined WORLD +#define EXE_NAME "world" +#elif defined PARSER +#define EXE_NAME "parser" +#elif defined PATCHER +#define EXE_NAME "patcher" +#else +#define EXE_NAME "whatprogyourunning" +#endif + +#define DATE_MAX 8 +#define LOG_NAME_MAX 32 + +struct logq_t { + LogType log_type; + char date[DATE_MAX + 1]; + char name[LOG_NAME_MAX + 1]; + char *text; + struct logq_t *next; + struct logq_t *prev; +}; + +//doubly linked list of logs +static logq_t head; +static logq_t tail; +static int num_logqs = 0; +static Mutex mlogqs; + +//loop until.... +static bool looping = false; + +//because our code has LogWrite's before main(), make sure any of those do the +//call to LogStart if it hasn't been called already... +static bool start_called = false; + +static void SetConsoleColor(int color) { +#ifdef _WIN32 + HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); + + if (handle == NULL || handle == INVALID_HANDLE_VALUE) + return; +#endif + + switch (color) { + case FOREGROUND_WHITE: + case FOREGROUND_WHITE_BOLD: + case FOREGROUND_RED: + case FOREGROUND_RED_BOLD: + case FOREGROUND_GREEN: + case FOREGROUND_GREEN_BOLD: + case FOREGROUND_BLUE: + case FOREGROUND_BLUE_BOLD: + case FOREGROUND_YELLOW: + case FOREGROUND_YELLOW_BOLD: + case FOREGROUND_CYAN: + case FOREGROUND_CYAN_BOLD: + case FOREGROUND_MAGENTA: + case FOREGROUND_MAGENTA_BOLD: +#ifdef _WIN32 + SetConsoleTextAttribute(handle, color); +#else + printf("\033[%i;%i;40m", color > 100 ? 1 : 0, color > 100 ? color - 100 : color); +#endif + break; + default: +#ifdef _WIN32 + SetConsoleTextAttribute(handle, FOREGROUND_WHITE_BOLD); +#else + printf("\033[0;37;40m"); +#endif + break; + } +} + +static FILE * OpenLogFile() { + char file[FILENAME_MAX + 1]; + struct stat st; + struct tm *tm; + time_t now; + FILE *f; + + now = time(NULL); + tm = localtime(&now); + + //make sure the logs directory exists + if (stat(LOG_DIR, &st) != 0) { +#ifdef _WIN32 + if (!CreateDirectory(LOG_DIR, NULL)) { + fprintf(stderr, "Unable to create directory '%s'\n", LOG_DIR); + return stderr; + } +#else + if (mkdir(LOG_DIR, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { + fprintf(stderr, "Unable to create direcotry '%s': %s\n", LOG_DIR, strerror(errno)); + return stderr; + } +#endif + } + +#ifdef NO_PIDLOG + snprintf(file, FILENAME_MAX, LOG_DIR"/%04i-%02i-%02i_eq2" EXE_NAME ".log", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); +#else + snprintf(file, FILENAME_MAX, LOG_DIR"/%04i-%02i-%02i_eq2" EXE_NAME "_%04i.log", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, getpid()); +#endif + + if ((f = fopen(file, "a")) == NULL) { + fprintf(stderr, "Could not open '%s' for writing: %s\n", file, strerror(errno)); + return stderr; + } + + return f; +} + +static void WriteQueuedLogs(int count) { + logq_t pending_head, pending_tail, *logq, *tmp; + int i = 0; + FILE *f; + + pending_head.next = &pending_tail; + pending_tail.prev = &pending_head; + + //loop through our queued logs and store at most `count` logs into a temporary list + //since io functions are expensive, we'll write from a temporary list so we don't hold the + //write lock of the main list for a long period of time + mlogqs.writelock(); + logq = head.next; + + while (head.next != &tail) { + //first remove the log from the master list + logq = head.next; + logq->next->prev = &head; + head.next = logq->next; + + //now insert it into the temporary list + tmp = pending_tail.prev; + tmp->next = logq; + logq->prev = tmp; + logq->next = &pending_tail; + pending_tail.prev = logq; + --num_logqs; + + logq = logq->next; + + //if we have a limit, check it + if (count > 0 && ++i == count) + break; + } + + //if we have no logs to write, we're done + if ((logq = pending_head.next) == &pending_tail) + { + mlogqs.releasewritelock(); + return; + } + + while (logq != &pending_tail) { + if (log_type_info[logq->log_type].console) { + SetConsoleColor(FOREGROUND_WHITE_BOLD); + printf("%s ", logq->date); + SetConsoleColor(log_type_info[logq->log_type].color); + printf("%s ", log_type_info[logq->log_type].display_name); + SetConsoleColor(FOREGROUND_WHITE_BOLD); + printf("%-10s: ", logq->name); + SetConsoleColor(log_type_info[logq->log_type].color); + printf("%s\n", logq->text); + SetConsoleColor(-1); + fflush(stdout); + } + + if (log_type_info[logq->log_type].logfile) { + f = OpenLogFile(); + + if (f != stderr || (f == stderr && !log_type_info[logq->log_type].console)) { + fprintf(f, "%s %s %s: %s\n", logq->date, log_type_info[logq->log_type].display_name, logq->name, logq->text); + fflush(f); + if (f != stderr) + fclose(f); + } + } + +#if defined WORLD + if (log_type_info[logq->log_type].client) { + // eventually output logging to the client who "subscribed" to the logger + // in-game, they type: + // /logsys add WORLD__DEBUG 5 + // to watch world debug loggers of level 5 or less + } +#endif + + tmp = logq; + logq = logq->next; + + free(tmp->text); + free(tmp); + } + + mlogqs.releasewritelock(); +} + +ThreadReturnType LogLoop(void *args) { + while (looping) { + WriteQueuedLogs(LOGS_PER_CYCLE); + Sleep(LOG_CYCLE); + } + + THREAD_RETURN(NULL); +} + +void LogStart() { + if (start_called) + return; + + //initialize the doubly linked list + head.prev = NULL; + head.next = &tail; + tail.prev = &head; + tail.next = NULL; + + mlogqs.SetName("logqueue"); + looping = true; + +#ifdef _WIN32 + _beginthread(LogLoop, 0, NULL); +#else + pthread_t thread; + pthread_create(&thread, NULL, LogLoop, NULL); + pthread_detach(thread); +#endif + + start_called = true; +} + +void LogStop() { + looping = false; + WriteQueuedLogs(-1); + start_called = false; +} + +static void LogQueueAdd(LogType log_type, char *text, int len, const char *cat_text = NULL) { + logq_t *logq; + struct tm *tm; + time_t now; + + if ((logq = (logq_t *)calloc(1, sizeof(logq_t))) == NULL) { + free(text); + fprintf(stderr, "%s: %u: Unable to allocate %zu bytes\n", __FUNCTION__, __LINE__, sizeof(logq_t)); + return; + } + + if ((logq->text = (char *)calloc(len + 1, sizeof(char))) == NULL) { + free(text); + free(logq); + fprintf(stderr, "%s: %u: Unable to allocate %i bytes\n", __FUNCTION__, __LINE__, len + 1); + return; + } + + now = time(NULL); + tm = localtime(&now); + + logq->log_type = log_type; + snprintf(logq->date, DATE_MAX + 1, "%02i:%02i:%02i", tm->tm_hour, tm->tm_min, tm->tm_sec); + strncpy(logq->name, cat_text == NULL || cat_text[0] == '\0' ? log_type_info[log_type].name : cat_text, LOG_NAME_MAX); + strncpy(logq->text, text, len); + free(text); + + if (!start_called) + LogStart(); + + //insert at the end + mlogqs.writelock(); + tail.prev->next = logq; + logq->prev = tail.prev; + logq->next = &tail; + tail.prev = logq; + ++num_logqs; + mlogqs.releasewritelock(); +} + +int8 GetLoggerLevel(LogType type) +{ + return log_type_info[type].level; +} + +// JA: horrific hack for Parser, since queued logging keeps crashing between parses. +#ifndef PARSER +void LogWrite(LogType type, int8 log_level, const char *cat_text, const char *fmt, ...) { + int count, size = 64; + char *buf; + va_list ap; + + // if there is no formatting, or the logger is DISABLED + // or the log_level param exceeds the minimum allowed value, abort logwrite + if (!log_type_info[type].enabled || (log_level > 0 && log_type_info[type].level < log_level)) + return; + + while (true) { + if ((buf = (char *)malloc(size)) == NULL) { + fprintf(stderr, "%s: %i: Unable to allocate %i bytes\n", __FUNCTION__, __LINE__, size); + return; + } + + va_start(ap, fmt); + count = vsnprintf(buf, size, fmt, ap); + va_end(ap); + + if (count > -1 && count < size) + break; + + free(buf); + if (count > 1) + size = count + 1; + else + size *= 2; + } + + LogQueueAdd(type, buf, count, cat_text); +} +#else +void LogWrite(LogType type, int8 log_level, const char *cat_text, const char *format, ...) +{ + // if there is no formatting, or the logger is DISABLED + // or the log_level param exceeds the minimum allowed value, abort logwrite + if ( !format || !log_type_info[type].enabled || (log_level > 0 && log_type_info[type].level < log_level) ) + return; + + time_t clock; + struct tm *tm; + + char buffer[LOG_BUFFER_SIZE], date[32]; + va_list args; + FILE *f; + size_t cat_text_len = 0; + + memset(buffer, 0, sizeof(buffer)); + memset(date, 0, sizeof(date)); + + va_start(args, format); + vsnprintf(buffer, sizeof(buffer) - 1, format, args); + va_end(args); + + time(&clock); + tm = localtime(&clock); + snprintf(date, sizeof(date)-1, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); + // DateString(date, sizeof(date)); + + cat_text_len = strlen(cat_text); + //if( strlen(cat_text) == 0 ) // cat_text was blank + // cat_text = (char*)log_type_info[type].name; + + /* write to the log file? */ + if (log_type_info[type].logfile) + { + char exename[200] = ""; + + #ifdef LOGIN + snprintf(exename, sizeof(exename), "login"); + #endif + #ifdef WORLD + snprintf(exename, sizeof(exename), "world"); + #endif + #ifdef PARSER + snprintf(exename, sizeof(exename), "parser"); + #endif + #ifdef PATCHER + snprintf(exename, sizeof(exename), "patcher"); + #endif + + char filename[200], log_header[200] = ""; + + #ifndef NO_PIDLOG + snprintf(filename, sizeof(filename)-1, "logs/%04d-%02d-%02d_eq2%s_%04i.log", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, exename, getpid()); + #else + snprintf(filename, sizeof(filename)-1, "logs/%04d-%02d-%02d_eq2%s.log", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, exename); + #endif + + f=fopen(filename, "r"); + if( !f ) + snprintf(log_header, sizeof(log_header), "===[ New log '%s' started ]===\n\n", filename); + else + fclose (f); + + f = fopen(filename, "a"); + if (f) { + if( strlen(log_header) > 0 ) + fprintf(f, "%s\n", log_header); + fprintf(f, "%s %s %s: %s\n", date, log_type_info[type].display_name, cat_text, buffer); + fclose(f); + } + } + + /* write to the console? */ + if (log_type_info[type].console) + { + #ifdef _WIN32 + ColorizeLog(log_type_info[type].color, date, log_type_info[type].display_name, cat_text_len == 0 ? log_type_info[type].name : cat_text, (string)buffer); + #else + printf("%s %s %s: %s\n", date, log_type_info[type].display_name, cat_text_len == 0 ? log_type_info[type].name : cat_text, buffer); + #endif + } +} + +void +ColorizeLog(int color, char *date, const char *display_name, const char *category, string buffer) +{ + #ifdef _WIN32 + HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE); + if (console == INVALID_HANDLE_VALUE) { + printf("%s %s %s: %s\n", date, display_name, category, buffer); + return; + } + printf("%s ", date); + SetConsoleTextAttribute(console, color); + printf("%s ", display_name); + SetConsoleTextAttribute(console, FOREGROUND_WHITE_BOLD); + printf("%s: ", category); + SetConsoleTextAttribute(console, color); + printf("%s\n", buffer.c_str()); + SetConsoleTextAttribute(console, FOREGROUND_WHITE); + #endif +} + +#endif + +LogTypeStatus * +GetLogTypeStatus(const char *category, const char *type) { + char combined[256]; + int i; + + memset(combined, 0, sizeof(combined)); + snprintf(combined, sizeof(combined) - 1, "%s__%s", category, type); + + for (i = 0; i < NUMBER_OF_LOG_TYPES; i++) { + if (strcasecmp(log_type_info[i].name, combined) == 0) + return &log_type_info[i]; + } + + return &log_type_info[NUMBER_OF_LOG_TYPES]; +} + +void +ProcessLogConfig(XMLNode node) { + int i; + const char *category, *type, *level, *color, *enabled, *logs; + LogTypeStatus *lfs; + XMLNode child; + + category = node.getAttribute("Category"); + if (!category) { + LogWrite(MISC__WARNING, 0, "Misc", "Error parsing log config. Config missing a Category"); + return; + } + + for (i = 0; i < node.nChildNode("ConfigType"); i++) { + child = node.getChildNode("ConfigType", i); + type = child.getAttribute("Type"); + + if (!type) { + LogWrite(MISC__WARNING, 0, "Misc", "Error parsing log config. Config missing a Type"); + continue; + } + + lfs = GetLogTypeStatus(category, type); + level = child.getAttribute("Level"); + enabled = child.getAttribute("Enabled"); + color = child.getAttribute("Color"); + logs = child.getAttribute("Logs"); + + if (!logs) { + LogWrite(MISC__WARNING, 0, "Misc", "Error parsing log config. Config missing 'Logs' attribute to specify which log(s) to write to"); + continue; + } + if (!IsNumber(logs)) { + LogWrite(MISC__WARNING, 0, "Misc", "Error parsing log config. Attribute 'Logs' must be a number. See LogTypes.h for the valid types."); + continue; + } + + if (enabled) { + if (!strcasecmp("true", enabled) || !strcasecmp("on", enabled)) + lfs->enabled = true; + else if (!strcasecmp("false", enabled) || !strcasecmp("off", enabled)) + lfs->enabled = false; + else + LogWrite(MISC__WARNING, 0, "Misc", "Error parsing log config. Log setting 'Enabled' has invalid value '%s'. 'true'/'on' or 'false'/'off' are valid values", enabled); + } + + if (IsNumber(level)) + lfs->level = atoi(level); + else + lfs->level = 0; + + if (color) { + if (IsNumber(color)) + lfs->color = atoi(color); + else if (!strcasecmp("White", color)) + lfs->color = FOREGROUND_WHITE; + else if (!strcasecmp("Green", color)) + lfs->color = FOREGROUND_GREEN; + else if (!strcasecmp("Yellow", color)) + lfs->color = FOREGROUND_YELLOW; + else if (!strcasecmp("Red", color)) + lfs->color = FOREGROUND_RED; + else if (!strcasecmp("Blue", color)) + lfs->color = FOREGROUND_BLUE; + else if (!strcasecmp("Cyan", color)) + lfs->color = FOREGROUND_CYAN; + else if (!strcasecmp("Magenta", color)) + lfs->color = FOREGROUND_MAGENTA; + else if (!strcasecmp("WhiteBold", color)) + lfs->color = FOREGROUND_WHITE_BOLD; + else if (!strcasecmp("GreenBold", color)) + lfs->color = FOREGROUND_GREEN_BOLD; + else if (!strcasecmp("YellowBold", color)) + lfs->color = FOREGROUND_YELLOW_BOLD; + else if (!strcasecmp("RedBold", color)) + lfs->color = FOREGROUND_RED_BOLD; + else if (!strcasecmp("BlueBold", color)) + lfs->color = FOREGROUND_BLUE_BOLD; + else if (!strcasecmp("CyanBold", color)) + lfs->color = FOREGROUND_CYAN_BOLD; + else if (!strcasecmp("MagentaBold", color)) + lfs->color = FOREGROUND_MAGENTA_BOLD; + else + LogWrite(MISC__WARNING, 0, "Misc", "Error parsing log config. Log setting 'Color' has invalid value '%s'", color); + } + + // JA: something was wrong here, lfs->logfile or console always was true, even if bit was off. Will ask Scatman about it someday. + lfs->logfile = (atoi(logs) & LOG_LOGFILE); + lfs->console = (atoi(logs) & LOG_CONSOLE); + lfs->client = (atoi(logs) & LOG_CLIENT); + } +} + +bool +LogParseConfigs() { + XMLNode main_node; + int i; + + main_node = XMLNode::openFileHelper("log_config.xml", "EQ2EmuLogConfigs"); + if (main_node.isEmpty()) { + LogWrite(MISC__WARNING, 0, "Misc", "Unable to parse the file 'log_config.xml' or it does not exist. Default values will be used"); + return false; + } + + for (i = 0; i < main_node.nChildNode("LogConfig"); i++) + ProcessLogConfig(main_node.getChildNode("LogConfig", i)); + + return true; +} diff --git a/old/common/Log.h b/old/common/Log.h new file mode 100644 index 0000000..8a7e6a6 --- /dev/null +++ b/old/common/Log.h @@ -0,0 +1,69 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef LOG_H_ +#define LOG_H_ + +#include +#include "../WorldServer/client.h" + +#define LOG_BUFFER_SIZE 4096 + +#define LOG_CATEGORY(category) LOG_ ##category , +enum LogCategory +{ + #include "LogTypes.h" + NUMBER_OF_LOG_CATEGORIES +}; + +#define LOG_TYPE(category, type, level, color, enabled, logfile, console, client, str) category##__##type , +enum LogType +{ + #include "LogTypes.h" + NUMBER_OF_LOG_TYPES +}; + +extern const char* log_category_names[NUMBER_OF_LOG_CATEGORIES]; + +struct LogTypeStatus +{ + int8 level; + int color; + bool enabled; + bool logfile; + bool console; + bool client; + LogCategory category; + const char *name; + const char *display_name; +}; + +extern LogTypeStatus* log_type_info; + +void LogStart(); +void LogStop(); +int8 GetLoggerLevel(LogType type); +void LogWrite(LogType type, int8 log_level, const char *cat_text, const char *fmt, ...); +#ifdef PARSER + void ColorizeLog(int color, char *date, const char *display_name, const char *category, string buffer); +#endif + +bool LogParseConfigs(); + +#endif diff --git a/old/common/log_types.hpp b/old/common/LogTypes.h similarity index 87% rename from old/common/log_types.hpp rename to old/common/LogTypes.h index 22c1c85..94aeae8 100644 --- a/old/common/log_types.hpp +++ b/old/common/LogTypes.h @@ -1,9 +1,22 @@ -// Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) - GNU GPL v3 +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) -#pragma once + This file is part of EQ2Emulator. -// Macro definitions for log category and type declarations -// These macros are redefined by the logging system to generate appropriate structures + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ #ifndef LOG_CATEGORY #define LOG_CATEGORY(name) #endif @@ -12,7 +25,6 @@ #define LOG_TYPE(category, type, level, color, enabled, logfile, console, client, str) #endif -// Convenience macros for enabling/disabling log types #ifndef ENABLED #define ENABLED true #endif @@ -21,50 +33,61 @@ #define DISABLED false #endif -// Console color definitions for Unix/Linux systems -// Used for colorized console output in logging -#define FOREGROUND_WHITE 37 -#define FOREGROUND_WHITE_BOLD 137 -#define FOREGROUND_RED 31 -#define FOREGROUND_RED_BOLD 131 -#define FOREGROUND_GREEN 32 -#define FOREGROUND_GREEN_BOLD 132 -#define FOREGROUND_BLUE 34 -#define FOREGROUND_BLUE_BOLD 134 -#define FOREGROUND_YELLOW 33 -#define FOREGROUND_YELLOW_BOLD 133 -#define FOREGROUND_CYAN 36 -#define FOREGROUND_CYAN_BOLD 136 -#define FOREGROUND_MAGENTA 35 -#define FOREGROUND_MAGENTA_BOLD 135 +#ifdef _WIN32 + #define FOREGROUND_WHITE (FOREGROUND_RED |FOREGROUND_GREEN | FOREGROUND_BLUE) + #define FOREGROUND_WHITE_BOLD (FOREGROUND_RED |FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY) + #define FOREGROUND_RED_BOLD (FOREGROUND_RED | FOREGROUND_INTENSITY) + #define FOREGROUND_GREEN_BOLD (FOREGROUND_GREEN | FOREGROUND_INTENSITY) + #define FOREGROUND_BLUE_BOLD (FOREGROUND_BLUE | FOREGROUND_INTENSITY) + #define FOREGROUND_YELLOW (FOREGROUND_RED | FOREGROUND_GREEN) + #define FOREGROUND_YELLOW_BOLD (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY) + #define FOREGROUND_CYAN (FOREGROUND_GREEN | FOREGROUND_BLUE) + #define FOREGROUND_CYAN_BOLD (FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY) + #define FOREGROUND_MAGENTA (FOREGROUND_RED | FOREGROUND_BLUE) + #define FOREGROUND_MAGENTA_BOLD (FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY) +#else + #define FOREGROUND_WHITE 37 + #define FOREGROUND_WHITE_BOLD 137 + #define FOREGROUND_RED 31 + #define FOREGROUND_RED_BOLD 131 + #define FOREGROUND_GREEN 32 + #define FOREGROUND_GREEN_BOLD 132 + #define FOREGROUND_BLUE 34 + #define FOREGROUND_BLUE_BOLD 134 + #define FOREGROUND_YELLOW 33 + #define FOREGROUND_YELLOW_BOLD 133 + #define FOREGROUND_CYAN 36 + #define FOREGROUND_CYAN_BOLD 136 + #define FOREGROUND_MAGENTA 35 + #define FOREGROUND_MAGENTA_BOLD 135 +#endif -// Log output destination flags - can be combined with bitwise OR -#define LOG_LOGFILE 1 // Output to log file -#define LOG_CONSOLE 2 // Output to console -#define LOG_CLIENT 4 // Output to client (not yet implemented) + + +#define LOG_LOGFILE 1 +#define LOG_CONSOLE 2 +#define LOG_CLIENT 4 /* not yet using */ /* - Legend for str output parameter: - I : Information messages - W : Warning messages - E : Error messages - D : Debug messages - P : DumpPacket/PrintPacket messages - should NEVER go to Client channel - T : Low-level debug tracing messages - should NEVER go to Client channel - Q : Database query messages - R : Database result messages - M : Miscellaneous/TODO messages + Legend for str output (optional): + I : Information messages + W : Warning messages + E : Error messages + D : Debug messages + P : DumpPacket/PrintPacket messages - should NEVER go to Client channel!!! + T : Low-level debug tracing messages - should NEVER go to Client channel!!! - Note: If str = null, output #category__#type to logs + Note: If str = null, output #catagory__#type to logs */ + /*** SYSTEM Loggers ******************************************************************************/ -// General events within the World code +// Logging specific to general events within the World code LOG_CATEGORY(WORLD) LOG_TYPE(WORLD, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") // Information messages (minimum output) LOG_TYPE(WORLD, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") // Warning messages -LOG_TYPE(WORLD, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") // Error messages (should always be enabled) -LOG_TYPE(WORLD, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") // Debug messages (enabled during alpha dev) +LOG_TYPE(WORLD, ERROR, 0, FOREGROUND_RED_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "E") // Error messages (should always be enabled) +LOG_TYPE(WORLD, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "D") // Debug messages (enabled during alpha dev) LOG_TYPE(WORLD, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") // DumpPacket/PrintPacket messages LOG_TYPE(WORLD, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") // Low-level debug tracing messages @@ -128,7 +151,7 @@ LOG_TYPE(MUTEX, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, D LOG_TYPE(MUTEX, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(MUTEX, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Miscellaneous logging including special DEV type "TODO" +// anything else... including a special DEV type "TODO" LOG_CATEGORY(MISC) LOG_TYPE(MISC, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(MISC, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -138,6 +161,8 @@ LOG_TYPE(MISC, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DI LOG_TYPE(MISC, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") LOG_TYPE(MISC, TODO, 0, FOREGROUND_YELLOW_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "M") + + /*** NETWORK Loggers *****************************************************************************/ // Client Communications Logging LOG_CATEGORY(CCLIENT) @@ -148,7 +173,7 @@ LOG_TYPE(CCLIENT, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, LOG_TYPE(CCLIENT, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(CCLIENT, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Network code logging +// Logging Net code LOG_CATEGORY(NET) LOG_TYPE(NET, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(NET, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -157,7 +182,7 @@ LOG_TYPE(NET, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DIS LOG_TYPE(NET, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(NET, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Opcode encounter logging +// Logging opcodes as they are encountered LOG_CATEGORY(OPCODE) LOG_TYPE(OPCODE, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(OPCODE, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -166,7 +191,7 @@ LOG_TYPE(OPCODE, DEBUG, 0, FOREGROUND_MAGENTA_BOLD, DISABLED, DISABLED, DISABLED LOG_TYPE(OPCODE, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(OPCODE, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Special category for excessive DumpPacket or Opcode debugging - All DISABLED by default +// Special category for dumping out excessive DumpPacket or Opcode debugging entries - All DISABLED by default! LOG_CATEGORY(PACKET) LOG_TYPE(PACKET, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(PACKET, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -175,8 +200,10 @@ LOG_TYPE(PACKET, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, LOG_TYPE(PACKET, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(PACKET, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + + /*** PLAYER Loggers ******************************************************************************/ -// Character progress events +// Events related to character progress LOG_CATEGORY(PLAYER) LOG_TYPE(PLAYER, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(PLAYER, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -185,6 +212,8 @@ LOG_TYPE(PLAYER, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, LOG_TYPE(PLAYER, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(PLAYER, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + + /*** SUBSYSTEM Loggers ***************************************************************************/ // Achievements Logging LOG_CATEGORY(ACHIEVEMENT) @@ -213,7 +242,7 @@ LOG_TYPE(COLLECTION, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABL LOG_TYPE(COLLECTION, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(COLLECTION, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Combat, aggro, hate, melee, damage events +// Events related to combat,aggro, hate, melee, damages, etc. LOG_CATEGORY(COMBAT) LOG_TYPE(COMBAT, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(COMBAT, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -222,7 +251,7 @@ LOG_TYPE(COMBAT, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, LOG_TYPE(COMBAT, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(COMBAT, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Commands (slash commands, UI commands, etc) +// Events related to commands (slash commands, UI commands, etc) LOG_CATEGORY(COMMAND) LOG_TYPE(COMMAND, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(COMMAND, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -231,7 +260,7 @@ LOG_TYPE(COMMAND, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, LOG_TYPE(COMMAND, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(COMMAND, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Faction-related events, adjustments, querying +// Faction-related events, adjustments, querying, etc. LOG_CATEGORY(FACTION) LOG_TYPE(FACTION, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(FACTION, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -240,7 +269,7 @@ LOG_TYPE(FACTION, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, LOG_TYPE(FACTION, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(FACTION, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Guild events, members, logging, permissions, recruiting +// Guild events, members, logging, permissions, recruiting, etc. LOG_CATEGORY(GUILD) LOG_TYPE(GUILD, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(GUILD, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -249,7 +278,7 @@ LOG_TYPE(GUILD, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, D LOG_TYPE(GUILD, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(GUILD, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Group events, members, permissions +// Group events, members, permissions, etc. LOG_CATEGORY(GROUP) LOG_TYPE(GROUP, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(GROUP, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -258,7 +287,7 @@ LOG_TYPE(GROUP, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, D LOG_TYPE(GROUP, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(GROUP, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Item events, stats, appearances, loading/reloading +// Item events, stats, appearances, loading/reloading, etc. LOG_CATEGORY(ITEM) LOG_TYPE(ITEM, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(ITEM, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -276,7 +305,7 @@ LOG_TYPE(LOOT, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DI LOG_TYPE(LOOT, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(LOOT, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// LUA subsystem events +// Events that occur within the LUA subsystem LOG_CATEGORY(LUA) LOG_TYPE(LUA, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(LUA, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -285,7 +314,7 @@ LOG_TYPE(LUA, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DIS LOG_TYPE(LUA, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(LUA, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Merchant events, buy/sell/broker, faction merchants +// Merchant events, buy/sell/broker, faction merchants, etc. LOG_CATEGORY(MERCHANT) LOG_TYPE(MERCHANT, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(MERCHANT, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -294,7 +323,7 @@ LOG_TYPE(MERCHANT, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED LOG_TYPE(MERCHANT, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(MERCHANT, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// NPC events, stats, appearances, movement, gear, abilities +// NPC events, stats, appearances, movement, gear, abilities, etc. LOG_CATEGORY(NPC) LOG_TYPE(NPC, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(NPC, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -307,7 +336,7 @@ LOG_TYPE(NPC, DAMAGE, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DI LOG_TYPE(NPC, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(NPC, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// NPC AI behavior and decision making +// What is that NPC thinking?! ...etc. LOG_CATEGORY(NPC_AI) LOG_TYPE(NPC_AI, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(NPC_AI, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -316,7 +345,7 @@ LOG_TYPE(NPC_AI, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, LOG_TYPE(NPC_AI, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(NPC_AI, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// PET events, stats, appearances, movement, gear, abilities +// PET events, stats, appearances, movement, gear, abilities, etc. LOG_CATEGORY(PET) LOG_TYPE(PET, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(PET, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -340,7 +369,7 @@ LOG_TYPE(QUEST, STEP, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DI LOG_TYPE(QUEST, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(QUEST, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Recipe system events +// Events relating to Recipes in the world LOG_CATEGORY(RECIPE) LOG_TYPE(RECIPE, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(RECIPE, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -349,7 +378,7 @@ LOG_TYPE(RECIPE, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, LOG_TYPE(RECIPE, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(RECIPE, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Rule system events +// Events relating to Rules in the world LOG_CATEGORY(RULESYS) LOG_TYPE(RULESYS, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(RULESYS, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -358,7 +387,7 @@ LOG_TYPE(RULESYS, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, LOG_TYPE(RULESYS, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(RULESYS, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Skill system, books, scribing, stats, usage +// The Skill system, books, scribing, stats, usage LOG_CATEGORY(SKILL) LOG_TYPE(SKILL, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(SKILL, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -367,7 +396,7 @@ LOG_TYPE(SKILL, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, D LOG_TYPE(SKILL, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(SKILL, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Spell system, books, scribing, stats, usage +// The Spell system, books, scribing, stats, usage LOG_CATEGORY(SPELL) LOG_TYPE(SPELL, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(SPELL, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -376,7 +405,7 @@ LOG_TYPE(SPELL, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, D LOG_TYPE(SPELL, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(SPELL, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Crafting system, recipes, reactions, progress +// The Crafting system, recipies, reactions, progress, etc. LOG_CATEGORY(TRADESKILL) LOG_TYPE(TRADESKILL, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(TRADESKILL, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -385,7 +414,7 @@ LOG_TYPE(TRADESKILL, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABL LOG_TYPE(TRADESKILL, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(TRADESKILL, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Transportation system, teleporters, mounts +// The Transportation system, teleporters, mounts, etc. LOG_CATEGORY(TRANSPORT) LOG_TYPE(TRANSPORT, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(TRANSPORT, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -394,8 +423,9 @@ LOG_TYPE(TRANSPORT, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLE LOG_TYPE(TRANSPORT, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(TRANSPORT, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + /*** SPAWN Loggers *******************************************************************************/ -// General spawn events, location, placement, grouping +// General Spawn events, location, placement, grouping LOG_CATEGORY(SPAWN) LOG_TYPE(SPAWN, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(SPAWN, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -404,7 +434,7 @@ LOG_TYPE(SPAWN, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, D LOG_TYPE(SPAWN, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(SPAWN, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Interactable objects in the world +// Events relating to interactable objects in the world LOG_CATEGORY(OBJECT) LOG_TYPE(OBJECT, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(OBJECT, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -413,7 +443,7 @@ LOG_TYPE(OBJECT, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, LOG_TYPE(OBJECT, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(OBJECT, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Signs in the world +// Events relating to Signs in the world LOG_CATEGORY(SIGN) LOG_TYPE(SIGN, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(SIGN, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -422,7 +452,7 @@ LOG_TYPE(SIGN, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DI LOG_TYPE(SIGN, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(SIGN, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Widgets in the world +// Events relating to Widgets in the world LOG_CATEGORY(WIDGET) LOG_TYPE(WIDGET, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(WIDGET, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -431,7 +461,7 @@ LOG_TYPE(WIDGET, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, LOG_TYPE(WIDGET, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(WIDGET, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Groundspawns in the world +// Events relating to Groundspawns in the world LOG_CATEGORY(GROUNDSPAWN) LOG_TYPE(GROUNDSPAWN, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(GROUNDSPAWN, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -440,6 +470,8 @@ LOG_TYPE(GROUNDSPAWN, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISAB LOG_TYPE(GROUNDSPAWN, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(GROUNDSPAWN, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + + /*** ZONE Loggers ********************************************************************************/ // Zone-related events, status, messaging, access LOG_CATEGORY(ZONE) @@ -450,7 +482,7 @@ LOG_TYPE(ZONE, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DI LOG_TYPE(ZONE, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(ZONE, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Instance loading/reloading +// Instance loading/reloading, etc. LOG_CATEGORY(INSTANCE) LOG_TYPE(INSTANCE, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(INSTANCE, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -459,6 +491,7 @@ LOG_TYPE(INSTANCE, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED LOG_TYPE(INSTANCE, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(INSTANCE, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + /*** MAP Loggers ********************************************************************************/ // Map-related events, status, messaging, access LOG_CATEGORY(MAP) @@ -469,6 +502,7 @@ LOG_TYPE(MAP, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, DIS LOG_TYPE(MAP, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(MAP, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") + /*** Region Map Loggers ********************************************************************************/ // RegionMap-related events, status, messaging, access LOG_CATEGORY(REGION) @@ -479,7 +513,7 @@ LOG_TYPE(REGION, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, LOG_TYPE(REGION, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(REGION, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Peering system logging +// Logging Peering code LOG_CATEGORY(PEERING) LOG_TYPE(PEERING, INFO, 0, FOREGROUND_WHITE_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "I") LOG_TYPE(PEERING, WARNING, 0, FOREGROUND_YELLOW_BOLD, ENABLED, ENABLED, ENABLED, DISABLED, "W") @@ -488,8 +522,7 @@ LOG_TYPE(PEERING, DEBUG, 0, FOREGROUND_GREEN_BOLD, DISABLED, DISABLED, DISABLED, LOG_TYPE(PEERING, PACKET, 0, FOREGROUND_CYAN_BOLD, DISABLED, DISABLED, DISABLED, DISABLED, "P") LOG_TYPE(PEERING, TRACE, 0, FOREGROUND_YELLOW, DISABLED, DISABLED, DISABLED, DISABLED, "T") -// Cleanup macro definitions #undef LOG_TYPE #undef LOG_CATEGORY #undef ENABLED -#undef DISABLED \ No newline at end of file +#undef DISABLED diff --git a/old/common/MiscFunctions.cpp b/old/common/MiscFunctions.cpp new file mode 100644 index 0000000..21ec052 --- /dev/null +++ b/old/common/MiscFunctions.cpp @@ -0,0 +1,973 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include "../common/debug.h" +#include "../common/Log.h" +#include "MiscFunctions.h" +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#include +#endif +#include +#include +#ifdef WIN32 + #include +#endif +#include "../common/timer.h" +#include "../common/seperator.h" +#include "../common/packet_dump.h" +#include + +using namespace std; + +#ifndef PATCHER +extern map EQOpcodeVersions; +#endif + +#ifdef WIN32 + #include + #include + + #define snprintf _snprintf + #define vsnprintf _vsnprintf + #define strncasecmp _strnicmp + #define strcasecmp _stricmp +#else + #include + #include + #include + #include + #include +#ifdef FREEBSD //Timothy Whitman - January 7, 2003 + #include + #include + #endif + #include + #include + #include + #include +#endif + +void CoutTimestamp(bool ms) { + time_t rawtime; + struct tm* gmt_t; + time(&rawtime); + gmt_t = gmtime(&rawtime); + + struct timeval read_time; + gettimeofday(&read_time,0); + + cout << (gmt_t->tm_year + 1900) << "/" << setw(2) << setfill('0') << (gmt_t->tm_mon + 1) << "/" << setw(2) << setfill('0') << gmt_t->tm_mday << " " << setw(2) << setfill('0') << gmt_t->tm_hour << ":" << setw(2) << setfill('0') << gmt_t->tm_min << ":" << setw(2) << setfill('0') << gmt_t->tm_sec; + if (ms) + cout << "." << setw(3) << setfill('0') << (read_time.tv_usec / 1000); + cout << " GMT"; +} + +string loadInt32String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_32BitString* eq_string){ + buffer += *pos; + int32 size = *(int32*)buffer; + if((size + *pos + sizeof(int16)) > buffer_size){ + cout << "Error in loadInt32String: Corrupt packet.\n"; + return string(""); + } + buffer += sizeof(int32); + string ret((char*)buffer, 0, size); + if(eq_string){ + eq_string->size = size; + eq_string->data = ret; + } + *pos += (size + sizeof(int32)); + return ret; +} +string loadInt16String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_16BitString* eq_string){ + buffer += *pos; + int16 size = *(int16*)buffer; + if((size + *pos + sizeof(int16))> buffer_size){ + cout << "Error in loadInt16String: Corrupt packet.\n"; + return string(""); + } + buffer += sizeof(int16); + string ret((char*)buffer, 0, size); + if(eq_string){ + eq_string->size = size; + eq_string->data = ret; + } + *pos += (size + sizeof(int16)); + return ret; +} +string loadInt8String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_8BitString* eq_string){ + buffer += *pos; + int8 size = *(int8*)buffer; + if((size + *pos + sizeof(int16)) > buffer_size){ + cout << "Error in loadInt8String: Corrupt packet.\n"; + return string(""); + } + buffer += sizeof(int8); + string ret((char*)buffer, 0, size); + if(eq_string){ + eq_string->size = size; + eq_string->data = ret; + } + *pos += (size + sizeof(int8)); + return ret; +} + +sint16 storeInt32String(uchar* buffer, int16 buffer_size, string in_str){ + sint16 string_size = in_str.length(); + if((string_size + sizeof(int32)) > buffer_size) + return -1; + memcpy(buffer, &string_size, sizeof(int32)); + buffer += sizeof(int32); + memcpy(buffer, in_str.c_str(), string_size); + buffer += string_size; + return (buffer_size - (string_size + sizeof(int32))); +} +sint16 storeInt16String(uchar* buffer, int16 buffer_size, string in_str){ + sint16 string_size = in_str.length(); + if((string_size + sizeof(int16)) > buffer_size) + return -1; + memcpy(buffer, &string_size, sizeof(int16)); + buffer += sizeof(int16); + memcpy(buffer, in_str.c_str(), string_size); + buffer += string_size; + return (buffer_size - (string_size + sizeof(int16))); +} +sint16 storeInt8String(uchar* buffer, int16 buffer_size, string in_str){ + sint16 string_size = in_str.length(); + if((string_size + sizeof(int8)) > buffer_size) + return -1; + memcpy(buffer, &string_size, sizeof(int8)); + buffer += sizeof(int8); + memcpy(buffer, in_str.c_str(), string_size); + buffer += string_size; + return (buffer_size - (string_size + sizeof(int8))); +} + + +sint32 filesize(FILE* fp) { +#ifdef WIN32 + return _filelength(_fileno(fp)); +#else + struct stat file_stat; + fstat(fileno(fp), &file_stat); + return (sint32) file_stat.st_size; +#endif +} + +int32 ResolveIP(const char* hostname, char* errbuf) { +#ifdef WIN32 + static InitWinsock ws; +#endif + if (errbuf) + errbuf[0] = 0; + if (hostname == 0) { + if (errbuf) + snprintf(errbuf, ERRBUF_SIZE, "ResolveIP(): hostname == 0"); + return 0; + } + struct sockaddr_in server_sin; +#ifdef WIN32 + PHOSTENT phostent = NULL; +#else + struct hostent *phostent = NULL; +#endif + server_sin.sin_family = AF_INET; + if ((phostent = gethostbyname(hostname)) == NULL) { +#ifdef WIN32 + if (errbuf) + snprintf(errbuf, ERRBUF_SIZE, "Unable to get the host name. Error: %i", WSAGetLastError()); +#else + if (errbuf) + snprintf(errbuf, ERRBUF_SIZE, "Unable to get the host name. Error: %s", strerror(errno)); +#endif + return 0; + } +#ifdef WIN32 + memcpy ((char FAR *)&(server_sin.sin_addr), phostent->h_addr, phostent->h_length); +#else + memcpy ((char*)&(server_sin.sin_addr), phostent->h_addr, phostent->h_length); +#endif + return server_sin.sin_addr.s_addr; +} + +#ifdef WIN32 +InitWinsock::InitWinsock() { + WORD version = MAKEWORD (1,1); + WSADATA wsadata; + WSAStartup (version, &wsadata); +} + +InitWinsock::~InitWinsock() { + WSACleanup(); +} + +#endif + +#ifndef WIN32 +const char * itoa(int value) { + static char temp[_ITOA_BUFLEN]; + memset(temp, 0, _ITOA_BUFLEN); + snprintf(temp, _ITOA_BUFLEN,"%d", value); + return temp; +} + + +char * itoa(int value, char *result, int base) { + char *ptr1, *ptr2; + char c; + int tmp_value; + + //need a valid base + if (base < 2 || base > 36) { + *result = '\0'; + return result; + } + + ptr1 = ptr2 = result; + do { + tmp_value = value; + value /= base; + + *ptr1++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; + } + while (value > 0); + + //apply a negative sign if need be + if (tmp_value < 0) + *ptr1++ = '-'; + + *ptr1-- = '\0'; + while (ptr2 < ptr1) { + c = *ptr1; + *ptr1-- = *ptr2; + *ptr2++ = c; + } + + return result; +} +#endif + +/* + * solar: generate a random integer in the range low-high + * this should be used instead of the rand()%limit method + */ +int MakeRandomInt(int low, int high) +{ + return (int)MakeRandomFloat((double)low, (double)high + 0.999); +} +int32 hextoi(char* num) { + int len = strlen(num); + if (len < 3) + return 0; + + if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) + return 0; + + int32 ret = 0; + int mul = 1; + for (int i=len-1; i>=2; i--) { + if (num[i] >= 'A' && num[i] <= 'F') + ret += ((num[i] - 'A') + 10) * mul; + else if (num[i] >= 'a' && num[i] <= 'f') + ret += ((num[i] - 'a') + 10) * mul; + else if (num[i] >= '0' && num[i] <= '9') + ret += (num[i] - '0') * mul; + else + return 0; + mul *= 16; + } + return ret; +} + +int64 hextoi64(char* num) { + int len = strlen(num); + if (len < 3) + return 0; + + if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) + return 0; + + int64 ret = 0; + int mul = 1; + for (int i=len-1; i>=2; i--) { + if (num[i] >= 'A' && num[i] <= 'F') + ret += ((num[i] - 'A') + 10) * mul; + else if (num[i] >= 'a' && num[i] <= 'f') + ret += ((num[i] - 'a') + 10) * mul; + else if (num[i] >= '0' && num[i] <= '9') + ret += (num[i] - '0') * mul; + else + return 0; + mul *= 16; + } + return ret; +} + +float MakeRandomFloat(float low, float high) { + // Handle edge case where range is zero or inverted + float diff = high - low; + if(!diff) return low; + + if (low == high) return low; + if (low > high) std::swap(low, high); + + // Use a thread-local random generator for thread safety + thread_local std::mt19937 generator(std::random_device{}()); // Seed once per thread + std::uniform_real_distribution distribution(low, high); + + return distribution(generator); +} + +int32 GenerateEQ2Color(float* r, float* g, float* b){ + int8 rgb[4] = {0}; + rgb[0] = (int8)((*r)*255); + rgb[1] = (int8)((*b)*255); + rgb[2] = (int8)((*g)*255); + int32 color = 0; + memcpy(&color, rgb, sizeof(int32)); + return color; +} +int32 GenerateEQ2Color(float* rgb[3]){ + return GenerateEQ2Color(rgb[0], rgb[1], rgb[2]); +} +int8 MakeInt8(float* input){ + float input2 = *input; + if(input2 < 0) + input2 *= -1; + return (int8)(input2*255); +} + +vector* SplitString(string str, char delim){ + vector* results = new vector; + int32 pos; + while((pos = str.find_first_of(delim))!= str.npos){ + if(pos > 0){ + results->push_back(str.substr(0,pos)); + } + if(str.length() > pos) + str = str.substr(pos+1); + else + break; + } + if(str.length() > 0) + results->push_back(str); + return results; +} + +bool Unpack(uchar* data, uchar* dst, int16 dstLen, int16 version, bool reverse){ + int32 srcLen = 0; + memcpy(&srcLen, data, sizeof(int32)); + return Unpack(srcLen, data + 4, dst, dstLen, version, reverse); +} +bool Unpack(int32 srcLen, uchar* data, uchar* dst, int16 dstLen, int16 version, bool reverse) { +// int32 srcLen = 0; +// memcpy(&srcLen, data, sizeof(int32)); +// data+=4; + if(reverse) + Reverse(data, srcLen); + int16 pos = 0; + int16 real_pos = 0; + while(srcLen && pos < dstLen) { + if(srcLen >= 0 && !srcLen--) + return false; + int8 code = data[real_pos++]; + + if(code >= 128) { + for(int8 index=0; index<7; index++) { + if(code & 1) { + if(pos >= dstLen) + return false; + if(srcLen >= 0 && !srcLen--) + return false; + dst[pos++] = data[real_pos++]; + } else { + if(pos < dstLen) dst[pos++] = 0; + } + code >>= 1; + } + } else { + if(pos + code > dstLen) + return false; + memset(dst+pos, 0, code); + pos+=code; + } + } + return srcLen <= 0; +} + +int32 Pack(uchar* data, uchar* src, int16 srcLen, int16 dstLen, int16 version, bool reverse) { + int16 real_pos = 4; + int32 pos = 0; + int32 code = 0; + int codePos = 0; + int codeLen = 0; + int8 zeroLen = 0; + memset(data,0,dstLen); + if (version > 1 && version <= 374) + reverse = false; + while(pos < srcLen) { + if(src[pos] || codeLen) { + if(!codeLen) { + /*if(zeroLen > 5) { + data[real_pos++] = zeroLen; + zeroLen = 0; + } + else if(zeroLen >= 1 && zeroLen<=5){ + for(;zeroLen>0;zeroLen--) + codeLen++; + }*/ + if (zeroLen) { + data[real_pos++] = zeroLen; + zeroLen = 0; + } + codePos = real_pos; + code = 0; + data[real_pos++] = 0; + } + if(src[pos]) { + data[real_pos++] = src[pos]; + code |= 0x80; + } + code >>= 1; + codeLen++; + + if(codeLen == 7) { + data[codePos] = int8(0x80 | code); + codeLen = 0; + } + } else { + if(zeroLen == 0x7F) { + data[real_pos++] = zeroLen; + zeroLen = 0; + } + zeroLen++; + } + pos++; + } + if(codeLen) { + code >>= (7 - codeLen); + data[codePos] = int8(0x80 | code); + } else if(zeroLen) { + data[real_pos++] = zeroLen; + } + if(reverse) + Reverse(data + 4, real_pos - 4); + int32 dataLen = real_pos - 4; + memcpy(&data[0], &dataLen, sizeof(int32)); + return dataLen + 4; +} +void Reverse(uchar* input, int32 srcLen){ + int16 real_pos = 0; + int16 orig_pos = 0; + int8 reverse_count = 0; + while(srcLen > 0 && srcLen < 0xFFFFFFFF){ // XXX it was >=0 before. but i think it was a bug + int8 code = input[real_pos++]; + srcLen--; + if(code >= 128) { + for(int8 index=0; index<7; index++) { + if(code & 1) { + if(srcLen >= 0 && !srcLen--) + return; + real_pos++; + reverse_count++; + } + code >>= 1; + } + } + if(reverse_count > 0){ + int8 tmp_data[8] = {0}; + for(int8 i=0;i 0){ + ret = atoul(input.c_str()); + } + } + catch(...){} + return ret; +} + +int64 ParseLongLongValue(string input){ + int64 ret = 0xFFFFFFFFFFFFFFFF; + try{ + if(input.length() > 0){ +#ifdef WIN32 + ret = _strtoui64(input.c_str(), NULL, 10); +#else + ret = strtoull(input.c_str(), 0, 10); +#endif + } + } + catch(...){} + return ret; +} + +map TranslateBrokerRequest(string request){ + map ret; + string key; + string value; + int32 start_pos = 0; + int32 end_pos = 0; + int32 pos = request.find("="); + bool str_val = false; + while(pos < 0xFFFFFFFF){ + str_val = false; + key = request.substr(start_pos, pos-start_pos); + if(request.find("|", pos) == pos+1){ + pos++; + end_pos = request.find("|", pos+1); + str_val = true; + } + else + end_pos = request.find(" ", pos); + if(end_pos < 0xFFFFFFFF){ + value = request.substr(pos+1, end_pos-pos-1); + start_pos = end_pos+1; + if(str_val){ + start_pos++; + ret[key] = ToLower(value); + } + else + ret[key] = value; + pos = request.find("=", start_pos); + } + else{ + value = request.substr(pos+1); + if(str_val){ + start_pos++; + ret[key] = ToLower(value); + } + else + ret[key] = value; + break; + } + } + return ret; +} + +int8 CheckOverLoadSize(int32 val){ + int8 ret = 1; + if(val >= 0xFFFF) //int32 + ret = sizeof(int16) + sizeof(int32); + else if(val >= 0xFF) + ret = sizeof(int8) + sizeof(int16); + return ret; +} + +int8 DoOverLoad(int32 val, uchar* data){ + int8 ret = 1; + if(val >= 0xFFFF){ //int32 + memset(data, 0xFF, sizeof(int16)); + memcpy(data + sizeof(int16), &val, sizeof(int32)); + ret = sizeof(int16) + sizeof(int32); + } + else if(val >= 0xFF){ //int16 + memset(data, 0xFF, sizeof(int8)); + memcpy(data + sizeof(int8), &val, sizeof(int16)); + ret = sizeof(int8) + sizeof(int16); + } + else + memcpy(data, &val, sizeof(int8)); + return ret; +} + +/* Treats contiguous spaces as one space. */ +int32 CountWordsInString(const char* text) { + int32 words = 0; + if (text && strlen(text) > 0) { + bool on_word = false; + for (int32 i = 0; i < strlen(text); i++) { + char letter = text[i]; + if (on_word && !((letter >= 48 && letter <= 57) || (letter >= 65 && letter <= 90) || (letter >= 97 && letter <= 122))) + on_word = false; + else if (!on_word && ((letter >= 48 && letter <= 57) || (letter >= 65 && letter <= 90) || (letter >= 97 && letter <= 122))){ + on_word = true; + words++; + } + } + } + return words; +} + +bool IsNumber(const char *num) { + size_t len, i; + + if (!num) + return false; + + len = strlen(num); + if (len == 0) + return false; + + for (i = 0; i < len; i++) { + if (!isdigit(num[i])) + return false; + } + + return true; +} + +void PrintSep(Seperator *sep, const char *name) { + int32 i = 0; + + LogWrite(MISC__DEBUG, 0, "Misc", "Printing sep %s", name ? name : "No Name"); + if (!sep) + LogWrite(MISC__DEBUG, 0, "Misc", "\tSep is null"); + else { + while (sep->arg[i] && strlen(sep->arg[i]) > 0) { + LogWrite(MISC__DEBUG, 0, "Misc", "\t%i => %s", i, sep->arg[i]); + i++; + } + } +} + +#define INI_IGNORE(c) (c == '\n' || c == '\r' || c == '#') + +static bool INIGoToSection(FILE *f, const char *section) { + size_t size = strlen(section) + 3; + char line[256], *buf, *tmp; + bool found = false; + + if ((buf = (char *)malloc(size)) == NULL) { + fprintf(stderr, "%s: %u: Unable to allocate %zu bytes\n", __FUNCTION__, __LINE__, size); + return false; + } + + sprintf(buf, "[%s]", section); + + while (fgets(line, sizeof(line), f) != NULL) { + if (INI_IGNORE(line[0])) + continue; + + if (line[0] == '[') { + if ((tmp = strstr(line, "\n")) != NULL) + *tmp = '\0'; + if ((tmp = strstr(line, "\r")) != NULL) + *tmp = '\0'; + + if (strcasecmp(buf, line) == 0) { + found = true; + break; + } + } + } + + free(buf); + return found; +} + +static char * INIFindValue(FILE *f, const char *section, const char *property) { + char line[256], *key, *val; + + if (section != NULL && !INIGoToSection(f, section)) + return NULL; + + while (fgets(line, sizeof(line), f) != NULL) { + if (INI_IGNORE(line[0])) + continue; + + if (section != NULL && line[0] == '[') + return NULL; + + if ((key = strtok(line, "=")) == NULL) + continue; + + if (strcasecmp(key, property) == 0) { + val = strtok(NULL, "\n\r"); + + if (val == NULL) + return NULL; + + return strdup(val); + } + } + + return NULL; +} + +bool INIReadInt(FILE *f, const char *section, const char *property, int *out) { + char *value; + + rewind(f); + + if ((value = INIFindValue(f, section, property)) == NULL) + return false; + + if (!IsNumber(value)) { + free(value); + return false; + } + + *out = atoi(value); + free(value); + + return true; +} + +bool INIReadBool(FILE *f, const char *section, const char *property, bool *out) { + char *value; + + rewind(f); + + if ((value = INIFindValue(f, section, property)) == NULL) + return false; + + *out = (strcasecmp(value, "1") == 0 || strcasecmp(value, "true") == 0 || strcasecmp(value, "on") == 0 || strcasecmp(value, "yes") == 0); + free(value); + + return true; + +} +string GetDeviceName(string device) { + if (device == "chemistry_table") + device = "Chemistry Table"; + else if (device == "work_desk") + device = "Engraved Desk"; + else if (device == "forge") + device = "Forge"; + else if (device == "stove and keg") + device = "Stove & Keg"; + else if (device == "sewing_table") + device = "Sewing Table & Mannequin"; + else if (device == "woodworking_table") + device = "Woodworking Table"; + else if (device == "work_bench") + device = "Work Bench"; + else if (device == "crafting_intro_anvil") + device = "Mender's Anvil"; + return device; +} + +int32 GetDeviceID(string device) { + if (device == "Chemistry Table") + return 3; + else if (device == "Engraved Desk") + return 4; + else if (device == "Forge") + return 2; + else if (device == "Stove & Keg") + return 7; + else if (device == "Sewing Table & Mannequin") + return 1; + else if (device == "Woodworking Table") + return 6; + else if (device == "Work Bench") + return 5; + else if (device == "Mender's Anvil") + return 0xFFFFFFFF; + return 0; +} +int16 GetItemPacketType(int32 version) { + int16 item_version; + if (version >= 64707) + item_version = 0x5CFE; + else if (version >= 63119) + item_version = 0x56FE; + else if (version >= 60024) + item_version = 0x51FE; + else if (version >= 57107) + item_version = 0x4CFE; + else if (version >= 57048) + item_version = 0x48FE; + else if (version >= 1199) + item_version = 0x44FE; + else if (version >= 1195) + item_version = 0x40FE; + else if (version >= 1193) + item_version = 0x3FFE; + else if (version >= 1190) + item_version = 0x3EFE; + else if (version >= 1188) + item_version = 0x3DFE; + else if (version >= 1096) + item_version = 0x35FE; + else if (version >= 1027) + item_version = 0x31FE; + else if (version >= 1008) + item_version = 0x2CFE; + else if (version >= 927) + item_version = 0x23FE; + else if (version >= 893) + item_version = 0x22FE; + else if (version >= 860) + item_version = 0x20FE; + else if (version > 546) + item_version = 0x1CFE; + else + item_version = 0; + + return item_version; +} + +#ifndef PATCHER +int16 GetOpcodeVersion(int16 version) { + int16 ret = version; + int16 version1 = 0; + int16 version2 = 0; + map::iterator itr; + for (itr = EQOpcodeVersions.begin(); itr != EQOpcodeVersions.end(); itr++) { + version1 = itr->first; + version2 = itr->second; + if (version >= version1 && version <= version2) { + ret = version1; + break; + } + } + + return ret; +} +#endif + +void SleepMS(int32 milliseconds) { +#if defined(_WIN32) + Sleep(milliseconds); +#else + usleep(milliseconds * 1000); +#endif +} + +size_t +strlcpy(char *dst, const char *src, size_t size) { + char *d = dst; + const char *s = src; + size_t n = size; + + if (n != 0 && --n != 0) { + do { + if ((*d++ = *s++) == 0) + break; + } while (--n != 0); + } + + if (n == 0) { + if (size != 0) + *d = '\0'; + while (*s++) + ; + } + + return(s - src - 1); +} + +float short_to_float(const ushort x) { // IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits + const uint32 e = (x & 0x7C00) >> 10; // exponent + const uint32 m = (x & 0x03FF) << 13; // mantissa + const uint32 v = as_uint((float)m) >> 23; // evil log2 bit hack to count leading zeros in denormalized format + return as_float((x & 0x8000) << 16 | (e != 0) * ((e + 112) << 23 | m) | ((e == 0) & (m != 0)) * ((v - 37) << 23 | ((m << (150 - v)) & 0x007FE000))); // sign : normalized : denormalized +} + +uint32 float_to_int(const float x) { // IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits + const uint32 b = as_uint(x) + 0x00001000; // round-to-nearest-even: add last bit after truncated mantissa + const uint32 e = (b & 0x7F800000) >> 23; // exponent + const uint32 m = b & 0x007FFFFF; // mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding + return (b & 0x80000000) >> 16 | (e > 112)* ((((e - 112) << 10) & 0x7C00) | m >> 13) | ((e < 113) & (e > 101))* ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) | (e > 143) * 0x7FFF; // sign : normalized : denormalized : saturate +} + +uint32 as_uint(const float x) { + return *(uint32*)&x; +} + +float as_float(const uint32 x) { + return *(float*)&x; +} + +// Function to get the current timestamp in milliseconds +int64 getCurrentTimestamp() { + auto now = std::chrono::steady_clock::now(); + auto duration = std::chrono::duration_cast(now.time_since_epoch()); + return duration.count(); +} + +std::tuple convertTimestampDuration(int64 total_milliseconds) { + std::chrono::milliseconds duration(total_milliseconds); + // Convert to days, hours, minutes, and seconds + auto hours = std::chrono::duration_cast(duration); + duration -= hours; + + auto days = hours / 24; + hours -= days * 24; + + auto minutes = std::chrono::duration_cast(duration); + duration -= minutes; + + auto seconds = std::chrono::duration_cast(duration); + + // Return the result as a tuple + return std::make_tuple(days.count(), hours.count(), minutes.count(), seconds.count()); +} \ No newline at end of file diff --git a/old/common/MiscFunctions.h b/old/common/MiscFunctions.h new file mode 100644 index 0000000..b9f39a6 --- /dev/null +++ b/old/common/MiscFunctions.h @@ -0,0 +1,228 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef MISCFUNCTIONS_H +#define MISCFUNCTIONS_H + +#include "types.h" +#include "seperator.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef ERRBUF_SIZE +#define ERRBUF_SIZE 1024 +#endif + +//int MakeAnyLenString(char** ret, const char* format, ...); +int32 hextoi(char* num); +int64 hextoi64(char* num); +sint32 filesize(FILE* fp); +int32 ResolveIP(const char* hostname, char* errbuf = 0); +void CoutTimestamp(bool ms = true); +//char* strn0cpy(char* dest, const char* source, int32 size); + // return value =true if entire string(source) fit, false if it was truncated +//bool strn0cpyt(char* dest, const char* source, int32 size); +string loadInt32String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_32BitString* eq_string = NULL); +string loadInt16String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_16BitString* eq_string = NULL); +string loadInt8String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_8BitString* eq_string = NULL); +sint16 storeInt32String(uchar* buffer, int16 buffer_size, string in_str); +sint16 storeInt16String(uchar* buffer, int16 buffer_size, string in_str); +sint16 storeInt8String(uchar* buffer, int16 buffer_size, string in_str); +int MakeRandomInt(int low, int high); +float MakeRandomFloat(float low, float high); + +float TransformToFloat(sint16 data, int8 bits); +sint16 TransformFromFloat(float data, int8 bits); + +int32 GenerateEQ2Color(float r, float g, float b); +int32 GenerateEQ2Color(float* rgb[3]); +void SetColor(EQ2_Color* color, long data); +//void CreateEQ2Color(EQ2_Color* color, uchar* data, int16* size); +int8 MakeInt8(uchar* data, int16* size); +int8 MakeInt8(float* input); +bool Unpack(int32 srcLen, uchar* data, uchar* dst, int16 dstLen, int16 version = 0, bool reverse = true); +bool Unpack(uchar* data, uchar* dst, int16 dstLen, int16 version = 0, bool reverse = true); +int32 Pack(uchar* data, uchar* src, int16 srcLen, int16 dstLen, int16 version = 0, bool reverse = true); +void Reverse(uchar* input, int32 srcLen); +void Encode(uchar* dst, uchar* src, int16 len); +void Decode(uchar* dst, uchar* src, int16 len); +string ToUpper(string input); +string ToLower(string input); +int32 ParseIntValue(string input); +int64 ParseLongLongValue(string input); +map TranslateBrokerRequest(string request); +void MovementDecode(uchar* dst, uchar* newval, uchar* orig, int16 len); +vector* SplitString(string str, char delim); +int8 DoOverLoad(int32 val, uchar* data); +int8 CheckOverLoadSize(int32 val); +int32 CountWordsInString(const char* text); +bool IsNumber(const char *num); +void PrintSep(Seperator *sep, const char *name = 0); +string GetDeviceName(string device); +int32 GetDeviceID(string device); +///Gets the packet type for the given version +///The client version +int16 GetItemPacketType(int32 version); +///Gets the opcode version_range1 from the clients version +///The client version +int16 GetOpcodeVersion(int16 version); +void SleepMS(int32 milliseconds); +size_t strlcpy(char *dst, const char *src, size_t size); + +float short_to_float(const ushort x); +uint32 float_to_int(const float x); +uint32 as_uint(const float x); +float as_float(const uint32 x); + +int64 getCurrentTimestamp(); +std::tuple convertTimestampDuration(int64 total_milliseconds); + +bool INIReadBool(FILE *f, const char *section, const char *property, bool *out); +bool INIReadInt(FILE *f, const char *section, const char *property, int *out); + +static bool IsPrivateAddress(uint32_t ip) +{ + uint8_t b1, b2;//, b3, b4; + b1 = (uint8_t)(ip >> 24); + b2 = (uint8_t)((ip >> 16) & 0x0ff); + //b3 = (uint8_t)((ip >> 8) & 0x0ff); + //b4 = (uint8_t)(ip & 0x0ff); + + // 10.x.y.z + if (b1 == 10) + return true; + + // 172.16.0.0 - 172.31.255.255 + if ((b1 == 172) && (b2 >= 16) && (b2 <= 31)) + return true; + + // 192.168.0.0 - 192.168.255.255 + if ((b1 == 192) && (b2 == 168)) + return true; + + return false; +} + +static std::string FormatCoinReceiveMessage(int64 total_copper, const std::string& reason) { + // breakdown into denominations + int64 platinum = total_copper / 1'000'000; + int64 rem = total_copper % 1'000'000; + int64 gold = rem / 10'000; + rem %= 10'000; + int64 silver = rem / 100; + int64 copper = rem % 100; + + std::ostringstream oss; + oss << "You received "; + + bool first = true; + if (platinum > 0) { + oss << platinum << " platinum"; + first = false; + } + if (gold > 0) { + if (!first) oss << ", "; + oss << gold << " gold"; + first = false; + } + if (silver > 0) { + if (!first) oss << ", "; + oss << silver << " silver"; + first = false; + } + // if nothing else or there's copper, show copper + if (copper > 0 || first) { + if (!first) oss << ", "; + oss << copper << " copper"; + } + + oss << " earned through " << reason; + return oss.str(); +} + + +template void AddData(Type input, string* datastring){ + if(datastring) + datastring->append((char*)&input, sizeof(input)); +} + +template void AddData(Type input, int32 array_size, string* datastring){ + if(array_size>0){ + for(int32 i=0;i class AutoDelete { +public: + AutoDelete(T** iVar, T* iSetTo = 0) { + init(iVar, iSetTo); + } + AutoDelete() {} + void init(T** iVar, T* iSetTo = 0) + { + pVar = iVar; + if (iSetTo) + *pVar = iSetTo; + } + ~AutoDelete() { + safe_delete(*pVar); + } +private: + T** pVar; +}; + +class VersionRange { +public: + VersionRange(int32 in_min_version, int32 in_max_version) + { + min_version = in_min_version; + max_version = in_max_version; + } + int32 GetMinVersion() { return min_version; } + int32 GetMaxVersion() { return max_version; } +private: + int32 min_version; + int32 max_version; +}; +#endif + + diff --git a/old/common/Mutex.cpp b/old/common/Mutex.cpp new file mode 100644 index 0000000..732e451 --- /dev/null +++ b/old/common/Mutex.cpp @@ -0,0 +1,361 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include "../common/Log.h" +#include "../common/debug.h" +#include "../common/Mutex.h" + +Mutex::Mutex() { + readers = 0; + mlocked = false; + writing = false; + name = ""; +#ifdef DEBUG + stack.clear(); +#endif + //CSLock is a pointer so we can use a different attribute type on create + CSLock = new CriticalSection(MUTEX_ATTRIBUTE_RECURSIVE); +} + +Mutex::~Mutex() { + safe_delete(CSLock); +#ifdef DEBUG + stack.clear(); +#endif +} + +void Mutex::SetName(string in_name) { +#ifdef DEBUG + name = in_name; +#endif +} + +void Mutex::lock() { +#ifdef DEBUG + int i = 0; +#endif + if (name.length() > 0) { + while (mlocked) { +#ifdef DEBUG + if (i > MUTEX_TIMEOUT_MILLISECONDS) { + LogWrite(MUTEX__ERROR, 0, "Mutex", "Possible deadlock attempt by '%s'!", name.c_str()); + return; + } + i++; +#endif + Sleep(1); + } + } + mlocked = true; + CSLock->lock(); +} + +bool Mutex::trylock() { + return CSLock->trylock(); +} + +void Mutex::unlock() { + CSLock->unlock(); + mlocked = false; +} + +void Mutex::readlock(const char* function, int32 line) { +#ifdef DEBUG + int32 i = 0; +#endif + while (true) { + //Loop until there isn't a writer, then we can read! + CSRead.lock(); + if (!writing) { + readers++; + CSRead.unlock(); +#ifdef DEBUG + CSStack.lock(); + if (function) + stack[(string)function]++; + CSStack.unlock(); +#endif + return; + } + CSRead.unlock(); +#ifdef DEBUG + if (i > MUTEX_TIMEOUT_MILLISECONDS) { + LogWrite(MUTEX__ERROR, 0, "Mutex", "The mutex %s called from %s at line %u timed out waiting for a readlock!", name.c_str(), function ? function : "name_not_provided", line); + LogWrite(MUTEX__ERROR, 0, "Mutex", "The following functions had locks:"); + map::iterator itr; + CSStack.lock(); + for (itr = stack.begin(); itr != stack.end(); itr++) { + if (itr->second > 0 && itr->first.length() > 0) + LogWrite(MUTEX__ERROR, 0, "Mutex", "%s, number of locks = %u", itr->first.c_str(), itr->second); + } + CSStack.unlock(); + i = 0; + continue; + } + i++; +#endif + Sleep(1); + } +} + +void Mutex::releasereadlock(const char* function, int32 line) { + //Wait for the readcount lock + CSRead.lock(); + //Lower the readcount by one, when readcount is 0 writers may start writing + readers--; + CSRead.unlock(); +#ifdef DEBUG + CSStack.lock(); + if (function) { + map::iterator itr = stack.find((string)function); + if (itr != stack.end()) { + if (--(itr->second) == 0) { + stack.erase(itr); + } + } + } + CSStack.unlock(); +#endif +} + +bool Mutex::tryreadlock(const char* function) { + //This returns true if able to instantly obtain a readlock, false if not + CSRead.lock(); + if (!writing) { + readers++; + CSRead.unlock(); + } + else { + CSRead.unlock(); + return false; + } + +#ifdef DEBUG + CSStack.lock(); + if (function) + stack[(string)function]++; + CSStack.unlock(); +#endif + + return true; +} + +void Mutex::writelock(const char* function, int32 line) { + //Wait until the writer lock becomes available, then we can be the only writer! +#ifdef DEBUG + int32 i = 0; +#endif + while (!CSWrite.trylock()) { +#ifdef DEBUG + if (i > MUTEX_TIMEOUT_MILLISECONDS) { + LogWrite(MUTEX__ERROR, 0, "Mutex", "The mutex %s called from %s at line %u timed out waiting on another writelock!", name.c_str(), function ? function : "name_not_provided", line); + LogWrite(MUTEX__ERROR, 0, "Mutex", "The following functions had locks:"); + map::iterator itr; + CSStack.lock(); + for (itr = stack.begin(); itr != stack.end(); itr++) { + if (itr->second > 0 && itr->first.length() > 0) + LogWrite(MUTEX__ERROR, 0, "Mutex", "%s, number of locks = %u", itr->first.c_str(), itr->second); + } + CSStack.unlock(); + i = 0; + continue; + } + i++; +#endif + Sleep(1); + } + waitReaders(function, line); +#ifdef DEBUG + CSStack.lock(); + if (function) + stack[(string)function]++; + CSStack.unlock(); +#endif +} + +void Mutex::releasewritelock(const char* function, int32 line) { + //Wait for the readcount lock + CSRead.lock(); + //Readers are aloud again + writing = false; + CSRead.unlock(); + //Allow other writers to write + CSWrite.unlock(); +#ifdef DEBUG + CSStack.lock(); + if (function) { + map::iterator itr = stack.find((string)function); + if (itr != stack.end()) { + if (--(itr->second) == 0) { + stack.erase(itr); + } + } + } + CSStack.unlock(); +#endif +} + +bool Mutex::trywritelock(const char* function) { + //This returns true if able to instantly obtain a writelock, false if not + if (CSWrite.trylock()) { + CSRead.lock(); + if (readers == 0) + writing = true; + CSRead.unlock(); + if (!writing) { + CSWrite.unlock(); + return false; + } + } + else + return false; + +#ifdef DEBUG + CSStack.lock(); + if (function) + stack[(string)function]++; + CSStack.unlock(); +#endif + + return true; +} + +void Mutex::waitReaders(const char* function, int32 line) +{ + //Wait for all current readers to stop, then we can write! +#ifdef DEBUG + int32 i = 0; +#endif + while (true) + { + CSRead.lock(); + if (readers == 0) + { + writing = true; + CSRead.unlock(); + break; + } + CSRead.unlock(); +#ifdef DEBUG + if (i > MUTEX_TIMEOUT_MILLISECONDS) { + LogWrite(MUTEX__ERROR, 0, "Mutex", "The mutex %s called from %s at line %u timed out while waiting on readers!", name.c_str(), function ? function : "name_not_provided", line); + LogWrite(MUTEX__ERROR, 0, "Mutex", "The following functions had locks:"); + map::iterator itr; + CSStack.lock(); + for (itr = stack.begin(); itr != stack.end(); itr++) { + if (itr->second > 0 && itr->first.length() > 0) + LogWrite(MUTEX__ERROR, 0, "Mutex", "%s, number of locks = %u", itr->first.c_str(), itr->second); + } + CSStack.unlock(); + i = 0; + continue; + } + i++; +#endif + Sleep(1); + } +} + +LockMutex::LockMutex(Mutex* in_mut, bool iLock) { + mut = in_mut; + locked = iLock; + if (locked) { + mut->lock(); + } +} + +LockMutex::~LockMutex() { + if (locked) { + mut->unlock(); + } +} + +void LockMutex::unlock() { + if (locked) + mut->unlock(); + locked = false; +} + +void LockMutex::lock() { + if (!locked) + mut->lock(); + locked = true; +} + +CriticalSection::CriticalSection(int attribute) { +#ifdef WIN32 + InitializeCriticalSection(&CSMutex); +#else + pthread_mutexattr_init(&type_attribute); + switch (attribute) + { + case MUTEX_ATTRIBUTE_FAST: + pthread_mutexattr_settype(&type_attribute, PTHREAD_MUTEX_FAST_NP); + break; + case MUTEX_ATTRIBUTE_RECURSIVE: + pthread_mutexattr_settype(&type_attribute, PTHREAD_MUTEX_RECURSIVE_NP); + break; + case MUTEX_ATTRIBUTE_ERRORCHK: + pthread_mutexattr_settype(&type_attribute, PTHREAD_MUTEX_ERRORCHECK_NP); + break; + default: + LogWrite(MUTEX__DEBUG, 0, "Critical Section", "Invalid mutex attribute type! Using PTHREAD_MUTEX_FAST_NP"); + pthread_mutexattr_settype(&type_attribute, PTHREAD_MUTEX_FAST_NP); + break; + } + pthread_mutex_init(&CSMutex, &type_attribute); +#endif +} + +CriticalSection::~CriticalSection() { +#ifdef WIN32 + DeleteCriticalSection(&CSMutex); +#else + pthread_mutex_destroy(&CSMutex); + pthread_mutexattr_destroy(&type_attribute); +#endif +} + +void CriticalSection::lock() { + //Waits for a lock on this critical section +#ifdef WIN32 + EnterCriticalSection(&CSMutex); +#else + pthread_mutex_lock(&CSMutex); +#endif +} + +void CriticalSection::unlock() { + //Gets rid of one of the current thread's locks on this critical section +#ifdef WIN32 + LeaveCriticalSection(&CSMutex); +#else + pthread_mutex_unlock(&CSMutex); +#endif +} + +bool CriticalSection::trylock() { + //Returns true if able to instantly get a lock on this critical section, false if not +#ifdef WIN32 + return TryEnterCriticalSection(&CSMutex); +#else + return (pthread_mutex_trylock(&CSMutex) == 0); +#endif +} + diff --git a/old/common/Mutex.h b/old/common/Mutex.h new file mode 100644 index 0000000..ae96333 --- /dev/null +++ b/old/common/Mutex.h @@ -0,0 +1,103 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef MYMUTEX_H +#define MYMUTEX_H +#ifdef WIN32 + #include + #include +#else + #include + #include "../common/unix.h" +#endif +#include "../common/types.h" +#include +#include + +#define MUTEX_ATTRIBUTE_FAST 1 +#define MUTEX_ATTRIBUTE_RECURSIVE 2 +#define MUTEX_ATTRIBUTE_ERRORCHK 3 +#define MUTEX_TIMEOUT_MILLISECONDS 10000 + +class CriticalSection { +public: + CriticalSection(int attribute = MUTEX_ATTRIBUTE_FAST); + ~CriticalSection(); + void lock(); + void unlock(); + bool trylock(); +private: +#ifdef WIN32 + CRITICAL_SECTION CSMutex; +#else + pthread_mutex_t CSMutex; + pthread_mutexattr_t type_attribute; +#endif +}; + +class Mutex { +public: + Mutex(); + ~Mutex(); + + void lock(); + void unlock(); + bool trylock(); + + void readlock(const char* function = 0, int32 line = 0); + void releasereadlock(const char* function = 0, int32 line = 0); + bool tryreadlock(const char* function = 0); + + void writelock(const char* function = 0, int32 line = 0); + void releasewritelock(const char* function = 0, int32 line = 0); + bool trywritelock(const char* function = 0); + + void waitReaders(const char* function = 0, int32 line = 0); + + void SetName(string in_name); +private: + CriticalSection CSRead; + CriticalSection CSWrite; + CriticalSection* CSLock; + +#ifdef DEBUG //Used for debugging only + CriticalSection CSStack; + map stack; +#endif + + int readers; + bool writing; + volatile bool mlocked; + string name; +}; + + +class LockMutex { +public: + LockMutex(Mutex* in_mut, bool iLock = true); + ~LockMutex(); + void unlock(); + void lock(); +private: + bool locked; + Mutex* mut; +}; + + +#endif diff --git a/old/common/packet/packet_struct.cpp b/old/common/PacketStruct.cpp similarity index 61% rename from old/common/packet/packet_struct.cpp rename to old/common/PacketStruct.cpp index 53e07f9..6dcdff2 100644 --- a/old/common/packet/packet_struct.cpp +++ b/old/common/PacketStruct.cpp @@ -1,14 +1,482 @@ -// Copyright (C) 2007 EQ2EMulator Development Team, GPL v3+ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) -#include "packet_struct.hpp" + This file is part of EQ2Emulator. + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include +#include +#include "PacketStruct.h" +#include "ConfigReader.h" +#include "../common/debug.h" +#include "MiscFunctions.h" +#include "Log.h" + +#ifdef WORLD +#include "../WorldServer/Items/Items.h" +#include "../WorldServer/Player.h" +#include "../WorldServer/World.h" +#endif + +extern ConfigReader configReader; using namespace std; -// Memory management implementation -void PacketStruct::deleteDataStructs(vector* data_structs) -{ - if (!data_structs || data_structs->size() == 0) - return; +DataStruct::DataStruct() { + item_size = 0; + type = 0; + type2 = 0; + length = 1; + if_flag_set = false; + if_flag_not_set = false; + if_set = false; + if_not_set = false; + if_not_equals = false; + if_equals = false; + is_set = false; + optional = false; + oversized = 0; + oversized_byte = 0; + add = false; + addType = 0; + maxArraySize = 0; + default_value = 0; +} +DataStruct::DataStruct(DataStruct* data_struct) { + type = data_struct->GetType(); + type2 = data_struct->GetType2(); + length = data_struct->GetLength(); + name = data_struct->GetName(); + array_size_variable = data_struct->array_size_variable; + default_value = data_struct->default_value; + add = true; + addType = type; + oversized = data_struct->GetOversized(); + oversized_byte = data_struct->GetOversizedByte(); + maxArraySize = data_struct->GetMaxArraySize(); + if_flag_set = false; + if_flag_not_set = false; + if_set = false; + if_not_set = false; + if_not_equals = false; + if_equals = false; + optional = false; + if (data_struct->GetIfSet()) + SetIfSetVariable(data_struct->GetIfSetVariable()); + if (data_struct->GetIfNotSet()) + SetIfNotSetVariable(data_struct->GetIfNotSetVariable()); + if (data_struct->GetIfNotEquals()) + SetIfNotEqualsVariable(data_struct->GetIfNotEqualsVariable()); + if (data_struct->GetIfEquals()) + SetIfEqualsVariable(data_struct->GetIfEqualsVariable()); + if (data_struct->GetIfFlagSet()) + SetIfFlagSetVariable(data_struct->GetIfFlagSetVariable()); + if (data_struct->GetIfFlagNotSet()) + SetIfFlagNotSetVariable(data_struct->GetIfFlagNotSetVariable()); + item_size = 0; + is_set = false; +} +DataStruct::DataStruct(const char* new_name, const char* new_type, int32 new_length, const char* new_type2) { + name = string(new_name); + type = 0; + type2 = 0; + SetType(new_type, &type); + if (new_type2) + SetType(new_type2, &type2); + length = new_length; + add = true; + addType = type; + if_set = false; + if_not_set = false; + is_set = false; + if_not_equals = false; + item_size = 0; +} +const char* DataStruct::GetArraySizeVariable() { + return array_size_variable.c_str(); +} +void DataStruct::SetArraySizeVariable(const char* new_name) { + array_size_variable = string(new_name); +} +void DataStruct::SetType(const char* new_type, int8* output_type) { + if (strlen(new_type) > 3 && strncasecmp("int", new_type, 3) == 0) { + if (strncasecmp("int8", new_type, 4) == 0) + *output_type = DATA_STRUCT_INT8; + else if (strncasecmp("int16", new_type, 5) == 0) + *output_type = DATA_STRUCT_INT16; + else if (strncasecmp("int32", new_type, 5) == 0) + *output_type = DATA_STRUCT_INT32; + else if (strncasecmp("int64", new_type, 5) == 0) + *output_type = DATA_STRUCT_INT64; + } + else if (strlen(new_type) > 4 && strncasecmp("sint", new_type, 4) == 0) { + if (strncasecmp("sint8", new_type, 5) == 0) + *output_type = DATA_STRUCT_SINT8; + else if (strncasecmp("sint16", new_type, 6) == 0) + *output_type = DATA_STRUCT_SINT16; + else if (strncasecmp("sint32", new_type, 6) == 0) + *output_type = DATA_STRUCT_SINT32; + else if (strncasecmp("sint64", new_type, 6) == 0) + *output_type = DATA_STRUCT_SINT64; + } + else if (strlen(new_type) == 4 && strncasecmp("char", new_type, 4) == 0) + *output_type = DATA_STRUCT_CHAR; + else if (strlen(new_type) == 5 && strncasecmp("float", new_type, 5) == 0) + *output_type = DATA_STRUCT_FLOAT; + else if (strlen(new_type) == 6 && strncasecmp("double", new_type, 6) == 0) + *output_type = DATA_STRUCT_DOUBLE; + else if (strlen(new_type) >= 5 && strncasecmp("EQ2_", new_type, 4) == 0) { + if (strncasecmp("EQ2_8", new_type, 5) == 0) + *output_type = DATA_STRUCT_EQ2_8BIT_STRING; + else if (strncasecmp("EQ2_16", new_type, 6) == 0) + *output_type = DATA_STRUCT_EQ2_16BIT_STRING; + else if (strncasecmp("EQ2_32", new_type, 6) == 0) + *output_type = DATA_STRUCT_EQ2_32BIT_STRING; + else if (strncasecmp("EQ2_E", new_type, 5) == 0) + *output_type = DATA_STRUCT_EQUIPMENT; + else if (strncasecmp("EQ2_C", new_type, 5) == 0) + *output_type = DATA_STRUCT_COLOR; + else if (strncasecmp("EQ2_I", new_type, 5) == 0) + *output_type = DATA_STRUCT_ITEM; + } + else if (strlen(new_type) >= 5) { + if (strncasecmp("Array", new_type, 5) == 0) + *output_type = DATA_STRUCT_ARRAY; + } + else + LogWrite(PACKET__ERROR, 0, "Packet", "Invalid Type: %s", new_type); +} +DataStruct::DataStruct(const char* new_name, int32 new_length) { + name = string(new_name); + length = new_length; + if_set = false; + if_not_set = false; + is_set = false; + item_size = 0; +} +DataStruct::DataStruct(const char* new_name, int8 new_type, int32 new_length, int8 new_type2) { + name = string(new_name); + type = new_type; + length = new_length; + type2 = new_type2; + addType = type; + if_set = false; + if_not_set = false; + is_set = false; + item_size = 0; +} +void DataStruct::SetType(int8 new_type) { + type = new_type; + addType = type; +} +void DataStruct::SetMaxArraySize(int8 size) { + maxArraySize = size; +} +void DataStruct::SetOversized(int8 val) { + oversized = val; +} +void DataStruct::SetDefaultValue(int8 new_val) { + default_value = new_val; +} +void DataStruct::SetName(const char* new_name) { + name = string(new_name); +} +void DataStruct::SetLength(int32 new_length) { + length = new_length; +} +void DataStruct::SetOversizedByte(int8 val) { + oversized_byte = val; +} +void DataStruct::SetItemSize(int32 val) { + item_size = val; + if(item_size) + is_set = true; + else + is_set = false; +} +void DataStruct::SetIfEqualsVariable(const char* variable) { + if (variable) { + if_equals = true; + if_equals_variable = string(variable); + } + else + if_equals = false; +} +void DataStruct::SetIfNotEqualsVariable(const char* variable) { + if (variable) { + if_not_equals = true; + if_not_equals_variable = string(variable); + } + else + if_not_equals = false; +} +void DataStruct::SetIfFlagNotSetVariable(const char* variable) { + if (variable) { + if_flag_not_set = true; + if_flag_not_set_variable = string(variable); + } + else + if_flag_not_set = false; +} +void DataStruct::SetIfFlagSetVariable(const char* variable) { + if (variable) { + if_flag_set = true; + if_flag_set_variable = string(variable); + } + else + if_flag_set = false; +} +void DataStruct::SetIfSetVariable(const char* variable) { + if (variable) { + if_set = true; + if_set_variable = string(variable); + } + else + if_set = false; +} +void DataStruct::SetIfNotSetVariable(const char* variable) { + if (variable) { + if_not_set = true; + if_not_set_variable = string(variable); + } + else + if_not_set = false; +} +void DataStruct::SetIsSet(bool val) { + is_set = val; +} +bool DataStruct::IsSet() { + return is_set; +} +void DataStruct::SetIsOptional(bool val) { + optional = val; +} +bool DataStruct::IsOptional() { + return optional; +} +int32 DataStruct::GetItemSize() { + return item_size; +} +bool DataStruct::GetIfSet() { + return if_set; +} +const char* DataStruct::GetIfSetVariable() { + if (if_set_variable.length() > 0) + return if_set_variable.c_str(); + return 0; +} +bool DataStruct::GetIfNotSet() { + return if_not_set; +} +const char* DataStruct::GetIfNotSetVariable() { + if (if_not_set_variable.length() > 0) + return if_not_set_variable.c_str(); + return 0; +} +bool DataStruct::GetIfEquals() { + return if_equals; +} +const char* DataStruct::GetIfEqualsVariable() { + if (if_equals_variable.length() > 0) + return if_equals_variable.c_str(); + return 0; +} +bool DataStruct::GetIfNotEquals() { + return if_not_equals; +} +const char* DataStruct::GetIfNotEqualsVariable() { + if (if_not_equals_variable.length() > 0) + return if_not_equals_variable.c_str(); + return 0; +} +bool DataStruct::GetIfFlagSet() { + return if_flag_set; +} +const char* DataStruct::GetIfFlagSetVariable() { + if (if_flag_set_variable.length() > 0) + return if_flag_set_variable.c_str(); + return 0; +} +bool DataStruct::GetIfFlagNotSet() { + return if_flag_not_set; +} +const char* DataStruct::GetIfFlagNotSetVariable() { + if (if_flag_not_set_variable.length() > 0) + return if_flag_not_set_variable.c_str(); + return 0; +} +int8 DataStruct::GetDefaultValue() { + return default_value; +} +int8 DataStruct::GetType() { + return type; +} +int8 DataStruct::GetType2() { + return type2; +} +const char* DataStruct::GetName() { + return name.c_str(); +} +int8 DataStruct::GetOversized() { + return oversized; +} +int8 DataStruct::GetOversizedByte() { + return oversized_byte; +} +int8 DataStruct::GetMaxArraySize() { + return maxArraySize; +} +int32 DataStruct::GetLength() { + return length; +} +string DataStruct::GetStringName() { + return name; +} +bool DataStruct::AddToStruct() { + return add; +} +void DataStruct::SetAddToStruct(bool val) { + add = val; +} +int8 DataStruct::GetAddType() { + return addType; +} +void DataStruct::SetAddType(int8 new_type) { + addType = new_type; +} +string DataStruct::AppendVariable(string orig, const char* val) { + if (!val) + return orig; + if(orig.length() == 0) + return string(val); + if (orig.find(",") < 0xFFFFFFFF) { //has more than one already + string valstr = string(val); + vector* varnames = SplitString(orig, ','); + if (varnames) { + for (int32 i = 0; i < varnames->size(); i++) { + if (valstr.compare(varnames->at(i)) == 0) { + return orig; //already in the variable, no need to append + } + } + safe_delete(varnames); + } + } + return orig.append(",").append(val); +} +int32 DataStruct::GetDataSizeInBytes() { + int32 ret = 0; + switch (type) { + case DATA_STRUCT_INT8: + ret = length * sizeof(int8); + break; + case DATA_STRUCT_INT16: + ret = length * sizeof(int16); + break; + case DATA_STRUCT_INT32: + ret = length * sizeof(int32); + break; + case DATA_STRUCT_INT64: + ret = length * sizeof(int64); + break; + case DATA_STRUCT_SINT8: + ret = length * sizeof(sint8); + break; + case DATA_STRUCT_SINT16: + ret = length * sizeof(sint16); + break; + case DATA_STRUCT_SINT32: + ret = length * sizeof(sint32); + break; + case DATA_STRUCT_SINT64: + ret = length * sizeof(sint64); + break; + case DATA_STRUCT_FLOAT: + ret = length * sizeof(float); + break; + case DATA_STRUCT_DOUBLE: + ret = length * sizeof(double); + break; + case DATA_STRUCT_ARRAY: + // Array elements won't have a size so get out now to avoid the warning. + break; + default: + LogWrite(PACKET__WARNING, 0, "DataStruct", "Tried retrieving a data size from an unsupported data struct type in GetDataSizeInBytes()"); + break; + } + return ret; +} + +PacketStruct::PacketStruct(PacketStruct* packet, int16 in_client_version) { + parent = packet->parent; + client_version = in_client_version; + vector::iterator itr2; + name = packet->name; + + for (itr2 = packet->structs.begin(); itr2 != packet->structs.end(); itr2++) { + add(new DataStruct(*itr2)); + } + vector::iterator itr; + for (itr = packet->flags.begin(); itr != packet->flags.end(); itr++) { + AddFlag((*itr).c_str()); + } + sub_packet = false; + opcode = packet->opcode; + version = packet->version; + opcode_type = packet->opcode_type; + sub_packet_size = 1; + + + addPacketArrays(packet); +} + +PacketStruct::PacketStruct() { + parent = 0; + opcode = OP_Unknown; + opcode_type = string(""); +} + +PacketStruct::PacketStruct(PacketStruct* packet, bool sub) { + vector::iterator itr2; + + for (itr2 = packet->structs.begin(); itr2 != packet->structs.end(); itr2++) { + add(new DataStruct(*itr2)); + } + vector::iterator itr; + for (itr = packet->flags.begin(); itr != packet->flags.end(); itr++) { + AddFlag((*itr).c_str()); + } + sub_packet = sub; + opcode = packet->opcode; + version = packet->version; + opcode_type = packet->opcode_type; + name = packet->name; + sub_packet_size = 0; + parent = 0; +} +PacketStruct::~PacketStruct() { + deleteDataStructs(&structs); + deleteDataStructs(&orig_structs); + deletePacketArrays(this); + struct_map.clear(); + struct_data.clear(); + flags.clear(); +} + +void PacketStruct::deleteDataStructs(vector* data_structs) { + if ( !data_structs || data_structs->size() == 0 ) + return; DataStruct* data = 0; vector::iterator itr; @@ -16,6 +484,7 @@ void PacketStruct::deleteDataStructs(vector* data_structs) data = *itr; void* ptr = GetStructPointer(data); + // stop the struct_data from growing with old data/ptr info, memory leaking and eventual buffer overflow (crash) map::iterator datastr = struct_data.find(data); if (datastr != struct_data.end()) struct_data.erase(datastr); @@ -111,9 +580,7 @@ void PacketStruct::deleteDataStructs(vector* data_structs) safe_delete(data); } } - -void PacketStruct::deletePacketArrays(PacketStruct* packet) -{ +void PacketStruct::deletePacketArrays(PacketStruct* packet) { if (!packet) return; vector::iterator itr; @@ -127,8 +594,16 @@ void PacketStruct::deletePacketArrays(PacketStruct* packet) packet->orig_packets.clear(); } -void PacketStruct::addPacketArrays(PacketStruct* packet) -{ +void PacketStruct::renameSubstructArray(const char* substruct, int32 index) { + vector::iterator itr; + char tmp[10] = { 0 }; + sprintf(tmp, "%i", index); + for (itr = arrays.begin(); itr != arrays.end(); itr++) { + (*itr)->SetName(string(substruct).append("_").append((*itr)->GetName()).append("_").append(tmp).c_str()); + } +} + +void PacketStruct::addPacketArrays(PacketStruct* packet) { if (!packet) return; vector::iterator itr; @@ -140,9 +615,67 @@ void PacketStruct::addPacketArrays(PacketStruct* packet) } } -// Core data setter implementations -void PacketStruct::setDataType(DataStruct* data_struct, sint8 data, int32 index) -{ +bool PacketStruct::IsStringValueType(string in_name, int32 index) { + DataStruct* data = findStruct(in_name.c_str(), index); + switch (data->GetType()) { + case DATA_STRUCT_CHAR: + case DATA_STRUCT_EQ2_8BIT_STRING: + case DATA_STRUCT_EQ2_16BIT_STRING: + case DATA_STRUCT_EQ2_32BIT_STRING: + return true; + } + return false; +} + +bool PacketStruct::IsColorValueType(string in_name, int32 index) { + DataStruct* data = findStruct(in_name.c_str(), index); + if (data->GetType() == DATA_STRUCT_COLOR) + return true; + else + return false; +} +void PacketStruct::setColor(DataStruct* data, int8 red, int8 green, int8 blue, int32 index = 0) { + if (data && data->GetType() == DATA_STRUCT_COLOR) { + EQ2_Color* color = (EQ2_Color*)GetStructPointer(data); + color[index].blue = blue; + color[index].red = red; + color[index].green = green; + if (blue > 0 || green > 0 || red > 0) + data->SetIsSet(true); + } +} +void PacketStruct::setEquipment(DataStruct* data, int16 type, int8 c_red, int8 c_blue, int8 c_green, int8 h_red, int8 h_blue, int8 h_green, int32 index) { + if (data) { + EQ2_EquipmentItem* equipment = (EQ2_EquipmentItem*)GetStructPointer(data); + EQ2_Color* color = (EQ2_Color*)&equipment[index].color; + EQ2_Color* highlight = (EQ2_Color*)&equipment[index].highlight; + equipment[index].type = type; + color->blue = c_blue; + color->red = c_red; + color->green = c_green; + highlight->blue = h_blue; + highlight->red = h_red; + highlight->green = h_green; + if (c_red > 0 || c_blue > 0 || c_green > 0 || h_red > 0 || h_blue > 0 || h_green > 0) + data->SetIsSet(true); + } +} +void PacketStruct::add(PacketStruct* packet_struct) { + packet_struct->parent = this; + arrays.push_back(packet_struct); +} +const char* PacketStruct::GetOpcodeType() { + return opcode_type.c_str(); +} + +void PacketStruct::SetOpcodeType(const char* in_type) { + if (in_type) + opcode_type = string(in_type); + else + opcode_type = string(""); +} + +void PacketStruct::setDataType(DataStruct* data_struct, sint8 data, int32 index) { if (data_struct) { sint8* ptr = (sint8*)GetStructPointer(data_struct); ptr[index] = data; @@ -150,9 +683,7 @@ void PacketStruct::setDataType(DataStruct* data_struct, sint8 data, int32 index) data_struct->SetIsSet(true); } } - -void PacketStruct::setDataType(DataStruct* data_struct, sint16 data, int32 index) -{ +void PacketStruct::setDataType(DataStruct* data_struct, sint16 data, int32 index) { if (data_struct) { sint16* ptr = (sint16*)GetStructPointer(data_struct); ptr[index] = data; @@ -160,9 +691,7 @@ void PacketStruct::setDataType(DataStruct* data_struct, sint16 data, int32 index data_struct->SetIsSet(true); } } - -void PacketStruct::setDataType(DataStruct* data_struct, sint32 data, int32 index) -{ +void PacketStruct::setDataType(DataStruct* data_struct, sint32 data, int32 index) { if (data_struct) { sint32* ptr = (sint32*)GetStructPointer(data_struct); ptr[index] = data; @@ -170,9 +699,7 @@ void PacketStruct::setDataType(DataStruct* data_struct, sint32 data, int32 index data_struct->SetIsSet(true); } } - -void PacketStruct::setDataType(DataStruct* data_struct, sint64 data, int32 index) -{ +void PacketStruct::setDataType(DataStruct* data_struct, sint64 data, int32 index) { if (data_struct) { sint64* ptr = (sint64*)GetStructPointer(data_struct); ptr[index] = data; @@ -180,9 +707,7 @@ void PacketStruct::setDataType(DataStruct* data_struct, sint64 data, int32 index data_struct->SetIsSet(true); } } - -void PacketStruct::setDataType(DataStruct* data_struct, char data, int32 index) -{ +void PacketStruct::setDataType(DataStruct* data_struct, char data, int32 index) { if (data_struct) { char* ptr = (char*)GetStructPointer(data_struct); ptr[index] = data; @@ -190,9 +715,7 @@ void PacketStruct::setDataType(DataStruct* data_struct, char data, int32 index) data_struct->SetIsSet(true); } } - -void PacketStruct::setDataType(DataStruct* data_struct, int8 data, int32 index) -{ +void PacketStruct::setDataType(DataStruct* data_struct, int8 data, int32 index) { if (data_struct) { int8* ptr = (int8*)GetStructPointer(data_struct); ptr[index] = data; @@ -200,9 +723,7 @@ void PacketStruct::setDataType(DataStruct* data_struct, int8 data, int32 index) data_struct->SetIsSet(true); } } - -void PacketStruct::setDataType(DataStruct* data_struct, int16 data, int32 index) -{ +void PacketStruct::setDataType(DataStruct* data_struct, int16 data, int32 index) { if (data_struct) { int16* ptr = (int16*)GetStructPointer(data_struct); ptr[index] = data; @@ -210,9 +731,7 @@ void PacketStruct::setDataType(DataStruct* data_struct, int16 data, int32 index) data_struct->SetIsSet(true); } } - -void PacketStruct::setDataType(DataStruct* data_struct, int32 data, int32 index) -{ +void PacketStruct::setDataType(DataStruct* data_struct, int32 data, int32 index) { if (data_struct) { int32* ptr = (int32*)GetStructPointer(data_struct); ptr[index] = data; @@ -220,9 +739,7 @@ void PacketStruct::setDataType(DataStruct* data_struct, int32 data, int32 index) data_struct->SetIsSet(true); } } - -void PacketStruct::setDataType(DataStruct* data_struct, int64 data, int32 index) -{ +void PacketStruct::setDataType(DataStruct* data_struct, int64 data, int32 index) { if (data_struct) { int64* ptr = (int64*)GetStructPointer(data_struct); ptr[index] = data; @@ -230,9 +747,7 @@ void PacketStruct::setDataType(DataStruct* data_struct, int64 data, int32 index) data_struct->SetIsSet(true); } } - -void PacketStruct::setDataType(DataStruct* data_struct, float data, int32 index) -{ +void PacketStruct::setDataType(DataStruct* data_struct, float data, int32 index) { if (data_struct) { float* ptr = (float*)GetStructPointer(data_struct); ptr[index] = data; @@ -240,9 +755,7 @@ void PacketStruct::setDataType(DataStruct* data_struct, float data, int32 index) data_struct->SetIsSet(true); } } - -void PacketStruct::setDataType(DataStruct* data_struct, double data, int32 index) -{ +void PacketStruct::setDataType(DataStruct* data_struct, double data, int32 index) { if (data_struct) { double* ptr = (double*)GetStructPointer(data_struct); ptr[index] = data; @@ -250,9 +763,7 @@ void PacketStruct::setDataType(DataStruct* data_struct, double data, int32 index data_struct->SetIsSet(true); } } - -void PacketStruct::setData(DataStruct* data_struct, EQ2_8BitString* input_string, int32 index, bool use_second_type) -{ +void PacketStruct::setData(DataStruct* data_struct, EQ2_8BitString* input_string, int32 index, bool use_second_type) { if (data_struct) { EQ2_8BitString* tmp = (EQ2_8BitString*)GetStructPointer(data_struct); tmp->data = input_string->data; @@ -261,9 +772,7 @@ void PacketStruct::setData(DataStruct* data_struct, EQ2_8BitString* input_string data_struct->SetIsSet(true); } } - -void PacketStruct::setData(DataStruct* data_struct, EQ2_16BitString* input_string, int32 index, bool use_second_type) -{ +void PacketStruct::setData(DataStruct* data_struct, EQ2_16BitString* input_string, int32 index, bool use_second_type) { if (data_struct) { EQ2_16BitString* tmp = (EQ2_16BitString*)GetStructPointer(data_struct); tmp->data = input_string->data; @@ -272,9 +781,7 @@ void PacketStruct::setData(DataStruct* data_struct, EQ2_16BitString* input_strin data_struct->SetIsSet(true); } } - -void PacketStruct::setData(DataStruct* data_struct, EQ2_32BitString* input_string, int32 index, bool use_second_type) -{ +void PacketStruct::setData(DataStruct* data_struct, EQ2_32BitString* input_string, int32 index, bool use_second_type) { if (data_struct) { EQ2_32BitString* tmp = (EQ2_32BitString*)GetStructPointer(data_struct); tmp->data = input_string->data; @@ -283,82 +790,7 @@ void PacketStruct::setData(DataStruct* data_struct, EQ2_32BitString* input_strin data_struct->SetIsSet(true); } } - -// String functions implementation -void PacketStruct::setSmallString(DataStruct* data_struct, const char* text, int32 index) -{ - EQ2_8BitString* string_data = new EQ2_8BitString; - string_data->data = string(text); - string_data->size = string_data->data.length(); - setData(data_struct, string_data, index); - safe_delete(string_data); -} - -void PacketStruct::setMediumString(DataStruct* data_struct, const char* text, int32 index) -{ - EQ2_16BitString* string_data = new EQ2_16BitString; - string_data->data = string(text); - string_data->size = string_data->data.length(); - setData(data_struct, string_data, index); - safe_delete(string_data); -} - -void PacketStruct::setLargeString(DataStruct* data_struct, const char* text, int32 index) -{ - EQ2_32BitString* string_data = new EQ2_32BitString; - string_data->data = string(text); - string_data->size = string_data->data.length(); - setData(data_struct, string_data, index); - safe_delete(string_data); -} - -void PacketStruct::setSmallStringByName(const char* name, const char* text, int32 index) -{ - setSmallString(findStruct(name, index), text, index); -} - -void PacketStruct::setMediumStringByName(const char* name, const char* text, int32 index) -{ - setMediumString(findStruct(name, index), text, index); -} - -void PacketStruct::setLargeStringByName(const char* name, const char* text, int32 index) -{ - setLargeString(findStruct(name, index), text, index); -} - -// Continue with remaining implementations... -// [Due to length constraints, I'll focus on the key functions. The full implementation would include all methods from the original .cpp file] - -bool PacketStruct::SetOpcode(const char* new_opcode) -{ - opcode = EQOpcodeManager[0]->NameSearch(new_opcode); - if (opcode == OP_Unknown) { -#ifndef MINILOGIN - LogWrite(PACKET__ERROR, 0, "Packet", "Warning: PacketStruct '%s' uses an unknown opcode named '%s', this struct cannot be serialized directly.", GetName(), new_opcode); -#endif - return false; - } - return true; -} - -EQ2Packet* PacketStruct::serialize() -{ - serializePacket(); - - if (GetOpcode() != OP_Unknown) - return new EQ2Packet(GetOpcode(), getData(), getDataSize()); - else { -#ifndef MINILOGIN - LogWrite(PACKET__ERROR, 0, "Packet", "Warning: PacketStruct '%s' uses an unknown opcode and cannot be serialized directly.", GetName()); -#endif - return 0; - } -} - -// Core packet management functions -void PacketStruct::add(DataStruct* data) -{ +void PacketStruct::add(DataStruct* data) { structs.push_back(data); struct_map[data->GetStringName()] = data; switch (data->GetType()) { @@ -561,15 +993,7 @@ void PacketStruct::add(DataStruct* data) } } } - -void PacketStruct::add(PacketStruct* packet_struct) -{ - packet_struct->parent = this; - arrays.push_back(packet_struct); -} - -void PacketStruct::remove(DataStruct* data) -{ +void PacketStruct::remove(DataStruct* data) { vector::iterator itr; for (itr = structs.begin(); itr != structs.end(); itr++) { if (data == (*itr)) { @@ -579,28 +1003,11 @@ void PacketStruct::remove(DataStruct* data) } } } - -void PacketStruct::remove(const char* name) -{ - DataStruct* data = 0; - vector::iterator itr; - for (itr = structs.begin(); itr != structs.end(); itr++) { - data = *itr; - if (strcmp(name, data->GetName()) == 0) { - structs.erase(itr); - safe_delete(data); - return; - } - } -} - -DataStruct* PacketStruct::findStruct(const char* name, int32 index) -{ +DataStruct* PacketStruct::findStruct(const char* name, int32 index) { return findStruct(name, index, index); } -DataStruct* PacketStruct::findStruct(const char* name, int32 index1, int32 index2) -{ +DataStruct* PacketStruct::findStruct(const char* name, int32 index1, int32 index2) { DataStruct* data = 0; if (struct_map.count(string(name)) > 0) { @@ -608,6 +1015,7 @@ DataStruct* PacketStruct::findStruct(const char* name, int32 index1, int32 index if (data && index2 < data->GetLength()) return data; } + vector::iterator itr; PacketStruct* packet = 0; vector::iterator itr2; @@ -630,93 +1038,439 @@ DataStruct* PacketStruct::findStruct(const char* name, int32 index1, int32 index } return 0; } - -// Color manipulation functions -void PacketStruct::setColor(DataStruct* data, int8 red, int8 green, int8 blue, int32 index) -{ - if (data && data->GetType() == DATA_STRUCT_COLOR) { - EQ2_Color* color = (EQ2_Color*)GetStructPointer(data); - color[index].blue = blue; - color[index].red = red; - color[index].green = green; - if (blue > 0 || green > 0 || red > 0) - data->SetIsSet(true); +void PacketStruct::remove(const char* name) { + DataStruct* data = 0; + vector::iterator itr; + for (itr = structs.begin(); itr != structs.end(); itr++) { + data = *itr; + if (strcmp(name, data->GetName()) == 0) { + structs.erase(itr); + safe_delete(data); + return; + } } } - -void PacketStruct::setColorByName(const char* name, int8 red, int8 green, int8 blue, int32 index) -{ - setColor(findStruct(name, index), red, green, blue, index); +string* PacketStruct::serializeString() { + serializePacket(); + return getDataString(); +} +void PacketStruct::setSmallString(DataStruct* data_struct, const char* text, int32 index) { + EQ2_8BitString* string_data = new EQ2_8BitString; + string_data->data = string(text); + string_data->size = string_data->data.length(); + setData(data_struct, string_data, index); + safe_delete(string_data); +} +void PacketStruct::setMediumString(DataStruct* data_struct, const char* text, int32 index) { + EQ2_16BitString* string_data = new EQ2_16BitString; + string_data->data = string(text); + string_data->size = string_data->data.length(); + setData(data_struct, string_data, index); + safe_delete(string_data); +} +void PacketStruct::setLargeString(DataStruct* data_struct, const char* text, int32 index) { + EQ2_32BitString* string_data = new EQ2_32BitString; + string_data->data = string(text); + string_data->size = string_data->data.length(); + setData(data_struct, string_data, index); + safe_delete(string_data); +} +void PacketStruct::setSmallStringByName(const char* name, const char* text, int32 index) { + setSmallString(findStruct(name, index), text, index); +} +void PacketStruct::setMediumStringByName(const char* name, const char* text, int32 index) { + setMediumString(findStruct(name, index), text, index); +} +void PacketStruct::setLargeStringByName(const char* name, const char* text, int32 index) { + setLargeString(findStruct(name, index), text, index); } -void PacketStruct::setColorByName(const char* name, EQ2_Color* data, int32 index) -{ - if (data) - setColorByName(name, data->red, data->green, data->blue, index); +bool PacketStruct::GetVariableIsSet(const char* name) { + DataStruct* ds2 = findStruct(name, 0); + if (!ds2 || !ds2->IsSet()) + return false; + return true; } -void PacketStruct::setColorByName(const char* name, EQ2_Color data, int32 index) -{ - setColorByName(name, data.red, data.green, data.blue, index); +bool PacketStruct::GetVariableIsNotSet(const char* name) { + DataStruct* ds2 = findStruct(name, 0); + if (ds2 && ds2->IsSet()) + return false; + return true; } -void PacketStruct::setColor(DataStruct* data_struct, EQ2_Color data, int32 index) -{ - if (data_struct) { - EQ2_Color* ptr = (EQ2_Color*)struct_data[data_struct]; - ptr[index] = data; +bool PacketStruct::CheckFlagExists(const char* name) { + vector::iterator itr; + for (itr = flags.begin(); itr != flags.end(); itr++) { + if (*itr == string(name)) + return true; } + return false; } -// Equipment manipulation functions -void PacketStruct::setEquipment(DataStruct* data, int16 type, int8 c_red, int8 c_blue, int8 c_green, int8 h_red, int8 h_blue, int8 h_green, int32 index) -{ - if (data) { - EQ2_EquipmentItem* equipment = (EQ2_EquipmentItem*)GetStructPointer(data); - EQ2_Color* color = (EQ2_Color*)&equipment[index].color; - EQ2_Color* highlight = (EQ2_Color*)&equipment[index].highlight; - equipment[index].type = type; - color->blue = c_blue; - color->red = c_red; - color->green = c_green; - highlight->blue = h_blue; - highlight->red = h_red; - highlight->green = h_green; - if (c_red > 0 || c_blue > 0 || c_green > 0 || h_red > 0 || h_blue > 0 || h_green > 0) - data->SetIsSet(true); +void PacketStruct::AddFlag(const char* name) { + if (flags.size() > 0) { + vector::iterator itr; + for (itr = flags.begin(); itr != flags.end(); itr++) { + if (*itr == string(name)) + return; + } } + flags.push_back(string(name)); } -void PacketStruct::setEquipmentByName(const char* name, EQ2_EquipmentItem data, int32 index) -{ - setEquipmentByName(findStruct(name, index), data, index); -} +bool PacketStruct::LoadPacketData(uchar* data, int32 data_len, bool create_color) { + loadedSuccessfully = true; + DataStruct* data_struct = 0; + try { + InitializeLoadData(data, data_len); + vector::iterator itr; -void PacketStruct::setEquipmentByName(const char* name, EQ2_EquipmentItem* data, int32 size) -{ - DataStruct* data_struct = findStruct(name, 0); - if (data_struct) { - for (int32 i = 0; i < size; i++) - setEquipmentByName(data_struct, data[i], i); + for (itr = structs.begin(); itr != structs.end(); itr++) { + data_struct = *itr; + if (!data_struct->AddToStruct()) + continue; + + if (data_struct->GetIfSet() && data_struct->GetIfSetVariable()) { + string varname = string(data_struct->GetIfSetVariable()); + if (varname.find(",") < 0xFFFFFFFF) { + vector* varnames = SplitString(varname, ','); + if (varnames) { + bool should_continue = true; + for (int32 i = 0; i < varnames->size(); i++) { + if (GetVariableIsSet(varnames->at(i).c_str())) { + should_continue = false; + break; + } + } + safe_delete(varnames); + if (should_continue) + continue; + } + } + else { + // Check to see if the variable contains %i, if it does assume we are in an array + // and get the current index from the end of the data struct's name + char name[250] = { 0 }; + if (varname.find("%i") < 0xFFFFFFFF) { + vector* varnames = SplitString(data_struct->GetName(), '_'); + int index = atoi(varnames->at(varnames->size() - 1).c_str()); + sprintf(name, varname.c_str(), index); + } + else + strcpy(name, varname.c_str()); + + if (!GetVariableIsSet(name)) + continue; + } + } + if (data_struct->GetIfNotSet() && data_struct->GetIfNotSetVariable()) { + string varname = string(data_struct->GetIfNotSetVariable()); + if (varname.find(",") < 0xFFFFFFFF) { + vector* varnames = SplitString(varname, ','); + if (varnames) { + bool should_continue = false; + for (int32 i = 0; i < varnames->size(); i++) { + if (!GetVariableIsNotSet(varnames->at(i).c_str())) { + should_continue = true; + break; + } + } + safe_delete(varnames); + if (should_continue) + continue; + } + } + else { + // Check to see if the variable contains %i, if it does assume we are in an array + // and get the current index from the end of the data struct's name + char name[250] = { 0 }; + if (varname.find("%i") < 0xFFFFFFFF) { + vector* varnames = SplitString(data_struct->GetName(), '_'); + int index = atoi(varnames->at(varnames->size() - 1).c_str()); + sprintf(name, varname.c_str(), index); + } + else + strcpy(name, varname.c_str()); + + if (!GetVariableIsNotSet(name)) + continue; + } + } + // Quick implementaion of IfVariableNotEquals + // probably not what it was intended for as it currently just checks to see if the given variable equals 1 + // should probably change it so you can define what the variable should or shouldn't equal + // + // ie: IfVariableNotEquals="stat_type_%i=1" + // would be a check to make sure that stat_type_%i does not equal 1 and if it does exclude this element + if (data_struct->GetIfNotEquals() && data_struct->GetIfNotEqualsVariable()) { + // Get the variable name + string varname = string(data_struct->GetIfNotEqualsVariable()); + char name[250] = { 0 }; + // Check to see if the variable has %i in the name, if it does assume we are in an array and get the current + // index and replace it + if (varname.find("%i") < 0xFFFFFFFF) { + // Get the current index by getting the number at the end of the name + vector* varnames = SplitString(data_struct->GetName(), '_'); + int index = atoi(varnames->at(varnames->size() - 1).c_str()); + + string substr = "stat_type"; + if (strncmp(varname.c_str(), substr.c_str(), strlen(substr.c_str())) == 0) { + // adorn_stat_subtype 18 chars + string temp = varname.substr(12); + char temp2[20] = { 0 }; + int index2 = atoi(temp.c_str()); + itoa(index2, temp2, 10); + varname = varname.substr(0, 12).append(temp2).append("_%i"); + } + sprintf(name, varname.c_str(), index); + safe_delete(varnames); + } + else + strcpy(name, varname.c_str()); + + // Get the data for the variable + int16 value = 0; + DataStruct* data_struct2 = findStruct(name, 0); + value = getType_int16(data_struct2); + // Hack for items as it is the only struct that currently uses IfVariableNotEquals + if (value == 1) + continue; + } + // copy and paste of the code above for IfEquals + if (data_struct->GetIfEquals() && data_struct->GetIfEqualsVariable()) { + // Get the variable name + string varname = string(data_struct->GetIfEqualsVariable()); + char name[250] = { 0 }; + // Check to see if the variable has %i in the name, if it does assume we are in an array and get the current + // index and replace it + if (varname.find("%i") < 0xFFFFFFFF) { + // Get the current index by getting the number at the end of the name + vector* varnames = SplitString(data_struct->GetName(), '_'); + int index = atoi(varnames->at(varnames->size() - 1).c_str()); + + string substr = "stat_type"; + if (strncmp(varname.c_str(), substr.c_str(), strlen(substr.c_str())) == 0) { + // adorn_stat_subtype 18 chars + string temp = varname.substr(12); + char temp2[20] = { 0 }; + int index2 = atoi(temp.c_str()); + itoa(index2, temp2, 10); + varname = varname.substr(0, 12).append(temp2).append("_%i"); + } + sprintf(name, varname.c_str(), index); + safe_delete(varnames); + } + else + strcpy(name, varname.c_str()); + + // Get the data for the variable + int16 value = 0; + DataStruct* data_struct2 = findStruct(name, 0); + value = getType_int16(data_struct2); + // Hack for items as it is the only struct that currently uses IfVariableNotEquals + if (value != 1) + continue; + } + + // The following is tailored to items as they are the only structs that use type2 + // if more type2's are needed outside of the item stats array we need to think up an element + // to determine when to use type2 over type. + // Added checks for set stats and adorn stats - Zcoretri + bool useType2 = false; + if (data_struct->GetType2() > 0) { + int16 type = 0; + char name[250] = { 0 }; + vector* varnames = SplitString(data_struct->GetName(), '_'); + string struct_name = data_struct->GetName(); + if (struct_name.find("set") < 0xFFFFFFFF) { + string tmp = "set_stat_type"; + struct_name.replace(0, 9, tmp); + sprintf(name, "%s", struct_name.c_str()); + } + else if (struct_name.find("adorn") < 0xFFFFFFFF) { + string tmp = "adorn_stat_type"; + struct_name.replace(0, 9, tmp); + sprintf(name, "%s", struct_name.c_str()); + } + else { + // set name to stat_type_# (where # is the current index of the array we are in) + sprintf(name, "%s_%s", "stat_type", varnames->at(varnames->size() - 1).c_str()); + } + // Look up the value for stat_type + DataStruct* data_struct2 = findStruct(name, 0); + type = getType_int16(data_struct2); + // If stat_type == 6 we use a float, else we use sint16 + if (type != 6 && type != 7) + useType2 = true; + safe_delete(varnames); + } + if (!StructLoadData(data_struct, GetStructPointer(data_struct), data_struct->GetLength(), useType2, create_color)) + { + loadedSuccessfully = false; + break; + } + } } -} - -void PacketStruct::setEquipmentByName(const char* name, int32 type, int8 c_red, int8 c_blue, int8 c_green, int8 h_red, int8 h_blue, int8 h_green, int32 index) -{ - setEquipment(findStruct(name, index), type, c_red, c_blue, c_green, h_red, h_blue, h_green, index); -} - -void PacketStruct::setEquipmentByName(DataStruct* data_struct, EQ2_EquipmentItem data, int32 index) -{ - if (data_struct) { - EQ2_EquipmentItem* ptr = (EQ2_EquipmentItem*)struct_data[data_struct]; - ptr[index] = data; + catch (...) { + loadedSuccessfully = false; } + return loadedSuccessfully; +} +bool PacketStruct::StructLoadData(DataStruct* data_struct, void* data, int32 len, bool useType2, bool create_color) { + int8 type = 0; + if (useType2) { + type = data_struct->GetType2(); + // Need to change the data the struct expects to type2 + data_struct->SetType(type); + LogWrite(PACKET__DEBUG, 7, "Items", "Using type2 = %i", type); + } + else + type = data_struct->GetType(); + + switch (type) { + case DATA_STRUCT_INT8: + LoadData((int8*)data, len); + data_struct->SetIsSet(*((int8*)data) > 0); + break; + case DATA_STRUCT_INT16: + if (data_struct->GetOversized() > 0) { + LoadData((int8*)data, len); + if (getType_int16(data_struct) == data_struct->GetOversizedByte()) { + LoadData((int16*)data, len); + } + } + else { + LoadData((int16*)data, len); + } + data_struct->SetIsSet(*((int16*)data) > 0); + break; + case DATA_STRUCT_INT32: + if (data_struct->GetOversized() > 0) { + LoadData((int8*)data, len); + if (getType_int32(data_struct) == data_struct->GetOversizedByte()) { + LoadData((int32*)data, len); + } + else + LoadData((int8*)data, len); + } + else { + LoadData((int32*)data, len); + } + data_struct->SetIsSet(*((int32*)data) > 0); + break; + case DATA_STRUCT_INT64: + LoadData((int64*)data, len); + data_struct->SetIsSet(*((int64*)data) > 0); + break; + case DATA_STRUCT_SINT8: + LoadData((sint8*)data, len); + data_struct->SetIsSet(*((sint8*)data) > 0); + break; + case DATA_STRUCT_SINT16: + if (data_struct->GetOversized() > 0) { + LoadData((sint8*)data, len); + sint8 val = (sint8)getType_sint16(data_struct); + if (val < 0) //necessary because when using memcpy from a smaller data type to a larger one, the sign is lost + setData(data_struct, val, 0); + if (getType_sint16(data_struct) == data_struct->GetOversizedByte()) + LoadData((sint16*)data, len); + } + else + LoadData((sint16*)data, len); + data_struct->SetIsSet(*((sint16*)data) > 0); + break; + case DATA_STRUCT_SINT32: + LoadData((sint32*)data, len); + data_struct->SetIsSet(*((sint32*)data) > 0); + break; + case DATA_STRUCT_SINT64: + LoadData((sint64*)data, len); + data_struct->SetIsSet(*((sint64*)data) > 0); + break; + case DATA_STRUCT_CHAR: + LoadData((char*)data, len); + data_struct->SetIsSet(true); + break; + case DATA_STRUCT_FLOAT: + LoadData((float*)data, len); + data_struct->SetIsSet(*((float*)data) > 0); + break; + case DATA_STRUCT_DOUBLE: + LoadData((double*)data, len); + data_struct->SetIsSet(*((double*)data) > 0); + break; + case DATA_STRUCT_EQ2_8BIT_STRING: { + LoadDataString((EQ2_8BitString*)data); + data_struct->SetIsSet(((EQ2_8BitString*)data)->data.length() > 0); + break; + } + case DATA_STRUCT_EQ2_16BIT_STRING: { + LoadDataString((EQ2_16BitString*)data); + data_struct->SetIsSet(((EQ2_16BitString*)data)->data.length() > 0); + break; + } + case DATA_STRUCT_EQ2_32BIT_STRING: { + LoadDataString((EQ2_32BitString*)data); + data_struct->SetIsSet(((EQ2_32BitString*)data)->data.length() > 0); + break; + } + case DATA_STRUCT_COLOR: { + // lets not do this again, DoF behaves differently than AoM, DoF is not compatible with CreateEQ2Color + //if (strcmp(GetName(), "CreateCharacter") == 0 || strcmp(GetName(), "WS_SubmitCharCust") == 0) + if(create_color) + CreateEQ2Color((EQ2_Color*)data); + else + LoadData((EQ2_Color*)data, len); + break; + } + case DATA_STRUCT_EQUIPMENT: { + LoadData((EQ2_EquipmentItem*)data); + break; + } + case DATA_STRUCT_ARRAY: { + int32 size = GetArraySize(data_struct, 0); + if (size > 0xFFFF || size > GetLoadLen()-GetLoadPos()) { + LogWrite(PACKET__WARNING, 1, "Packet", "Possible corrupt packet while loading struct array, orig array size: %u in struct name %s, data name %s, load_len %u, load_pos %u", size, GetName(), (data_struct && data_struct->GetName()) ? data_struct->GetName() : "??", GetLoadLen(), GetLoadPos()); + return false; + } + PacketStruct* ps = GetPacketStructByName(data_struct->GetName()); + if (ps && ps->GetSubPacketSize() != size) { + if (data_struct->GetMaxArraySize() > 0 && size > data_struct->GetMaxArraySize()) + size = data_struct->GetMaxArraySize(); + ps->reAddAll(size); + } + if (ps && size > 0) { + //for(int i=0;i 0;i++){ + if(ps->LoadPacketData(GetLoadBuffer() + GetLoadPos(), GetLoadLen() - GetLoadPos(), create_color)) { + SetLoadPos(GetLoadPos() + ps->GetLoadPos()); + } + //} + } + break; + } + default: { + data_struct->SetIsSet(false); + } + } + + return true; +} +PacketStruct* PacketStruct::GetPacketStructByName(const char* name) { + PacketStruct* ps = 0; + vector::iterator itr; + for (itr = arrays.begin(); itr != arrays.end(); itr++) { + ps = *itr; + if (strcmp(ps->GetName(), name) == 0) + return ps; + ps = ps->GetPacketStructByName(name); + if (ps) + return ps; + } + return 0; } -// Array management functions -void PacketStruct::reAddAll(int32 length) -{ +void PacketStruct::reAddAll(int32 length) { vector::iterator itr; DataStruct* ds = 0; PacketStruct* ps = 0; @@ -775,28 +1529,14 @@ void PacketStruct::reAddAll(int32 length) } sub_packet_size = length; } - -void PacketStruct::renameSubstructArray(const char* substruct, int32 index) -{ - vector::iterator itr; - char tmp[10] = { 0 }; - sprintf(tmp, "%i", index); - for (itr = arrays.begin(); itr != arrays.end(); itr++) { - (*itr)->SetName(string(substruct).append("_").append((*itr)->GetName()).append("_").append(tmp).c_str()); - } -} - -int32 PacketStruct::GetArraySize(DataStruct* data_struct, int32 index) -{ +int32 PacketStruct::GetArraySize(DataStruct* data_struct, int32 index) { if (data_struct) { const char* name = data_struct->GetArraySizeVariable(); return GetArraySize(name, index); } return 0; } - -int32 PacketStruct::GetArraySize(const char* name, int32 index) -{ +int32 PacketStruct::GetArraySize(const char* name, int32 index) { int32 ret = 0; DataStruct* ds = findStruct(name, index); if (ds) { @@ -820,14 +1560,7 @@ int32 PacketStruct::GetArraySize(const char* name, int32 index) return ret; } -int32 PacketStruct::GetArraySizeByName(const char* name, int32 index) -{ - DataStruct* ds1 = findStruct(name, index); - return GetArraySize(ds1, index); -} - -void PacketStruct::UpdateArrayByArrayLength(DataStruct* data_struct, int32 index, int32 size) -{ +void PacketStruct::UpdateArrayByArrayLength(DataStruct* data_struct, int32 index, int32 size) { if (data_struct) { PacketStruct* packet = 0; DataStruct* data = 0; @@ -850,108 +1583,51 @@ void PacketStruct::UpdateArrayByArrayLength(DataStruct* data_struct, int32 index } } -void PacketStruct::UpdateArrayByArrayLengthName(const char* name, int32 index, int32 size) -{ +void PacketStruct::UpdateArrayByArrayLengthName(const char* name, int32 index, int32 size) { UpdateArrayByArrayLength(findStruct(name, index), index, size); } - -// Packet structure utility functions -void PacketStruct::setAddToPacketByName(const char* name, bool new_val, int32 index) -{ - DataStruct* data = findStruct(name, index); - if (data) - data->SetAddToStruct(new_val); +int32 PacketStruct::GetArraySizeByName(const char* name, int32 index) { + DataStruct* ds1 = findStruct(name, index); + return GetArraySize(ds1, index); } -void PacketStruct::setArrayAddToPacketByName(const char* name, bool new_val, int32 index1, int32 index2) -{ - char tmp[10] = { 0 }; - sprintf(tmp, "_%i", index1); - string name2 = string(name).append(tmp); - DataStruct* data = findStruct(name2.c_str(), index2); - if (data) - data->SetAddToStruct(new_val); -} - -void PacketStruct::setAddTypePacketByName(const char* name, int8 new_val, int32 index) -{ - DataStruct* data = findStruct(name, index); - if (data) - data->SetAddType(new_val); -} - -// Type checking functions -bool PacketStruct::IsStringValueType(string in_name, int32 index) -{ - DataStruct* data = findStruct(in_name.c_str(), index); - switch (data->GetType()) { - case DATA_STRUCT_CHAR: - case DATA_STRUCT_EQ2_8BIT_STRING: - case DATA_STRUCT_EQ2_16BIT_STRING: - case DATA_STRUCT_EQ2_32BIT_STRING: - return true; +int16 PacketStruct::GetOpcodeValue(int16 client_version) { + int16 opcode = 0xFFFF; + bool client_cmd = false; + int16 OpcodeVersion = 0; +#ifndef LOGIN + if (GetOpcode() == OP_ClientCmdMsg && strlen(GetOpcodeType()) > 0 && !IsSubPacket()) + client_cmd = true; +#endif + if (client_cmd) { + EmuOpcode sub_opcode = EQOpcodeManager[0]->NameSearch(GetOpcodeType()); + if (sub_opcode != OP_Unknown) { //numbers should be used at OpcodeTypes, define them! + OpcodeVersion = GetOpcodeVersion(client_version); + if (EQOpcodeManager.count(OpcodeVersion) > 0) { + opcode = EQOpcodeManager[OpcodeVersion]->EmuToEQ(sub_opcode); + if (opcode == 0xCDCD) { + LogWrite(PACKET__ERROR, 0, "Packet", "Could not find valid opcode for opcode: %s and client_version: %i", EQOpcodeManager[OpcodeVersion]->EmuToName(sub_opcode), client_version); + } + } + } } - return false; -} - -bool PacketStruct::IsColorValueType(string in_name, int32 index) -{ - DataStruct* data = findStruct(in_name.c_str(), index); - if (data->GetType() == DATA_STRUCT_COLOR) - return true; - else - return false; -} - -// Variable state functions -bool PacketStruct::GetVariableIsSet(const char* name) -{ - DataStruct* ds2 = findStruct(name, 0); - if (!ds2 || !ds2->IsSet()) - return false; - return true; -} - -bool PacketStruct::GetVariableIsNotSet(const char* name) -{ - DataStruct* ds2 = findStruct(name, 0); - if (ds2 && ds2->IsSet()) - return false; - return true; -} - -// Flag management functions -bool PacketStruct::CheckFlagExists(const char* name) -{ - vector::iterator itr; - for (itr = flags.begin(); itr != flags.end(); itr++) { - if (*itr == string(name)) - return true; - } - return false; -} - -void PacketStruct::AddFlag(const char* name) -{ - if (flags.size() > 0) { - vector::iterator itr; - for (itr = flags.begin(); itr != flags.end(); itr++) { - if (*itr == string(name)) - return; + else { + OpcodeVersion = GetOpcodeVersion(client_version); + if (EQOpcodeManager.count(OpcodeVersion) > 0) { + opcode = EQOpcodeManager[OpcodeVersion]->EmuToEQ(GetOpcode()); + if (opcode == 0xCDCD) { + LogWrite(PACKET__ERROR, 0, "Packet", "Could not find valid opcode for opcode: %s and client_version: %i", EQOpcodeManager[OpcodeVersion]->EmuToName(GetOpcode()), client_version); + } } } - flags.push_back(string(name)); +#ifndef LOGIN + if(opcode == 0) + opcode = 0xFFFF; +#endif + return opcode; } -// Serialization functions -string* PacketStruct::serializeString() -{ - serializePacket(); - return getDataString(); -} - -void PacketStruct::serializePacket(bool clear) -{ +void PacketStruct::serializePacket(bool clear) { if (clear) Clear(); bool client_cmd = false; @@ -964,7 +1640,7 @@ void PacketStruct::serializePacket(bool clear) vector::iterator itr; for (itr = structs.begin(); itr != structs.end(); itr++) { data = *itr; - if (data->IsOptional()) + if (data->IsOptional())//this would be false if the datastruct WAS optional, but was set continue; if (data->AddToStruct()) { if (data->GetIfFlagNotSet() && CheckFlagExists(data->GetIfFlagNotSetVariable())) @@ -1011,6 +1687,8 @@ void PacketStruct::serializePacket(bool clear) } } else { + // Check to see if the variable contains %i, if it does assume we are in an array + // and get the current index from the end of the data struct's name char name[250] = { 0 }; if (varname.find("%i") < 0xFFFFFFFF) { vector* varnames = SplitString(varname, '_'); @@ -1027,15 +1705,26 @@ void PacketStruct::serializePacket(bool clear) continue; } } + // Quick implementaion of IfVariableNotEquals + // probably not what it was intended for as it currently just checks to see if the given variable equals 1 + // should probably change it so you can define what the variable should or shouldn't equal + // + // ie: IfVariableNotEquals="stat_type_%i=1" + // would be a check to make sure that stat_type_%i does not equal 1 and if it does exclude this element if (data->GetIfNotEquals() && data->GetIfNotEqualsVariable()) { + // Get the variable name string varname = string(data->GetIfNotEqualsVariable()); char name[250] = { 0 }; + // Check to see if the variable has %i in the name, if it does assume we are in an array and get the current + // index and replace it if (varname.find("%i") < 0xFFFFFFFF) { + // Get the current index by getting the number at the end of the name vector* varnames = SplitString(data->GetName(), '_'); int index = atoi(varnames->at(varnames->size() - 1).c_str()); string substr = "stat_type"; if (strncmp(varname.c_str(), substr.c_str(), strlen(substr.c_str())) == 0) { + // adorn_stat_subtype 18 chars string temp = varname.substr(12); char temp2[20] = { 0 }; int index2 = atoi(temp.c_str()); @@ -1048,16 +1737,23 @@ void PacketStruct::serializePacket(bool clear) else strcpy(name, varname.c_str()); + // Get the data for the variable int16 value = 0; DataStruct* data_struct2 = findStruct(name, 0); value = getType_int16(data_struct2); + // Hack for items as it is the only struct that currently uses IfVariableNotEquals if (value == 1) continue; } + // copy and paste of the code above for IfEquals if (data->GetIfEquals() && data->GetIfEqualsVariable()) { + // Get the variable name string varname = string(data->GetIfEqualsVariable()); char name[250] = { 0 }; + // Check to see if the variable has %i in the name, if it does assume we are in an array and get the current + // index and replace it if (varname.find("%i") < 0xFFFFFFFF) { + // Get the current index by getting the number at the end of the name vector* varnames = SplitString(data->GetName(), '_'); int index = 0; if (varnames) @@ -1065,6 +1761,7 @@ void PacketStruct::serializePacket(bool clear) string substr = "stat_type"; if (strncmp(varname.c_str(), substr.c_str(), strlen(substr.c_str())) == 0) { + // adorn_stat_subtype 18 chars string temp = varname.substr(12); char temp2[20] = { 0 }; int index2 = atoi(temp.c_str()); @@ -1077,9 +1774,11 @@ void PacketStruct::serializePacket(bool clear) else strcpy(name, varname.c_str()); + // Get the data for the variable int16 value = 0; DataStruct* data_struct2 = findStruct(name, 0); value = getType_int16(data_struct2); + // Hack for items as it is the only struct that currently uses IfVariableNotEquals if (value != 1) continue; } @@ -1093,7 +1792,7 @@ void PacketStruct::serializePacket(bool clear) if (client_cmd) { int16 opcode_val = GetOpcodeValue(client_version); Clear(); - int32 size = client_data.length() + 3; + int32 size = client_data.length() + 3; //gotta add the opcode and oversized int8 oversized = 0xFF; int16 OpcodeVersion = GetOpcodeVersion(client_version); if (opcode_val == EQOpcodeManager[OpcodeVersion]->EmuToEQ(OP_EqExamineInfoCmd) && client_version > 561) @@ -1118,9 +1817,85 @@ void PacketStruct::serializePacket(bool clear) } #endif } +int32 PacketStruct::GetTotalPacketSize() { + int32 retSize = 0; + DataStruct* data = 0; + vector::iterator itr; + EQ2_8BitString* tmp1 = 0; + EQ2_16BitString* tmp2 = 0; + EQ2_32BitString* tmp3 = 0; + for (itr = structs.begin(); itr != structs.end(); itr++) { + data = *itr; + switch (data->GetType()) { + case DATA_STRUCT_INT8: + case DATA_STRUCT_SINT8: + case DATA_STRUCT_CHAR: + retSize += (1 * data->GetLength()); + break; + case DATA_STRUCT_SINT16: + case DATA_STRUCT_INT16: + retSize += (2 * data->GetLength()); + break; + case DATA_STRUCT_INT32: + case DATA_STRUCT_SINT32: + case DATA_STRUCT_FLOAT: + case DATA_STRUCT_DOUBLE: + retSize += (4 * data->GetLength()); + break; + case DATA_STRUCT_SINT64: + case DATA_STRUCT_INT64: + retSize += (8 * data->GetLength()); + break; + case DATA_STRUCT_EQ2_8BIT_STRING: + tmp1 = (EQ2_8BitString*)GetStructPointer(data); + if (tmp1) { + for (int16 i = 0; i < data->GetLength(); i++) + retSize += tmp1[i].data.length(); + } + retSize += (1 * data->GetLength()); + break; + case DATA_STRUCT_EQ2_16BIT_STRING: { + tmp2 = (EQ2_16BitString*)GetStructPointer(data); + if (tmp2) { + for (int16 i = 0; i < data->GetLength(); i++) + retSize += tmp2[i].data.length(); + } + retSize += (2 * data->GetLength()); + break; + } + case DATA_STRUCT_EQ2_32BIT_STRING: { + tmp3 = (EQ2_32BitString*)GetStructPointer(data); + if (tmp3) { + for (int16 i = 0; i < data->GetLength(); i++) + retSize += tmp3[i].data.length(); + } + retSize += (4 * data->GetLength()); + break; + } + case DATA_STRUCT_ARRAY: { + int32 size = GetArraySize(data, 0); + PacketStruct* ps = GetPacketStructByName(data->GetName()); + if (ps && ps->GetSubPacketSize() != size) { + ps->reAddAll(size); + } + if (ps && size > 0) + retSize += ps->GetTotalPacketSize(); + break; + } + case DATA_STRUCT_COLOR: { + retSize += ((sizeof(int8) * 3) * data->GetLength()); + break; + } + case DATA_STRUCT_EQUIPMENT: { + retSize += ((((sizeof(int8) * 3) * 2) + sizeof(int16)) * data->GetLength()); + break; + } + } + } + return retSize; +} -void PacketStruct::AddSerializedData(DataStruct* data, int32 index, string* datastring) -{ +void PacketStruct::AddSerializedData(DataStruct* data, int32 index, string* datastring) { switch (data->GetAddType()) { case DATA_STRUCT_INT8: StructAddData((int8*)GetStructPointer(data), data->GetLength(), sizeof(int8), datastring); @@ -1226,18 +2001,253 @@ void PacketStruct::AddSerializedData(DataStruct* data, int32 index, string* data break; } case DATA_STRUCT_ITEM: { + //DumpPacket((uchar*)GetStructPointer(data), data->GetItemSize()); AddCharArray((char*)GetStructPointer(data), data->GetItemSize(), datastring); break; } } } -EQ2Packet* PacketStruct::serializeCountPacket(int16 version, int8 offset, uchar* orig_packet, uchar* xor_packet) -{ +int8 PacketStruct::getType_int8_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_int8(data_struct, index, force); +} + +int16 PacketStruct::getType_int16_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_int16(data_struct, index, force); +} + +int32 PacketStruct::getType_int32_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_int32(data_struct, index, force); +} + +int64 PacketStruct::getType_int64_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_int64(data_struct, index, force); +} + +sint8 PacketStruct::getType_sint8_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_sint8(data_struct, index, force); +} + +sint16 PacketStruct::getType_sint16_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_sint16(data_struct, index, force); +} + +sint32 PacketStruct::getType_sint32_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_sint32(data_struct, index, force); +} + +sint64 PacketStruct::getType_sint64_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_sint64(data_struct, index, force); +} + +float PacketStruct::getType_float_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_float(data_struct, index, force); +} + +double PacketStruct::getType_double_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_double(data_struct, index, force); +} + +char PacketStruct::getType_char_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_char(data_struct, index, force); +} + +EQ2_8BitString PacketStruct::getType_EQ2_8BitString_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_EQ2_8BitString(data_struct, index, force); +} + +EQ2_16BitString PacketStruct::getType_EQ2_16BitString_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_EQ2_16BitString(data_struct, index, force); +} + +EQ2_32BitString PacketStruct::getType_EQ2_32BitString_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_EQ2_32BitString(data_struct, index, force); +} + +EQ2_Color PacketStruct::getType_EQ2_Color_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_EQ2_Color(data_struct, index, force); +} + +EQ2_EquipmentItem PacketStruct::getType_EQ2_EquipmentItem_ByName(const char* name, int32 index, bool force) { + DataStruct* data_struct = findStruct(name, index); + return getType_EQ2_EquipmentItem(data_struct, index, force); +} + +int8 PacketStruct::getType_int8(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_INT8) || force)) { + int8* ptr = (int8*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +int16 PacketStruct::getType_int16(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_INT16) || force)) { + int16* ptr = (int16*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +int32 PacketStruct::getType_int32(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_INT32) || force)) { + int32* ptr = (int32*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +int64 PacketStruct::getType_int64(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_INT64) || force)) { + int64* ptr = (int64*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +sint8 PacketStruct::getType_sint8(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_SINT8) || force)) { + sint8* ptr = (sint8*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +sint16 PacketStruct::getType_sint16(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_SINT16) || force)) { + sint16* ptr = (sint16*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +sint32 PacketStruct::getType_sint32(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_SINT32) || force)) { + sint32* ptr = (sint32*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +sint64 PacketStruct::getType_sint64(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_SINT64) || force)) { + sint64* ptr = (sint64*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +float PacketStruct::getType_float(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_FLOAT) || force)) { + float* ptr = (float*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +double PacketStruct::getType_double(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_DOUBLE) || force)) { + double* ptr = (double*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +char PacketStruct::getType_char(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_CHAR) || force)) { + char* ptr = (char*)GetStructPointer(data_struct); + return ptr[index]; + } + return 0; +} +EQ2_8BitString PacketStruct::getType_EQ2_8BitString(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_EQ2_8BIT_STRING) || force)) { + EQ2_8BitString* ptr = (EQ2_8BitString*)GetStructPointer(data_struct); + return ptr[index]; + } + EQ2_8BitString ret; + ret.size = 0; + return ret; +} +EQ2_16BitString PacketStruct::getType_EQ2_16BitString(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_EQ2_16BIT_STRING) || force)) { + EQ2_16BitString* ptr = (EQ2_16BitString*)GetStructPointer(data_struct); + return ptr[index]; + } + EQ2_16BitString ret; + ret.size = 0; + return ret; +} +EQ2_32BitString PacketStruct::getType_EQ2_32BitString(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_EQ2_32BIT_STRING) || force)) { + EQ2_32BitString* ptr = (EQ2_32BitString*)GetStructPointer(data_struct); + return ptr[index]; + } + EQ2_32BitString ret; + ret.size = 0; + return ret; +} +EQ2_Color PacketStruct::getType_EQ2_Color(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_COLOR) || force)) { + EQ2_Color* ptr = (EQ2_Color*)GetStructPointer(data_struct); + return ptr[index]; + } + EQ2_Color ret; + ret.blue = 0; + ret.red = 0; + ret.green = 0; + return ret; +} +EQ2_EquipmentItem PacketStruct::getType_EQ2_EquipmentItem(DataStruct* data_struct, int32 index, bool force) { + if (data_struct && ((data_struct->GetType() == DATA_STRUCT_EQUIPMENT) || force)) { + EQ2_EquipmentItem* ptr = (EQ2_EquipmentItem*)GetStructPointer(data_struct); + return ptr[index]; + } + EQ2_EquipmentItem ret; + ret.color.blue = 0; + ret.color.red = 0; + ret.color.green = 0; + ret.highlight.blue = 0; + ret.highlight.red = 0; + ret.highlight.green = 0; + ret.type = 0; + return ret; +} + +bool PacketStruct::SetOpcode(const char* new_opcode) { + opcode = EQOpcodeManager[0]->NameSearch(new_opcode); + if (opcode == OP_Unknown) { +#ifndef MINILOGIN + LogWrite(PACKET__ERROR, 0, "Packet", "Warning: PacketStruct '%s' uses an unknown opcode named '%s', this struct cannot be serialized directly.", GetName(), new_opcode); +#endif + return false; + } + return true; +} + +EQ2Packet* PacketStruct::serialize() { + serializePacket(); + + if (GetOpcode() != OP_Unknown) + return new EQ2Packet(GetOpcode(), getData(), getDataSize()); + else { +#ifndef MINILOGIN + LogWrite(PACKET__ERROR, 0, "Packet", "Warning: PacketStruct '%s' uses an unknown opcode and cannot be serialized directly.", GetName()); +#endif + return 0; + } +} + +EQ2Packet* PacketStruct::serializeCountPacket(int16 version, int8 offset, uchar* orig_packet, uchar* xor_packet) { string* packet_data = serializeString(); uchar* data = (uchar*)packet_data->c_str(); int32 size = packet_data->size(); - uchar* packed_data = new uchar[size + 1000]; + uchar* packed_data = new uchar[size + 1000]; // this size + 20 is poorly defined, depending on the packet data, we could use a additional length of 1K+ memset(packed_data, 0, size + 1000); if (orig_packet && xor_packet) { memcpy(xor_packet, data + 6, size - 6 - offset); @@ -1264,463 +2274,19 @@ EQ2Packet* PacketStruct::serializeCountPacket(int16 version, int8 offset, uchar* return app; } -// Data loading functions -bool PacketStruct::LoadPacketData(uchar* data, int32 data_len, bool create_color) -{ - loadedSuccessfully = true; - DataStruct* data_struct = 0; - try { - InitializeLoadData(data, data_len); - vector::iterator itr; - - for (itr = structs.begin(); itr != structs.end(); itr++) { - data_struct = *itr; - if (!data_struct->AddToStruct()) - continue; - - if (data_struct->GetIfSet() && data_struct->GetIfSetVariable()) { - string varname = string(data_struct->GetIfSetVariable()); - if (varname.find(",") < 0xFFFFFFFF) { - vector* varnames = SplitString(varname, ','); - if (varnames) { - bool should_continue = true; - for (int32 i = 0; i < varnames->size(); i++) { - if (GetVariableIsSet(varnames->at(i).c_str())) { - should_continue = false; - break; - } - } - safe_delete(varnames); - if (should_continue) - continue; - } - } - else { - char name[250] = { 0 }; - if (varname.find("%i") < 0xFFFFFFFF) { - vector* varnames = SplitString(data_struct->GetName(), '_'); - int index = atoi(varnames->at(varnames->size() - 1).c_str()); - sprintf(name, varname.c_str(), index); - } - else - strcpy(name, varname.c_str()); - - if (!GetVariableIsSet(name)) - continue; - } - } - - bool useType2 = false; - if (data_struct->GetType2() > 0) { - int16 type = 0; - char name[250] = { 0 }; - vector* varnames = SplitString(data_struct->GetName(), '_'); - string struct_name = data_struct->GetName(); - if (struct_name.find("set") < 0xFFFFFFFF) { - string tmp = "set_stat_type"; - struct_name.replace(0, 9, tmp); - sprintf(name, "%s", struct_name.c_str()); - } - else if (struct_name.find("adorn") < 0xFFFFFFFF) { - string tmp = "adorn_stat_type"; - struct_name.replace(0, 9, tmp); - sprintf(name, "%s", struct_name.c_str()); - } - else { - sprintf(name, "%s_%s", "stat_type", varnames->at(varnames->size() - 1).c_str()); - } - DataStruct* data_struct2 = findStruct(name, 0); - type = getType_int16(data_struct2); - if (type != 6 && type != 7) - useType2 = true; - safe_delete(varnames); - } - if (!StructLoadData(data_struct, GetStructPointer(data_struct), data_struct->GetLength(), useType2, create_color)) { - loadedSuccessfully = false; - break; - } - } - } - catch (...) { - loadedSuccessfully = false; - } - return loadedSuccessfully; +bool PacketStruct::IsSubPacket() { + return sub_packet; } - -bool PacketStruct::StructLoadData(DataStruct* data_struct, void* data, int32 len, bool useType2, bool create_color) -{ - int8 type = 0; - if (useType2) { - type = data_struct->GetType2(); - data_struct->SetType(type); - LogWrite(PACKET__DEBUG, 7, "Items", "Using type2 = %i", type); - } - else - type = data_struct->GetType(); - - switch (type) { - case DATA_STRUCT_INT8: - LoadData((int8*)data, len); - data_struct->SetIsSet(*((int8*)data) > 0); - break; - case DATA_STRUCT_INT16: - if (data_struct->GetOversized() > 0) { - LoadData((int8*)data, len); - if (getType_int16(data_struct) == data_struct->GetOversizedByte()) { - LoadData((int16*)data, len); - } - } - else { - LoadData((int16*)data, len); - } - data_struct->SetIsSet(*((int16*)data) > 0); - break; - case DATA_STRUCT_INT32: - if (data_struct->GetOversized() > 0) { - LoadData((int8*)data, len); - if (getType_int32(data_struct) == data_struct->GetOversizedByte()) { - LoadData((int32*)data, len); - } - else - LoadData((int8*)data, len); - } - else { - LoadData((int32*)data, len); - } - data_struct->SetIsSet(*((int32*)data) > 0); - break; - case DATA_STRUCT_INT64: - LoadData((int64*)data, len); - data_struct->SetIsSet(*((int64*)data) > 0); - break; - case DATA_STRUCT_SINT8: - LoadData((sint8*)data, len); - data_struct->SetIsSet(*((sint8*)data) > 0); - break; - case DATA_STRUCT_SINT16: - if (data_struct->GetOversized() > 0) { - LoadData((sint8*)data, len); - sint8 val = (sint8)getType_sint16(data_struct); - if (val < 0) - setData(data_struct, val, 0); - if (getType_sint16(data_struct) == data_struct->GetOversizedByte()) - LoadData((sint16*)data, len); - } - else - LoadData((sint16*)data, len); - data_struct->SetIsSet(*((sint16*)data) > 0); - break; - case DATA_STRUCT_SINT32: - LoadData((sint32*)data, len); - data_struct->SetIsSet(*((sint32*)data) > 0); - break; - case DATA_STRUCT_SINT64: - LoadData((sint64*)data, len); - data_struct->SetIsSet(*((sint64*)data) > 0); - break; - case DATA_STRUCT_CHAR: - LoadData((char*)data, len); - data_struct->SetIsSet(true); - break; - case DATA_STRUCT_FLOAT: - LoadData((float*)data, len); - data_struct->SetIsSet(*((float*)data) > 0); - break; - case DATA_STRUCT_DOUBLE: - LoadData((double*)data, len); - data_struct->SetIsSet(*((double*)data) > 0); - break; - case DATA_STRUCT_EQ2_8BIT_STRING: { - LoadDataString((EQ2_8BitString*)data); - data_struct->SetIsSet(((EQ2_8BitString*)data)->data.length() > 0); - break; - } - case DATA_STRUCT_EQ2_16BIT_STRING: { - LoadDataString((EQ2_16BitString*)data); - data_struct->SetIsSet(((EQ2_16BitString*)data)->data.length() > 0); - break; - } - case DATA_STRUCT_EQ2_32BIT_STRING: { - LoadDataString((EQ2_32BitString*)data); - data_struct->SetIsSet(((EQ2_32BitString*)data)->data.length() > 0); - break; - } - case DATA_STRUCT_COLOR: { - if (create_color) - CreateEQ2Color((EQ2_Color*)data); - else - LoadData((EQ2_Color*)data, len); - break; - } - case DATA_STRUCT_EQUIPMENT: { - LoadData((EQ2_EquipmentItem*)data); - break; - } - case DATA_STRUCT_ARRAY: { - int32 size = GetArraySize(data_struct, 0); - if (size > 0xFFFF || size > GetLoadLen() - GetLoadPos()) { - LogWrite(PACKET__WARNING, 1, "Packet", "Possible corrupt packet while loading struct array, orig array size: %u in struct name %s, data name %s, load_len %u, load_pos %u", size, GetName(), (data_struct && data_struct->GetName()) ? data_struct->GetName() : "??", GetLoadLen(), GetLoadPos()); - return false; - } - PacketStruct* ps = GetPacketStructByName(data_struct->GetName()); - if (ps && ps->GetSubPacketSize() != size) { - if (data_struct->GetMaxArraySize() > 0 && size > data_struct->GetMaxArraySize()) - size = data_struct->GetMaxArraySize(); - ps->reAddAll(size); - } - if (ps && size > 0) { - if (ps->LoadPacketData(GetLoadBuffer() + GetLoadPos(), GetLoadLen() - GetLoadPos(), create_color)) { - SetLoadPos(GetLoadPos() + ps->GetLoadPos()); - } - } - break; - } - default: { - data_struct->SetIsSet(false); - } - } - - return true; +void PacketStruct::IsSubPacket(bool new_val) { + sub_packet = new_val; } - -void PacketStruct::LoadFromPacketStruct(PacketStruct* packet, char* substruct_name) -{ - vector::iterator itr; - DataStruct* ds = 0; - char name[512]; - - EQ2_8BitString str8; - EQ2_16BitString str16; - EQ2_32BitString str32; - EQ2_EquipmentItem equip; - - for (itr = structs.begin(); itr != structs.end(); itr++) { - ds = *itr; - for (int16 i = 0; i < ds->GetLength(); i++) { - memset(name, 0, sizeof(name)); - - if (substruct_name) - snprintf(name, sizeof(name), "%s_%s_0", substruct_name, ds->GetName()); - else - strncpy(name, ds->GetName(), sizeof(name)); - name[sizeof(name) - 1] = '\0'; - - switch (ds->GetType()) { - case DATA_STRUCT_INT8: - setData(ds, packet->getType_int8_ByName(name, i), i); - break; - case DATA_STRUCT_SINT8: - setData(ds, packet->getType_sint8_ByName(name, i), i); - break; - case DATA_STRUCT_CHAR: - setData(ds, packet->getType_char_ByName(name, i), i); - break; - case DATA_STRUCT_SINT16: - setData(ds, packet->getType_sint16_ByName(name, i), i); - break; - case DATA_STRUCT_INT16: - setData(ds, packet->getType_int16_ByName(name, i), i); - break; - case DATA_STRUCT_INT32: - setData(ds, packet->getType_int32_ByName(name, i), i); - break; - case DATA_STRUCT_INT64: - setData(ds, packet->getType_int64_ByName(name, i), i); - break; - case DATA_STRUCT_SINT32: - setData(ds, packet->getType_sint32_ByName(name, i), i); - break; - case DATA_STRUCT_SINT64: - setData(ds, packet->getType_sint64_ByName(name, i), i); - break; - case DATA_STRUCT_FLOAT: - setData(ds, packet->getType_float_ByName(name, i), i); - break; - case DATA_STRUCT_DOUBLE: - setData(ds, packet->getType_double_ByName(name, i), i); - break; - case DATA_STRUCT_EQ2_8BIT_STRING: - str8 = packet->getType_EQ2_8BitString_ByName(name, i); - setData(ds, &str8, i); - break; - case DATA_STRUCT_EQ2_16BIT_STRING: - str16 = packet->getType_EQ2_16BitString_ByName(name, i); - setData(ds, &str16, i); - break; - case DATA_STRUCT_EQ2_32BIT_STRING: - str32 = packet->getType_EQ2_32BitString_ByName(name, i); - setData(ds, &str32, i); - break; - case DATA_STRUCT_ARRAY: { - int32 size = GetArraySize(ds, 0); - PacketStruct* ps = GetPacketStructByName(ds->GetName()); - if (ps && size > 0) - ps->LoadFromPacketStruct(packet, substruct_name); - break; - } - case DATA_STRUCT_COLOR: - setColor(ds, packet->getType_EQ2_Color_ByName(name, i), i); - break; - case DATA_STRUCT_EQUIPMENT: - equip = packet->getType_EQ2_EquipmentItem_ByName(name, i); - setEquipmentByName(ds->GetName(), &equip, i); - break; - default: - break; - } - } - } +int32 PacketStruct::GetSubPacketSize() { + return sub_packet_size; } - -// Opcode management functions -int16 PacketStruct::GetOpcodeValue(int16 client_version) -{ - int16 opcode = 0xFFFF; - bool client_cmd = false; - int16 OpcodeVersion = 0; -#ifndef LOGIN - if (GetOpcode() == OP_ClientCmdMsg && strlen(GetOpcodeType()) > 0 && !IsSubPacket()) - client_cmd = true; -#endif - if (client_cmd) { - EmuOpcode sub_opcode = EQOpcodeManager[0]->NameSearch(GetOpcodeType()); - if (sub_opcode != OP_Unknown) { - OpcodeVersion = GetOpcodeVersion(client_version); - if (EQOpcodeManager.count(OpcodeVersion) > 0) { - opcode = EQOpcodeManager[OpcodeVersion]->EmuToEQ(sub_opcode); - if (opcode == 0xCDCD) { - LogWrite(PACKET__ERROR, 0, "Packet", "Could not find valid opcode for opcode: %s and client_version: %i", EQOpcodeManager[OpcodeVersion]->EmuToName(sub_opcode), client_version); - } - } - } - } - else { - OpcodeVersion = GetOpcodeVersion(client_version); - if (EQOpcodeManager.count(OpcodeVersion) > 0) { - opcode = EQOpcodeManager[OpcodeVersion]->EmuToEQ(GetOpcode()); - if (opcode == 0xCDCD) { - LogWrite(PACKET__ERROR, 0, "Packet", "Could not find valid opcode for opcode: %s and client_version: %i", EQOpcodeManager[OpcodeVersion]->EmuToName(GetOpcode()), client_version); - } - } - } -#ifndef LOGIN - if (opcode == 0) - opcode = 0xFFFF; -#endif - return opcode; +void PacketStruct::SetSubPacketSize(int32 new_size) { + sub_packet_size = new_size; } - -const char* PacketStruct::GetOpcodeType() -{ - return opcode_type.c_str(); -} - -void PacketStruct::SetOpcodeType(const char* in_type) -{ - if (in_type) - opcode_type = string(in_type); - else - opcode_type = string(""); -} - -// Utility accessor functions -int32 PacketStruct::GetTotalPacketSize() -{ - int32 retSize = 0; - DataStruct* data = 0; - vector::iterator itr; - EQ2_8BitString* tmp1 = 0; - EQ2_16BitString* tmp2 = 0; - EQ2_32BitString* tmp3 = 0; - for (itr = structs.begin(); itr != structs.end(); itr++) { - data = *itr; - switch (data->GetType()) { - case DATA_STRUCT_INT8: - case DATA_STRUCT_SINT8: - case DATA_STRUCT_CHAR: - retSize += (1 * data->GetLength()); - break; - case DATA_STRUCT_SINT16: - case DATA_STRUCT_INT16: - retSize += (2 * data->GetLength()); - break; - case DATA_STRUCT_INT32: - case DATA_STRUCT_SINT32: - case DATA_STRUCT_FLOAT: - case DATA_STRUCT_DOUBLE: - retSize += (4 * data->GetLength()); - break; - case DATA_STRUCT_SINT64: - case DATA_STRUCT_INT64: - retSize += (8 * data->GetLength()); - break; - case DATA_STRUCT_EQ2_8BIT_STRING: - tmp1 = (EQ2_8BitString*)GetStructPointer(data); - if (tmp1) { - for (int16 i = 0; i < data->GetLength(); i++) - retSize += tmp1[i].data.length(); - } - retSize += (1 * data->GetLength()); - break; - case DATA_STRUCT_EQ2_16BIT_STRING: { - tmp2 = (EQ2_16BitString*)GetStructPointer(data); - if (tmp2) { - for (int16 i = 0; i < data->GetLength(); i++) - retSize += tmp2[i].data.length(); - } - retSize += (2 * data->GetLength()); - break; - } - case DATA_STRUCT_EQ2_32BIT_STRING: { - tmp3 = (EQ2_32BitString*)GetStructPointer(data); - if (tmp3) { - for (int16 i = 0; i < data->GetLength(); i++) - retSize += tmp3[i].data.length(); - } - retSize += (4 * data->GetLength()); - break; - } - case DATA_STRUCT_ARRAY: { - int32 size = GetArraySize(data, 0); - PacketStruct* ps = GetPacketStructByName(data->GetName()); - if (ps && ps->GetSubPacketSize() != size) { - ps->reAddAll(size); - } - if (ps && size > 0) - retSize += ps->GetTotalPacketSize(); - break; - } - case DATA_STRUCT_COLOR: { - retSize += ((sizeof(int8) * 3) * data->GetLength()); - break; - } - case DATA_STRUCT_EQUIPMENT: { - retSize += ((((sizeof(int8) * 3) * 2) + sizeof(int16)) * data->GetLength()); - break; - } - } - } - return retSize; -} - -// Advanced functions -PacketStruct* PacketStruct::GetPacketStructByName(const char* name) -{ - PacketStruct* ps = 0; - vector::iterator itr; - for (itr = arrays.begin(); itr != arrays.end(); itr++) { - ps = *itr; - if (strcmp(ps->GetName(), name) == 0) - return ps; - ps = ps->GetPacketStructByName(name); - if (ps) - return ps; - } - return 0; -} - -void* PacketStruct::GetStructPointer(DataStruct* data_struct, bool erase) -{ +void* PacketStruct::GetStructPointer(DataStruct* data_struct, bool erase) { try { map::iterator tmpitr = struct_data.find(data_struct); if (tmpitr != struct_data.end()) { @@ -1747,8 +2313,7 @@ void* PacketStruct::GetStructPointer(DataStruct* data_struct, bool erase) return 0; } -vector PacketStruct::GetDataStructs() -{ +vector PacketStruct::GetDataStructs() { vector ret; DataStruct* ds = 0; vector::iterator itr; @@ -1788,8 +2353,7 @@ vector PacketStruct::GetDataStructs() return ret; } -void PacketStruct::PrintPacket() -{ +void PacketStruct::PrintPacket() { DataStruct* ds = 0; vector::iterator itr; for (itr = structs.begin(); itr != structs.end(); itr++) { @@ -1901,29 +2465,120 @@ void PacketStruct::PrintPacket() } } -#ifdef WORLD -void PacketStruct::setItem(DataStruct* ds, Item* item, Player* player, int32 index, sint8 offset, bool loot_item, bool make_empty_item_packet, bool inspect) -{ +void PacketStruct::LoadFromPacketStruct(PacketStruct* packet, char* substruct_name) { + vector::iterator itr; + DataStruct* ds = 0; + char name[512]; + + //scatman (1/30/2012): these declarations are here to get rid of a linux compile error "taking address of temporary" + EQ2_8BitString str8; + EQ2_16BitString str16; + EQ2_32BitString str32; + EQ2_EquipmentItem equip; + + for (itr = structs.begin(); itr != structs.end(); itr++) { + ds = *itr; + for (int16 i = 0; i < ds->GetLength(); i++) { + memset(name, 0, sizeof(name)); + + if (substruct_name) + snprintf(name, sizeof(name), "%s_%s_0", substruct_name, ds->GetName()); + else + strncpy(name, ds->GetName(), sizeof(name)); + name[sizeof(name) - 1] = '\0'; + + switch (ds->GetType()) { + case DATA_STRUCT_INT8: + setData(ds, packet->getType_int8_ByName(name, i), i); + break; + case DATA_STRUCT_SINT8: + setData(ds, packet->getType_sint8_ByName(name, i), i); + break; + case DATA_STRUCT_CHAR: + setData(ds, packet->getType_char_ByName(name, i), i); + break; + case DATA_STRUCT_SINT16: + setData(ds, packet->getType_sint16_ByName(name, i), i); + break; + case DATA_STRUCT_INT16: + setData(ds, packet->getType_int16_ByName(name, i), i); + break; + case DATA_STRUCT_INT32: + setData(ds, packet->getType_int32_ByName(name, i), i); + break; + case DATA_STRUCT_INT64: + setData(ds, packet->getType_int64_ByName(name, i), i); + break; + case DATA_STRUCT_SINT32: + setData(ds, packet->getType_sint32_ByName(name, i), i); + break; + case DATA_STRUCT_SINT64: + setData(ds, packet->getType_sint64_ByName(name, i), i); + break; + case DATA_STRUCT_FLOAT: + setData(ds, packet->getType_float_ByName(name, i), i); + break; + case DATA_STRUCT_DOUBLE: + setData(ds, packet->getType_double_ByName(name, i), i); + break; + case DATA_STRUCT_EQ2_8BIT_STRING: + str8 = packet->getType_EQ2_8BitString_ByName(name, i); + setData(ds, &str8, i); + break; + case DATA_STRUCT_EQ2_16BIT_STRING: + str16 = packet->getType_EQ2_16BitString_ByName(name, i); + setData(ds, &str16, i); + break; + case DATA_STRUCT_EQ2_32BIT_STRING: + str32 = packet->getType_EQ2_32BitString_ByName(name, i); + setData(ds, &str32, i); + break; + case DATA_STRUCT_ARRAY: { + int32 size = GetArraySize(ds, 0); + PacketStruct* ps = GetPacketStructByName(ds->GetName()); + if (ps && size > 0) + ps->LoadFromPacketStruct(packet, substruct_name); + break; + } + case DATA_STRUCT_COLOR: + setColor(ds, packet->getType_EQ2_Color_ByName(name, i), i); + break; + case DATA_STRUCT_EQUIPMENT: + equip = packet->getType_EQ2_EquipmentItem_ByName(name, i); + setEquipmentByName(ds->GetName(), &equip, i); + break; + default: + break; + } + } + } +} + +#ifdef WORLD +void PacketStruct::setItem(DataStruct* ds, Item* item, Player* player, int32 index, sint8 offset, bool loot_item, bool make_empty_item_packet, bool inspect) { if (!ds) return; uchar* ptr = (uchar*)GetStructPointer(ds); - - if (!item) { - if (make_empty_item_packet) { - if (client_version <= 373) { - uchar bogusItemBuffer[] = { 0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; + + if(!item) { + if(make_empty_item_packet) { + if(client_version <= 373) { + // for player inspection this will offset the parts of the packet that have no items + uchar bogusItemBuffer[] = {0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x3C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; int sizeOfArray = sizeof(bogusItemBuffer) / sizeof(bogusItemBuffer[0]); ds->SetItemSize(sizeOfArray); - memcpy(ptr, bogusItemBuffer, sizeOfArray); + memcpy(ptr, bogusItemBuffer, sizeOfArray); } - else if (client_version <= 561) { - uchar bogusItemBuffer[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x8C,0x5A,0xF1,0xD2,0x8C,0x5A,0xF1,0xD2,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00 }; + else if(client_version <= 561) { + // for player inspection this will offset the parts of the packet that have no items + uchar bogusItemBuffer[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x8C,0x5A,0xF1,0xD2,0x8C,0x5A,0xF1,0xD2,0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00}; int sizeOfArray = sizeof(bogusItemBuffer) / sizeof(bogusItemBuffer[0]); ds->SetItemSize(sizeOfArray); - memcpy(ptr, bogusItemBuffer, sizeOfArray); + memcpy(ptr, bogusItemBuffer, sizeOfArray); } else { - uchar bogusItemBuffer[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; + // for player inspection this will offset the parts of the packet that have no items + uchar bogusItemBuffer[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00 /*0x68 was item flags*/,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; int sizeOfArray = sizeof(bogusItemBuffer) / sizeof(bogusItemBuffer[0]); ds->SetItemSize(sizeOfArray); memcpy(ptr, bogusItemBuffer, sizeOfArray); @@ -1934,19 +2589,22 @@ void PacketStruct::setItem(DataStruct* ds, Item* item, Player* player, int32 ind PacketStruct* packet = item->PrepareItem(client_version, false, loot_item, inspect); if (packet) { int16 item_version = GetItemPacketType(packet->GetVersion()); + // newer clients can handle the item structure without the loot_item flag set to true, older clients like DoF need a smaller subpacket of item item->serialize(packet, true, player, item_version, 0, (packet->GetVersion() <= 561) ? loot_item : false, inspect); string* generic_string_data = packet->serializeString(); - int32 size = generic_string_data->length(); + int32 size = generic_string_data->length(); // had to set to 81 int32 actual_length = size; - if (client_version <= 373 && make_empty_item_packet && inspect) { - size += 2; + if(client_version <= 373 && make_empty_item_packet && inspect) { + size += 2; // end padding for the specialized item (used for player inspection) } - else if (offset > 12) { - size += 6; + else if(offset > 12) { + size += 6; // end padding for the specialized item (used for player inspection) } - if (size <= 13) { + //DumpPacket((uchar*)generic_string_data->c_str(), size); + if (size <= 13) + { ds->SetIsSet(false); return; } @@ -1954,15 +2612,18 @@ void PacketStruct::setItem(DataStruct* ds, Item* item, Player* player, int32 ind if (client_version > 561 && item->IsBag() == false && item->IsBauble() == false && item->IsFood() == false && (offset == 0 || offset == -1 || offset == 2)) size = (size * 2) - 5; uchar* out_data = new uchar[size + 1]; - memset(out_data, 0, size + 1); + memset(out_data, 0, size+1); uchar* out_ptr = out_data; memcpy(out_ptr, (uchar*)generic_string_data->c_str() + (9 + offset), actual_length - (9 + offset)); + //DumpPacket((uchar*)generic_string_data->c_str() + (9 + offset), size); + //without these it will prompt for your character name if (offset == 0 || offset == -1 || offset == 2) { if (client_version <= 561 && item->details.count > 0) out_data[0] = item->details.count; else out_data[0] = 1; } + // out_ptr += generic_string_data->length() - (10 + offset); if (client_version > 561 && item->IsBag() == false && item->IsBauble() == false && item->IsFood() == false && (offset == 0 || offset == -1 || offset == 2)) { out_data[4] = 0x80; @@ -1973,23 +2634,19 @@ void PacketStruct::setItem(DataStruct* ds, Item* item, Player* player, int32 ind safe_delete_array(out_data); delete packet; } + //DumpPacket(ptr2, ds->GetItemSize()); } - -void PacketStruct::setItemByName(const char* name, Item* item, Player* player, int32 index, sint8 offset, bool loot_item, bool make_empty_item_packet, bool inspect) -{ +void PacketStruct::setItemByName(const char* name, Item* item, Player* player, int32 index, sint8 offset, bool loot_item, bool make_empty_item_packet, bool inspect) { setItem(findStruct(name, index), item, player, index, offset, loot_item, make_empty_item_packet, inspect); } - -void PacketStruct::setItemArrayDataByName(const char* name, Item* item, Player* player, int32 index1, int32 index2, sint8 offset, bool loot_item, bool make_empty_item_packet, bool inspect) -{ +void PacketStruct::setItemArrayDataByName(const char* name, Item* item, Player* player, int32 index1, int32 index2, sint8 offset, bool loot_item, bool make_empty_item_packet, bool inspect) { char tmp[10] = { 0 }; sprintf(tmp, "_%i", index1); string name2 = string(name).append(tmp); setItem(findStruct(name2.c_str(), index1, index2), item, player, index2, offset, loot_item, make_empty_item_packet, inspect); } -void PacketStruct::ResetData() -{ +void PacketStruct::ResetData() { if (this == nullptr) { LogWrite(PACKET__ERROR, 0, "PacketStruct", "ResetData called on a null PacketStruct pointer"); return; @@ -2000,7 +2657,8 @@ void PacketStruct::ResetData() void* ptr = GetStructPointer(ds); if (!ptr) continue; - switch (ds->GetType()) { + switch (ds->GetType()) + { case DATA_STRUCT_EQ2_8BIT_STRING: { EQ2_8BitString* real_ptr = (EQ2_8BitString*)ptr; real_ptr->size = 0; @@ -2081,4 +2739,4 @@ void PacketStruct::ResetData() for (itr2 = arrays.begin(); itr2 != arrays.end(); itr2++) (*itr2)->ResetData(); } -#endif \ No newline at end of file +#endif diff --git a/old/common/PacketStruct.h b/old/common/PacketStruct.h new file mode 100644 index 0000000..98bca7d --- /dev/null +++ b/old/common/PacketStruct.h @@ -0,0 +1,513 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef __EQ2_PACKETSTRUCT__ +#define __EQ2_PACKETSTRUCT__ +#include "types.h" +#include "DataBuffer.h" +#include "opcodemgr.h" + +#include +#include +#ifdef WORLD +class Item; +class Player; +#endif +extern mapEQOpcodeManager; +using namespace std; + +#define DATA_STRUCT_NONE 0 +#define DATA_STRUCT_INT8 1 +#define DATA_STRUCT_INT16 2 +#define DATA_STRUCT_INT32 3 +#define DATA_STRUCT_INT64 4 +#define DATA_STRUCT_FLOAT 5 +#define DATA_STRUCT_DOUBLE 6 +#define DATA_STRUCT_COLOR 7 +#define DATA_STRUCT_SINT8 8 +#define DATA_STRUCT_SINT16 9 +#define DATA_STRUCT_SINT32 10 +#define DATA_STRUCT_CHAR 11 +#define DATA_STRUCT_EQ2_8BIT_STRING 12 +#define DATA_STRUCT_EQ2_16BIT_STRING 13 +#define DATA_STRUCT_EQ2_32BIT_STRING 14 +#define DATA_STRUCT_EQUIPMENT 15 +#define DATA_STRUCT_ARRAY 16 +#define DATA_STRUCT_ITEM 17 +#define DATA_STRUCT_SINT64 18 + +class DataStruct { +public: + DataStruct(); + DataStruct(DataStruct* data_struct); + DataStruct(const char* new_name, int8 new_type, int32 new_length = 1, int8 new_type2 = DATA_STRUCT_NONE); + DataStruct(const char* new_name, const char* new_type, int32 new_length = 1, const char* new_type2 = 0); + DataStruct(const char* new_name, int32 new_length); + void SetType(const char* new_type, int8* output_type); + void SetType(int8 new_type); + void SetName(const char* new_name); + void SetLength(int32 new_length); + void SetArraySizeVariable(const char* new_name); + void SetDefaultValue(int8 new_val); + void SetMaxArraySize(int8 size); + void SetOversized(int8 val); + void SetOversizedByte(int8 val); + void SetAddToStruct(bool val); + void SetAddType(int8 new_type); + void SetPackedIndex(int8 new_index); + void SetPackedSizeVariable(const char* new_name); + void SetPacked(const char* value); + void SetItemSize(int32 val); + void SetIfSetVariable(const char* variable); + void SetIfNotSetVariable(const char* variable); + void SetIfEqualsVariable(const char* variable); + void SetIfNotEqualsVariable(const char* variable); + void SetIfFlagSetVariable(const char* variable); + void SetIfFlagNotSetVariable(const char* variable); + void SetIsSet(bool val); + void SetIsOptional(bool val); + + int8 GetPackedIndex(); + const char* GetPackedSizeVariable(); + const char* GetArraySizeVariable(); + int8 GetDefaultValue(); + int8 GetOversized(); + int8 GetOversizedByte(); + int8 GetMaxArraySize(); + int8 GetType(); + int8 GetType2(); + const char* GetName(); + string GetStringName(); + int32 GetLength(); + bool AddToStruct(); + int8 GetAddType(); + int32 GetItemSize(); + bool GetIfSet(); + const char* GetIfSetVariable(); + bool GetIfNotSet(); + const char* GetIfNotSetVariable(); + bool GetIfEquals(); + const char* GetIfEqualsVariable(); + bool GetIfNotEquals(); + const char* GetIfNotEqualsVariable(); + bool GetIfFlagSet(); + const char* GetIfFlagSetVariable(); + bool GetIfFlagNotSet(); + const char* GetIfFlagNotSetVariable(); + bool IsSet(); + bool IsOptional(); + int32 GetDataSizeInBytes(); + string AppendVariable(string orig, const char* val); + void AddIfSetVariable(const char* val) { + if (val) { + if_set_variable = AppendVariable(if_set_variable, val); + is_set = true; + } + } + void AddIfNotSetVariable(const char* val) { + if (val) { + if_not_set_variable = AppendVariable(if_not_set_variable, val); + if_not_set = true; + } + } + +private: + bool is_set; + bool if_not_set; + bool optional; + bool if_set; + bool if_not_equals; + bool if_equals; + bool if_flag_set; + bool if_flag_not_set; + string if_flag_not_set_variable; + string if_flag_set_variable; + string if_not_equals_variable; + string if_equals_variable; + string if_not_set_variable; + string if_set_variable; + int8 oversized; + int8 oversized_byte; + bool add; + int8 addType; + int8 maxArraySize; + string array_size_variable; + string name; + int8 type; + int8 default_value; + int8 type2; + int32 length; + int32 item_size; +}; +class PacketStruct : public DataBuffer { +public: + PacketStruct(); + PacketStruct(PacketStruct* packet, bool sub); + PacketStruct(PacketStruct* packet, int16 in_client_version); + ~PacketStruct(); + void add(DataStruct* data); + void reAddAll(int32 length); + void add(PacketStruct* packet_struct); + void addPacketArrays(PacketStruct* packet); + void deletePacketArrays(PacketStruct* packet); + void deleteDataStructs(vector* data_structs); + void setSmallStringByName(const char* name, const char* text, int32 index = 0); + void setMediumStringByName(const char* name, const char* text, int32 index = 0); + void setLargeStringByName(const char* name, const char* text, int32 index = 0); + void setSmallString(DataStruct* data_struct, const char* text, int32 index = 0); + void setMediumString(DataStruct* data_struct, const char* text, int32 index = 0); + void setLargeString(DataStruct* data_struct, const char* text, int32 index = 0); + void renameSubstructArray(const char* substruct, int32 index); + template void setSubstructSubstructDataByName(const char* substruct_name1, const char* substruct_name2, const char* name, Data data, int32 substruct_index1 = 0, int32 substruct_index2 = 0, int32 index = 0) { + char tmp[15] = { 0 }; + sprintf(tmp, "_%i_%i", substruct_index1, substruct_index2); + string name2 = string(substruct_name1).append("_").append(substruct_name2).append("_").append(name).append(tmp); + setData(findStruct(name2.c_str(), index), data, index); + } + template void setSubstructDataByName(const char* substruct_name, const char* name, Data data, int32 substruct_index = 0, int32 index = 0) { + char tmp[10] = { 0 }; + sprintf(tmp, "_%i", substruct_index); + string name2 = string(substruct_name).append("_").append(name).append(tmp); + setData(findStruct(name2.c_str(), index), data, index); + } + template void setSubstructColorByName(const char* substruct_name, const char* name, Data data, int32 substruct_index = 0, int32 index = 0) { + char tmp[10] = { 0 }; + sprintf(tmp, "_%i", substruct_index); + string name2 = string(substruct_name).append("_").append(name).append(tmp); + setColor(findStruct(name2.c_str(), index), data, index); + } + template void setSubstructArrayDataByName(const char* substruct_name, const char* name, Data data, int32 index = 0, int32 substruct_index = 0) { + char tmp[10] = { 0 }; + sprintf(tmp, "_%i", substruct_index); + string name2 = string(substruct_name).append("_").append(name).append(tmp); + setData(findStruct(name2.c_str(), substruct_index, index), data, index); + } + template void setSubstructArrayColorByName(const char* substruct_name, const char* name, Data data, int32 substruct_index = 0, int32 index = 0) { + char tmp[10] = { 0 }; + sprintf(tmp, "_%i", substruct_index); + string name2 = string(substruct_name).append("_").append(name).append(tmp); + setColor(findStruct(name2.c_str(), index, substruct_index), data, index); + } + template void setDataByName(const char* name, Data data, int32 index = 0, bool use_second_type = false) { + setData(findStruct(name, index), data, index, use_second_type); + } + template void setDataByName(const char* name, Data* data, int32 index = 0, bool use_second_type = false) { + setData(findStruct(name, index), data, index, use_second_type); + } + template void setSubArrayDataByName(const char* name, Data data, int32 index1 = 0, int32 index2 = 0, int32 index3 = 0) { + char tmp[20] = { 0 }; + sprintf(tmp, "%i_%i", index1, index2); + string name2 = string(name).append(tmp); + setData(findStruct(name2.c_str(), index2, index3), data, index3); + } + template void setArrayDataByName(const char* name, Data data, int32 index1 = 0, int32 index2 = 0, bool use_second_type = false) { + char tmp[10] = { 0 }; + sprintf(tmp, "_%i", index1); + string name2 = string(name).append(tmp); + setData(findStruct(name2.c_str(), index1, index2), data, index2, use_second_type); + } + void setArrayAddToPacketByName(const char* name, bool new_val, int32 index1 = 0, int32 index2 = 0) { + char tmp[10] = { 0 }; + sprintf(tmp, "_%i", index1); + string name2 = string(name).append(tmp); + DataStruct* data = findStruct(name2.c_str(), index2); + if (data) + data->SetAddToStruct(new_val); + } + void setAddToPacketByName(const char* name, bool new_val, int32 index = 0) { + DataStruct* data = findStruct(name, index); + if (data) + data->SetAddToStruct(new_val); + } + void setAddTypePacketByName(const char* name, int8 new_val, int32 index = 0) { + DataStruct* data = findStruct(name, index); + if (data) + data->SetAddType(new_val); + } + const char* GetOpcodeType(); + bool IsSubPacket(); + void IsSubPacket(bool new_val); + int32 GetSubPacketSize(); + void SetSubPacketSize(int32 new_size); + void SetOpcodeType(const char* opcodeType); + int32 GetArraySizeByName(const char* name, int32 index); + int32 GetArraySize(DataStruct* data_struct, int32 index); + int32 GetArraySize(const char* name, int32 index); + void LoadFromPacketStruct(PacketStruct* packet, char* substruct_name = 0); + bool GetVariableIsSet(const char* name); + bool GetVariableIsNotSet(const char* name); + + int8 getType_int8_ByName(const char* name, int32 index = 0, bool force = false); + int16 getType_int16_ByName(const char* name, int32 index = 0, bool force = false); + int32 getType_int32_ByName(const char* name, int32 index = 0, bool force = false); + int64 getType_int64_ByName(const char* name, int32 index = 0, bool force = false); + sint8 getType_sint8_ByName(const char* name, int32 index = 0, bool force = false); + sint16 getType_sint16_ByName(const char* name, int32 index = 0, bool force = false); + sint32 getType_sint32_ByName(const char* name, int32 index = 0, bool force = false); + sint64 getType_sint64_ByName(const char* name, int32 index = 0, bool force = false); + float getType_float_ByName(const char* name, int32 index = 0, bool force = false); + double getType_double_ByName(const char* name, int32 index = 0, bool force = false); + char getType_char_ByName(const char* name, int32 index = 0, bool force = false); + EQ2_8BitString getType_EQ2_8BitString_ByName(const char* name, int32 index = 0, bool force = false); + EQ2_16BitString getType_EQ2_16BitString_ByName(const char* name, int32 index = 0, bool force = false); + EQ2_32BitString getType_EQ2_32BitString_ByName(const char* name, int32 index = 0, bool force = false); + EQ2_Color getType_EQ2_Color_ByName(const char* name, int32 index = 0, bool force = false); + EQ2_EquipmentItem getType_EQ2_EquipmentItem_ByName(const char* name, int32 index = 0, bool force = false); + + int8 getType_int8(DataStruct* data_struct, int32 index = 0, bool force = false); + int16 getType_int16(DataStruct* data_struct, int32 index = 0, bool force = false); + int32 getType_int32(DataStruct* data_struct, int32 index = 0, bool force = false); + int64 getType_int64(DataStruct* data_struct, int32 index = 0, bool force = false); + sint8 getType_sint8(DataStruct* data_struct, int32 index = 0, bool force = false); + sint16 getType_sint16(DataStruct* data_struct, int32 index = 0, bool force = false); + sint32 getType_sint32(DataStruct* data_struct, int32 index = 0, bool force = false); + sint64 getType_sint64(DataStruct* data_struct, int32 index = 0, bool force = false); + float getType_float(DataStruct* data_struct, int32 index = 0, bool force = false); + double getType_double(DataStruct* data_struct, int32 index = 0, bool force = false); + char getType_char(DataStruct* data_struct, int32 index = 0, bool force = false); + EQ2_8BitString getType_EQ2_8BitString(DataStruct* data_struct, int32 index = 0, bool force = false); + EQ2_16BitString getType_EQ2_16BitString(DataStruct* data_struct, int32 index = 0, bool force = false); + EQ2_32BitString getType_EQ2_32BitString(DataStruct* data_struct, int32 index = 0, bool force = false); + EQ2_Color getType_EQ2_Color(DataStruct* data_struct, int32 index = 0, bool force = false); + EQ2_EquipmentItem getType_EQ2_EquipmentItem(DataStruct* data_struct, int32 index = 0, bool force = false); + + void setDataType(DataStruct* data_struct, char data, int32 index); + void setDataType(DataStruct* data_struct, int8 data, int32 index); + void setDataType(DataStruct* data_struct, int16 data, int32 index); + void setDataType(DataStruct* data_struct, int32 data, int32 index); + void setDataType(DataStruct* data_struct, int64 data, int32 index); + void setDataType(DataStruct* data_struct, sint8 data, int32 index); + void setDataType(DataStruct* data_struct, sint16 data, int32 index); + void setDataType(DataStruct* data_struct, sint32 data, int32 index); + void setDataType(DataStruct* data_struct, sint64 data, int32 index); + void setDataType(DataStruct* data_struct, float data, int32 index); + void setDataType(DataStruct* data_struct, double data, int32 index); + void setData(DataStruct* data_struct, EQ2_8BitString* input_string, int32 index, bool use_second_type = false); + void setData(DataStruct* data_struct, EQ2_16BitString* input_string, int32 index, bool use_second_type = false); + void setData(DataStruct* data_struct, EQ2_32BitString* input_string, int32 index, bool use_second_type = false); + + template void setData(DataStruct* data_struct, Data* data, int32 index, bool use_second_type = false) { + if (!data_struct) + return; + data_struct->SetIsOptional(false); + int8 type_to_use = (use_second_type) ? data_struct->GetType2() : data_struct->GetType(); + if (type_to_use >= DATA_STRUCT_EQ2_8BIT_STRING && type_to_use <= DATA_STRUCT_EQ2_32BIT_STRING) { + if (type_to_use == DATA_STRUCT_EQ2_8BIT_STRING) { + setSmallString(data_struct, data, index); + } + else if (type_to_use == DATA_STRUCT_EQ2_16BIT_STRING) { + setMediumString(data_struct, data, index); + } + else { + setLargeString(data_struct, data, index); + } + } + else { + if (data_struct && index == 0 && data_struct->GetLength() > 1) { + if (type_to_use == DATA_STRUCT_CHAR) { + for (int32 i = 0; data && i < data_struct->GetLength() && i < strlen(data); i++) + setData(data_struct, data[i], i); + } + else { + for (int32 i = 0; i < data_struct->GetLength(); i++) + setData(data_struct, data[i], i); + } + } + else + setData(data_struct, *data, index); + } + } + template void setData(DataStruct* data_struct, Data data, int32 index, bool use_second_type = false) { + if (data_struct && index < data_struct->GetLength()) { + data_struct->SetIsOptional(false); + int8 type_to_use = (use_second_type) ? data_struct->GetType2() : data_struct->GetType(); + if (use_second_type) { + // Need to figure out why type2 always seems to be 205 + // since only items use type2 for now just hardcoded the value needed (BAD!!!) + //type_to_use = DATA_STRUCT_SINT16; // 9; + data_struct->SetType(type_to_use); + } + switch (type_to_use) { + case DATA_STRUCT_INT8: + setDataType(data_struct, (int8)data, index); + break; + case DATA_STRUCT_INT16: + setDataType(data_struct, (int16)data, index); + break; + case DATA_STRUCT_INT32: + setDataType(data_struct, (int32)data, index); + break; + case DATA_STRUCT_INT64: + setDataType(data_struct, (int64)data, index); + break; + case DATA_STRUCT_SINT8: + setDataType(data_struct, (sint8)data, index); + break; + case DATA_STRUCT_SINT16: + setDataType(data_struct, (sint16)data, index); + break; + case DATA_STRUCT_SINT32: + setDataType(data_struct, (sint32)data, index); + break; + case DATA_STRUCT_SINT64: + setDataType(data_struct, (sint64)data, index); + break; + case DATA_STRUCT_CHAR: + setDataType(data_struct, (char)data, index); + break; + case DATA_STRUCT_FLOAT: + setDataType(data_struct, (float)data, index); + break; + case DATA_STRUCT_DOUBLE: + setDataType(data_struct, (double)data, index); + break; + case DATA_STRUCT_COLOR: + setColor(data_struct, *((EQ2_Color*)&data), index); + break; + case DATA_STRUCT_EQUIPMENT: + setEquipmentByName(data_struct, *((EQ2_EquipmentItem*)&data), index); + break; + case DATA_STRUCT_ITEM: + break; + } + } + } + + template void setSubArrayLengthByName(const char* name, Data data, int32 index1 = 0, int32 index2 = 0) { + char tmp[10] = { 0 }; + sprintf(tmp, "_%i", index1); + string name2 = string(name).append(tmp); + DataStruct* data_struct = findStruct(name2.c_str(), index2); + setData(data_struct, data, index2); + UpdateArrayByArrayLength(data_struct, index2, data); + } + template void setArrayLengthByName(const char* name, Data data, int32 index = 0) { + DataStruct* data_struct = findStruct(name, index); + setData(data_struct, data, index); + UpdateArrayByArrayLength(data_struct, index, data); + } + template void setSubstructArrayLengthByName(const char* substruct_name, const char* name, Data data, int32 substruct_index = 0, int32 index = 0) { + char tmp[10] = { 0 }; + sprintf(tmp, "_%i", substruct_index); + string name2 = string(substruct_name).append("_").append(name).append(tmp); + + DataStruct* data_struct = findStruct(name2.c_str(), index); + setData(data_struct, data, index); + UpdateArrayByArrayLength(data_struct, index, data); + } + void UpdateArrayByArrayLengthName(const char* name, int32 index, int32 size); + void UpdateArrayByArrayLength(DataStruct* data_struct, int32 index, int32 size); + bool StructLoadData(DataStruct* data_struct, void* data, int32 len, bool useType2 = false, bool create_color = false); + bool LoadPacketData(uchar* data, int32 data_len, bool create_color = false); + bool CheckFlagExists(const char* name); + + void setColorByName(const char* name, EQ2_Color* data, int32 index = 0) { + if (data) + setColorByName(name, data->red, data->green, data->blue, index); + } + void setColorByName(const char* name, EQ2_Color data, int32 index = 0) { + setColorByName(name, data.red, data.green, data.blue, index); + } + void setColor(DataStruct* data_struct, EQ2_Color data, int32 index = 0) { + if (data_struct) { + EQ2_Color* ptr = (EQ2_Color*)struct_data[data_struct]; + ptr[index] = data; + } + } + void setColorByName(const char* name, int8 red, int8 green, int8 blue, int32 index = 0) { + setColor(findStruct(name, index), red, green, blue, index); + } + void setColor(DataStruct* data, int8 red, int8 green, int8 blue, int32 index); + void setEquipmentByName(DataStruct* data_struct, EQ2_EquipmentItem data, int32 index = 0) { + if (data_struct) { + EQ2_EquipmentItem* ptr = (EQ2_EquipmentItem*)struct_data[data_struct]; + ptr[index] = data; + } + } +#ifdef WORLD + void setItem(DataStruct* ds, Item* item, Player* player, int32 index, sint8 offset = 0, bool loot_item = false, bool make_empty_item_packet = false, bool inspect = false); + void setItemByName(const char* name, Item* item, Player* player, int32 index = 0, sint8 offset = 0, bool loot_item = false, bool make_empty_item_packet = false, bool inspect = false); + void setItemArrayDataByName(const char* name, Item* item, Player* player, int32 index1 = 0, int32 index2 = 0, sint8 offset = 0, bool loot_item = false, bool make_empty_item_packet = false, bool inspect = false); +#endif + void setEquipmentByName(const char* name, EQ2_EquipmentItem data, int32 index = 0) { + setEquipmentByName(findStruct(name, index), data, index); + } + void setEquipmentByName(const char* name, EQ2_EquipmentItem* data, int32 size) { + DataStruct* data_struct = findStruct(name, 0); + if (data_struct) { + for (int32 i = 0; i < size; i++) + setEquipmentByName(data_struct, data[i], i); + } + } + void setEquipmentByName(const char* name, int32 type, int8 c_red, int8 c_blue, int8 c_green, int8 h_red, int8 h_blue, int8 h_green, int32 index = 0) { + setEquipment(findStruct(name, index), type, c_red, c_blue, c_green, h_red, h_blue, h_green, index); + } + void setEquipment(DataStruct* data, int16 type, int8 c_red, int8 c_blue, int8 c_green, int8 h_red, int8 h_blue, int8 h_green, int32 index); + void remove(DataStruct* data); + vector* getStructs() { return &structs; } + DataStruct* findStruct(const char* name, int32 index); + DataStruct* findStruct(const char* name, int32 index1, int32 index2); + void remove(const char* name); + void remove(int32 position); + void serializePacket(bool clear = true); + + void AddSerializedData(DataStruct* data, int32 index = 0, string* datastring = 0); + EQ2Packet* serialize(); + EQ2Packet* serializeCountPacket(int16 version, int8 offset = 0, uchar* orig_packet = 0, uchar* xor_packet = 0); + string* serializeString(); + int32 GetVersion() { return version; } + void SetVersion(int32 in_version) { version = in_version; } + bool SetOpcode(const char* new_opcode); + EmuOpcode GetOpcode() { return opcode; } + int16 GetOpcodeValue(int16 client_version); + const char* GetName() { return name.c_str(); } + void SetName(const char* in_name) { name = string(in_name); } + bool LoadedSuccessfully() { return loadedSuccessfully; } + bool IsStringValueType(string in_name, int32 index); + bool IsColorValueType(string in_name, int32 index); + int32 GetTotalPacketSize(); + PacketStruct* GetPacketStructByName(const char* name); + void* GetStructPointer(DataStruct* data_struct, bool erase = false); + void PrintPacket(); + string GetSQLQuery(const char* table_name); + vector GetDataStructs(); + void AddPackedData(); + void ResetData(); + void AddFlag(const char* name); + +private: + PacketStruct* parent; + int32 sub_packet_size; + string opcode_type; + bool sub_packet; + bool loadedSuccessfully; + string name; + EmuOpcode opcode; + int16 version; + int16 client_version; + vector arrays; + vector flags; + map struct_data; + map packed_data; + map struct_map; + vector structs; + vector orig_structs; + vector orig_packets; +}; +#endif \ No newline at end of file diff --git a/old/common/RC4.cpp b/old/common/RC4.cpp new file mode 100644 index 0000000..15d9f78 --- /dev/null +++ b/old/common/RC4.cpp @@ -0,0 +1,93 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include "RC4.h" +#include + +static bool g_bInitStateInitialized = false; +static uchar g_byInitState[256]; + +RC4::RC4(int64 nKey) +{ + if( !g_bInitStateInitialized ) + { + for(int16 i = 0; i < 256; i++ ) + g_byInitState[i] = i; + } + Init(nKey); +} + +RC4::~RC4() +{ +} + +void RC4::Init(int64 nKey) +{ + memcpy(m_state, g_byInitState, 256); + m_x = 0; + m_y = 0; + + ulong dwKeyIndex = 0; + ulong dwStateIndex = 0; + uchar* pKey = (uchar*)&nKey; + for(int16 i = 0; i < 256; i++ ) + { + ulong dwTemp = m_state[i]; + dwStateIndex += pKey[dwKeyIndex] + dwTemp; + dwStateIndex &= 0xFF; + m_state[i] = m_state[dwStateIndex]; + m_state[dwStateIndex] = (uchar)dwTemp; + dwKeyIndex++; + dwKeyIndex &= 7; + } +} + +// A = m_state[X + 1] +// B = m_state[Y + A] +// C ^= m_state[(A + B)] + +// X = 20 +// Y = ? +// C = 0 +// m_state[(A + B)] = Cypher Byte + +void RC4::Cypher(uchar* pBuffer, int32 nLength) +{ + int32 nOffset = 0; + uchar byKey1 = m_x; + uchar byKey2 = m_y; + if( nLength > 0 ) + { + do + { + byKey1++; + uchar byKeyVal1 = m_state[byKey1]; + + byKey2 += byKeyVal1; + uchar byKeyVal2 = m_state[byKey2]; + + m_state[byKey1] = byKeyVal2; + m_state[byKey2] = byKeyVal1; + + pBuffer[nOffset++] ^= m_state[(byKeyVal1 + byKeyVal2) & 0xFF]; + } while( nOffset < nLength ); + } + m_x = byKey1; + m_y = byKey2; +} diff --git a/old/common/RC4.h b/old/common/RC4.h new file mode 100644 index 0000000..52a3ea9 --- /dev/null +++ b/old/common/RC4.h @@ -0,0 +1,38 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef _EQ2_RC4_H +#define _EQ2_RC4_H +#include "../common/types.h" +class RC4 +{ +public: + RC4(int64 nKey); + ~RC4(); + + void Init(int64 nKey); + void Cypher(uchar* pData, int32 nLen); + +private: + uchar m_state[256]; + uchar m_x; + uchar m_y; +}; +#endif + diff --git a/old/common/TCPConnection.cpp b/old/common/TCPConnection.cpp new file mode 100644 index 0000000..81493b2 --- /dev/null +++ b/old/common/TCPConnection.cpp @@ -0,0 +1,1729 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include "../common/debug.h" + +#include +using namespace std; +#include +#include +#include +using namespace std; + +#include "TCPConnection.h" +#include "../common/servertalk.h" +#include "../common/timer.h" +#include "../common/packet_dump.h" +#include "Log.h" + +#ifdef FREEBSD //Timothy Whitman - January 7, 2003 +#define MSG_NOSIGNAL 0 +#endif + +#ifdef WIN32 +InitWinsock winsock; +#endif + +#define LOOP_GRANULARITY 3 //# of ms between checking our socket/queues +#define SERVER_LOOP_GRANULARITY 3 //# of ms between checking our socket/queues + +#define TCPN_DEBUG 0 +#define TCPN_DEBUG_Console 0 +#define TCPN_DEBUG_Memory 0 +#define TCPN_LOG_PACKETS 0 +#define TCPN_LOG_RAW_DATA_OUT 0 +#define TCPN_LOG_RAW_DATA_IN 0 + +TCPConnection::TCPNetPacket_Struct* TCPConnection::MakePacket(ServerPacket* pack, int32 iDestination) { + sint32 size = sizeof(TCPNetPacket_Struct) + pack->size; + if (pack->compressed) { + size += 4; + } + if (iDestination) { + size += 4; + } + TCPNetPacket_Struct* tnps = (TCPNetPacket_Struct*) new uchar[size]; + tnps->size = size; + tnps->opcode = pack->opcode; + *((int8*) &tnps->flags) = 0; + uchar* buffer = tnps->buffer; + if (pack->compressed) { + tnps->flags.compressed = 1; + *((sint32*) buffer) = pack->InflatedSize; + buffer += 4; + } + if (iDestination) { + tnps->flags.destination = 1; + *((sint32*) buffer) = iDestination; + buffer += 4; + } + memcpy(buffer, pack->pBuffer, pack->size); + return tnps; +} + +TCPConnection::TCPConnection(bool iOldFormat, TCPServer* iRelayServer, eTCPMode iMode) { + id = 0; + Server = iRelayServer; + if (Server) + RelayServer = true; + else + RelayServer = false; + RelayLink = 0; + RelayCount = 0; + RemoteID = 0; + pOldFormat = iOldFormat; + ConnectionType = Outgoing; + TCPMode = iMode; + pState = TCPS_Ready; + pFree = false; + pEcho = false; + sock = 0; + rIP = 0; + rPort = 0; + keepalive_timer = new Timer(SERVER_TIMEOUT); + timeout_timer = new Timer(SERVER_TIMEOUT * 2); + recvbuf = 0; + sendbuf = 0; + pRunLoop = false; + charAsyncConnect = 0; + pAsyncConnect = false; + connection_socket = 0; + recvbuf_size = 0; + recvbuf_used = 0; + recvbuf_echo = 0; + sendbuf_size = 0; + sendbuf_used = 0; +#if TCPN_DEBUG_Memory >= 7 + cout << "Constructor #1 on outgoing TCP# " << GetID() << endl; +#endif +} + +TCPConnection::TCPConnection(TCPServer* iServer, SOCKET in_socket, int32 irIP, int16 irPort, bool iOldFormat) { + Server = iServer; + RelayLink = 0; + RelayServer = false; + RelayCount = 0; + RemoteID = 0; + id = Server->GetNextID(); + ConnectionType = Incomming; + pOldFormat = iOldFormat; + TCPMode = modePacket; + pState = TCPS_Connected; + pFree = false; + pEcho = false; + sock = 0; + connection_socket = in_socket; + rIP = irIP; + rPort = irPort; + keepalive_timer = new Timer(SERVER_TIMEOUT); + timeout_timer = new Timer(SERVER_TIMEOUT * 2); + recvbuf = 0; + sendbuf = 0; + pRunLoop = false; + charAsyncConnect = 0; + pAsyncConnect = false; + recvbuf_size = 0; + recvbuf_used = 0; + recvbuf_echo = 0; + sendbuf_size = 0; + sendbuf_used = 0; +#if TCPN_DEBUG_Memory >= 7 + cout << "Constructor #2 on outgoing TCP# " << GetID() << endl; +#endif +} + +TCPConnection::TCPConnection(TCPServer* iServer, TCPConnection* iRelayLink, int32 iRemoteID, int32 irIP, int16 irPort) { + Server = iServer; + RelayLink = iRelayLink; + RelayServer = true; + id = Server->GetNextID(); + RelayCount = 0; + RemoteID = iRemoteID; + if (!RemoteID) + ThrowError("Error: TCPConnection: RemoteID == 0 on RelayLink constructor"); + pOldFormat = false; + ConnectionType = Incomming; + TCPMode = modePacket; + pState = TCPS_Connected; + pFree = false; + pEcho = false; + sock = 0; + connection_socket = 0; + rIP = irIP; + rPort = irPort; + keepalive_timer = 0; + timeout_timer = 0; + recvbuf = 0; + sendbuf = 0; + pRunLoop = false; + charAsyncConnect = 0; + pAsyncConnect = false; + recvbuf_size = 0; + recvbuf_used = 0; + recvbuf_echo = 0; + sendbuf_size = 0; + sendbuf_used = 0; +#if TCPN_DEBUG_Memory >= 7 + cout << "Constructor #3 on outgoing TCP# " << GetID() << endl; +#endif +} + +TCPConnection::~TCPConnection() { + Disconnect(); + ClearBuffers(); + if (ConnectionType == Outgoing) { + MRunLoop.lock(); + pRunLoop = false; + MRunLoop.unlock(); + MLoopRunning.lock(); + MLoopRunning.unlock(); +#if TCPN_DEBUG_Memory >= 6 + cout << "Deconstructor on outgoing TCP# " << GetID() << endl; +#endif + } +#if TCPN_DEBUG_Memory >= 5 + else { + cout << "Deconstructor on incomming TCP# " << GetID() << endl; + } +#endif + safe_delete(keepalive_timer); + safe_delete(timeout_timer); + safe_delete_array(recvbuf); + safe_delete_array(sendbuf); + safe_delete_array(charAsyncConnect); +} + +void TCPConnection::SetState(int8 in_state) { + MState.lock(); + pState = in_state; + MState.unlock(); +} + +int8 TCPConnection::GetState() { + int8 ret; + MState.lock(); + ret = pState; + MState.unlock(); + return ret; +} + +void TCPConnection::Free() { + if (ConnectionType == Outgoing) { + ThrowError("TCPConnection::Free() called on an Outgoing connection"); + } +#if TCPN_DEBUG_Memory >= 5 + cout << "Free on TCP# " << GetID() << endl; +#endif + Disconnect(); + pFree = true; +} + +bool TCPConnection::SendPacket(ServerPacket* pack, int32 iDestination) { + LockMutex lock(&MState); + if (!Connected()) + return false; + eTCPMode tmp = GetMode(); + if (tmp != modePacket && tmp != modeTransition) + return false; + if (RemoteID) + return RelayLink->SendPacket(pack, RemoteID); + else { + TCPNetPacket_Struct* tnps = MakePacket(pack, iDestination); + if (tmp == modeTransition) { + InModeQueuePush(tnps); + } + else { +#if TCPN_LOG_PACKETS >= 1 + if (pack && pack->opcode != 0) { + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Logging outgoing TCP packet. OPCode: 0x" << hex << setw(4) << setfill('0') << pack->opcode << dec << ", size: " << setw(5) << setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << endl; +#if TCPN_LOG_PACKETS == 2 + if (pack->size >= 32) + DumpPacket(pack->pBuffer, 32); + else + DumpPacket(pack); +#endif +#if TCPN_LOG_PACKETS >= 3 + DumpPacket(pack); +#endif + } +#endif + ServerSendQueuePushEnd((uchar**) &tnps, tnps->size); + } + } + return true; +} + +bool TCPConnection::SendPacket(TCPNetPacket_Struct* tnps) { + LockMutex lock(&MState); + if (RemoteID) + return false; + if (!Connected()) + return false; + eTCPMode tmp = GetMode(); + if (tmp == modeTransition) { + TCPNetPacket_Struct* tnps2 = (TCPNetPacket_Struct*) new uchar[tnps->size]; + memcpy(tnps2, tnps, tnps->size); + InModeQueuePush(tnps2); + return true; + } + if (GetMode() != modePacket) + return false; +#if TCPN_LOG_PACKETS >= 1 + if (tnps && tnps->opcode != 0) { + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Logging outgoing TCP NetPacket. OPCode: 0x" << hex << setw(4) << setfill('0') << tnps->opcode << dec << ", size: " << setw(5) << setfill(' ') << tnps->size << " " << inet_ntoa(in) << ":" << GetrPort(); + if (pOldFormat) + cout << " (OldFormat)"; + cout << endl; +#if TCPN_LOG_PACKETS == 2 + if (tnps->size >= 32) + DumpPacket((uchar*) tnps, 32); + else + DumpPacket((uchar*) tnps, tnps->size); +#endif +#if TCPN_LOG_PACKETS >= 3 + DumpPacket((uchar*) tnps, tnps->size); +#endif + } +#endif + ServerSendQueuePushEnd((const uchar*) tnps, tnps->size); + return true; +} + +bool TCPConnection::Send(const uchar* data, sint32 size) { + if (!Connected()) + return false; + if (GetMode() != modeConsole) + return false; + if (!size) + return true; + ServerSendQueuePushEnd(data, size); + return true; +} + +void TCPConnection::InModeQueuePush(TCPNetPacket_Struct* tnps) { + MSendQueue.lock(); + InModeQueue.push(tnps); + MSendQueue.unlock(); +} + +void TCPConnection::ServerSendQueuePushEnd(const uchar* data, sint32 size) { + MSendQueue.lock(); + if (sendbuf == 0) { + sendbuf = new uchar[size]; + sendbuf_size = size; + sendbuf_used = 0; + } + else if (size > (sendbuf_size - sendbuf_used)) { + sendbuf_size += size + 1024; + uchar* tmp = new uchar[sendbuf_size]; + memcpy(tmp, sendbuf, sendbuf_used); + safe_delete_array(sendbuf); + sendbuf = tmp; + } + memcpy(&sendbuf[sendbuf_used], data, size); + sendbuf_used += size; + MSendQueue.unlock(); +} + +void TCPConnection::ServerSendQueuePushEnd(uchar** data, sint32 size) { + MSendQueue.lock(); + if (sendbuf == 0) { + sendbuf = *data; + sendbuf_size = size; + sendbuf_used = size; + MSendQueue.unlock(); + *data = 0; + return; + } + if (size > (sendbuf_size - sendbuf_used)) { + sendbuf_size += size; + uchar* tmp = new uchar[sendbuf_size]; + memcpy(tmp, sendbuf, sendbuf_used); + safe_delete_array(sendbuf); + sendbuf = tmp; + } + memcpy(&sendbuf[sendbuf_used], *data, size); + sendbuf_used += size; + MSendQueue.unlock(); + delete[] (TCPNetPacket_Struct*)*data; +} + +void TCPConnection::ServerSendQueuePushFront(uchar* data, sint32 size) { + MSendQueue.lock(); + if (sendbuf == 0) { + sendbuf = new uchar[size]; + sendbuf_size = size; + sendbuf_used = 0; + } + else if (size > (sendbuf_size - sendbuf_used)) { + sendbuf_size += size; + uchar* tmp = new uchar[sendbuf_size]; + memcpy(&tmp[size], sendbuf, sendbuf_used); + safe_delete_array(sendbuf); + sendbuf = tmp; + } + memcpy(sendbuf, data, size); + sendbuf_used += size; + MSendQueue.unlock(); +} + +bool TCPConnection::ServerSendQueuePop(uchar** data, sint32* size) { + bool ret; + if (!MSendQueue.trylock()) + return false; + if (sendbuf) { + *data = sendbuf; + *size = sendbuf_used; + sendbuf = 0; + ret = true; + } + else { + ret = false; + } + MSendQueue.unlock(); + return ret; +} + +ServerPacket* TCPConnection::PopPacket() { + ServerPacket* ret; + if (!MOutQueueLock.trylock()) + return 0; + ret = OutQueue.pop(); + MOutQueueLock.unlock(); + return ret; +} + +char* TCPConnection::PopLine() { + char* ret; + if (!MOutQueueLock.trylock()) + return 0; + ret = (char*) LineOutQueue.pop(); + MOutQueueLock.unlock(); + return ret; +} + +void TCPConnection::OutQueuePush(ServerPacket* pack) { + MOutQueueLock.lock(); + OutQueue.push(pack); + MOutQueueLock.unlock(); +} + +void TCPConnection::LineOutQueuePush(char* line) { +#if defined(GOTFRAGS) && 0 + if (strcmp(line, "**CRASHME**") == 0) { + int i = 0; + cout << (5 / i) << endl; + } +#endif + if (strcmp(line, "**PACKETMODE**") == 0) { + MSendQueue.lock(); + safe_delete_array(sendbuf); + if (TCPMode == modeConsole) + Send((const uchar*) "\0**PACKETMODE**\r", 16); + TCPMode = modePacket; + TCPNetPacket_Struct* tnps = 0; + while ((tnps = InModeQueue.pop())) { + SendPacket(tnps); + safe_delete_array(tnps); + } + MSendQueue.unlock(); + safe_delete_array(line); + return; + } + MOutQueueLock.lock(); + LineOutQueue.push(line); + MOutQueueLock.unlock(); +} + +void TCPConnection::Disconnect(bool iSendRelayDisconnect) { + if (connection_socket != INVALID_SOCKET && connection_socket != 0) { + MState.lock(); + if (pState == TCPS_Connected || pState == TCPS_Disconnecting || pState == TCPS_Disconnected) + SendData(); + pState = TCPS_Closing; + MState.unlock(); + shutdown(connection_socket, 0x01); + shutdown(connection_socket, 0x00); +#ifdef WIN32 + closesocket(connection_socket); +#else + close(connection_socket); +#endif + connection_socket = 0; + rIP = 0; + rPort = 0; + ClearBuffers(); + } + SetState(TCPS_Ready); + if (RelayLink) { + RelayLink->RemoveRelay(this, iSendRelayDisconnect); + RelayLink = 0; + } +} + +bool TCPConnection::GetAsyncConnect() { + bool ret; + MAsyncConnect.lock(); + ret = pAsyncConnect; + MAsyncConnect.unlock(); + return ret; +} + +bool TCPConnection::SetAsyncConnect(bool iValue) { + bool ret; + MAsyncConnect.lock(); + ret = pAsyncConnect; + pAsyncConnect = iValue; + MAsyncConnect.unlock(); + return ret; +} + +void TCPConnection::AsyncConnect(char* irAddress, int16 irPort) { + if (ConnectionType != Outgoing) { + // If this code runs, we got serious problems + // Crash and burn. + ThrowError("TCPConnection::AsyncConnect() call on a Incomming connection object!"); + return; + } + if (GetState() != TCPS_Ready) + return; + MAsyncConnect.lock(); + if (pAsyncConnect) { + MAsyncConnect.unlock(); + return; + } + pAsyncConnect = true; + safe_delete_array(charAsyncConnect); + charAsyncConnect = new char[strlen(irAddress) + 1]; + strcpy(charAsyncConnect, irAddress); + rPort = irPort; + MAsyncConnect.unlock(); + if (!pRunLoop) { + pRunLoop = true; +#ifdef WIN32 + _beginthread(TCPConnectionLoop, 0, this); +#else + pthread_t thread; + pthread_create(&thread, NULL, TCPConnectionLoop, this); + pthread_detach(thread); +#endif + } + return; +} + +void TCPConnection::AsyncConnect(int32 irIP, int16 irPort) { + if (ConnectionType != Outgoing) { + // If this code runs, we got serious problems + // Crash and burn. + ThrowError("TCPConnection::AsyncConnect() call on a Incomming connection object!"); + return; + } + if (GetState() != TCPS_Ready) + return; + MAsyncConnect.lock(); + if (pAsyncConnect) { + MAsyncConnect.unlock(); + return; + } + pAsyncConnect = true; + safe_delete(charAsyncConnect); + rIP = irIP; + rPort = irPort; + MAsyncConnect.unlock(); + if (!pRunLoop) { + pRunLoop = true; +#ifdef WIN32 + _beginthread(TCPConnectionLoop, 0, this); +#else + pthread_t thread; + pthread_create(&thread, NULL, TCPConnectionLoop, this); + pthread_detach(thread); +#endif + } + return; +} + +bool TCPConnection::Connect(char* irAddress, int16 irPort, char* errbuf) { + if (errbuf) + errbuf[0] = 0; + int32 tmpIP = ResolveIP(irAddress); + if (!tmpIP) { + if (errbuf) { +#ifdef WIN32 + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Couldnt resolve hostname. Error: %i", WSAGetLastError()); +#else + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Couldnt resolve hostname. Error #%i: %s", errno, strerror(errno)); +#endif + } + return false; + } + return Connect(tmpIP, irPort, errbuf); +} + +bool TCPConnection::Connect(int32 in_ip, int16 in_port, char* errbuf) { + if (errbuf) + errbuf[0] = 0; + if (ConnectionType != Outgoing) { + // If this code runs, we got serious problems + // Crash and burn. + ThrowError("TCPConnection::Connect() call on a Incomming connection object!"); + return false; + } + MState.lock(); + if (pState == TCPS_Ready) { + pState = TCPS_Connecting; + } + else { + MState.unlock(); + SetAsyncConnect(false); + return false; + } + MState.unlock(); + if (!pRunLoop) { + pRunLoop = true; +#ifdef WIN32 + _beginthread(TCPConnectionLoop, 0, this); +#else + pthread_t thread; + pthread_create(&thread, NULL, TCPConnectionLoop, this); + pthread_detach(thread); +#endif + } + + connection_socket = INVALID_SOCKET; + struct sockaddr_in server_sin; + // struct in_addr in; + + if ((connection_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET || connection_socket == 0) { +#ifdef WIN32 + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Allocating socket failed. Error: %i", WSAGetLastError()); +#else + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Allocating socket failed. Error: %s", strerror(errno)); +#endif + SetState(TCPS_Ready); + SetAsyncConnect(false); + return false; + } + server_sin.sin_family = AF_INET; + server_sin.sin_addr.s_addr = in_ip; + server_sin.sin_port = htons(in_port); + + // Establish a connection to the server socket. +#ifdef WIN32 + if (connect(connection_socket, (PSOCKADDR) &server_sin, sizeof (server_sin)) == SOCKET_ERROR) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): connect() failed. Error: %i", WSAGetLastError()); + closesocket(connection_socket); + connection_socket = 0; + SetState(TCPS_Ready); + SetAsyncConnect(false); + return false; + } +#else + if (connect(connection_socket, (struct sockaddr *) &server_sin, sizeof (server_sin)) == SOCKET_ERROR) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): connect() failed. Error: %s", strerror(errno)); + close(connection_socket); + connection_socket = 0; + SetState(TCPS_Ready); + SetAsyncConnect(false); + return false; + } +#endif + int bufsize = 64 * 1024; // 64kbyte recieve buffer, up from default of 8k + setsockopt(connection_socket, SOL_SOCKET, SO_RCVBUF, (char*) &bufsize, sizeof(bufsize)); +#ifdef WIN32 + unsigned long nonblocking = 1; + ioctlsocket(connection_socket, FIONBIO, &nonblocking); +#else + fcntl(connection_socket, F_SETFL, O_NONBLOCK); +#endif + + SetEcho(false); + MSendQueue.lock(); + ClearBuffers(); + TCPMode = modePacket; + + MSendQueue.unlock(); + + rIP = in_ip; + rPort = in_port; + SetState(TCPS_Connected); + SetAsyncConnect(false); + return true; +} + +void TCPConnection::ClearBuffers() { + LockMutex lock1(&MSendQueue); + LockMutex lock2(&MOutQueueLock); + LockMutex lock3(&MRunLoop); + LockMutex lock4(&MState); + safe_delete_array(recvbuf); + safe_delete_array(sendbuf); + ServerPacket* pack = 0; + while ((pack = PopPacket())) + safe_delete(pack); + TCPNetPacket_Struct* tnps = 0; + while ((tnps = InModeQueue.pop())) + safe_delete(tnps); + char* line = 0; + while ((line = LineOutQueue.pop())) + safe_delete_array(line); + keepalive_timer->Start(); + timeout_timer->Start(); +} + +bool TCPConnection::CheckNetActive() { + MState.lock(); + if (pState == TCPS_Connected || pState == TCPS_Disconnecting) { + MState.unlock(); + return true; + } + MState.unlock(); + return false; +} + +bool TCPConnection::Process() { + char errbuf[TCPConnection_ErrorBufferSize]; + if (!CheckNetActive()) { + if (ConnectionType == Outgoing) { + if (GetAsyncConnect()) { + if (charAsyncConnect) + rIP = ResolveIP(charAsyncConnect); + Connect(rIP, rPort); + } + } + if (GetState() == TCPS_Disconnected) { + Disconnect(); + return false; + } + else if (GetState() == TCPS_Connecting) + return true; + else + return false; + } + if (!SendData(errbuf)) { + struct in_addr in; + in.s_addr = GetrIP(); + cout << inet_ntoa(in) << ":" << GetrPort() << ": " << errbuf << endl; + return false; + } + if (!Connected()) + return false; + if (!RecvData(errbuf)) { + struct in_addr in; + in.s_addr = GetrIP(); + cout << inet_ntoa(in) << ":" << GetrPort() << ": " << errbuf << endl; + return false; + } + return true; +} + +bool TCPConnection::RecvData(char* errbuf) { + if (errbuf) + errbuf[0] = 0; + if (!Connected()) { + return false; + } + + int status = 0; + if (recvbuf == 0) { + recvbuf = new uchar[5120]; + recvbuf_size = 5120; + recvbuf_used = 0; + recvbuf_echo = 0; + } + else if ((recvbuf_size - recvbuf_used) < 2048) { + uchar* tmpbuf = new uchar[recvbuf_size + 5120]; + memcpy(tmpbuf, recvbuf, recvbuf_used); + recvbuf_size += 5120; + safe_delete_array(recvbuf); + recvbuf = tmpbuf; + if (recvbuf_size >= MaxTCPReceiveBufferSize) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): recvbuf_size >= MaxTCPReceiveBufferSize"); + return false; + } + } + + status = recv(connection_socket, (char *) &recvbuf[recvbuf_used], (recvbuf_size - recvbuf_used), 0); + if (status >= 1) { +#if TCPN_LOG_RAW_DATA_IN >= 1 + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Read " << status << " bytes from network. (recvbuf_used = " << recvbuf_used << ") " << inet_ntoa(in) << ":" << GetrPort(); + if (pOldFormat) + cout << " (OldFormat)"; + cout << endl; +#if TCPN_LOG_RAW_DATA_IN == 2 + sint32 tmp = status; + if (tmp > 32) + tmp = 32; + DumpPacket(&recvbuf[recvbuf_used], status); +#elif TCPN_LOG_RAW_DATA_IN >= 3 + DumpPacket(&recvbuf[recvbuf_used], status); +#endif +#endif + recvbuf_used += status; + timeout_timer->Start(); + if (!ProcessReceivedData(errbuf)) + return false; + } + else if (status == SOCKET_ERROR) { +#ifdef WIN32 + if (!(WSAGetLastError() == WSAEWOULDBLOCK)) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): Error: %i", WSAGetLastError()); + return false; + } +#else + if (!(errno == EWOULDBLOCK)) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): Error: %s", strerror(errno)); + return false; + } +#endif + } + if ((TCPMode == modePacket || TCPMode == modeTransition) && timeout_timer->Check()) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): Connection timeout"); + return false; + } + + return true; +} + + +bool TCPConnection::GetEcho() { + bool ret; + MEcho.lock(); + ret = pEcho; + MEcho.unlock(); + return ret; +} + +void TCPConnection::SetEcho(bool iValue) { + MEcho.lock(); + pEcho = iValue; + MEcho.unlock(); +} + +bool TCPConnection::ProcessReceivedData(char* errbuf) { + if (errbuf) + errbuf[0] = 0; + if (!recvbuf) + return true; + if (TCPMode == modePacket) { + //if (pOldFormat) + // return ProcessReceivedDataAsOldPackets(errbuf); + //else + return ProcessReceivedDataAsPackets(errbuf); + } + else { +#if TCPN_DEBUG_Console >= 4 + if (recvbuf_used) { + cout << "Starting Processing: recvbuf=" << recvbuf_used << endl; + DumpPacket(recvbuf, recvbuf_used); + } +#endif + for (int i=0; i < recvbuf_used; i++) { + if (GetEcho() && i >= recvbuf_echo) { + Send(&recvbuf[i], 1); + recvbuf_echo = i + 1; + } + switch(recvbuf[i]) { + case 0: { // 0 is the code for clear buffer + if (i==0) { + recvbuf_used--; + recvbuf_echo--; + memcpy(recvbuf, &recvbuf[1], recvbuf_used); + i = -1; + } else { + if (i == recvbuf_used) { + safe_delete_array(recvbuf); + i = -1; + } + else { + uchar* tmpdel = recvbuf; + recvbuf = new uchar[recvbuf_size]; + memcpy(recvbuf, &tmpdel[i+1], recvbuf_used-i); + recvbuf_used -= i + 1; + recvbuf_echo -= i + 1; + safe_delete(tmpdel); + i = -1; + } + } +#if TCPN_DEBUG_Console >= 5 + cout << "Removed 0x00" << endl; + if (recvbuf_used) { + cout << "recvbuf left: " << recvbuf_used << endl; + DumpPacket(recvbuf, recvbuf_used); + } + else + cout << "recbuf left: None" << endl; +#endif + break; + } + case 10: + case 13: // newline marker + { + if (i==0) { // empty line + recvbuf_used--; + recvbuf_echo--; + memcpy(recvbuf, &recvbuf[1], recvbuf_used); + i = -1; + } else { + char* line = new char[i+1]; + memset(line, 0, i+1); + memcpy(line, recvbuf, i); +#if TCPN_DEBUG_Console >= 3 + cout << "Line Out: " << endl; + DumpPacket((uchar*) line, i); +#endif + //line[i] = 0; + uchar* tmpdel = recvbuf; + recvbuf = new uchar[recvbuf_size]; + recvbuf_used -= i+1; + recvbuf_echo -= i+1; + memcpy(recvbuf, &tmpdel[i+1], recvbuf_used); +#if TCPN_DEBUG_Console >= 5 + cout << "i+1=" << i+1 << endl; + if (recvbuf_used) { + cout << "recvbuf left: " << recvbuf_used << endl; + DumpPacket(recvbuf, recvbuf_used); + } + else + cout << "recbuf left: None" << endl; +#endif + safe_delete(tmpdel); + if (strlen(line) > 0) + LineOutQueuePush(line); + else + safe_delete_array(line); + if (TCPMode == modePacket) { + return ProcessReceivedDataAsPackets(errbuf); + } + i = -1; + } + break; + } + case 8: // backspace + { + if (i==0) { // nothin to backspace + recvbuf_used--; + recvbuf_echo--; + memcpy(recvbuf, &recvbuf[1], recvbuf_used); + i = -1; + } else { + uchar* tmpdel = recvbuf; + recvbuf = new uchar[recvbuf_size]; + memcpy(recvbuf, tmpdel, i-1); + memcpy(&recvbuf[i-1], &tmpdel[i+1], recvbuf_used-i); + recvbuf_used -= 2; + recvbuf_echo -= 2; + safe_delete(tmpdel); + i -= 2; + } + break; + } + } + } + if (recvbuf_used < 0) + safe_delete_array(recvbuf); + } + return true; +} + +bool TCPConnection::ProcessReceivedDataAsPackets(char* errbuf) { + if (errbuf) + errbuf[0] = 0; + sint32 base = 0; + sint32 size = 0; + uchar* buffer; + sint32 sizeReq = sizeof(TCPNetPacket_Struct); + ServerPacket* pack = 0; + while ((recvbuf_used - base) >= size) { + TCPNetPacket_Struct* tnps = (TCPNetPacket_Struct*) &recvbuf[base]; + buffer = tnps->buffer; + size = tnps->size; + + if (size < sizeReq || recvbuf_used < sizeReq || size >= MaxTCPReceiveBufferSize) { +#if TCPN_DEBUG_Memory >= 1 + cout << "TCPConnection[" << GetID() << "]::ProcessReceivedDataAsPackets(): size[" << size << "] >= MaxTCPReceiveBufferSize" << endl; +#endif + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::ProcessReceivedDataAsPackets(): size provided %i, recvbuf_used %i, checks failed: struct_size < %i || recvbuf_used < sizeReq || size >= MaxTCPReceiveBufferSize", size, recvbuf_used, sizeReq); + return false; + } + if ((recvbuf_used - base) >= size) { + // ok, we got enough data to make this packet! + safe_delete(pack); + pack = new ServerPacket; + pack->size = size - sizeof(TCPNetPacket_Struct); + // read headers + pack->opcode = tnps->opcode; + if (tnps->flags.compressed) { + sizeReq += 4; + if(size < sizeReq || recvbuf_used < sizeReq) + { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::ProcessReceivedDataAsPackets(Flags.Compressed): size provided %i, recvbuf_used %i, checks failed: struct_size < %i || recvbuf_used < sizeReq", size, recvbuf_used, sizeReq); + safe_delete(pack); + return false; + } + pack->compressed = true; + pack->InflatedSize = *((sint32*)buffer); + pack->size -= 4; + buffer += 4; + } + if (tnps->flags.destination) { + sizeReq += 4; + if(size < sizeReq || recvbuf_used < sizeReq) + { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::ProcessReceivedDataAsPackets(Flags.Destination): size provided %i, recvbuf_used %i, checks failed: struct_size < %i || recvbuf_used < sizeReq", size, recvbuf_used, sizeReq); + safe_delete(pack); + return false; + } + pack->destination = *((sint32*)buffer); + pack->size -= 4; + buffer += 4; + } + // end read headers + if (pack->size > 0) { + if (tnps->flags.compressed) { + // Lets decompress the packet here + pack->compressed = false; + if(pack->InflatedSize < MaxTCPReceiveBufferSize) + { + pack->pBuffer = new uchar[pack->InflatedSize]; + pack->size = InflatePacket(buffer, pack->size, pack->pBuffer, pack->InflatedSize); + if(!pack->size) + { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::ProcessReceivedDataAsPackets(InflatePacket): size provided %i, recvbuf_used: %i, sizeReq: %i, could not inflate packet", size, recvbuf_used, sizeReq); + + safe_delete(pack); + return false; + } + } + else + { + cout << "Invalid inflated packet." << endl; + safe_delete(pack); + return false; + } + } + else { + pack->pBuffer = new uchar[pack->size]; + memcpy(pack->pBuffer, buffer, pack->size); + } + } + if (pack->opcode == 0) { + if (pack->size) { +#if TCPN_DEBUG >= 2 + cout << "Received TCP Network layer packet" << endl; +#endif + ProcessNetworkLayerPacket(pack); + } +#if TCPN_DEBUG >= 5 + else { + cout << "Received TCP keepalive packet. (opcode=0)" << endl; + } +#endif + } + else { +#if TCPN_LOG_PACKETS >= 1 + if (pack && pack->opcode != 0) { + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Logging incoming TCP packet. OPCode: 0x" << hex << setw(4) << setfill('0') << pack->opcode << dec << ", size: " << setw(5) << setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << endl; +#if TCPN_LOG_PACKETS == 2 + if (pack->size >= 32) + DumpPacket(pack->pBuffer, 32); + else + DumpPacket(pack); +#endif +#if TCPN_LOG_PACKETS >= 3 + DumpPacket(pack); +#endif + } +#endif + if (RelayServer && Server && pack->destination) { + TCPConnection* con = Server->GetConnection(pack->destination); + if (!con) { +#if TCPN_DEBUG >= 1 + cout << "Error relaying packet: con = 0" << endl; +#endif + } + else{ + con->OutQueuePush(pack); + pack = 0; + } + } + else{ + OutQueuePush(pack); + pack = 0; + } + } + base += size; + size = 7; + } + } + safe_delete(pack); + if (base != 0) { + if (base >= recvbuf_used) { + safe_delete_array(recvbuf); + } + else { + uchar* tmpbuf = new uchar[recvbuf_size - base]; + memcpy(tmpbuf, &recvbuf[base], recvbuf_used - base); + safe_delete_array(recvbuf); + recvbuf = tmpbuf; + recvbuf_used -= base; + recvbuf_size -= base; + } + } + return true; +} + +bool TCPConnection::ProcessReceivedDataAsOldPackets(char* errbuf) { + sint32 base = 0; + sint32 size = 4; + uchar* buffer; + ServerPacket* pack = 0; + while ((recvbuf_used - base) >= size) { + buffer = &recvbuf[base]; + memcpy(&size, &buffer[2], 2); + if (size >= MaxTCPReceiveBufferSize) { +#if TCPN_DEBUG_Memory >= 1 + cout << "TCPConnection[" << GetID() << "]::ProcessReceivedDataAsPackets(): size[" << size << "] >= MaxTCPReceiveBufferSize" << endl; +#endif + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::ProcessReceivedDataAsPackets(): size >= MaxTCPReceiveBufferSize"); + return false; + } + if ((recvbuf_used - base) >= size) { + // ok, we got enough data to make this packet! + pack = new ServerPacket; + memcpy(&pack->opcode, &buffer[0], 2); + pack->size = size - 4; + + LogWrite(MISC__TODO, 1, "TODO", "Checksum or size check or something similar\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__); + /* + if () { // TODO: Checksum or size check or something similar + // Datastream corruption, get the hell outta here! + delete pack; + return false; + } + */ + + if (pack->size > 0) { + pack->pBuffer = new uchar[pack->size]; + memcpy(pack->pBuffer, &buffer[4], pack->size); + } + if (pack->opcode == 0) { + // keepalive, no need to process + safe_delete(pack); + } + else { +#if TCPN_LOG_PACKETS >= 1 + if (pack && pack->opcode != 0) { + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Logging incoming TCP OldPacket. OPCode: 0x" << hex << setw(4) << setfill('0') << pack->opcode << dec << ", size: " << setw(5) << setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << endl; +#if TCPN_LOG_PACKETS == 2 + if (pack->size >= 32) + DumpPacket(pack->pBuffer, 32); + else + DumpPacket(pack); +#endif +#if TCPN_LOG_PACKETS >= 3 + DumpPacket(pack); +#endif + } +#endif + OutQueuePush(pack); + } + base += size; + size = 4; + } + } + if (base != 0) { + if (base >= recvbuf_used) { + safe_delete_array(recvbuf); + } + else { + uchar* tmpbuf = new uchar[recvbuf_size - base]; + memcpy(tmpbuf, &recvbuf[base], recvbuf_used - base); + safe_delete_array(recvbuf); + recvbuf = tmpbuf; + recvbuf_used -= base; + recvbuf_size -= base; + } + } + return true; +} + +void TCPConnection::ProcessNetworkLayerPacket(ServerPacket* pack) { + int8 opcode = pack->pBuffer[0]; + + + /** disabling RELAY capabilities, this functionality is poorly implemented + even if such a feature needs to be re-used need authentication BEFORE allowing relay to take place + secondly we need to protect the LS accepting new connections as bogus data can be passed to open + fake TCP connections + opcode 0 is OK, that is Keep-Alive + **/ + + if (opcode > 0) + { + Disconnect(); + return; + } + + int8* data = &pack->pBuffer[1]; + switch (opcode) { + case 0: { + break; + } + case 1: { // Switch to RelayServer mode + if (pack->size != 1) { + SendNetErrorPacket("New RelayClient: wrong size, expected 1"); + break; + } + if (RelayServer) { + SendNetErrorPacket("Switch to RelayServer mode when already in RelayServer mode"); + break; + } + if (RemoteID) { + SendNetErrorPacket("Switch to RelayServer mode by a Relay Client"); + break; + } + if (ConnectionType != Incomming) { + SendNetErrorPacket("Switch to RelayServer mode on outgoing connection"); + break; + } +#if TCPC_DEBUG >= 3 + struct in_addr in; + in.s_addr = GetrIP(); + cout << "Switching to RelayServer mode: " << inet_ntoa(in) << ":" << GetPort() << endl; +#endif + RelayServer = true; + break; + } + case 2: { // New Relay Client + if (!RelayServer) { + SendNetErrorPacket("New RelayClient when not in RelayServer mode"); + break; + } + if (pack->size != 11) { + SendNetErrorPacket("New RelayClient: wrong size, expected 11"); + break; + } + if (ConnectionType != Incomming) { + SendNetErrorPacket("New RelayClient: illegal on outgoing connection"); + break; + } + TCPConnection* con = new TCPConnection(Server, this, *((int32*) data), *((int32*) &data[4]), *((int16*) &data[8])); + Server->AddConnection(con); + RelayCount++; + break; + } + case 3: { // Delete Relay Client + if (!RelayServer) { + SendNetErrorPacket("Delete RelayClient when not in RelayServer mode"); + break; + } + if (pack->size != 5) { + SendNetErrorPacket("Delete RelayClient: wrong size, expected 5"); + break; + } + TCPConnection* con = Server->GetConnection(*((int32*)data)); + if (con) { + if (ConnectionType == Incomming) { + if (con->GetRelayLink() != this) { + SendNetErrorPacket("Delete RelayClient: RelayLink != this"); + break; + } + } + con->Disconnect(false); + } + break; + } + case 255: { +#if TCPC_DEBUG >= 1 + struct in_addr in; + in.s_addr = GetrIP(); + cout "Received NetError: '"; + if (pack->size > 1) + cout << (char*) data; + cout << "': " << inet_ntoa(in) << ":" << GetPort() << endl; +#endif + break; + } + } +} + +void TCPConnection::SendNetErrorPacket(const char* reason) { +#if TCPC_DEBUG >= 1 + struct in_addr in; + in.s_addr = GetrIP(); + cout "NetError: '"; + if (reason) + cout << reason; + cout << "': " << inet_ntoa(in) << ":" << GetPort() << endl; +#endif + ServerPacket* pack = new ServerPacket(0); + pack->size = 1; + if (reason) + pack->size += strlen(reason) + 1; + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer, 0, pack->size); + pack->pBuffer[0] = 255; + strcpy((char*) &pack->pBuffer[1], reason); + SendPacket(pack); + safe_delete(pack); +} + +void TCPConnection::RemoveRelay(TCPConnection* relay, bool iSendRelayDisconnect) { + if (iSendRelayDisconnect) { + ServerPacket* pack = new ServerPacket(0, 5); + pack->pBuffer[0] = 3; + *((int32*) &pack->pBuffer[1]) = relay->GetRemoteID(); + SendPacket(pack); + safe_delete(pack); + } + RelayCount--; +} + +bool TCPConnection::SendData(char* errbuf) { + if (errbuf) + errbuf[0] = 0; + /************ Get first send packet on queue and send it! ************/ + uchar* data = 0; + sint32 size = 0; + int status = 0; + if (ServerSendQueuePop(&data, &size)) { +#ifdef WIN32 + status = send(connection_socket, (const char *) data, size, 0); +#else + status = send(connection_socket, data, size, MSG_NOSIGNAL); + if(errno==EPIPE) status = SOCKET_ERROR; +#endif + if (status >= 1) { +#if TCPN_LOG_RAW_DATA_OUT >= 1 + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Wrote " << status << " bytes to network. " << inet_ntoa(in) << ":" << GetrPort(); + if (pOldFormat) + cout << " (OldFormat)"; + cout << endl; +#if TCPN_LOG_RAW_DATA_OUT == 2 + sint32 tmp = status; + if (tmp > 32) + tmp = 32; + DumpPacket(data, status); +#elif TCPN_LOG_RAW_DATA_OUT >= 3 + DumpPacket(data, status); +#endif +#endif + keepalive_timer->Start(); + if (status < (signed)size) { +#if TCPN_LOG_RAW_DATA_OUT >= 1 + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Pushed " << (size - status) << " bytes back onto the send queue. " << inet_ntoa(in) << ":" << GetrPort(); + if (pOldFormat) + cout << " (OldFormat)"; + cout << endl; +#endif + // If there's network congestion, the number of bytes sent can be less than + // what we tried to give it... Push the extra back on the queue for later + ServerSendQueuePushFront(&data[status], size - status); + } + else if (status > (signed)size) { + ThrowError("TCPConnection::SendData(): WTF! status > size"); + return false; + } + // else if (status == size) {} + } + else { + ServerSendQueuePushFront(data, size); + } + + safe_delete_array(data); + if (status == SOCKET_ERROR) { +#ifdef WIN32 + if (WSAGetLastError() != WSAEWOULDBLOCK) +#else + if (errno != EWOULDBLOCK) +#endif + { + if (errbuf) { +#ifdef WIN32 + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::SendData(): send(): Errorcode: %i", WSAGetLastError()); +#else + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::SendData(): send(): Errorcode: %s", strerror(errno)); +#endif + } + return false; + } + } + } + if (TCPMode == modePacket && keepalive_timer->Check()) { + ServerPacket* pack = new ServerPacket(0, 0); + SendPacket(pack); + safe_delete(pack); +#if TCPN_DEBUG >= 5 + cout << "Sending TCP keepalive packet. (timeout=" << timeout_timer->GetRemainingTime() << " remaining)" << endl; +#endif + } + return true; +} + +ThreadReturnType TCPConnectionLoop(void* tmp) { +#ifdef WIN32 + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); +#endif + if (tmp == 0) { + ThrowError("TCPConnectionLoop(): tmp = 0!"); + THREAD_RETURN(NULL); + } + TCPConnection* tcpc = (TCPConnection*) tmp; + tcpc->MLoopRunning.lock(); + while (tcpc->RunLoop()) { + Sleep(LOOP_GRANULARITY); + if (tcpc->GetState() != TCPS_Ready) { + if (!tcpc->Process()) { + tcpc->Disconnect(); + } + } + else if (tcpc->GetAsyncConnect()) { + if (tcpc->charAsyncConnect) + tcpc->Connect(tcpc->charAsyncConnect, tcpc->GetrPort()); + else + tcpc->Connect(tcpc->GetrIP(), tcpc->GetrPort()); + tcpc->SetAsyncConnect(false); + } + else + Sleep(10); + } + tcpc->MLoopRunning.unlock(); + + THREAD_RETURN(NULL); +} + +bool TCPConnection::RunLoop() { + bool ret; + MRunLoop.lock(); + ret = pRunLoop; + MRunLoop.unlock(); + return ret; +} + + + + + +TCPServer::TCPServer(int16 in_port, bool iOldFormat) { + NextID = 1; + pPort = in_port; + sock = 0; + pOldFormat = iOldFormat; + list = new LinkedList; + pRunLoop = true; +#ifdef WIN32 + _beginthread(TCPServerLoop, 0, this); +#else + pthread_t thread; + pthread_create(&thread, NULL, &TCPServerLoop, this); + pthread_detach(thread); +#endif +} + +TCPServer::~TCPServer() { + MRunLoop.lock(); + pRunLoop = false; + MRunLoop.unlock(); + MLoopRunning.lock(); + MLoopRunning.unlock(); + + while (NewQueue.pop()); // the objects are deleted with the list, clear this queue so it doesnt try to delete them again + safe_delete(list); +} + +bool TCPServer::RunLoop() { + bool ret; + MRunLoop.lock(); + ret = pRunLoop; + MRunLoop.unlock(); + return ret; +} + +ThreadReturnType TCPServerLoop(void* tmp) { +#ifdef WIN32 + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); +#endif + if (tmp == 0) { + ThrowError("TCPServerLoop(): tmp = 0!"); + THREAD_RETURN(NULL); + } + TCPServer* tcps = (TCPServer*) tmp; + tcps->MLoopRunning.lock(); + while (tcps->RunLoop()) { + Sleep(SERVER_LOOP_GRANULARITY); + tcps->Process(); + } + tcps->MLoopRunning.unlock(); + + THREAD_RETURN(NULL); +} + +void TCPServer::Process() { + CheckInQueue(); + ListenNewConnections(); + LinkedListIterator iterator(*list); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->IsFree() && (!iterator.GetData()->CheckNetActive())) { +#if _DEBUG + LogWrite(NET__DEBUG, 0, "Net", "EQStream Connection deleted."); +#endif + iterator.RemoveCurrent(); + } + else { + if (!iterator.GetData()->Process()) + iterator.GetData()->Disconnect(); + iterator.Advance(); + } + } +} + +void TCPServer::ListenNewConnections() { + SOCKET tmpsock; + struct sockaddr_in from; + struct in_addr in; + unsigned int fromlen; + + TCPConnection* con; + + from.sin_family = AF_INET; + fromlen = sizeof(from); + LockMutex lock(&MSock); + if (!sock) + return; + + // Check for pending connects +#ifdef WIN32 + unsigned long nonblocking = 1; + while ((tmpsock = accept(sock, (struct sockaddr*) &from, (int *) &fromlen)) != INVALID_SOCKET) { + ioctlsocket (tmpsock, FIONBIO, &nonblocking); +#else + while ((tmpsock = accept(sock, (struct sockaddr*) &from, &fromlen)) != INVALID_SOCKET) { + fcntl(tmpsock, F_SETFL, O_NONBLOCK); +#endif + int bufsize = 64 * 1024; // 64kbyte recieve buffer, up from default of 8k + setsockopt(tmpsock, SOL_SOCKET, SO_RCVBUF, (char*) &bufsize, sizeof(bufsize)); + in.s_addr = from.sin_addr.s_addr; + + // New TCP connection + con = new TCPConnection(this, tmpsock, in.s_addr, ntohs(from.sin_port), pOldFormat); +#if TCPN_DEBUG >= 1 + cout << "New TCP connection: " << inet_ntoa(in) << ":" << con->GetrPort() << endl; +#endif + AddConnection(con); + } +} + +bool TCPServer::Open(int16 in_port, char* errbuf) { + if (errbuf) + errbuf[0] = 0; + LockMutex lock(&MSock); + if (sock != 0) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "Listening socket already open"); + return false; + } + if (in_port != 0) { + pPort = in_port; + } + +#ifdef WIN32 + SOCKADDR_IN address; + unsigned long nonblocking = 1; +#else + struct sockaddr_in address; +#endif + int reuse_addr = 1; + + // Setup internet address information. + // This is used with the bind() call + memset((char *) &address, 0, sizeof(address)); + address.sin_family = AF_INET; + address.sin_port = htons(pPort); + address.sin_addr.s_addr = htonl(INADDR_ANY); + + // Setting up TCP port for new TCP connections + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "socket(): INVALID_SOCKET"); + return false; + } + + // Quag: dont think following is good stuff for TCP, good for UDP + // Mis: SO_REUSEADDR shouldn't be a problem for tcp--allows you to restart + // without waiting for conns in TIME_WAIT to die + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &reuse_addr, sizeof(reuse_addr)); + + + if (::bind(sock, (struct sockaddr *) &address, sizeof(address)) < 0) { +#ifdef WIN32 + closesocket(sock); +#else + close(sock); +#endif + sock = 0; + if (errbuf) + sprintf(errbuf, "bind(): <0"); + return false; + } + + int bufsize = 64 * 1024; // 64kbyte recieve buffer, up from default of 8k + setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*) &bufsize, sizeof(bufsize)); +#ifdef WIN32 + ioctlsocket (sock, FIONBIO, &nonblocking); +#else + fcntl(sock, F_SETFL, O_NONBLOCK); +#endif + + if (listen(sock, SOMAXCONN) == SOCKET_ERROR) { +#ifdef WIN32 + closesocket(sock); + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "listen() failed, Error: %d", WSAGetLastError()); +#else + close(sock); + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "listen() failed, Error: %s", strerror(errno)); +#endif + sock = 0; + return false; + } + + return true; +} + +void TCPServer::Close() { + LockMutex lock(&MSock); + if (sock) { +#ifdef WIN32 + closesocket(sock); +#else + close(sock); +#endif + } + sock = 0; +} + +bool TCPServer::IsOpen() { + MSock.lock(); + bool ret = (bool) (sock != 0); + MSock.unlock(); + return ret; +} + +TCPConnection* TCPServer::NewQueuePop() { + TCPConnection* ret; + MNewQueue.lock(); + ret = NewQueue.pop(); + MNewQueue.unlock(); + return ret; +} + +void TCPServer::AddConnection(TCPConnection* con) { + list->Append(con); + MNewQueue.lock(); + NewQueue.push(con); + MNewQueue.unlock(); +} + +TCPConnection* TCPServer::GetConnection(int32 iID) { + LinkedListIterator iterator(*list); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->GetID() == iID) + return iterator.GetData(); + iterator.Advance(); + } + return 0; +} + +void TCPServer::SendPacket(ServerPacket* pack) { + TCPConnection::TCPNetPacket_Struct* tnps = TCPConnection::MakePacket(pack); + SendPacket(&tnps); +} + +void TCPServer::SendPacket(TCPConnection::TCPNetPacket_Struct** tnps) { + MInQueue.lock(); + InQueue.push(*tnps); + MInQueue.unlock(); + tnps = 0; +} + +void TCPServer::CheckInQueue() { + LinkedListIterator iterator(*list); + TCPConnection::TCPNetPacket_Struct* tnps = 0; + + while (( tnps = InQueuePop() )) { + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->GetMode() != modeConsole && iterator.GetData()->GetRemoteID() == 0) + iterator.GetData()->SendPacket(tnps); + iterator.Advance(); + } + safe_delete(tnps); + } +} + +TCPConnection::TCPNetPacket_Struct* TCPServer::InQueuePop() { + TCPConnection::TCPNetPacket_Struct* ret; + MInQueue.lock(); + ret = InQueue.pop(); + MInQueue.unlock(); + return ret; +} + + diff --git a/old/common/TCPConnection.h b/old/common/TCPConnection.h new file mode 100644 index 0000000..ae47526 --- /dev/null +++ b/old/common/TCPConnection.h @@ -0,0 +1,277 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef TCP_CONNECTION_H +#define TCP_CONNECTION_H +/* + Parent classes for interserver TCP Communication. + -Quagmire +*/ + +#ifdef WIN32 + #define snprintf _snprintf + #define vsnprintf _vsnprintf + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + + #include +#else + #include + #include + #include + #include + #include + #include + #include + #include + #define INVALID_SOCKET -1 + #define SOCKET_ERROR -1 + #include "unix.h" + +#endif + +#include "types.h" +#include "Mutex.h" +#include "linked_list.h" +#include "queue.h" +#include "servertalk.h" +#include "timer.h" +#include "MiscFunctions.h" + +class TCPServer; + +#define TCPConnection_ErrorBufferSize 1024 +#define MaxTCPReceiveBufferSize 524288 + +#define TCPS_Ready 0 +#define TCPS_Connecting 1 +#define TCPS_Connected 100 +#define TCPS_Disconnecting 200 +#define TCPS_Disconnected 201 +#define TCPS_Closing 250 +#define TCPS_Error 255 + +#ifndef DEF_eConnectionType +#define DEF_eConnectionType +enum eConnectionType {Incomming, Outgoing}; +#endif + +#ifdef WIN32 + void TCPServerLoop(void* tmp); + void TCPConnectionLoop(void* tmp); +#else + void* TCPServerLoop(void* tmp); + void* TCPConnectionLoop(void* tmp); +#endif + +enum eTCPMode { modeConsole, modeTransition, modePacket }; +class TCPConnection { +public: +#pragma pack(1) + struct TCPNetPacket_Struct { + int32 size; + struct { + int8 + compressed : 1, + destination : 1, + flag3 : 1, + flag4 : 1, + flag5 : 1, + flag6 : 1, + flag7 : 1, + flag8 : 1; + } flags; + int16 opcode; + uchar buffer[0]; + }; +#pragma pack() + + static TCPNetPacket_Struct* MakePacket(ServerPacket* pack, int32 iDestination = 0); + + TCPConnection(TCPServer* iServer, SOCKET iSock, int32 irIP, int16 irPort, bool iOldFormat = false); + TCPConnection(bool iOldFormat = false, TCPServer* iRelayServer = 0, eTCPMode iMode = modePacket); // for outgoing connections + TCPConnection(TCPServer* iServer, TCPConnection* iRelayLink, int32 iRemoteID, int32 irIP, int16 irPort); // for relay connections + virtual ~TCPConnection(); + + // Functions for outgoing connections + bool Connect(char* irAddress, int16 irPort, char* errbuf = 0); + bool Connect(int32 irIP, int16 irPort, char* errbuf = 0); + void AsyncConnect(char* irAddress, int16 irPort); + void AsyncConnect(int32 irIP, int16 irPort); + virtual void Disconnect(bool iSendRelayDisconnect = true); + + virtual bool SendPacket(ServerPacket* pack, int32 iDestination = 0); + virtual bool SendPacket(TCPNetPacket_Struct* tnps); + bool Send(const uchar* data, sint32 size); + + char* PopLine(); + ServerPacket* PopPacket(); // OutQueuePop() + inline int32 GetrIP() { return rIP; } + inline int16 GetrPort() { return rPort; } + virtual int8 GetState(); + eTCPMode GetMode() { return TCPMode; } + inline bool Connected() { return (GetState() == TCPS_Connected); } + inline bool ConnectReady() { return (bool) (GetState() == TCPS_Ready && ConnectionType == Outgoing); } + void Free(); // Inform TCPServer that this connection object is no longer referanced + + inline int32 GetID() { return id; } + inline bool IsRelayServer() { return RelayServer; } + inline int32 GetRemoteID() { return RemoteID; } + inline TCPConnection* GetRelayLink() { return RelayLink; } + + bool GetEcho(); + void SetEcho(bool iValue); +protected: + friend class TCPServer; + virtual bool Process(); + void SetState(int8 iState); + inline bool IsFree() { return pFree; } + bool CheckNetActive(); + +#ifdef WIN32 + friend void TCPConnectionLoop(void* tmp); +#else + friend void* TCPConnectionLoop(void* tmp); +#endif + SOCKET sock; + bool RunLoop(); + Mutex MLoopRunning; + Mutex MAsyncConnect; + bool GetAsyncConnect(); + bool SetAsyncConnect(bool iValue); + char* charAsyncConnect; + +#ifdef WIN32 + friend class TCPConnection; +#endif + void OutQueuePush(ServerPacket* pack); + void RemoveRelay(TCPConnection* relay, bool iSendRelayDisconnect); +private: + void ProcessNetworkLayerPacket(ServerPacket* pack); + void SendNetErrorPacket(const char* reason = 0); + TCPServer* Server; + TCPConnection* RelayLink; + int32 RemoteID; + sint32 RelayCount; + + bool pOldFormat; + bool SendData(char* errbuf = 0); + bool RecvData(char* errbuf = 0); + bool ProcessReceivedData(char* errbuf = 0); + bool ProcessReceivedDataAsPackets(char* errbuf = 0); + bool ProcessReceivedDataAsOldPackets(char* errbuf = 0); + void ClearBuffers(); + + bool pAsyncConnect; + + eConnectionType ConnectionType; + eTCPMode TCPMode; + bool RelayServer; + Mutex MRunLoop; + bool pRunLoop; + + SOCKET connection_socket; + int32 id; + int32 rIP; + int16 rPort; // host byte order + bool pFree; + + Mutex MState; + int8 pState; + + void LineOutQueuePush(char* line); + MyQueue LineOutQueue; + MyQueue OutQueue; + Mutex MOutQueueLock; + + Timer* keepalive_timer; + Timer* timeout_timer; + + uchar* recvbuf; + sint32 recvbuf_size; + sint32 recvbuf_used; + + sint32 recvbuf_echo; + bool pEcho; + Mutex MEcho; + + void InModeQueuePush(TCPNetPacket_Struct* tnps); + MyQueue InModeQueue; + Mutex MSendQueue; + uchar* sendbuf; + sint32 sendbuf_size; + sint32 sendbuf_used; + bool ServerSendQueuePop(uchar** data, sint32* size); + void ServerSendQueuePushEnd(const uchar* data, sint32 size); + void ServerSendQueuePushEnd(uchar** data, sint32 size); + void ServerSendQueuePushFront(uchar* data, sint32 size); +}; + +class TCPServer { +public: + TCPServer(int16 iPort = 0, bool iOldFormat = false); + virtual ~TCPServer(); + + bool Open(int16 iPort = 0, char* errbuf = 0); // opens the port + void Close(); // closes the port + bool IsOpen(); + inline int16 GetPort() { return pPort; } + + TCPConnection* NewQueuePop(); + + void SendPacket(ServerPacket* pack); + void SendPacket(TCPConnection::TCPNetPacket_Struct** tnps); +protected: +#ifdef WIN32 + friend void TCPServerLoop(void* tmp); +#else + friend void* TCPServerLoop(void* tmp); +#endif + void Process(); + bool RunLoop(); + Mutex MLoopRunning; + + friend class TCPConnection; + inline int32 GetNextID() { return NextID++; } + void AddConnection(TCPConnection* con); + TCPConnection* GetConnection(int32 iID); +private: + void ListenNewConnections(); + + int32 NextID; + bool pOldFormat; + + Mutex MRunLoop; + bool pRunLoop; + + Mutex MSock; + SOCKET sock; + int16 pPort; + + Mutex MNewQueue; + MyQueue NewQueue; + + void CheckInQueue(); + Mutex MInQueue; + TCPConnection::TCPNetPacket_Struct* InQueuePop(); + MyQueue InQueue; + + LinkedList* list; +}; +#endif diff --git a/old/common/Web/WebServer.cpp b/old/common/Web/WebServer.cpp new file mode 100644 index 0000000..f24769e --- /dev/null +++ b/old/common/Web/WebServer.cpp @@ -0,0 +1,364 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2005 - 2026 EQ2EMulator Development Team (http://www.eq2emu.com formerly http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ + +#include "WebServer.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../version.h" + +#ifdef WORLD + #include "../../WorldServer/WorldDatabase.h" + extern WorldDatabase database; +#endif +#ifdef LOGIN + #include "../../LoginServer/LoginDatabase.h" + extern LoginDatabase database; +#endif + +#ifdef WIN32 + #include + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + #include +#else + #include + #include "../unix.h" +#endif + +ThreadReturnType RunWebServer (void* tmp); + +static std::string keypasswd = ""; + +void web_handle_version(const http::request& req, http::response& res) { + res.set(http::field::content_type, "application/json"); + boost::property_tree::ptree pt; + + // Add key-value pairs to the property tree + pt.put("eq2emu_process", std::string(EQ2EMU_MODULE)); + pt.put("version", std::string(CURRENT_VERSION)); + pt.put("compile_date", std::string(COMPILE_DATE)); + pt.put("compile_time", std::string(COMPILE_TIME)); + + // Create an output string stream to hold the JSON string + std::ostringstream oss; + + // Write the property tree to the output string stream as JSON + boost::property_tree::write_json(oss, pt); + + // Get the JSON string from the output string stream + std::string json = oss.str(); + res.body() = json; + res.prepare_payload(); +} + +void web_handle_root(const http::request& req, http::response& res) { + res.set(http::field::content_type, "text/html"); + res.body() = "Hello!"; + res.prepare_payload(); +} + +// this function is called to obtain password info about an encrypted key +std::string WebServer::my_password_callback( + std::size_t max_length, // the maximum length for a password + ssl::context::password_purpose purpose ) // for_reading or for_writing +{ + return keypasswd; +} + +//void handle_root(const http::request& req, http::response& res); + +WebServer::WebServer(const std::string& address, unsigned short port, const std::string& cert_file, const std::string& key_file, const std::string& key_password, const std::string& hardcode_user, const std::string& hardcode_password) + : ioc_(1), + ssl_ctx_(ssl::context::tlsv13_server), + acceptor_(ioc_, {boost_net::ip::make_address(address), port}) { + keypasswd = key_password; + // Initialize SSL context + if(cert_file.size() < 1 || key_file.size() < 1) { + is_ssl = false; + } + else { + ssl_ctx_.set_password_callback(my_password_callback); + ssl_ctx_.use_certificate_chain_file(cert_file); + ssl_ctx_.use_private_key_file(key_file, ssl::context::file_format::pem); + is_ssl = true; + } + keypasswd = ""; // reset no longer needed + + if(hardcode_user.size() > 0 && hardcode_password.size() > 0) + credentials_[hardcode_user] = hardcode_password; + + register_route("/", web_handle_root); + register_route("/version", web_handle_version); +} + +WebServer::~WebServer() { + ioc_.stop(); +} + +ThreadReturnType RunWebServer (void* tmp) { + if(tmp == nullptr) { + THREAD_RETURN(NULL); + } + WebServer* ws = (WebServer*)tmp; + ws->start(); + THREAD_RETURN(NULL); +} + +void WebServer::start() { + do_accept(); + ioc_.run(); +} + +void WebServer::run() { + pthread_t thread; + pthread_create(&thread, NULL, RunWebServer, this); + pthread_detach(thread); +} + + +void WebServer::register_route(const std::string& uri, std::function&, http::response&)> handler, bool auth_req) { + int32 status = database.NoAuthRoute((char*)uri.c_str()); // overrides the default hardcode settings via DB + if(status == 0) { + auth_req = false; + } + if(auth_req) { + routes_[uri] = handler; + } + else { + noauth_routes_[uri] = handler; + } + route_required_status_[uri] = status; +} + +void WebServer::do_accept() { + acceptor_.async_accept( + [this](beast::error_code ec, tcp::socket socket) { + this->on_accept(ec, std::move(socket)); + }); +} + +void WebServer::on_accept(beast::error_code ec, tcp::socket socket) { + if (!ec) { + if(is_ssl) { + std::thread(&WebServer::do_session_ssl, this, std::move(socket)).detach(); + } + else { + std::thread(&WebServer::do_session, this, std::move(socket)).detach(); + } + } + do_accept(); +} + +void WebServer::do_session(tcp::socket socket) { + try { + bool close = false; + beast::flat_buffer buffer; + + while (!close) { + // 1) Read a complete request + http::request req; + http::read(socket, buffer, req); + + // 2) Invoke your handler, giving it a lambda that + // sets up version/keep_alive on the response + handle_request(std::move(req), [&](auto&& response) { + // mirror HTTP version + response.version(req.version()); + // propagate the client’s keep-alive choice + response.keep_alive(req.keep_alive()); + + // if the client asked us to close, mark for shutdown + if (! req.keep_alive()) + close = true; + + http::write(socket, response); + }); + + // 3) Discard anything left in the buffer so the next + // http::read starts fresh + buffer.consume(buffer.size()); + } + + beast::error_code ec; + socket.shutdown(tcp::socket::shutdown_send, ec); + } + catch (const std::exception& e) { + // irrelevant spam for now really + } +} + +void WebServer::do_session_ssl(tcp::socket socket) { + try { + ssl::stream stream(std::move(socket), ssl_ctx_); + stream.handshake(ssl::stream_base::server); + + bool close = false; + beast::flat_buffer buffer; + + while (!close) { + http::request req; + http::read(stream, buffer, req); + + handle_request(std::move(req), [&](auto&& response) { + response.version(req.version()); + response.keep_alive(req.keep_alive()); + if (! req.keep_alive()) + close = true; + http::write(stream, response); + }); + + buffer.consume(buffer.size()); + } + + beast::error_code ec; + stream.next_layer().shutdown(tcp::socket::shutdown_send, ec); + } + catch (const std::exception& e) { + // irrelevant spam for now really + } +} + +template +void WebServer::handle_request(http::request>&& req, std::function&&)> send) { + auto it = noauth_routes_.find(req.target().to_string()); + if (it != noauth_routes_.end()) { + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + it->second(req, res); + return send(std::move(res)); + } + int32 user_status = 0; + std::string session_id = authenticate(req, &user_status); + if (session_id.size() < 1) { + http::response res{http::status::unauthorized, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::www_authenticate, "Basic realm=\"example\""); + res.body() = "Unauthorized"; + res.prepare_payload(); + return send(std::move(res)); + } + + auto status_it = route_required_status_.find(req.target().to_string()); + if (status_it != route_required_status_.end()) { + if(status_it->second > 0 && status_it->second != 0xFFFFFFFF && status_it->second > user_status) { + http::response res{http::status::unauthorized, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.body() = "Unauthorized status"; + res.prepare_payload(); + return send(std::move(res)); + } + } + + it = routes_.find(req.target().to_string()); + if (it != routes_.end()) { + http::response res{http::status::ok, req.version()}; + res.set(http::field::set_cookie, "session_id=" + session_id); + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + it->second(req, res); + return send(std::move(res)); + } +/* + + http::response res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.body() = "Not Found"; + res.prepare_payload(); + return send(std::move(res)); + */ + return send(http::response{http::status::bad_request, req.version()}); +} + + +std::string WebServer::authenticate(const http::request& req, int32* user_status) { + auto it = req.find(http::field::cookie); + if (it != req.end()) { + std::istringstream cookie_stream(it->value().to_string()); + std::string session_id; + std::getline(cookie_stream, session_id, '='); + if (session_id == "session_id") { + std::string id; + std::getline(cookie_stream, id); + if (sessions_.find(id) != sessions_.end()) { + if(sessions_status_.find(id) != sessions_status_.end()) { + *user_status = sessions_status_[id]; + } + return id; + } + } + } + + it = req.find(http::field::authorization); + if (it != req.end()) { + std::string auth_header = it->value().to_string(); + if (auth_header.substr(0, 6) == "Basic ") { + std::string encoded_credentials = auth_header.substr(6); + std::string decoded_credentials; + decoded_credentials.resize(boost::beast::detail::base64::decoded_size(encoded_credentials.size())); + auto result = boost::beast::detail::base64::decode( + &decoded_credentials[0], + encoded_credentials.data(), + encoded_credentials.size() + ); + decoded_credentials.resize(result.first); + + std::istringstream credentials_stream(decoded_credentials); + std::string username, password; + std::getline(credentials_stream, username, ':'); + std::getline(credentials_stream, password); + int32 out_status = 0; + if ((credentials_.find(username) != credentials_.end() && credentials_[username] == password) || (database.AuthenticateWebUser((char*)username.c_str(),(char*)password.c_str(), &out_status) > 0)) { + std::string session_id = generate_session_id(); + sessions_[session_id] = username; + sessions_status_[session_id] = out_status; + *user_status = out_status; + return session_id; + } + } + } + + return std::string(""); +} + +std::string WebServer::generate_session_id() { + static std::mt19937 rng{std::random_device{}()}; + static std::uniform_int_distribution<> dist(0, 15); + std::string session_id; + for (int i = 0; i < 32; ++i) { + session_id += "0123456789abcdef"[dist(rng)]; + } + return session_id; +} + +// Explicit template instantiation +template void WebServer::handle_request>( + http::request>>&&, + std::function&&)> +); \ No newline at end of file diff --git a/old/common/Web/WebServer.h b/old/common/Web/WebServer.h new file mode 100644 index 0000000..e30ae90 --- /dev/null +++ b/old/common/Web/WebServer.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../types.h" + +namespace beast = boost::beast; // from +namespace http = beast::http; // from +namespace boost_net = boost::asio; // from +namespace ssl = boost::asio::ssl; // from +using tcp = boost_net::ip::tcp; // from + +class WebServer { +public: + WebServer(const std::string& address, unsigned short port, const std::string& cert_file, const std::string& key_file, const std::string& key_password, const std::string& hardcode_user, const std::string& hardcode_password); + ~WebServer(); + + void run(); + void start(); + + void register_route(const std::string& uri, std::function&, http::response&)> handler, bool auth_required = true); +private: + bool is_ssl; + static std::string my_password_callback(std::size_t max_length, ssl::context::password_purpose purpose); + void do_accept(); + void on_accept(beast::error_code ec, tcp::socket socket); + void do_session_ssl(tcp::socket socket); + void do_session(tcp::socket socket); + + template + void handle_request(http::request>&& req, std::function&&)> send); + + std::string authenticate(const http::request& req, int32* user_status = 0); + std::string generate_session_id(); + + boost_net::io_context ioc_; + ssl::context ssl_ctx_; + tcp::acceptor acceptor_; + std::unordered_map sessions_; // session_id -> username + std::unordered_map sessions_status_; // session_id -> status + + std::unordered_map credentials_; // username -> password + std::unordered_map route_required_status_; // route -> status + std::unordered_map&, http::response&)>> routes_; + std::unordered_map&, http::response&)>> noauth_routes_; +}; diff --git a/old/common/common_defines.hpp b/old/common/common_defines.hpp deleted file mode 100644 index 951e272..0000000 --- a/old/common/common_defines.hpp +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GNU GPL v3+ - http://www.eq2emulator.net - -#pragma once - -#define BASEDIR "./" - -#ifndef DB_INI_FILE - #ifdef LOGIN - #define DB_INI_FILE BASEDIR "login_db.ini" - #else - #define DB_INI_FILE BASEDIR "world_db.ini" - #endif -#endif - -#ifndef MAIN_CONFIG_FILE - #define MAIN_CONFIG_FILE BASEDIR "server_config.json" -#endif \ No newline at end of file diff --git a/old/common/config_reader.hpp b/old/common/config_reader.hpp deleted file mode 100644 index 10deab4..0000000 --- a/old/common/config_reader.hpp +++ /dev/null @@ -1,354 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GNU GPL v3 License - -#pragma once - -#include -#include -#include -#include -#include - -#include "log.hpp" -#include "xml_parser.hpp" -#include "packet/packet_struct.hpp" - -using namespace std; - -class ConfigReader -{ -public: - // Destructor - cleans up all allocated packet structures and vectors - ~ConfigReader() - { - lock_guard lock(m_structs_mutex); - DestroyStructs(); - } - - // Adds a new packet structure with specified name and version to the collection - void addStruct(const char* name, int16 version, PacketStruct* new_struct) - { - string strname(name); - vector* struct_versions = structs[strname]; - if (struct_versions) { - struct_versions->push_back(new_struct); - } else { - struct_versions = new vector; - struct_versions->push_back(new_struct); - structs[strname] = struct_versions; - } - } - - // Retrieves a packet structure by name, finding the best version match (highest version <= requested) - PacketStruct* getStruct(const char* name, int16 version) - { - PacketStruct* latest_version = 0; - PacketStruct* new_latest_version = 0; - lock_guard lock(m_structs_mutex); - vector* struct_versions = structs[string(name)]; - - if (struct_versions) { - vector::iterator iter; - for (iter = struct_versions->begin(); iter != struct_versions->end(); iter++) { - if ((*iter)->GetVersion() <= version && (!latest_version || (*iter)->GetVersion() > latest_version->GetVersion())) - latest_version = *iter; - } - - if (latest_version) { - if (latest_version->GetOpcode() != OP_Unknown && (latest_version->GetOpcodeValue(version) == 0xFFFF || latest_version->GetOpcodeValue(version)==0xCDCD)) { - LogWrite(PACKET__ERROR, 0, "Packet", "Could not find valid opcode for Packet Struct '%s' and client version %d", latest_version->GetName(), version); - } else if (strlen(latest_version->GetOpcodeType()) == 0 || latest_version->GetOpcode() != OP_Unknown) { - new_latest_version = new PacketStruct(latest_version, version); - } - } - } - - if (!new_latest_version && !latest_version) - LogWrite(PACKET__ERROR, 0, "Packet", "Could not find struct named '%s' with version: %i", name, version); - return new_latest_version; - } - - // Retrieves a packet structure by exact version match - PacketStruct* getStructByVersion(const char* name, int16 version) - { - PacketStruct* packet = 0; - PacketStruct* newpacket = 0; - lock_guard lock(m_structs_mutex); - vector* struct_versions = structs[string(name)]; - - if (struct_versions) { - vector::iterator iter; - for (iter = struct_versions->begin(); iter != struct_versions->end(); iter++) { - packet = *iter; - if (packet && packet->GetVersion() == version) { - newpacket = new PacketStruct(packet, version); - break; - } - } - } - - if (!newpacket) - LogWrite(PACKET__ERROR, 0, "Packet", "Could not find struct named '%s' with version: %i", name, version); - return newpacket; - } - - // Returns the version number of the best matching struct for given name and version - int16 GetStructVersion(const char* name, int16 version) - { - lock_guard lock(m_structs_mutex); - vector* struct_versions = structs[string(name)]; - int16 ret = 0; - - if (struct_versions) { - vector::iterator iter; - PacketStruct* latest_version = 0; - for (iter = struct_versions->begin(); iter != struct_versions->end(); iter++) { - if (!latest_version || ((*iter)->GetVersion() > latest_version->GetVersion() && (*iter)->GetVersion() <= version)) - latest_version = *iter; - } - if (latest_version) - ret = latest_version->GetVersion(); - } - - return ret; - } - - // Reloads all structures from previously loaded XML files - void ReloadStructs() - { - lock_guard lock(m_structs_mutex); - DestroyStructs(); - for (int32 i = 0; i < load_files.size(); i++) - processXML_Elements(load_files[i].c_str()); - } - - // Loads packet structure definitions from an XML file - bool LoadFile(const char* name) - { - load_files.push_back(name); - return processXML_Elements(name); - } - - // Recursively loads data structure definitions from XML nodes into a packet structure - void loadDataStruct(PacketStruct* packet, XMLNode parentNode, bool array_packet = false) - { - for (int x = 0; x < parentNode.nChildNode(); x++) { - const char* name = parentNode.getChildNode("Data", x).getAttribute("ElementName"); - const char* type = parentNode.getChildNode("Data", x).getAttribute("Type"); - const char* size = parentNode.getChildNode("Data", x).getAttribute("Size"); - const char* type2 = parentNode.getChildNode("Data", x).getAttribute("Type2"); - const char* array_size = parentNode.getChildNode("Data", x).getAttribute("ArraySizeVariable"); - const char* max_array = parentNode.getChildNode("Data", x).getAttribute("MaxArraySize"); - const char* substruct = parentNode.getChildNode("Data", x).getAttribute("Substruct"); - const char* default_value = parentNode.getChildNode("Data", x).getAttribute("DefaultByteValue"); - const char* oversized = parentNode.getChildNode("Data", x).getAttribute("OversizedValue"); - const char* oversized_byte = parentNode.getChildNode("Data", x).getAttribute("OversizedByte"); - const char* if_variable = parentNode.getChildNode("Data", x).getAttribute("IfVariableSet"); - const char* if_not_variable = parentNode.getChildNode("Data", x).getAttribute("IfVariableNotSet"); - const char* if_equals_variable = parentNode.getChildNode("Data", x).getAttribute("IfVariableEquals"); - const char* if_not_equals_variable = parentNode.getChildNode("Data", x).getAttribute("IfVariableNotEquals"); - const char* optional = parentNode.getChildNode("Data", x).getAttribute("Optional"); - const char* if_flag_not_set_variable = parentNode.getChildNode("Data", x).getAttribute("IfFlagNotSet"); - const char* if_flag_set_variable = parentNode.getChildNode("Data", x).getAttribute("IfFlagSet"); - - // Parse array size with error handling - int8 max_array_size = 0; - try { - if (max_array) - max_array_size = atoi(max_array); - } catch (...) {} - - // Parse element size with error handling - int16 num_size = 1; - try { - if (size) - num_size = atoi(size); - } catch (...) {} - - // Parse default byte value with error handling - int8 byte_val = 0; - try { - if (default_value) - byte_val = atoi(default_value); - } catch (...) {} - - // Handle substruct elements - recursively include another struct's definition - if (substruct && name) { - PacketStruct* substruct_packet = getStruct(substruct, packet->GetVersion()); - if (substruct_packet) { - vector::iterator itr; - vector* structs = substruct_packet->getStructs(); - DataStruct* ds = 0; - int i = 0; - char tmp[12] = {0}; - - // Create multiple instances of substruct if size > 1 - for (i = 0; i < num_size; i++) { - snprintf(tmp, sizeof(tmp)-1, "%i", i); - for (itr = structs->begin(); itr != structs->end(); itr++) { - ds = *itr; - string new_name; - if (array_packet) - new_name = string(name).append("_").append(ds->GetStringName()); - else - new_name = string(name).append("_").append(ds->GetStringName()).append("_").append(tmp); - - DataStruct* ds2 = new DataStruct(new_name.c_str(), ds->GetType(), ds->GetLength(), ds->GetType2()); - - // Copy array size variable with proper naming - if (!array_packet && strlen(ds->GetArraySizeVariable()) > 1) - ds2->SetArraySizeVariable(string(name).append("_").append(ds->GetArraySizeVariable()).append("_").append(tmp).c_str()); - - // Copy all properties from original datastruct - ds2->SetOversized(ds->GetOversized()); - ds2->SetOversizedByte(ds->GetOversizedByte()); - ds2->SetDefaultValue(ds->GetDefaultValue()); - ds2->SetMaxArraySize(ds->GetMaxArraySize()); - ds2->SetIfSetVariable(ds->GetIfSetVariable() ? ds->GetIfSetVariable() : if_variable); - ds2->SetIfNotSetVariable(ds->GetIfSetVariable() ? ds->GetIfNotSetVariable() : if_not_variable); - ds2->SetIfNotEqualsVariable(ds->GetIfNotEqualsVariable()); - ds2->SetIfFlagNotSetVariable(ds->GetIfFlagNotSetVariable()); - ds2->SetIfFlagSetVariable(ds->GetIfFlagSetVariable()); - ds2->SetIsOptional(ds->IsOptional()); - ds2->AddIfSetVariable(if_variable); - ds2->AddIfNotSetVariable(if_not_variable); - packet->add(ds2); - } - } - - // Handle array renaming for non-array packets - if (!array_packet) { - i--; - substruct_packet->renameSubstructArray(name, i); - packet->addPacketArrays(substruct_packet); - } - - delete substruct_packet; - } - continue; - } else if (type && strncasecmp(type,"Array", 5)==0 && array_size) { - // Handle array type elements - create subpacket for array structure - PacketStruct* new_packet = new PacketStruct; - new_packet->SetName(name); - new_packet->IsSubPacket(true); - new_packet->SetVersion(packet->GetVersion()); - loadDataStruct(new_packet, parentNode.getChildNode("Data", x), true); - packet->add(new_packet); - } - - // Validate required attributes - if (!name || !type) { - LogWrite(MISC__WARNING, 0, "Misc", "Ignoring invalid Data Element, all elements must include at least an ElementName and Type!"); - LogWrite(MISC__WARNING, 0, "Misc", "\tStruct: '%s', version: %i", parentNode.getAttribute("Name"), parentNode.getAttribute("ClientVersion")); - continue; - } - - // Create new data structure element - DataStruct* ds = new DataStruct(name, type, num_size, type2); - - // Parse oversized values with error handling - int8 oversized_value = 0; - int8 oversized_byte_value = 255; - if (oversized) { - try { - oversized_value = atoi(oversized); - } catch (...) {} - } - if (oversized_byte) { - try { - oversized_byte_value = atoi(oversized_byte); - } catch (...) {} - } - - // Set all properties on the data structure - ds->SetOversizedByte(oversized_byte_value); - ds->SetOversized(oversized_value); - ds->SetMaxArraySize(max_array_size); - if (array_size) - ds->SetArraySizeVariable(array_size); - ds->SetDefaultValue(byte_val); - ds->SetIfSetVariable(if_variable); - ds->SetIfNotSetVariable(if_not_variable); - ds->SetIfEqualsVariable(if_equals_variable); - ds->SetIfNotEqualsVariable(if_not_equals_variable); - ds->SetIfFlagNotSetVariable(if_flag_not_set_variable); - ds->SetIfFlagSetVariable(if_flag_set_variable); - - // Parse optional flag - if (optional && strlen(optional) > 0 && (strcmp("true", optional) == 0 || strcmp("TRUE", optional) == 0 || strcmp("True", optional) == 0)) - ds->SetIsOptional(true); - - packet->add(ds); - } - } - -private: - // Parses XML file and loads all struct definitions into memory - bool processXML_Elements(const char* fileName) - { - XMLNode xMainNode = XMLNode::openFileHelper(fileName,"EQ2Emulator"); - if (xMainNode.isEmpty()) - return false; - - // Process each struct definition in the XML file - for (int i = 0; i < xMainNode.nChildNode("Struct"); i++) { - const char* struct_name = xMainNode.getChildNode("Struct", i).getAttribute("Name"); - const char* str_version = xMainNode.getChildNode("Struct", i).getAttribute("ClientVersion"); - const char* opcode_name = xMainNode.getChildNode("Struct", i).getAttribute("OpcodeName"); - const char* opcode_type = xMainNode.getChildNode("Struct", i).getAttribute("OpcodeType"); - - // Validate required attributes - if (!struct_name || !str_version) { - LogWrite(MISC__WARNING, 0, "Misc", "Ignoring invalid struct, all structs must include at least a Name and ClientVersion!"); - continue; - } - - // Parse version number with error handling - int16 version = 1; - try { - version = atoi(str_version); - } catch (...) { - LogWrite(MISC__WARNING, 0, "Misc", "Ignoring invalid version for struct named '%s': '%s'", struct_name, str_version); - continue; - } - - // Create new packet structure - PacketStruct* new_struct = new PacketStruct(); - new_struct->SetName(struct_name); - if (opcode_type) - new_struct->SetOpcodeType(opcode_type); - if (opcode_name) { - if (!new_struct->SetOpcode(opcode_name)) { - delete new_struct; - continue; - } - } - new_struct->SetVersion(version); - - // Load all data elements for this struct - loadDataStruct(new_struct, xMainNode.getChildNode("Struct", i)); - addStruct(struct_name, version, new_struct); - } - return true; - } - - // Destroys all allocated packet structures and clears collections - void DestroyStructs() - { - map*>::iterator struct_iterator; - for (struct_iterator = structs.begin(); struct_iterator != structs.end(); struct_iterator++) { - vector* versions = struct_iterator->second; - vector::iterator version_iter; - if (versions) { - for (version_iter = versions->begin(); version_iter != versions->end(); version_iter++) { - delete *version_iter; - } - } - delete versions; - } - structs.clear(); - } - - mutex m_structs_mutex; // Thread synchronization for struct access - vector load_files; // List of loaded XML files for reloading - map*> structs; // Map of struct names to version vectors -}; \ No newline at end of file diff --git a/old/common/crc16.hpp b/old/common/crc16.hpp deleted file mode 100644 index 5860437..0000000 --- a/old/common/crc16.hpp +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team, GPL v3+ License - -#pragma once - -#include -#include - -// CRC32 lookup table for polynomial 0xEDB88320 (IEEE 802.3 standard) -constexpr std::array crc_table = { - 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, - 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, - 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, - 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, - 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, - 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, - 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, - 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, - 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, - 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, - 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, - 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, - 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, - 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, - 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, - 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, - 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, - 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, - 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, - 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, - 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, - 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, - 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, - 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, - 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, - 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, - 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, - 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, - 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, - 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, - 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, - 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D -}; - -// Computes CRC32 checksum with custom key initialization for EQ2 protocol -// This function implements the reverse-engineered CRC calculation used in EQ2 -// despite being named CRC16, it actually performs 32-bit CRC calculation -uint32_t CRC16(const unsigned char *buf, int size, int key) -{ - uint32_t crc = key; // Initialize with provided key - - // Pre-process the key through multiple CRC table lookups - // This mirrors the original assembly implementation's key initialization - crc = ~crc; // Invert initial key - crc &= 0xFF; // Mask to lowest byte - crc = crc_table[crc]; // First table lookup - crc ^= 0x00FFFFFF; // XOR with mask - - // Process second byte of original key - uint32_t temp = key >> 8; // Shift key right 8 bits - temp ^= crc; // XOR with current CRC - crc >>= 8; // Shift CRC right 8 bits - temp &= 0xFF; // Mask to byte - crc &= 0x00FFFFFF; // Mask CRC to 24 bits - crc ^= crc_table[temp]; // Second table lookup - - // Process third byte of original key - temp = key >> 16; // Shift key right 16 bits - temp ^= crc; // XOR with current CRC - crc >>= 8; // Shift CRC right 8 bits - temp &= 0xFF; // Mask to byte - uint32_t temp2 = crc_table[temp]; // Third table lookup - crc &= 0x00FFFFFF; // Mask CRC to 24 bits - crc ^= temp2; // XOR with lookup result - - // Process fourth byte of original key - uint32_t fourth_byte = key >> 24; // Extract highest byte - fourth_byte ^= crc; // XOR with current CRC - fourth_byte &= 0xFF; // Mask to byte - temp2 = crc_table[fourth_byte]; // Fourth table lookup - crc >>= 8; // Shift CRC right 8 bits - crc &= 0x00FFFFFF; // Mask CRC to 24 bits - crc ^= temp2; // XOR with lookup result - - // Process each byte in the input buffer - for (int x = 0; x < size; x++) { - uint32_t byte_val = buf[x] & 0xFF; // Extract current byte - byte_val ^= crc; // XOR byte with current CRC - crc >>= 8; // Shift CRC right 8 bits - byte_val &= 0xFF; // Ensure byte is masked - uint32_t lookup = crc_table[byte_val]; // Table lookup for this byte - crc &= 0x00FFFFFF; // Mask CRC to 24 bits - crc ^= lookup; // XOR with lookup result - } - - return ~crc; // Return inverted final CRC -} \ No newline at end of file diff --git a/old/common/crypto.hpp b/old/common/crypto.hpp deleted file mode 100644 index b02543f..0000000 --- a/old/common/crypto.hpp +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team, GPL v3 License - -#pragma once - -#include -#include -#include -#include - -#include "rc4.hpp" -#include "types.hpp" -#include "packet/packet_dump.hpp" - -using namespace std; - -/** - * Crypto class handles RSA decryption and RC4 encryption/decryption operations - * for secure client-server communication in the EQ2 emulator - */ -class Crypto -{ -public: - /** - * Default constructor - initializes crypto state to unencrypted - */ - Crypto() - { - rc4_key = 0; - encrypted = false; - client = nullptr; - server = nullptr; - } - - /** - * Destructor - cleans up RC4 cipher instances - */ - ~Crypto() - { - safe_delete(client); - safe_delete(server); - } - - /** - * Decrypts RSA-encrypted data by reversing byte order and converting to int64 - */ - static int64 RSADecrypt(uchar* text, int16 size) - { - int64 ret = 0; - uchar* buffer = new uchar[8]; - - // Reverse byte order for proper endianness - for (int i = 7; i >= 0; i--) { - buffer[7 - i] = text[i]; - } - - memcpy(&ret, buffer, 8); - safe_delete_array(buffer); - return ret; - } - - /** - * Encrypts data using RC4 server cipher with thread safety - */ - void RC4Encrypt(uchar* text, int32 size) - { - MCrypto.lock(); - server->Cypher(text, size); - MCrypto.unlock(); - } - - /** - * Decrypts data using RC4 client cipher with thread safety - */ - void RC4Decrypt(uchar* text, int32 size) - { - MCrypto.lock(); - client->Cypher(text, size); - MCrypto.unlock(); - } - - /** - * Gets the current RC4 encryption key - */ - int64 getRC4Key() - { - return rc4_key; - } - - /** - * Sets the RC4 encryption key and initializes cipher instances - */ - void setRC4Key(int64 key) - { - rc4_key = key; - - if (key > 0) { - encrypted = true; - client = new RC4(~key); // Client uses inverted key - server = new RC4(key); // Server uses original key - - // Initialize cipher states with dummy data - uchar temp[20]; - client->Cypher(temp, 20); - server->Cypher(temp, 20); - } - else { - encrypted = false; - safe_delete(client); - safe_delete(server); - } - } - - /** - * Checks if encryption is currently enabled - */ - bool isEncrypted() - { - return encrypted; - } - - /** - * Manually sets the encryption state - */ - void setEncrypted(bool in_val) - { - encrypted = in_val; - } - -private: - RC4* server; // Server-side RC4 cipher instance - RC4* client; // Client-side RC4 cipher instance - bool encrypted; // Current encryption state flag - int64 rc4_key; // RC4 encryption key value - mutex MCrypto; // Mutex for thread-safe cipher operations -}; \ No newline at end of file diff --git a/old/common/data_buffer.hpp b/old/common/data_buffer.hpp deleted file mode 100644 index 0d16a94..0000000 --- a/old/common/data_buffer.hpp +++ /dev/null @@ -1,435 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License - -#pragma once - -#include -#include -#include - -#include "types.hpp" -#include "eq_common_structs.hpp" -#include "packet/eq_packet.hpp" - -#ifdef WORLD - #include "../WorldServer/SpawnLists.h" -#endif - -using namespace std; - -/** - * DataBuffer class for handling binary data serialization and deserialization - * Provides templated methods for reading/writing various data types to/from buffers - */ -class DataBuffer -{ -public: - bool changed; // Flag indicating if buffer has been modified - - /** - * Get raw pointer to buffer data - * @return Pointer to buffer contents as unsigned char array - */ - uchar* getData() - { - return (uchar*)buffer.c_str(); - } - - /** - * Get size of buffer data in bytes - * @return Size of buffer in bytes - */ - int32 getDataSize() - { - return buffer.length(); - } - - /** - * Get reference to internal buffer string - * @return Pointer to internal string buffer - */ - string* getDataString() - { - return &buffer; - } - - /** - * Create EQ2 color from current load position (reference version) - * @param color Reference to EQ2_Color structure to populate - */ - void CreateEQ2Color(EQ2_Color& color) - { - CreateEQ2Color(&color); - } - - /** - * Get current load buffer pointer - * @return Pointer to load buffer - */ - uchar* GetLoadBuffer() - { - return load_buffer; - } - - /** - * Get current position in load buffer - * @return Current load position - */ - int32 GetLoadPos() - { - return load_pos; - } - - /** - * Get total length of load buffer - * @return Load buffer length - */ - int32 GetLoadLen() - { - return load_len; - } - - /** - * Set new position in load buffer - * @param new_pos New position to set - */ - void SetLoadPos(int32 new_pos) - { - load_pos = new_pos; - } - - /** - * Create EQ2 color from current load position (pointer version) - * Reads 3 floats representing RGB values and converts to 0-255 range - * @param color Pointer to EQ2_Color structure to populate - */ - void CreateEQ2Color(EQ2_Color* color) - { - int8 rgb[3]; - float* tmp = 0; - // Read 3 float values and convert to RGB bytes - for(int i = 0; i < 3; i++) { - tmp = (float*)(load_buffer + load_pos); - rgb[i] = (int8)((*tmp) * 255); - load_pos += sizeof(float); - } - color->red = rgb[0]; - color->green = rgb[1]; - color->blue = rgb[2]; - } - - /** - * Convert float to signed 8-bit integer scaled by 100 (reference version) - * @param output Reference to output variable - */ - template void MakeEQ2_Int8(Type& output) - { - MakeEQ2_Int8(&output); - } - - /** - * Convert float to signed 8-bit integer scaled by 100 (pointer version) - * Takes absolute value of float, multiplies by 100, and stores as sint8 - * @param output Pointer to output variable - */ - template void MakeEQ2_Int8(Type* output) - { - float* tmp = (float*)(load_buffer + load_pos); - if(*tmp < 0) - *tmp *= -1; - sint8 result = (sint8)((*tmp) * 100); - memcpy(output, &result, sizeof(sint8)); - load_pos += sizeof(float); - } - - /** - * Initialize buffer for data retrieval operations - * Sets up get_buffer pointer and related variables - */ - void InitializeGetData() - { - get_buffer = (uchar*)buffer.c_str(); - get_len = buffer.length(); - get_pos = 0; - } - - /** - * Initialize buffer for data loading operations - * @param input Input data array - * @param size Size of input data - */ - void InitializeLoadData(uchar* input, int32 size) - { - buffer = string((char*)input, size); - load_buffer = (uchar*)buffer.c_str(); - load_len = size; - load_pos = 0; - } - - /** - * Load string data from buffer (reference version) - * @param output Reference to string structure to populate - */ - template void LoadDataString(String& output) - { - LoadDataString(&output); - } - - /** - * Load string data from buffer (pointer version) - * Reads size field followed by string data - * @param output Pointer to string structure to populate - */ - template void LoadDataString(String* output) - { - // Read string size if enough bytes available - if((sizeof(output->size) + load_pos) <= load_len) { - memcpy(&output->size, load_buffer + load_pos, sizeof(output->size)); - load_pos += sizeof(output->size); - } - // Read string data if enough bytes available - if((output->size + load_pos) <= load_len) { - output->data = string((char*)(load_buffer + load_pos), output->size); - load_pos += output->size; - } - } - - /** - * Load data from buffer (reference version) - * @param output Reference to variable to populate - */ - template void LoadData(Type& output) - { - LoadData(&output); - } - - /** - * Load array data from buffer - * @param output Pointer to array to populate - * @param array_size Number of elements to load - */ - template void LoadData(Type* output, int32 array_size) - { - if(array_size <= 1) { - LoadData(output); - } - else { - // Load each array element sequentially - for(int32 i = 0; i < array_size; i++) - LoadData(&output[i]); - } - } - - /** - * Load single data element from buffer - * @param output Pointer to variable to populate - */ - template void LoadData(Type* output) - { - // Only load if enough bytes available in buffer - if((sizeof(Type) + load_pos) <= load_len) { - memcpy(output, load_buffer + load_pos, sizeof(Type)); - load_pos += sizeof(Type); - } - } - - /** - * Load array data from buffer (reference version) - * @param output Reference to array to populate - * @param array_size Number of elements to load - */ - template void LoadData(Type& output, int32 array_size) - { - LoadData(&output, array_size); - } - - /** - * Skip bytes in load buffer - * @param bytes Number of bytes to skip - */ - void LoadSkip(int8 bytes) - { - load_pos += bytes; - } - - /** - * Skip data type in load buffer (reference version) - * @param skip Reference to type (used for sizeof calculation) - */ - template void LoadSkip(Type& skip) - { - LoadSkip(&skip); - } - - /** - * Skip data type in load buffer (pointer version) - * @param skip Pointer to type (used for sizeof calculation) - */ - template void LoadSkip(Type* skip) - { - load_pos += sizeof(Type); - } - - /** - * Get data from get buffer - * @param output Pointer to store result - */ - template void GetData(Type* output) - { - // Only get if enough bytes available - if((sizeof(Type) + get_pos) <= get_len) { - *output = (Type*)get_buffer; - get_pos += sizeof(output); - } - } - - /** - * Add zero bytes to buffer - * @param num Number of zero bytes to add - */ - void AddZeros(int16 num) - { - auto data = std::make_unique(num); - memset(data.get(), 0, num); - AddData(*data); - } - - /** - * Add struct data with specific size - * @param input Input data to add - * @param size Size of data to add - * @param datastring Optional target string (uses internal buffer if null) - */ - template void StructAddData(Type input, int16 size, string* datastring) - { - if(datastring) - datastring->append((char*)&input, size); - else - buffer.append((char*)&input, size); - } - - /** - * Add array of struct data with specific size - * @param input Input array to add - * @param array_size Number of array elements - * @param size Size of each element - * @param datastring Optional target string - */ - template void StructAddData(Type input, int32 array_size, int16 size, string* datastring) - { - if(array_size > 0) { - // Add each array element - for(int32 i = 0; i < array_size; i++) - StructAddData(input[i], size, datastring); - } - else - StructAddData(input, size, datastring); - } - - /** - * Add data to buffer using sizeof for size - * @param input Data to add - * @param datastring Optional target string (uses internal buffer if null) - */ - template void AddData(Type input, string* datastring = 0) - { - if(!datastring) - datastring = &buffer; - datastring->append((char*)&input, sizeof(input)); - } - - /** - * Add array data to buffer - * @param input Array data to add - * @param array_size Number of array elements - * @param datastring Optional target string - */ - template void AddData(Type input, int32 array_size, string* datastring = 0) - { - if(array_size > 0) { - // Add each array element - for(int32 i = 0; i < array_size; i++) - AddData(input[i], datastring); - } - else - AddData(input, datastring); - } - - /** - * Add string data with size prefix (pointer version) - * @param input Pointer to string structure - * @param datastring Optional target string - */ - template void AddDataString(String* input, string* datastring = 0) - { - AddDataString(*input, datastring); - } - - /** - * Add string data with size prefix (reference version) - * Adds size field followed by string data - * @param input String structure to add - * @param datastring Optional target string - */ - template void AddDataString(String input, string* datastring = 0) - { - input.size = input.data.length(); - if(!datastring) - datastring = &buffer; - datastring->append((char*)&input.size, sizeof(input.size)); - datastring->append(input.data); - } - - /** - * Add null-terminated character array - * @param array Character array to add - * @param datastring Optional target string - */ - void AddCharArray(char* array, string* datastring = 0) - { - if(!datastring) - datastring = &buffer; - datastring->append(array); - } - - /** - * Add character array with specific size - * @param array Character array to add - * @param size Number of characters to add - * @param datastring Optional target string - */ - void AddCharArray(char* array, int16 size, string* datastring = 0) - { - if(!datastring) - datastring = &buffer; - datastring->append(array, size); - } - - /** - * Add string data directly - * @param data String data to add - * @param datastring Optional target string - */ - void AddData(string data, string* datastring = 0) - { - if(!datastring) - datastring = &buffer; - datastring->append(data); - } - - /** - * Clear all buffer contents - */ - void Clear() - { - buffer.clear(); - } - -private: - string buffer; // Main data buffer - uchar* get_buffer; // Pointer for get operations - uchar* load_buffer; // Pointer for load operations - int32 get_len; // Length for get operations - int32 get_pos; // Current position for get operations - int32 load_len; // Length for load operations - int32 load_pos; // Current position for load operations -}; \ No newline at end of file diff --git a/old/common/database.cpp b/old/common/database.cpp new file mode 100644 index 0000000..866676c --- /dev/null +++ b/old/common/database.cpp @@ -0,0 +1,602 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include "../common/debug.h" + +#include +using namespace std; +#include +#include +#include +#include +//#include +#include +#include +#include +#include + +// Disgrace: for windows compile +#ifdef WIN32 +#include +#include +#define snprintf _snprintf +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#else +#include "unix.h" +#include +#endif + +#include "database.h" +#include "EQStream.h" +#include "packet_functions.h" +#include "emu_opcodes.h" +#ifdef WORLD + #include "../WorldServer/WorldDatabase.h" + #include "../WorldServer/Web/PeerManager.h" + extern WorldDatabase database; + extern PeerManager peer_manager; +#endif +#ifdef LOGIN + #include "../LoginServer/LoginDatabase.h" + extern LoginDatabase database; +#endif +#ifdef PARSER + #include "../PacketParser/ParserDatabase.h" + extern ParserDatabase database; +#endif + +#ifdef PATCHER + #include "../PatchServer/PatcherDatabase.h" + extern PatcherDatabase database; +#endif +#include "../common/EQEMuError.h" +#include "../common/packet_dump.h" +#include "../common/Log.h" + +#ifdef WORLD +ThreadReturnType DBAsyncQueries(void* str) +{ + // allow some buffer for multiple queries to collect + Sleep(10); + DBStruct* data = (DBStruct*)str; + database.RunAsyncQueries(data->queryid); + delete data; + THREAD_RETURN(NULL); +} +#endif + +Database::Database() +{ + InitVars(); +} + +bool Database::Init(bool silentLoad) { + char host[200], user[200], passwd[200], database[200]; + unsigned int port=0; + bool compression = false; + bool items[6] = {false, false, false, false, false, false}; + const char* exampleIni[] = { "[Database]", "host = localhost", "user = root", "password = pass", "database = dbname", "### --- Assure each parameter is on a new line!" }; + + if(!ReadDBINI(host, user, passwd, database, &port, &compression, items)) { + //exit(1); + return false; + } + + if (!items[0] || !items[1] || !items[2] || !items[3]) + { + LogWrite(DATABASE__ERROR, 0, "DB", "Database file %s is incomplete.", DB_INI_FILE); + int i; + for (i = 0; i < 4; i++) + { + if ( !items[i] ) + LogWrite(DATABASE__ERROR, 0, "DB", "Could not find parameter %s", exampleIni[i+1]); // offset by 1 because the [Database] entry + } + LogWrite(DATABASE__ERROR, 0, "DB", "Example File:"); + int length = sizeof exampleIni / sizeof exampleIni[0]; + for(i=0;i Database::GetVersions(){ + map opcodes; + Query query; + MYSQL_ROW row; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "select distinct version_range1, version_range2 from opcodes"); + while(result && (row = mysql_fetch_row(result))){ + if(row[0] && row[1]) + opcodes[atoi(row[0])] = atoi(row[1]); + } + return opcodes; +} + +map Database::GetOpcodes(int16 version){ + map opcodes; + Query query; + MYSQL_ROW row; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "select name, opcode from opcodes where %i between version_range1 and version_range2 order by version_range1, id", version); + while(result && (row = mysql_fetch_row(result))){ + opcodes[row[0]] = atoi(row[1]); + } + return opcodes; +} + +int32 Database::AuthenticateWebUser(char* userName, char* passwd, int32* status){ + if(status) { + *status = 0; + } + Query query; + MYSQL_ROW row; + int32 id = 0; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "select id, status from web_users where username='%s' and passwd = sha2('%s', 512)", getSafeEscapeString(userName).c_str(), getSafeEscapeString(passwd).c_str()); + if(result && (row = mysql_fetch_row(result))){ + id = atoul(row[0]); + if(status) { + *status = atoul(row[1]); + } + } + return id; +} + +int32 Database::NoAuthRoute(char* route){ + Query query; + MYSQL_ROW row; + int32 status = 0xFFFFFFFF; + MYSQL_RES* result = query.RunQuery2(Q_SELECT, "select status from web_routes where route='%s'", getSafeEscapeString(route).c_str()); + if(result && (row = mysql_fetch_row(result))){ + status = atoul(row[0]); + } + return status; +} + +void Database::HandleMysqlError(int32 errnum) { + switch(errnum) { + case 0: + break; + case 1045: // Access Denied + case 2001: { + AddEQEMuError(EQEMuError_Mysql_1405, true); + break; + } + case 2003: { // Unable to connect + AddEQEMuError(EQEMuError_Mysql_2003, true); + break; + } + case 2005: { // Unable to connect + AddEQEMuError(EQEMuError_Mysql_2005, true); + break; + } + case 2007: { // Unable to connect + AddEQEMuError(EQEMuError_Mysql_2007, true); + break; + } + } +} + +void Database::InitVars() { + +} + +Database::~Database() +{ +#ifdef WORLD + DBQueryMutex.writelock(__FUNCTION__, __LINE__); + activeQuerySessions.clear(); + DBQueryMutex.releasewritelock(__FUNCTION__, __LINE__); + + DBAsyncMutex.writelock(); + continueAsync = false; + map>::iterator itr; + for (itr = asyncQueries.begin(); itr != asyncQueries.end(); itr++) + { + asyncQueriesMutex[itr->first]->writelock(); + deque queries = itr->second; + while (queries.size() > 0) + { + Query* cur = queries.front(); + queries.pop_front(); + safe_delete(cur); + } + asyncQueriesMutex[itr->first]->releasewritelock(); + Mutex* mutex = asyncQueriesMutex[itr->first]; + asyncQueriesMutex.erase(itr->first); + safe_delete(mutex); + } + asyncQueries.clear(); + + asyncQueriesMutex.clear(); + DBAsyncMutex.releasewritelock(); + + PurgeDBInstances(); +#endif +} + +#ifdef WORLD +void Query::AddQueryAsync(int32 queryID, Database* db, QUERY_TYPE type, const char* format, ...) { + in_type = type; + va_list args; + va_start(args, format); +#ifdef WIN32 + char* buffer; + int buf_len = _vscprintf(format, args) + 1; + buffer = new char[buf_len]; + vsprintf(buffer, format, args); +#else + char* buffer; + int buf_len; + va_list argcopy; + va_copy(argcopy, args); + buf_len = vsnprintf(NULL, 0, format, argcopy) + 1; + va_end(argcopy); + + buffer = new char[buf_len]; + vsnprintf(buffer, buf_len, format, args); +#endif + va_end(args); + query = string(buffer); + + Query* asyncQuery = new Query(this, queryID, query); + + safe_delete_array(buffer); + + db->AddAsyncQuery(asyncQuery); +} + +void Query::RunQueryAsync(Database* db) { + db->RunQuery(query.c_str(), query.length(), errbuf, &result, affected_rows, last_insert_id, &errnum, retry); +} +#endif + +MYSQL_RES* Query::RunQuery2(QUERY_TYPE type, const char* format, ...){ + va_list args; + va_start( args, format ); + #ifdef WIN32 + char * buffer; + int buf_len = _vscprintf( format, args ) + 1; + buffer = new char[buf_len]; + vsprintf( buffer, format, args ); + #else + char* buffer; + int buf_len; + va_list argcopy; + va_copy(argcopy, args); + buf_len = vsnprintf(NULL, 0, format, argcopy) + 1; + va_end(argcopy); + + buffer = new char[buf_len]; + vsnprintf(buffer, buf_len, format, args); + #endif + va_end(args); + query = string(buffer); + + + safe_delete_array( buffer ); + + + return RunQuery2(query.c_str(), type); +} +MYSQL_RES* Query::RunQuery2(string in_query, QUERY_TYPE type){ + switch(type){ + case Q_SELECT: + break; + case Q_DBMS: + case Q_REPLACE: + case Q_DELETE: + case Q_UPDATE: + safe_delete(affected_rows); + affected_rows = new int32; + break; + case Q_INSERT: + safe_delete(last_insert_id); + last_insert_id = new int32; + } + if(result){ + if(!multiple_results) + multiple_results = new vector(); + multiple_results->push_back(result); + } + query = in_query; + +#if defined WORLD && defined _DEBUG + if (type == Q_UPDATE || type == Q_INSERT || type == Q_DELETE || type == Q_REPLACE) + { + char* filteredTables[] = { " characters", " character_", " `character_", " statistics", " variables", " char_colors", " `guild", " bugs" }; + + bool match = false; + for (int i = 0; i < sizeof(filteredTables) / sizeof(filteredTables[0]); i++) + { + if (query.find(filteredTables[i]) != std::string::npos) { + match = true; + break; + } + } + try + { + if (!match) + { + FILE* pFile; + pFile = fopen("sql_updates.sql", "a+"); + fwrite(query.c_str(), 1, query.length(), pFile); + fwrite(";", sizeof(char), 1, pFile); + fwrite("\n", sizeof(char), 1, pFile); + fclose(pFile); + } + } + catch (...) {} + } +#endif + + + database.RunQuery(query.c_str(), query.length(), errbuf, &result, affected_rows, last_insert_id, &errnum, retry); + return result; +} + +#ifdef WORLD +void Database::RunAsyncQueries(int32 queryid) +{ + Database* asyncdb = FindFreeInstance(); + DBAsyncMutex.writelock(); + map>::iterator itr = asyncQueries.find(queryid); + if (itr == asyncQueries.end()) + { + DBAsyncMutex.releasewritelock(); + return; + } + + asyncQueriesMutex[queryid]->writelock(); + deque queries; + while (itr->second.size()) + { + Query* cur = itr->second.front(); + queries.push_back(cur); + itr->second.pop_front(); + } + itr->second.clear(); + asyncQueries.erase(itr); + DBAsyncMutex.releasewritelock(); + asyncQueriesMutex[queryid]->releasewritelock(); + + int32 count = 0; + while (queries.size() > 0) + { + Query* cur = queries.front(); + cur->RunQueryAsync(asyncdb); + this->RemoveActiveQuery(cur); + queries.pop_front(); + safe_delete(cur); + } + FreeDBInstance(asyncdb); + + bool isActive = LocalIsActiveQuery(queryid); + if (isActive) + { + continueAsync = true; + DBStruct* tmp = new DBStruct; + tmp->queryid = queryid; +#ifdef WIN32 + _beginthread(DBAsyncQueries, 0, (void*)tmp); +#else + pthread_t t1; + pthread_create(&t1, NULL, DBAsyncQueries, (void*)tmp); + pthread_detach(t1); +#endif + } +} + +void Database::AddAsyncQuery(Query* query) +{ + DBAsyncMutex.writelock(); + map::iterator mutexItr = asyncQueriesMutex.find(query->GetQueryID()); + if (mutexItr == asyncQueriesMutex.end()) + { + Mutex* queryMutex = new Mutex(); + queryMutex->SetName("AsyncQuery" + query->GetQueryID()); + asyncQueriesMutex.insert(make_pair(query->GetQueryID(), queryMutex)); + } + map>::iterator itr = asyncQueries.find(query->GetQueryID()); + asyncQueriesMutex[query->GetQueryID()]->writelock(); + + if ( itr != asyncQueries.end()) + itr->second.push_back(query); + else + { + deque queue; + queue.push_back(query); + asyncQueries.insert(make_pair(query->GetQueryID(), queue)); + } + + AddActiveQuery(query); + + asyncQueriesMutex[query->GetQueryID()]->releasewritelock(); + DBAsyncMutex.releasewritelock(); + + bool isActive = LocalIsActiveQuery(query->GetQueryID(), query); + if (!isActive) + { + continueAsync = true; + DBStruct* tmp = new DBStruct; + tmp->queryid = query->GetQueryID(); +#ifdef WIN32 + _beginthread(DBAsyncQueries, 0, (void*)tmp); +#else + pthread_t t1; + pthread_create(&t1, NULL, DBAsyncQueries, (void*)tmp); + pthread_detach(t1); +#endif + } +} + +Database* Database::FindFreeInstance() +{ + Database* db_inst = 0; + map::iterator itr; + DBInstanceMutex.writelock(__FUNCTION__, __LINE__); + for (itr = dbInstances.begin(); itr != dbInstances.end(); itr++) { + if (!itr->second) + { + db_inst = itr->first; + itr->second = true; + break; + } + } + + if (!db_inst) + { + WorldDatabase* tmp = new WorldDatabase(); + db_inst = (Database*)tmp; + tmp->Init(); + tmp->ConnectNewDatabase(); + dbInstances.insert(make_pair(db_inst, true)); + } + DBInstanceMutex.releasewritelock(__FUNCTION__, __LINE__); + + return db_inst; +} + +void Database::PurgeDBInstances() +{ + map::iterator itr; + DBInstanceMutex.writelock(__FUNCTION__, __LINE__); + for (itr = dbInstances.begin(); itr != dbInstances.end(); itr++) { + WorldDatabase* tmpInst = (WorldDatabase*)itr->first; + safe_delete(tmpInst); + } + dbInstances.clear(); + DBInstanceMutex.releasewritelock(__FUNCTION__, __LINE__); +} + + +void Database::PingAsyncDatabase() +{ + map::iterator itr; + DBInstanceMutex.readlock(__FUNCTION__, __LINE__); + for (itr = dbInstances.begin(); itr != dbInstances.end(); itr++) { + Database* tmpInst = itr->first; + tmpInst->ping(); + } + DBInstanceMutex.releasereadlock(__FUNCTION__, __LINE__); +} + +void Database::FreeDBInstance(Database* cur) +{ + DBInstanceMutex.writelock(__FUNCTION__, __LINE__); + dbInstances[cur] = false; + DBInstanceMutex.releasewritelock(__FUNCTION__, __LINE__); +} + +void Database::RemoveActiveQuery(Query* query) +{ + DBQueryMutex.writelock(__FUNCTION__, __LINE__); + + vector::iterator itr; + for (itr = activeQuerySessions.begin(); itr != activeQuerySessions.end(); itr++) + { + Query* curQuery = *itr; + if (query == curQuery) + { + activeQuerySessions.erase(itr); + break; + } + } + DBQueryMutex.releasewritelock(__FUNCTION__, __LINE__); + + bool isActive = LocalIsActiveQuery(query->GetQueryID()); + if(!isActive) { + peer_manager.sendPeersActiveQuery(query->GetQueryID(), true); + } +} + +void Database::AddActiveQuery(Query* query) +{ + peer_manager.sendPeersActiveQuery(query->GetQueryID(), false); + DBQueryMutex.writelock(__FUNCTION__, __LINE__); + activeQuerySessions.push_back(query); + DBQueryMutex.releasewritelock(__FUNCTION__, __LINE__); +} + +bool Database::IsActiveQuery(int32 id, Query* skip) { + if (LocalIsActiveQuery(id, skip)) + return true; + + { + auto now = std::chrono::steady_clock::now(); + std::lock_guard lock(_peerMtx); + + // remove any entries older than timeout + for (auto it = _peerActive.begin(); it != _peerActive.end(); ) { + if (now - it->second > kStaleTimeout) + it = _peerActive.erase(it); + else + ++it; + } + + // if this id is still in the map, it's active + if (_peerActive.find(id) != _peerActive.end()) + return true; + } + + return false; +} + +bool Database::LocalIsActiveQuery(int32 id, Query* skip) +{ + bool isActive = false; + DBQueryMutex.readlock(__FUNCTION__, __LINE__); + for (auto query : activeQuerySessions) { + if (query == skip) continue; + if (query->GetQueryID() == id) { + isActive = true; + break; + } + } + DBQueryMutex.releasereadlock(__FUNCTION__, __LINE__); + return isActive; +} + +void Database::AddPeerActiveQuery(int32 charID) { + auto now = std::chrono::steady_clock::now(); + std::lock_guard lock(_peerMtx); + _peerActive[charID] = now; // inserts or updates timestamp +} + +void Database::RemovePeerActiveQuery(int32 charID) { + std::lock_guard lock(_peerMtx); + _peerActive.erase(charID); +} +#endif \ No newline at end of file diff --git a/old/common/database.h b/old/common/database.h new file mode 100644 index 0000000..c5aa88f --- /dev/null +++ b/old/common/database.h @@ -0,0 +1,192 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef EQ2EMU_DATABASE_H +#define EQ2EMU_DATABASE_H + +#ifdef WIN32 + #include + #include +#endif +#include + +#include "dbcore.h" +#include "types.h" +#include "linked_list.h" +#include "EQStream.h" +#include "MiscFunctions.h" +#include "Mutex.h" +#include +#include +#include +#include +#include +#include + +using namespace std; +class Query; + +class Database : public DBcore +{ +public: + Database(); + ~Database(); + bool Init(bool silentLoad=false); + bool LoadVariables(); + void HandleMysqlError(int32 errnum); + map GetOpcodes(int16 version); + int32 AuthenticateWebUser(char* userName, char* passwd,int32* status = 0); + int32 NoAuthRoute(char* route); + map GetVersions(); + +#ifdef WORLD + void AddAsyncQuery(Query* query); + void RunAsyncQueries(int32 queryid); + Database* FindFreeInstance(); + void RemoveActiveQuery(Query* query); + void AddActiveQuery(Query* query); + bool IsActiveQuery(int32 id, Query* skip=0); + void PingAsyncDatabase(); + + void AddPeerActiveQuery(int32 charID); + void RemovePeerActiveQuery(int32 charID); +#endif +protected: + +private: + void InitVars(); + bool LocalIsActiveQuery(int32 id, Query* skip = nullptr); +#ifdef WORLD + void PurgeDBInstances(); + void FreeDBInstance(Database* cur); + bool continueAsync; + map> asyncQueries; + map asyncQueriesMutex; + map dbInstances; + vector activeQuerySessions; + Mutex DBAsyncMutex; + Mutex DBInstanceMutex; + Mutex DBQueryMutex; + std::unordered_map _peerActive; + std::mutex _peerMtx; + static constexpr std::chrono::seconds kStaleTimeout{30}; +#endif +}; + +typedef struct { + int32 queryid; +}DBStruct; + +class Query{ +public: + Query() { + result = 0; + affected_rows = 0; + last_insert_id = 0; + errnum = 0; + row = 0; + retry = true; + escaped_name = 0; + escaped_pass = 0; + escaped_data1 = 0; + multiple_results = 0; + memset(errbuf, 0, sizeof(errbuf)); + queryID = 0; + } + Query(Query* queryPtr, int32 in_id, std::string in_query) { + result = 0; + affected_rows = 0; + last_insert_id = 0; + errnum = 0; + row = 0; + retry = true; + escaped_name = 0; + escaped_pass = 0; + escaped_data1 = 0; + multiple_results = 0; + memset(errbuf, 0, sizeof(errbuf)); + query = std::move(in_query); + in_type = queryPtr->GetQueryType(); + queryID = in_id; + } + + ~Query(){ + if(result) + mysql_free_result(result); + result = 0; + safe_delete(affected_rows); + safe_delete(last_insert_id); + safe_delete_array(escaped_name); + safe_delete_array(escaped_pass); + safe_delete_array(escaped_data1); + if(multiple_results){ + vector::iterator itr; + for(itr = multiple_results->begin(); itr != multiple_results->end(); itr++){ + mysql_free_result(*itr); + } + safe_delete(multiple_results); + } + } + int32 GetLastInsertedID() { return *last_insert_id; } + int32 GetAffectedRows() { return *affected_rows; } + MYSQL_RES* GetResult() { return result; } + MYSQL_RES* RunQuery2(string in_query, QUERY_TYPE type); + char* GetError() { return errbuf; } + int32 GetErrorNumber(){ return errnum; } + const char* GetQuery() { return query.c_str(); } + char* GetField(int8 field_num) { + if(!row && result) + *row = mysql_fetch_row(result); + if(row && result && field_num < mysql_num_fields(result)) + return *row[field_num]; + else + return NULL; + } + void NextRow(){ + if(result) + *row = mysql_fetch_row(result); + } + void AddQueryAsync(int32 queryID, Database* db, QUERY_TYPE type, const char* format, ...); + void RunQueryAsync(Database* db); + MYSQL_RES* RunQuery2(QUERY_TYPE type, const char* format, ...); + + QUERY_TYPE GetQueryType() { + return in_type; + } + + int32 GetQueryID() { return queryID; } + + char* escaped_name; + char* escaped_pass; + char* escaped_data1; +private: + string query; + char errbuf[MYSQL_ERRMSG_SIZE]; + MYSQL_RES *result; + vector* multiple_results; + int32* affected_rows; + int32* last_insert_id; + int32 errnum; + QUERY_TYPE in_type; + bool retry; + MYSQL_ROW* row; + MYSQL mysql; + int32 queryID; +}; +#endif diff --git a/old/common/database/database.hpp b/old/common/database/database.hpp deleted file mode 100644 index c95becf..0000000 --- a/old/common/database/database.hpp +++ /dev/null @@ -1,392 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "database_core.hpp" -#include "query.hpp" -#include "../stream/eq_stream.hpp" -#include "../packet/packet_functions.hpp" -#include "../opcodes/emu_opcodes.hpp" -#include "../eq_emu_error.hpp" -#include "../packet/packet_dump.hpp" - -#ifdef WORLD - #include "../../WorldServer/WorldDatabase.h" -#endif -#ifdef LOGIN - #include "../../LoginServer/login_database.hpp" -#endif -#ifdef PARSER - #include "../PacketParser/ParserDatabase.h" -#endif -#ifdef PATCHER - #include "../PatchServer/PatcherDatabase.h" -#endif - - -using namespace std; - -// Forward declaration -class Query; - -// Structure for async database operations -typedef struct -{ - int32 queryid; -} DBStruct; - -// Advanced database class with async processing and game-specific functionality -class Database : public DatabaseCore -{ -public: - // Constructor - initializes database variables - Database() - { -#ifdef WORLD - continueAsync = false; -#endif - } - - // Destructor - cleans up resources and async queries - ~Database() - { -#ifdef WORLD - DBQueryMutex.writelock(__FUNCTION__, __LINE__); - activeQuerySessions.clear(); - DBQueryMutex.releasewritelock(__FUNCTION__, __LINE__); - - DBAsyncMutex.writelock(); - continueAsync = false; - for (auto& itr : asyncQueries) { - asyncQueriesMutex[itr.first]->writelock(); - auto& queries = itr.second; - while (queries.size() > 0) { - Query* cur = queries.front(); - queries.pop_front(); - safe_delete(cur); - } - asyncQueriesMutex[itr.first]->releasewritelock(); - Mutex* mutex = asyncQueriesMutex[itr.first]; - asyncQueriesMutex.erase(itr.first); - safe_delete(mutex); - } - asyncQueries.clear(); - asyncQueriesMutex.clear(); - DBAsyncMutex.releasewritelock(); - - PurgeDBInstances(); -#endif - } - - // Initialize database connection with optional silent loading - bool Init(bool silentLoad = false) - { - return Connect(); - } - - // Comprehensive query execution method - inherits from DatabaseCore but ensures Database access - using DatabaseCore::RunQuery; - - // Retrieve opcode mappings for specified client version - map GetOpcodes(int16 version) - { - map opcodes; - DatabaseResult result; - if (Select(&result, "select name, opcode from opcodes where %i between version_range1 and version_range2 order by version_range1, id", version)) { - while (result.Next()) { - opcodes[result.GetStringStr("name")] = result.GetInt16Str("opcode"); - } - } - return opcodes; - } - - // Get available client version ranges - map GetVersions() - { - map versions; - DatabaseResult result; - if (Select(&result, "select distinct version_range1, version_range2 from opcodes")) { - while (result.Next()) { - versions[result.GetInt16Str("version_range1")] = result.GetInt16Str("version_range2"); - } - } - return versions; - } - - // Authenticate web user credentials and return user ID - int32 AuthenticateWebUser(char* userName, char* passwd, int32* status = 0) - { - if (status) { - *status = 0; - } - int32 id = 0; - DatabaseResult result; - if (Select(&result, "select id, status from web_users where username='%s' and passwd = sha2('%s', 512)", EscapeStr(userName).c_str(), EscapeStr(passwd).c_str())) { - if (result.Next()) { - id = result.GetInt32Str("id"); - if (status) { - *status = result.GetInt32Str("status"); - } - } - } - return id; - } - - // Check if route requires authentication - int32 NoAuthRoute(char* route) - { - int32 status = 0xFFFFFFFF; - DatabaseResult result; - if (Select(&result, "select status from web_routes where route='%s'", EscapeStr(route).c_str())) { - if (result.Next()) { - status = result.GetInt32Str("status"); - } - } - return status; - } - - // Handle MySQL error codes with appropriate logging - void HandleMysqlError(int32 errnum) - { - switch (errnum) { - case 0: - break; - case 1045: // Access Denied - case 2001: { - AddEQEMuError(EQEMuError_Mysql_1405, true); - break; - } - case 2003: { // Unable to connect - AddEQEMuError(EQEMuError_Mysql_2003, true); - break; - } - case 2005: { // Unable to connect - AddEQEMuError(EQEMuError_Mysql_2005, true); - break; - } - case 2007: { // Unable to connect - AddEQEMuError(EQEMuError_Mysql_2007, true); - break; - } - } - } - -#ifdef WORLD - // Add query to async processing queue - void AddAsyncQuery(Query* query) - { - DBAsyncMutex.writelock(); - map::iterator mutexItr = asyncQueriesMutex.find(query->GetQueryID()); - if (mutexItr == asyncQueriesMutex.end()) { - Mutex* queryMutex = new Mutex(); - queryMutex->SetName("AsyncQuery" + query->GetQueryID()); - asyncQueriesMutex.insert(make_pair(query->GetQueryID(), queryMutex)); - } - map>::iterator itr = asyncQueries.find(query->GetQueryID()); - asyncQueriesMutex[query->GetQueryID()]->writelock(); - - if (itr != asyncQueries.end()) - itr->second.push_back(query); - else { - deque queue; - queue.push_back(query); - asyncQueries.insert(make_pair(query->GetQueryID(), queue)); - } - - AddActiveQuery(query); - - asyncQueriesMutex[query->GetQueryID()]->releasewritelock(); - DBAsyncMutex.releasewritelock(); - - bool isActive = IsActiveQuery(query->GetQueryID(), query); - if (!isActive) { - continueAsync = true; - DBStruct* tmp = new DBStruct; - tmp->queryid = query->GetQueryID(); - - thread asyncThread(DBAsyncQueries, (void*)tmp); - asyncThread.detach(); - } - } - - // Process all async queries for specified query ID - void RunAsyncQueries(int32 queryid) - { - Database* asyncdb = FindFreeInstance(); - DBAsyncMutex.writelock(); - map>::iterator itr = asyncQueries.find(queryid); - if (itr == asyncQueries.end()) { - DBAsyncMutex.releasewritelock(); - return; - } - - asyncQueriesMutex[queryid]->writelock(); - deque queries; - while (itr->second.size()) { - Query* cur = itr->second.front(); - queries.push_back(cur); - itr->second.pop_front(); - } - itr->second.clear(); - asyncQueries.erase(itr); - DBAsyncMutex.releasewritelock(); - asyncQueriesMutex[queryid]->releasewritelock(); - - while (queries.size() > 0) { - Query* cur = queries.front(); - cur->RunQueryAsync(asyncdb); - this->RemoveActiveQuery(cur); - queries.pop_front(); - safe_delete(cur); - } - FreeDBInstance(asyncdb); - - bool isActive = IsActiveQuery(queryid); - if (isActive) { - continueAsync = true; - DBStruct* tmp = new DBStruct; - tmp->queryid = queryid; - - thread asyncThread(DBAsyncQueries, (void*)tmp); - asyncThread.detach(); - } - } - - // Find available database instance from pool - Database* FindFreeInstance() - { - Database* db_inst = 0; - map::iterator itr; - DBInstanceMutex.writelock(__FUNCTION__, __LINE__); - for (itr = dbInstances.begin(); itr != dbInstances.end(); itr++) { - if (!itr->second) { - db_inst = itr->first; - itr->second = true; - break; - } - } - - if (!db_inst) { - WorldDatabase* tmp = new WorldDatabase(); - db_inst = (Database*)tmp; - tmp->Init(); - tmp->ConnectNewDatabase(); - dbInstances.insert(make_pair(db_inst, true)); - } - DBInstanceMutex.releasewritelock(__FUNCTION__, __LINE__); - - return db_inst; - } - - // Remove query from active session tracking - void RemoveActiveQuery(Query* query) - { - DBQueryMutex.writelock(__FUNCTION__, __LINE__); - - vector::iterator itr; - for (itr = activeQuerySessions.begin(); itr != activeQuerySessions.end(); itr++) { - Query* curQuery = *itr; - if (query == curQuery) { - activeQuerySessions.erase(itr); - break; - } - } - DBQueryMutex.releasewritelock(__FUNCTION__, __LINE__); - } - - // Add query to active session tracking - void AddActiveQuery(Query* query) - { - DBQueryMutex.writelock(__FUNCTION__, __LINE__); - activeQuerySessions.push_back(query); - DBQueryMutex.releasewritelock(__FUNCTION__, __LINE__); - } - - // Check if query ID has active operations - bool IsActiveQuery(int32 id, Query* skip = 0) - { - bool isActive = false; - - DBQueryMutex.readlock(__FUNCTION__, __LINE__); - vector::iterator itr; - for (itr = activeQuerySessions.begin(); itr != activeQuerySessions.end(); itr++) { - Query* query = *itr; - if (query == skip) - continue; - - if (query->GetQueryID() == id) { - isActive = true; - break; - } - } - DBQueryMutex.releasereadlock(__FUNCTION__, __LINE__); - - return isActive; - } - - // Ping all async database connections - void PingAsyncDatabase() - { - map::iterator itr; - DBInstanceMutex.readlock(__FUNCTION__, __LINE__); - for (itr = dbInstances.begin(); itr != dbInstances.end(); itr++) { - Database* tmpInst = itr->first; - tmpInst->Ping(); - } - DBInstanceMutex.releasereadlock(__FUNCTION__, __LINE__); - } - -private: - // Clean up all database instances in pool - void PurgeDBInstances() - { - map::iterator itr; - DBInstanceMutex.writelock(__FUNCTION__, __LINE__); - for (itr = dbInstances.begin(); itr != dbInstances.end(); itr++) { - WorldDatabase* tmpInst = (WorldDatabase*)itr->first; - safe_delete(tmpInst); - } - dbInstances.clear(); - DBInstanceMutex.releasewritelock(__FUNCTION__, __LINE__); - } - - // Return database instance to available pool - void FreeDBInstance(Database* cur) - { - DBInstanceMutex.writelock(__FUNCTION__, __LINE__); - dbInstances[cur] = false; - DBInstanceMutex.releasewritelock(__FUNCTION__, __LINE__); - } - - bool continueAsync; // Flag to continue async processing - map> asyncQueries; // Queue of async queries by ID - map asyncQueriesMutex; // Mutex per query ID for thread safety - map dbInstances; // Pool of database instances - vector activeQuerySessions; // Currently active query sessions - Mutex DBAsyncMutex; // Mutex for async operations - Mutex DBInstanceMutex; // Mutex for instance pool management - Mutex DBQueryMutex; // Mutex for query session tracking -#endif -}; - -#ifdef WORLD -// Global database instance declaration - would be defined elsewhere -extern Database database; - -// Thread function for processing async database queries -ThreadReturnType DBAsyncQueries(void* str) -{ - this_thread::sleep_for(chrono::milliseconds(10)); - DBStruct* data = (DBStruct*)str; - database.RunAsyncQueries(data->queryid); - delete data; - THREAD_RETURN(NULL); -} -#endif \ No newline at end of file diff --git a/old/common/database/database_core.hpp b/old/common/database/database_core.hpp deleted file mode 100644 index bb5ab6d..0000000 --- a/old/common/database/database_core.hpp +++ /dev/null @@ -1,573 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "database_result.hpp" -#include "../types.hpp" -#include "../misc_functions.hpp" -#include "../log.hpp" - -using namespace std; - -// Database configuration file names based on build target -#ifdef LOGIN - #define DB_INI_FILE "login_db.ini" -#elif defined WORLD - #define DB_INI_FILE "world_db.ini" -#elif defined PARSER - #define DB_INI_FILE "parser_db.ini" -#elif defined PATCHER - #define DB_INI_FILE "patcher_db.ini" -#endif - -// Initial buffer size for query allocation -#define QUERY_INITIAL_SIZE 512 - -// Core database class providing MySQL connection management and query execution -class DatabaseCore -{ -public: - // Database connection status enumeration - enum eStatus { Closed, Connected, Error }; - - // Constructor - initializes MySQL connection and member variables - DatabaseCore() - { - mysql_init(&mysql); - int timeout = 10; - mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout); - pHost = nullptr; - pPort = 0; - pUser = nullptr; - pPassword = nullptr; - pDatabase = nullptr; - pCompress = false; - pSSL = false; - pStatus = Closed; - MMysql.SetName("DatabaseCore::mysql"); - } - - // Destructor - closes connection and frees allocated memory - virtual ~DatabaseCore() - { - pStatus = Closed; - mysql_close(&mysql); -#if MYSQL_VERSION_ID >= 50003 - mysql_library_end(); -#else - mysql_server_end(); -#endif - safe_delete_array(pHost); - safe_delete_array(pUser); - safe_delete_array(pPassword); - safe_delete_array(pDatabase); - } - - // Get current database connection status - eStatus GetStatus() { return pStatus; } - - // Get MySQL error number from last operation - unsigned int GetError() { return mysql_errno(&mysql); } - - // Get MySQL error message from last operation - const char* GetErrorMsg() { return mysql_error(&mysql); } - - // Connect to database using configuration file settings - bool Connect() - { - char line[256], *key, *val; - char host[256], user[64], password[64], database[64], port[64]; - bool found_section = false; - FILE* f; - - if ((f = fopen(DB_INI_FILE, "r")) == NULL) { - LogWrite(DATABASE__ERROR, 0, "Database", "Unable to read %s\n", DB_INI_FILE); - return false; - } - - memset(host, 0, sizeof(host)); - memset(user, 0, sizeof(user)); - memset(password, 0, sizeof(password)); - memset(database, 0, sizeof(database)); - memset(port, 0, sizeof(port)); - - while (fgets(line, sizeof(line), f) != NULL) { - if (line[0] == '#' || line[0] == '\n' || line[0] == '\r') - continue; - - if (!found_section) { - if (strncasecmp(line, "[Database]", 10) == 0) - found_section = true; - } - else { - if ((key = strtok(line, "=")) != NULL) { - if ((val = strtok(NULL, "\r\n")) != NULL) { - if (strncasecmp(line, "host", 4) == 0) - strncpy(host, val, sizeof(host) - 1); - else if (strncasecmp(line, "user", 4) == 0) - strncpy(user, val, sizeof(user) - 1); - else if (strncasecmp(line, "password", 8) == 0) - strncpy(password, val, sizeof(password) - 1); - else if (strncasecmp(line, "database", 8) == 0) - strncpy(database, val, sizeof(database) - 1); - else if (strncasecmp(line, "port", 4) == 0) - strncpy(port, val, sizeof(port) - 1); - } - } - } - } - - fclose(f); - - if (host[0] == '\0') { - LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'host' in '%s'\n", DB_INI_FILE); - return false; - } - if (user[0] == '\0') { - LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'user' in '%s'\n", DB_INI_FILE); - return false; - } - if (password[0] == '\0') { - LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'password' in '%s'\n", DB_INI_FILE); - return false; - } - if (database[0] == '\0') { - LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'database' in '%s'\n", DB_INI_FILE); - return false; - } - - unsigned int portnum = atoul(port); - return Connect(host, user, password, database, portnum); - } - - // Connect to database with explicit connection parameters - bool Connect(const char* host, const char* user, const char* password, const char* database, unsigned int port = 3306) - { - LockMutex lock(&MMysql); - safe_delete_array(pHost); - safe_delete_array(pUser); - safe_delete_array(pPassword); - safe_delete_array(pDatabase); - - pHost = new char[strlen(host) + 1]; - strcpy(pHost, host); - pUser = new char[strlen(user) + 1]; - strcpy(pUser, user); - pPassword = new char[strlen(password) + 1]; - strcpy(pPassword, password); - pDatabase = new char[strlen(database) + 1]; - strcpy(pDatabase, database); - pPort = port; - - if (mysql_real_connect(&mysql, pHost, pUser, pPassword, pDatabase, pPort, NULL, CLIENT_FOUND_ROWS) == NULL) { - LogWrite(DATABASE__ERROR, 0, "Database", "Unable to connect to MySQL server at %s:%u: %s\n", host, port, mysql_error(&mysql)); - pStatus = Error; - return false; - } - - pStatus = Connected; - return true; - } - - // Comprehensive query execution method used by Query class and async operations - bool RunQuery(const char* query, int32 querylen, char* errbuf = 0, MYSQL_RES** result = 0, int32* affected_rows = 0, int32* last_insert_id = 0, int32* errnum = 0, bool retry = true) - { - if (errnum) - *errnum = 0; - if (errbuf) - errbuf[0] = 0; - bool ret = false; - LockMutex lock(&MMysql); - if (pStatus != Connected) - Connect(); - - LogWrite(DATABASE__QUERY, 0, "Database", query); - if (mysql_real_query(&mysql, query, querylen)) { - if (mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) - pStatus = Error; - if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) { - if (retry) { - LogWrite(DATABASE__ERROR, 0, "Database", "Lost connection, attempting to recover..."); - ret = RunQuery(query, querylen, errbuf, result, affected_rows, last_insert_id, errnum, false); - } - else { - pStatus = Error; - if (errnum) - *errnum = mysql_errno(&mysql); - if (errbuf) - snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); - LogWrite(DATABASE__ERROR, 0, "Database", "#%i: %s\nQuery:\n%s", mysql_errno(&mysql), mysql_error(&mysql), query); - ret = false; - } - } - else { - if (errnum) - *errnum = mysql_errno(&mysql); - if (errbuf) - snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); - LogWrite(DATABASE__ERROR, 0, "Database", "#%i: %s\nQuery:\n%s", mysql_errno(&mysql), mysql_error(&mysql), query); - ret = false; - } - } - else { - if (result && mysql_field_count(&mysql)) { - *result = mysql_store_result(&mysql); - } - else if (result) - *result = 0; - if (affected_rows) - *affected_rows = mysql_affected_rows(&mysql); - if (last_insert_id) - *last_insert_id = mysql_insert_id(&mysql); - if (result) { - if (*result) { - ret = true; - } - else { - if (errnum) - *errnum = UINT_MAX; - if (errbuf) { - if ((!affected_rows || (affected_rows && *affected_rows == 0)) && (!last_insert_id || (last_insert_id && *last_insert_id == 0))) - LogWrite(DATABASE__RESULT, 1, "Database", "No Result."); - } - ret = false; - } - } - else { - ret = true; - } - } - - if (ret) { - char tmp1[200] = { 0 }; - char tmp2[200] = { 0 }; - if (result && (*result)) - snprintf(tmp1, sizeof(tmp1), ", %i rows returned", (int)mysql_num_rows(*result)); - if (affected_rows) - snprintf(tmp2, sizeof(tmp2), ", %i rows affected", (*affected_rows)); - - LogWrite(DATABASE__DEBUG, 0, "Database", "Query Successful%s%s", tmp1, tmp2); - } - else - LogWrite(DATABASE__DEBUG, 0, "Database", "Query returned no results in %s!\n%s", __FUNCTION__, query); - - return ret; - } - - // Execute non-SELECT query with printf-style formatting and automatic reconnection - bool Query(const char* query, ...) - { - char* buf; - size_t size = QUERY_INITIAL_SIZE; - int num_chars; - va_list args; - bool ret = true; - - LockMutex lock(&MMysql); - while (true) { - if ((buf = (char*)malloc(size)) == NULL) { - LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate database query of %u bytes\n", size); - return false; - } - - va_start(args, query); - num_chars = vsnprintf(buf, size, query, args); - va_end(args); - - if (num_chars > -1 && (size_t)num_chars < size) - break; - - if (num_chars > -1) - size = num_chars + 1; - else - size *= 2; - - free(buf); - } - - if (pStatus != Connected) - Connect(); - - LogWrite(DATABASE__QUERY, 0, "Database", query); - if (mysql_real_query(&mysql, buf, num_chars) != 0) { - if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) { - LogWrite(DATABASE__ERROR, 0, "Database", "Lost connection, attempting to recover and retry query..."); - Connect(); - if (mysql_real_query(&mysql, buf, num_chars) != 0) { - ret = false; - } - } - else if (!IsIgnoredErrno(mysql_errno(&mysql))) { - LogWrite(DATABASE__ERROR, 0, "Database", "Error %i running MySQL query: %s\n%s\n", mysql_errno(&mysql), mysql_error(&mysql), buf); - ret = false; - } - } - free(buf); - return ret; - } - - // Execute SELECT query and store results in DatabaseResult object - bool Select(DatabaseResult* result, const char* query, ...) - { - char* buf; - size_t size = QUERY_INITIAL_SIZE; - int num_chars; - va_list args; - MYSQL_RES* res; - bool ret = true; - - LockMutex lock(&MMysql); - while (true) { - if ((buf = (char*)malloc(size)) == NULL) { - LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate database query of %u bytes\n", size); - return false; - } - - va_start(args, query); - num_chars = vsnprintf(buf, size, query, args); - va_end(args); - - if (num_chars > -1 && (size_t)num_chars < size) - break; - - if (num_chars > -1) - size = num_chars + 1; - else - size *= 2; - - free(buf); - } - - if (pStatus != Connected) - Connect(); - - if (mysql_real_query(&mysql, buf, (unsigned long)num_chars) != 0) { - if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) { - LogWrite(DATABASE__ERROR, 0, "Database", "Lost connection, attempting to recover and retry query..."); - mysql_close(&mysql); - Connect(); - if (mysql_real_query(&mysql, buf, (unsigned long)num_chars) != 0) { - ret = false; - } - } - else if (!IsIgnoredErrno(mysql_errno(&mysql))) { - LogWrite(DATABASE__ERROR, 0, "Database", "Error %i running MySQL query: %s\n%s\n", mysql_errno(&mysql), mysql_error(&mysql), buf); - ret = false; - } - } - - if (ret && !IsIgnoredErrno(mysql_errno(&mysql))) { - res = mysql_store_result(&mysql); - if (res != NULL) { - uint8 num_rows = mysql_affected_rows(&mysql); - uint8 num_fields = mysql_field_count(&mysql); - ret = result->StoreResult(res, num_fields, num_rows); - } - else { - LogWrite(DATABASE__ERROR, 0, "Database", "Error storing MySql query result (%d): %s\n%s", mysql_errno(&mysql), mysql_error(&mysql), buf); - ret = false; - } - } - - free(buf); - return ret; - } - - // Get the auto-increment ID from the last INSERT operation - int32 LastInsertID() - { - return (int32)mysql_insert_id(&mysql); - } - - // Get number of rows affected by the last operation - long AffectedRows() - { - return mysql_affected_rows(&mysql); - } - - // Escape string for safe SQL usage - caller must free() returned pointer - char* Escape(const char* str, size_t len) - { - char* buf = (char*)malloc(len * 2 + 1); - if (buf == NULL) { - LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", len * 2 + 1, __FUNCTION__, __LINE__); - return NULL; - } - mysql_real_escape_string(&mysql, buf, str, len); - return buf; - } - - // Escape null-terminated string for safe SQL usage - caller must free() returned pointer - char* Escape(const char* str) - { - return Escape(str, strlen(str)); - } - - // Escape string and return as std::string - no manual memory management required - string EscapeStr(const char* str, size_t len) - { - char* buf = (char*)malloc(len * 2 + 1); - string ret; - if (buf == NULL) { - LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", len * 2 + 1, __FUNCTION__, __LINE__); - return ret; - } - mysql_real_escape_string(&mysql, buf, str, len); - ret.append(buf); - free(buf); - return ret; - } - - // Escape null-terminated string and return as std::string - string EscapeStr(const char* str) - { - return EscapeStr(str, strlen(str)); - } - - // Escape std::string and return as std::string - string EscapeStr(string str) - { - return EscapeStr(str.c_str(), str.length()); - } - - // Legacy function for compatibility - use EscapeStr instead - string getSafeEscapeString(const char* from_string) - { - return EscapeStr(from_string); - } - - // Execute multiple SQL statements from a file with proper result processing - bool QueriesFromFile(const char* file) - { - bool success = true; - long size; - char* buf; - int ret; - MYSQL_RES* res; - FILE* f; - - f = fopen(file, "rb"); - if (f == NULL) { - LogWrite(DATABASE__ERROR, 0, "Database", "Unable to open '%s' for reading: %s", file, strerror(errno)); - return false; - } - - fseek(f, 0, SEEK_END); - size = ftell(f); - fseek(f, 0, SEEK_SET); - - buf = (char*)malloc(size + 1); - if (buf == NULL) { - fclose(f); - LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", size + 1, __FUNCTION__, __LINE__); - return false; - } - - if (fread(buf, sizeof(*buf), size, f) != (size_t)size) { - LogWrite(DATABASE__ERROR, 0, "Database", "Failed to read from '%s': %s", file, strerror(errno)); - fclose(f); - free(buf); - return false; - } - - buf[size] = '\0'; - fclose(f); - - mysql_set_server_option(&mysql, MYSQL_OPTION_MULTI_STATEMENTS_ON); - ret = mysql_real_query(&mysql, buf, size); - free(buf); - - if (ret != 0) { - LogWrite(DATABASE__ERROR, 0, "Database", "Error running MySQL queries from file '%s' (%d): %s", file, mysql_errno(&mysql), mysql_error(&mysql)); - success = false; - } - else { - do { - res = mysql_store_result(&mysql); - if (res != NULL) - mysql_free_result(res); - ret = mysql_next_result(&mysql); - if (ret > 0) { - LogWrite(DATABASE__ERROR, 0, "Database", "Error running MySQL queries from file '%s' (%d): %s", file, mysql_errno(&mysql), mysql_error(&mysql)); - success = false; - } - } while (ret == 0); - } - mysql_set_server_option(&mysql, MYSQL_OPTION_MULTI_STATEMENTS_OFF); - - return success; - } - - // Add MySQL error number to ignored list for suppressing specific error logging - void SetIgnoredErrno(unsigned int db_errno) - { - for (auto& errno_val : ignored_errnos) { - if (errno_val == db_errno) - return; - } - ignored_errnos.push_back(db_errno); - } - - // Remove MySQL error number from ignored list - void RemoveIgnoredErrno(unsigned int db_errno) - { - for (auto itr = ignored_errnos.begin(); itr != ignored_errnos.end(); itr++) { - if ((*itr) == db_errno) { - ignored_errnos.erase(itr); - break; - } - } - } - - // Check if MySQL error number should be ignored for logging - bool IsIgnoredErrno(unsigned int db_errno) - { - for (auto& errno_val : ignored_errnos) { - if (errno_val == db_errno) - return true; - } - return false; - } - - // Send keepalive ping to MySQL server and handle connection errors - void Ping() - { - if (!MMysql.trylock()) { - return; // If locked, someone's using it and it doesn't need a keepalive - } - mysql_ping(&mysql); - - int32 errnum = mysql_errno(&mysql); - switch (errnum) { - case CR_COMMANDS_OUT_OF_SYNC: - case CR_SERVER_GONE_ERROR: - case CR_UNKNOWN_ERROR: { - LogWrite(DATABASE__ERROR, 0, "Database", "[Database] We lost connection to the database., errno: %i", errnum); - break; - } - } - MMysql.unlock(); - } - -protected: - MYSQL mysql; // MySQL connection handle - Mutex MMysql; // Thread safety mutex for database operations - eStatus pStatus; // Current connection status - char* pHost; // Database host address - char* pUser; // Database username - char* pPassword; // Database password - char* pDatabase; // Database name - bool pCompress; // Enable compression flag - int32 pPort; // Database port number - bool pSSL; // Enable SSL flag - vector ignored_errnos; // List of MySQL error numbers to ignore in logging -}; \ No newline at end of file diff --git a/old/common/database/database_result.hpp b/old/common/database/database_result.hpp deleted file mode 100644 index 0235202..0000000 --- a/old/common/database/database_result.hpp +++ /dev/null @@ -1,249 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License - -#pragma once - -#include -#include -#include -#include -#include - -#include "../types.hpp" -#include "../log.hpp" - -using namespace std; - -// Return this instead of NULL for string functions to prevent crashes -static const char* empty_str = ""; - -// Database result wrapper class for MySQL query results with type-safe accessors -class DatabaseResult -{ -public: - // Constructor - initializes empty result set - DatabaseResult() : field_map(), result(0), num_fields(0), row(0) - { - } - - // Destructor - frees MySQL result and clears field mappings - virtual ~DatabaseResult() - { - if (result != NULL) - mysql_free_result(result); - if (field_map.size()) { - field_map.clear(); - } - } - - // Store MySQL result set and build field name to index mapping - bool StoreResult(MYSQL_RES* res, uint8 field_count, uint8 row_count) - { - if (result != NULL) - mysql_free_result(result); - if (field_map.size()) { - field_map.clear(); - } - - result = res; - num_rows = row_count; - num_fields = field_count; - - if (!num_rows || !num_fields) { - mysql_free_result(res); - result = NULL; - return false; - } - - const MYSQL_FIELD* fields = mysql_fetch_fields(result); - for (uint8 i = 0; i < num_fields; ++i) { - field_map.emplace(std::make_pair(std::string_view(fields[i].name), i)); - } - - return true; - } - - // Advance to next row in result set - bool Next() - { - return (result != NULL && (row = mysql_fetch_row(result)) != NULL); - } - - // Get total number of rows in result set - const unsigned int GetNumRows() { return num_rows; } - - // Check if field value at index is NULL - bool IsNull(unsigned int index) - { - const char* value = GetFieldValue(index); - return value == NULL; - } - - // Check if field value by name is NULL - bool IsNullStr(const char* field_name) - { - const char* value = GetFieldValueStr(field_name); - return value == NULL; - } - - // Type-safe getters by index - int8 GetInt8(unsigned int index) - { - const char* value = GetFieldValue(index); - return value == NULL ? 0 : atoi(value); - } - - sint8 GetSInt8(unsigned int index) - { - const char* value = GetFieldValue(index); - return value == NULL ? 0 : atoi(value); - } - - int16 GetInt16(unsigned int index) - { - const char* value = GetFieldValue(index); - return value == NULL ? 0 : atoi(value); - } - - sint16 GetSInt16(unsigned int index) - { - const char* value = GetFieldValue(index); - return value == NULL ? 0 : atoi(value); - } - - int32 GetInt32(unsigned int index) - { - const char* value = GetFieldValue(index); - return value == NULL ? 0U : strtoul(value, NULL, 10); - } - - sint32 GetSInt32(unsigned int index) - { - const char* value = GetFieldValue(index); - return value == NULL ? 0 : atoi(value); - } - - uint64 GetInt64(unsigned int index) - { - const char* value = GetFieldValue(index); - return value == NULL ? 0UL : strtoull(value, NULL, 10); - } - - sint64 GetSInt64(unsigned int index) - { - const char* value = GetFieldValue(index); - return value == NULL ? 0L : strtoll(value, NULL, 10); - } - - float GetFloat(unsigned int index) - { - const char* value = GetFieldValue(index); - return value == NULL ? 0.0F : atof(value); - } - - char GetChar(unsigned int index) - { - const char* value = GetFieldValue(index); - return value == NULL ? '\0' : value[0]; - } - - const char* GetString(unsigned int index) - { - const char* value = GetFieldValue(index); - return value == NULL ? empty_str : value; - } - - // Type-safe getters by field name - int8 GetInt8Str(const char* field_name) - { - const char* value = GetFieldValueStr(field_name); - return value == NULL ? 0 : atoi(value); - } - - sint8 GetSInt8Str(const char* field_name) - { - const char* value = GetFieldValueStr(field_name); - return value == NULL ? 0 : atoi(value); - } - - int16 GetInt16Str(const char* field_name) - { - const char* value = GetFieldValueStr(field_name); - return value == NULL ? 0 : atoi(value); - } - - sint16 GetSInt16Str(const char* field_name) - { - const char* value = GetFieldValueStr(field_name); - return value == NULL ? 0 : atoi(value); - } - - int32 GetInt32Str(const char* field_name) - { - const char* value = GetFieldValueStr(field_name); - return value == NULL ? 0U : strtoul(value, NULL, 10); - } - - sint32 GetSInt32Str(const char* field_name) - { - const char* value = GetFieldValueStr(field_name); - return value == NULL ? 0 : atoi(value); - } - - uint64 GetInt64Str(const char* field_name) - { - const char* value = GetFieldValueStr(field_name); - return value == NULL ? 0UL : strtoull(value, NULL, 10); - } - - sint64 GetSInt64Str(const char* field_name) - { - const char* value = GetFieldValueStr(field_name); - return value == NULL ? 0L : strtoll(value, NULL, 10); - } - - float GetFloatStr(const char* field_name) - { - const char* value = GetFieldValueStr(field_name); - return value == NULL ? 0.0F : atof(value); - } - - char GetCharStr(const char* field_name) - { - const char* value = GetFieldValueStr(field_name); - return value == NULL ? '\0' : value[0]; - } - - const char* GetStringStr(const char* field_name) - { - const char* value = GetFieldValueStr(field_name); - return value == NULL ? empty_str : value; - } - - // Get raw field value by index with bounds checking - const char* GetFieldValue(unsigned int index) - { - if (index >= num_fields) { - LogWrite(DATABASE__ERROR, 0, "Database Result", "Attempt to access field at index %u but there %s only %u field%s", index, num_fields == 1 ? "is" : "are", num_fields, num_fields == 1 ? "" : "s"); - return NULL; - } - return row[index]; - } - - // Get raw field value by name with field mapping lookup - const char* GetFieldValueStr(const char* field_name) - { - const auto& map_iterator = field_map.find(std::string_view(field_name)); - if (map_iterator != field_map.end()) { - return row[map_iterator->second]; - } - LogWrite(DATABASE__ERROR, 0, "Database Result", "Unknown field name '%s'", field_name); - return NULL; - } - -private: - MYSQL_RES* result; // MySQL result set from query - MYSQL_ROW row; // Current row data - unsigned int num_rows; // Total number of rows in result - unsigned int num_fields; // Total number of fields per row - std::map field_map; // Field name to index mapping for fast lookups -}; \ No newline at end of file diff --git a/old/common/database/query.hpp b/old/common/database/query.hpp deleted file mode 100644 index 6011c29..0000000 --- a/old/common/database/query.hpp +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "../types.hpp" -#include "../misc_functions.hpp" - -using namespace std; - -class Database; -class DatabaseCore; - -#ifdef WORLD - extern class WorldDatabase database; -#endif - -#ifdef LOGIN - extern class LoginDatabase database; -#endif - -#ifdef PARSER - extern class ParserDatabase database; -#endif - -#ifdef PATCHER - extern class PatcherDatabase database; -#endif - -// Simplified query class for executing SQL statements with result handling -class Query -{ -public: - // Default constructor - initializes all members to safe defaults - Query() - { - result = 0; - affected_rows = 0; - last_insert_id = 0; - errnum = 0; - row = 0; - retry = true; - multiple_results = 0; - memset(errbuf, 0, sizeof(errbuf)); - queryID = 0; - } - - // Copy constructor for async query creation - Query(Query* queryPtr, int32 in_id) - { - result = 0; - affected_rows = 0; - last_insert_id = 0; - errnum = 0; - row = 0; - retry = true; - multiple_results = 0; - memset(errbuf, 0, sizeof(errbuf)); - query = string(queryPtr->GetQuery()); - in_type = queryPtr->GetQueryType(); - queryID = in_id; - } - - // Destructor - frees MySQL results and allocated memory - ~Query() - { - if (result) - mysql_free_result(result); - result = 0; - safe_delete(affected_rows); - safe_delete(last_insert_id); - if (multiple_results) { - for (auto& res : *multiple_results) { - mysql_free_result(res); - } - safe_delete(multiple_results); - } - } - - // Get the last inserted auto-increment ID - int32 GetLastInsertedID() - { - return last_insert_id ? *last_insert_id : 0; - } - - // Get number of rows affected by last query - int32 GetAffectedRows() - { - return affected_rows ? *affected_rows : 0; - } - - // Get MySQL result set from last query - MYSQL_RES* GetResult() - { - return result; - } - - // Get error message from last failed query - char* GetError() - { - return errbuf; - } - - // Get MySQL error number from last failed query - int32 GetErrorNumber() - { - return errnum; - } - - // Get the SQL query string - const char* GetQuery() - { - return query.c_str(); - } - - // Get the type of this query (SELECT, INSERT, etc.) - QUERY_TYPE GetQueryType() - { - return in_type; - } - - // Get unique identifier for this query - int32 GetQueryID() - { - return queryID; - } - - // Get specific field value from current row - char* GetField(int8 field_num) - { - if (!row && result) - *row = mysql_fetch_row(result); - if (row && result && field_num < mysql_num_fields(result)) - return *row[field_num]; - else - return NULL; - } - - // Advance to next row in result set - void NextRow() - { - if (result) - *row = mysql_fetch_row(result); - } - - // Execute query with specified string and type - MYSQL_RES* RunQuery2(string in_query, QUERY_TYPE type) - { - switch (type) { - case Q_SELECT: - break; - case Q_DBMS: - case Q_REPLACE: - case Q_DELETE: - case Q_UPDATE: - safe_delete(affected_rows); - affected_rows = new int32; - break; - case Q_INSERT: - safe_delete(last_insert_id); - last_insert_id = new int32; - } - if (result) { - if (!multiple_results) - multiple_results = new vector(); - multiple_results->push_back(result); - } - query = in_query; - - #if defined WORLD && defined _DEBUG - if (type == Q_UPDATE || type == Q_INSERT || type == Q_DELETE || type == Q_REPLACE) { - char* filteredTables[] = { " characters", " character_", " `character_", " statistics", " variables", " char_colors", " `guild", " bugs" }; - - bool match = false; - for (int i = 0; i < sizeof(filteredTables) / sizeof(filteredTables[0]); i++) { - if (query.find(filteredTables[i]) != std::string::npos) { - match = true; - break; - } - } - try { - if (!match) { - FILE* pFile; - pFile = fopen("sql_updates.sql", "a+"); - fwrite(query.c_str(), 1, query.length(), pFile); - fwrite(";", sizeof(char), 1, pFile); - fwrite("\n", sizeof(char), 1, pFile); - fclose(pFile); - } - } - catch (...) {} - } - #endif // WORLD && _DEBUG - - database.RunQuery(query.c_str(), query.length(), errbuf, &result, affected_rows, last_insert_id, &errnum, retry); - return result; - } - - // Execute formatted query with variable arguments - MYSQL_RES* RunQuery2(QUERY_TYPE type, const char* format, ...) - { - va_list args; - va_start(args, format); - - char* buffer; - int buf_len; - va_list argcopy; - va_copy(argcopy, args); - buf_len = vsnprintf(NULL, 0, format, argcopy) + 1; - va_end(argcopy); - - buffer = new char[buf_len]; - vsnprintf(buffer, buf_len, format, args); - - va_end(args); - query = string(buffer); - - safe_delete_array(buffer); - - return RunQuery2(query.c_str(), type); - } - -#ifdef WORLD - // Add formatted query to async processing queue - void AddQueryAsync(int32 queryID, Database* db, QUERY_TYPE type, const char* format, ...) - { - in_type = type; - va_list args; - va_start(args, format); - - char* buffer; - int buf_len; - va_list argcopy; - va_copy(argcopy, args); - buf_len = vsnprintf(NULL, 0, format, argcopy) + 1; - va_end(argcopy); - - buffer = new char[buf_len]; - vsnprintf(buffer, buf_len, format, args); - - va_end(args); - query = string(buffer); - - Query* asyncQuery = new Query(this, queryID); - - safe_delete_array(buffer); - - db->AddAsyncQuery(asyncQuery); - } - - // Execute this query asynchronously using provided database instance - void RunQueryAsync(Database* db) - { - db->RunQuery(query.c_str(), query.length(), errbuf, &result, affected_rows, last_insert_id, &errnum, retry); - } -#endif - -private: - string query; // SQL query string - char errbuf[MYSQL_ERRMSG_SIZE]; // Error message buffer - MYSQL_RES* result; // MySQL result set - vector* multiple_results; // Multiple result sets for complex queries - int32* affected_rows; // Number of rows affected - int32* last_insert_id; // Last auto-increment ID - int32 errnum; // MySQL error number - QUERY_TYPE in_type; // Type of query being executed - bool retry; // Whether to retry failed queries - MYSQL_ROW* row; // Current row data - int32 queryID; // Unique query identifier -}; \ No newline at end of file diff --git a/old/common/dbcore.cpp b/old/common/dbcore.cpp new file mode 100644 index 0000000..e200e9d --- /dev/null +++ b/old/common/dbcore.cpp @@ -0,0 +1,368 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include "debug.h" + +#include +using namespace std; +#include +//#include +#include +#include "dbcore.h" +#include +#include +#include +#include "types.h" +#include "MiscFunctions.h" +#include "Log.h" + +#ifdef WIN32 + #define snprintf _snprintf + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + #include +#else + #include "unix.h" + #include +#endif + +#ifdef _EQDEBUG + #define DEBUG_MYSQL_QUERIES 0 +#else + #define DEBUG_MYSQL_QUERIES 0 +#endif + +DBcore::DBcore() { + mysql_init(&mysql); + pHost = 0; + pPort = 0; + pUser = 0; + pPassword = 0; + pDatabase = 0; + pCompress = false; +pSSL = false; +pStatus = Closed; +} + +DBcore::~DBcore() { + pStatus = Closed; + mysql_close(&mysql); +#if MYSQL_VERSION_ID >= 50003 + mysql_library_end(); +#else + mysql_server_end(); +#endif + safe_delete_array(pHost); + safe_delete_array(pUser); + safe_delete_array(pPassword); + safe_delete_array(pDatabase); +} + + +bool DBcore::ReadDBINI(char* host, char* user, char* passwd, char* database, unsigned int* port, bool* compress, bool* items) { + char line[256], * key, * val; + bool on_database_section = false; + FILE* f; + + if ((f = fopen(DB_INI_FILE, "r")) == NULL) { + LogWrite(DATABASE__ERROR, 0, "DBCore", "Unable to open '%s' for reading", DB_INI_FILE); + return false; + } + + //read each line + while (fgets(line, sizeof(line), f) != NULL) { + + //remove any new line or carriage return + while ((key = strstr(line, "\n")) != NULL) + *key = '\0'; + while ((key = strstr(line, "\r")) != NULL) + *key = '\0'; + + //ignore blank lines and commented lines + if (strlen(line) == 0 || line[0] == '#') + continue; + + key = strtok(line, "="); + + if (key == NULL) + continue; + + //don't do anything until we find the [Database] section + if (!on_database_section && strncasecmp(key, "[Database]", 10) == 0) + on_database_section = true; + else { + val = strtok(NULL, "="); + + if (val == NULL) + { + if (strcasecmp(key, "password") == 0) { + strcpy(passwd, ""); + items[2] = true; + } + continue; + } + if (strcasecmp(key, "host") == 0) { + strcpy(host, val); + items[0] = true; + } + else if (strcasecmp(key, "user") == 0) { + strcpy(user, val); + items[1] = true; + } + else if (strcasecmp(key, "password") == 0) { + strcpy(passwd, val); + items[2] = true; + } + else if (strcasecmp(key, "database") == 0) { + strcpy(database, val); + items[3] = true; + } + else if (strcasecmp(key, "port") == 0 && port) { + *port = atoul(val); + items[4] = true; + } + else if (strcasecmp(key, "compression") == 0) { + if (strcasecmp(val, "on") == 0) { + if(compress) { + *compress = true; + items[5] = true; + LogWrite(DATABASE__INFO, 0, "DBCore", "DB Compression on."); + } + } + } + } + } + + fclose(f); + + if (!on_database_section) { + LogWrite(DATABASE__ERROR, 0, "DBCore", "[Database] section not found in '%s'", DB_INI_FILE); + return false; + } + + return true; +} + + +// Sends the MySQL server a keepalive +void DBcore::ping() { + if (!MDatabase.trylock()) { + // well, if's it's locked, someone's using it. If someone's using it, it doesnt need a keepalive + return; + } + mysql_ping(&mysql); + + int32* errnum = new int32; + *errnum = mysql_errno(&mysql); + + switch (*errnum) + { + case CR_COMMANDS_OUT_OF_SYNC: + case CR_SERVER_GONE_ERROR: + case CR_UNKNOWN_ERROR: + { + LogWrite(DATABASE__ERROR, 0, "DBCore", "[Database] We lost connection to the database., errno: %i", errno); + break; + } + } + + safe_delete(errnum); + MDatabase.unlock(); +} + +bool DBcore::RunQuery(const char* query, int32 querylen, char* errbuf, MYSQL_RES** result, int32* affected_rows, int32* last_insert_id, int32* errnum, bool retry) { + if (errnum) + *errnum = 0; + if (errbuf) + errbuf[0] = 0; + bool ret = false; + LockMutex lock(&MDatabase); + if (pStatus != Connected) + Open(); + + LogWrite(DATABASE__QUERY, 0, "DBCore", query); + if (mysql_real_query(&mysql, query, querylen)) { + if (mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) + pStatus = Error; + if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) { + if (retry) { + LogWrite(DATABASE__ERROR, 0, "DBCore", "Lost connection, attempting to recover..."); + ret = RunQuery(query, querylen, errbuf, result, affected_rows, last_insert_id, errnum, false); + } + else { + pStatus = Error; + if (errnum) + *errnum = mysql_errno(&mysql); + if (errbuf) + snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); + LogWrite(DATABASE__ERROR, 0, "DBCore", "#%i: %s\nQuery:\n%s", mysql_errno(&mysql), mysql_error(&mysql), query); + ret = false; + } + } + else { + if (errnum) + *errnum = mysql_errno(&mysql); + if (errbuf) + snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); + LogWrite(DATABASE__ERROR, 0, "DBCore", "#%i: %s\nQuery:\n%s", mysql_errno(&mysql), mysql_error(&mysql), query); + ret = false; + } + } + else { + if (result && mysql_field_count(&mysql)) { + *result = mysql_store_result(&mysql); + } + else if (result) + *result = 0; + if (affected_rows) + *affected_rows = mysql_affected_rows(&mysql); + if (last_insert_id) + *last_insert_id = mysql_insert_id(&mysql); + if (result) { + if (*result) { + ret = true; + } + else { + if (errnum) + *errnum = UINT_MAX; + if (errbuf){ + if((!affected_rows || (affected_rows && *affected_rows == 0)) && (!last_insert_id || (last_insert_id && *last_insert_id == 0))) + LogWrite(DATABASE__RESULT, 1, "DBCore", "No Result."); + } + ret = false; + } + } + else { + ret = true; + } + } + + if (ret) + { + char tmp1[200] = {0}; + char tmp2[200] = {0}; + if (result && (*result)) + snprintf(tmp1, sizeof(tmp1), ", %i rows returned", (int) mysql_num_rows(*result)); + if (affected_rows) + snprintf(tmp2, sizeof(tmp2), ", %i rows affected", (*affected_rows)); + + LogWrite(DATABASE__DEBUG, 0, "DBCore", "Query Successful%s%s", tmp1, tmp2); + } + else + LogWrite(DATABASE__DEBUG, 0, "DBCore", "Query returned no results in %s!\n%s", __FUNCTION__, query); + + return ret; +} + +int32 DBcore::DoEscapeString(char* tobuf, const char* frombuf, int32 fromlen) { + LockMutex lock(&MDatabase); + return mysql_real_escape_string(&mysql, tobuf, frombuf, fromlen); +} + +bool DBcore::Open(const char* iHost, const char* iUser, const char* iPassword, const char* iDatabase,int32 iPort, int32* errnum, char* errbuf, bool iCompress, bool iSSL) { + LockMutex lock(&MDatabase); + safe_delete_array(pHost); + safe_delete_array(pUser); + safe_delete_array(pPassword); + safe_delete_array(pDatabase); + pHost = new char[strlen(iHost) + 1]; + strcpy(pHost, iHost); + pUser = new char[strlen(iUser) + 1]; + strcpy(pUser, iUser); + pPassword = new char[strlen(iPassword) + 1]; + strcpy(pPassword, iPassword); + pDatabase = new char[strlen(iDatabase) + 1]; + strcpy(pDatabase, iDatabase); + pCompress = iCompress; + pPort = iPort; + pSSL = iSSL; + return Open(errnum, errbuf); +} + +bool DBcore::Open(int32* errnum, char* errbuf) { + if (errbuf) + errbuf[0] = 0; + LockMutex lock(&MDatabase); + if (GetStatus() == Connected) + return true; + if (GetStatus() == Error) + mysql_close(&mysql); + if (!pHost) + return false; + /* + Quagmire - added CLIENT_FOUND_ROWS flag to the connect + otherwise DB update calls would say 0 rows affected when the value already equalled + what the function was tring to set it to, therefore the function would think it failed + */ + int32 flags = CLIENT_FOUND_ROWS; + if (pCompress) + flags |= CLIENT_COMPRESS; + if (pSSL) + flags |= CLIENT_SSL; + if (mysql_real_connect(&mysql, pHost, pUser, pPassword, pDatabase, pPort, 0, flags)) { + pStatus = Connected; + return true; + } + else { + if (errnum) + *errnum = mysql_errno(&mysql); + if (errbuf) + snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); + pStatus = Error; + return false; + } +} + +char* DBcore::getEscapeString(const char* from_string){ + if(!from_string) + from_string =""; + int orig_size = strlen(from_string); + int escape_size = (orig_size * 2) + 1; + char* escaped = new char[escape_size]; + memset(escaped, 0, escape_size); + DoEscapeString(escaped, from_string, orig_size); + return escaped; +} + +string DBcore::getSafeEscapeString(const char* from_string){ + if(!from_string) + from_string =""; + int orig_size = strlen(from_string); + int escape_size = (orig_size * 2) + 1; + char* escaped = new char[escape_size]; + memset(escaped, 0, escape_size); + DoEscapeString(escaped, from_string, orig_size); + string ret = string(escaped); + safe_delete_array(escaped); + return ret; +} + +string DBcore::getSafeEscapeString(string* from_string){ + if(!from_string) + return ""; + int orig_size = from_string->length(); + int escape_size = (orig_size * 2) + 1; + char* escaped = new char[escape_size]; + memset(escaped, 0, escape_size); + DoEscapeString(escaped, from_string->c_str(), orig_size); + string ret = string(escaped); + safe_delete_array(escaped); + return ret; +} + diff --git a/old/common/dbcore.h b/old/common/dbcore.h new file mode 100644 index 0000000..b6cbfd2 --- /dev/null +++ b/old/common/dbcore.h @@ -0,0 +1,80 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef DBCORE_H +#define DBCORE_H + +#ifdef WIN32 + #include + #include + //#include +#endif +#include +#include "../common/types.h" +#include "../common/Mutex.h" +#include "../common/linked_list.h" +#include "../common/queue.h" +#include "../common/timer.h" +#include "../common/Condition.h" +#ifdef LOGIN + #define DB_INI_FILE "login_db.ini" +#endif +#ifdef WORLD + #define DB_INI_FILE "world_db.ini" +#endif +#ifdef PARSER + #define DB_INI_FILE "parser_db.ini" +#endif +#ifdef PATCHER + #define DB_INI_FILE "patcher_db.ini" +#endif +class DBcore{ +public: + enum eStatus { Closed, Connected, Error }; + DBcore(); + ~DBcore(); + eStatus GetStatus() { return pStatus; } + bool RunQuery(const char* query, int32 querylen, char* errbuf = 0, MYSQL_RES** result = 0, int32* affected_rows = 0, int32* last_insert_id = 0, int32* errnum = 0, bool retry = true); + int32 DoEscapeString(char* tobuf, const char* frombuf, int32 fromlen); + void ping(); + char* getEscapeString(const char* from_string); + string getSafeEscapeString(const char* from_string); + string getSafeEscapeString(string* from_string); + +protected: + bool Open(const char* iHost, const char* iUser, const char* iPassword, const char* iDatabase, int32 iPort, int32* errnum = 0, char* errbuf = 0, bool iCompress = false, bool iSSL = false); + bool ReadDBINI(char *host, char *user, char *pass, char *db, unsigned int* port, bool* compress, bool *items); +private: + bool Open(int32* errnum = 0, char* errbuf = 0); + + MYSQL mysql; + Mutex MDatabase; + eStatus pStatus; + + char* pHost; + char* pUser; + char* pPassword; + char* pDatabase; + bool pCompress; + int32 pPort; + bool pSSL; +}; +#endif + + diff --git a/old/common/debug.cpp b/old/common/debug.cpp new file mode 100644 index 0000000..3f2f98b --- /dev/null +++ b/old/common/debug.cpp @@ -0,0 +1,336 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ + +/* + JA: File rendered obsolete (2011-08-12) + +#include "debug.h" + +#include +using namespace std; +#include +#include +#ifdef WIN32 + #include + + #define snprintf _snprintf + #define vsnprintf _vsnprintf + #define strncasecmp _strnicmp + #define strcasecmp _stricmp +#else + #include + #include + #include +#endif +#include "../common/MiscFunctions.h" + +EQEMuLog* LogFile = new EQEMuLog; +AutoDelete adlf(&LogFile); + +static const char* FileNames[EQEMuLog::MaxLogID] = { "logs/eq2emu", "logs/eq2emu", "logs/eq2emu_error", "logs/eq2emu_debug", "logs/eq2emu_quest", "logs/eq2emu_commands" }; +static const char* LogNames[EQEMuLog::MaxLogID] = { "Status", "Normal", "Error", "Debug", "Quest", "Command" }; + +EQEMuLog::EQEMuLog() { + for (int i=0; i= 2 + pLogStatus[i] = 1 | 2; +#else + pLogStatus[i] = 0; +#endif + logCallbackFmt[i] = NULL; + logCallbackBuf[i] = NULL; + } +#if EQDEBUG < 2 + pLogStatus[Status] = 3; + pLogStatus[Error] = 3; + pLogStatus[Debug] = 3; + pLogStatus[Quest] = 2; + pLogStatus[Commands] = 2; +#endif +} + +EQEMuLog::~EQEMuLog() { + for (int i=0; i= MaxLogID) { + return false; + } + LockMutex lock(&MOpen); + if (pLogStatus[id] & 4) { + return false; + } + if (fp[id]) { + return true; + } + + char exename[200] = ""; +#if defined(WORLD) + snprintf(exename, sizeof(exename), "_world"); +#elif defined(ZONE) + snprintf(exename, sizeof(exename), "_zone"); +#endif + char filename[200]; +#ifndef NO_PIDLOG + snprintf(filename, sizeof(filename), "%s%s_%04i.log", FileNames[id], exename, getpid()); +#else + snprintf(filename, sizeof(filename), "%s%s.log", FileNames[id], exename); +#endif + fp[id] = fopen(filename, "a"); + if (!fp[id]) { + cerr << "Failed to open log file: " << filename << endl; + pLogStatus[id] |= 4; // set file state to error + return false; + } + fputs("---------------------------------------------\n",fp[id]); + return true; +} + +bool EQEMuLog::write(LogIDs id, const char *fmt, ...) { + char buffer[4096]; + + if (!this) { + return false; + } + if (id >= MaxLogID) { + return false; + } + bool dofile = false; + if (pLogStatus[id] & 1) { + dofile = open(id); + } + if (!(dofile || pLogStatus[id] & 2)) + return false; + LockMutex lock(&MLog[id]); + + time_t aclock; + struct tm *newtime; + + time( &aclock ); //Get time in seconds + newtime = localtime( &aclock ); //Convert time to struct + + if (dofile){ +#ifndef NO_PIDLOG + fprintf(fp[id], "[%04d%02d%02d %02d:%02d:%02d] ", newtime->tm_year+1900, newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec); +#else + fprintf(fp[id], "%04i [%04d%02d%02d %02d:%02d:%02d] ", getpid(), newtime->tm_year+1900, newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec); +#endif + } + + va_list argptr; + va_start(argptr, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, argptr); + va_end(argptr); + if (dofile) + fprintf(fp[id], "%s\n", buffer); + if(logCallbackFmt[id]) { + msgCallbackFmt p = logCallbackFmt[id]; + p(id, fmt, argptr ); + } + + if (pLogStatus[id] & 2) { + if (pLogStatus[id] & 8) { + fprintf(stderr, "[%04d%02d%02d %02d:%02d:%02d] [%s] ", newtime->tm_year+1900, newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec, LogNames[id]); + fprintf(stderr, "%s\n", buffer); + } + else { + fprintf(stdout, "[%04d%02d%02d %02d:%02d:%02d] [%s] ", newtime->tm_year+1900, newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec, LogNames[id]); + fprintf(stdout, "%s\n", buffer); + } + } + if (dofile) + fprintf(fp[id], "\n"); + if (pLogStatus[id] & 2) { + if (pLogStatus[id] & 8) + fprintf(stderr, "\n"); + else + fprintf(stdout, "\n"); + } + if(dofile) + fflush(fp[id]); + return true; +} + +bool EQEMuLog::writebuf(LogIDs id, const char *buf, int8 size, int32 count) { + if (!this) { + return false; + } + if (id >= MaxLogID) { + return false; + } + bool dofile = false; + if (pLogStatus[id] & 1) { + dofile = open(id); + } + if (!(dofile || pLogStatus[id] & 2)) + return false; + LockMutex lock(&MLog[id]); + + time_t aclock; + struct tm *newtime; + + time( &aclock ); // Get time in seconds + newtime = localtime( &aclock ); // Convert time to struct + + if (dofile){ +#ifndef NO_PIDLOG + fprintf(fp[id], "[%02d.%02d. - %02d:%02d:%02d] ", newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec); +#else + fprintf(fp[id], "%04i [%02d.%02d. - %02d:%02d:%02d] ", getpid(), newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec); +#endif + } + + if (dofile) { + fwrite(buf, size, count, fp[id]); + fprintf(fp[id], "\n"); + } + if(logCallbackBuf[id]) { + msgCallbackBuf p = logCallbackBuf[id]; + p(id, buf, size, count); + } + if (pLogStatus[id] & 2) { + if (pLogStatus[id] & 8) { + fprintf(stderr, "[%s] ", LogNames[id]); + fwrite(buf, size, count, stderr); + fprintf(stderr, "\n"); + } else { + fprintf(stdout, "[%s] ", LogNames[id]); + fwrite(buf, size, count, stdout); + fprintf(stdout, "\n"); + } + } + if(dofile) + fflush(fp[id]); + return true; +} + +bool EQEMuLog::writeNTS(LogIDs id, bool dofile, const char *fmt, ...) { + char buffer[4096]; + va_list argptr; + va_start(argptr, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, argptr); + va_end(argptr); + if (dofile) + fprintf(fp[id], "%s\n", buffer); + if (pLogStatus[id] & 2) { + if (pLogStatus[id] & 8) + fprintf(stderr, "%s\n", buffer); + else + fprintf(stdout, "%s\n", buffer); + } + return true; +}; + +bool EQEMuLog::Dump(LogIDs id, int8* data, int32 size, int32 cols, int32 skip) { + if (!this) { +#if EQDEBUG >= 10 + cerr << "Error: Dump() from null pointer"<= MaxLogID) + return false; + bool dofile = false; + if (pLogStatus[id] & 1) { + dofile = open(id); + } + if (!(dofile || pLogStatus[id] & 2)) + return false; + LockMutex lock(&MLog[id]); + write(id, "Dumping Packet: %i", size); + // Output as HEX + int j = 0; char* ascii = new char[cols+1]; memset(ascii, 0, cols+1); + int32 i; + for(i=skip; i= 32 && data[i] < 127) + ascii[j++] = data[i]; + else + ascii[j++] = '.'; + } + int32 k = ((i-skip)-1)%cols; + if (k < 8) + writeNTS(id, dofile, " "); + for (int32 h = k+1; h < cols; h++) { + writeNTS(id, dofile, " "); + } + writeNTS(id, dofile, " | %s\n", ascii); + if (dofile) + fflush(fp[id]); + safe_delete_array(ascii); + return true; +} + +void EQEMuLog::SetCallback(LogIDs id, msgCallbackFmt proc) { + if (!this) + return; + if (id >= MaxLogID) { + return; + } + logCallbackFmt[id] = proc; +} + +void EQEMuLog::SetCallback(LogIDs id, msgCallbackBuf proc) { + if (!this) + return; + if (id >= MaxLogID) { + return; + } + logCallbackBuf[id] = proc; +} + +void EQEMuLog::SetAllCallbacks(msgCallbackFmt proc) { + if (!this) + return; + int r; + for(r = Status; r < MaxLogID; r++) { + SetCallback((LogIDs)r, proc); + } +} + +void EQEMuLog::SetAllCallbacks(msgCallbackBuf proc) { + if (!this) + return; + int r; + for(r = Status; r < MaxLogID; r++) { + SetCallback((LogIDs)r, proc); + } +} +*/ \ No newline at end of file diff --git a/old/common/debug.h b/old/common/debug.h new file mode 100644 index 0000000..7422d5e --- /dev/null +++ b/old/common/debug.h @@ -0,0 +1,143 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef EQDEBUG_H +#define EQDEBUG_H + +// Debug Levels +/* + 1 = Normal + 3 = Some extended debug info + 5 = Light DETAIL info + 7 = Heavy DETAIL info + 9 = DumpPacket/PrintPacket + You should use even numbers too, to define any subset of the above basic template +*/ +#ifndef EQDEBUG + #define EQDEBUG 1 +#endif + + +#if defined(DEBUG) && defined(WIN32) + //#ifndef _CRTDBG_MAP_ALLOC + #include + #include + #if (_MSC_VER < 1300) + #include + #include + #define _CRTDBG_MAP_ALLOC + #define new new(_NORMAL_BLOCK, __FILE__, __LINE__) + #define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) + #endif + //#endif +#endif + +#ifndef ThrowError + void CatchSignal(int); + #if defined(CATCH_CRASH) || defined(_EQDEBUG) + #define ThrowError(errstr) { cout << "Fatal error: " << errstr << " (" << __FILE__ << ", line " << __LINE__ << ")" << endl; LogWrite(WORLD__ERROR, 0, "Debug", "Thrown Error: %s (%s:%i)", errstr, __FILE__, __LINE__); throw errstr; } + #else + #define ThrowError(errstr) { cout << "Fatal error: " << errstr << " (" << __FILE__ << ", line " << __LINE__ << ")" << endl; LogWrite(WORLD__ERROR, 0, "Debug", "Thrown Error: %s (%s:%i)", errstr, __FILE__, __LINE__); CatchSignal(0); } + #endif +#endif + +#ifdef WIN32 + // VS6 doesn't like the length of STL generated names: disabling + #pragma warning(disable:4786) +#endif + +#ifndef WIN32 + #define DebugBreak() if(0) {} +#endif + +#ifdef WIN32 + #include + #include +#endif + +#include "../common/Mutex.h" +#include +#include + + +class EQEMuLog { +public: + EQEMuLog(); + ~EQEMuLog(); + + enum LogIDs { + Status = 0, //this must stay the first entry in this list + Normal, + Error, + Debug, + Quest, + Commands, + MaxLogID + }; + + //these are callbacks called for each + typedef void (* msgCallbackBuf)(LogIDs id, const char *buf, int8 size, int32 count); + typedef void (* msgCallbackFmt)(LogIDs id, const char *fmt, va_list ap); + + void SetAllCallbacks(msgCallbackFmt proc); + void SetAllCallbacks(msgCallbackBuf proc); + void SetCallback(LogIDs id, msgCallbackFmt proc); + void SetCallback(LogIDs id, msgCallbackBuf proc); + + bool writebuf(LogIDs id, const char *buf, int8 size, int32 count); + bool write(LogIDs id, const char *fmt, ...); + bool Dump(LogIDs id, int8* data, int32 size, int32 cols=16, int32 skip=0); +private: + bool open(LogIDs id); + bool writeNTS(LogIDs id, bool dofile, const char *fmt, ...); // no error checking, assumes is open, no locking, no timestamp, no newline + + Mutex MOpen; + Mutex MLog[MaxLogID]; + FILE* fp[MaxLogID]; +/* LogStatus: bitwise variable + 1 = output to file + 2 = output to stdout + 4 = fopen error, dont retry + 8 = use stderr instead (2 must be set) +*/ + int8 pLogStatus[MaxLogID]; + + msgCallbackFmt logCallbackFmt[MaxLogID]; + msgCallbackBuf logCallbackBuf[MaxLogID]; +}; + +//extern EQEMuLog* LogFile; + +#ifdef _EQDEBUG +class PerformanceMonitor { +public: + PerformanceMonitor(sint64* ip) { + p = ip; + QueryPerformanceCounter(&tmp); + } + ~PerformanceMonitor() { + LARGE_INTEGER tmp2; + QueryPerformanceCounter(&tmp2); + *p += tmp2.QuadPart - tmp.QuadPart; + } + LARGE_INTEGER tmp; + sint64* p; +}; +#endif +#endif diff --git a/old/common/debug.hpp b/old/common/debug.hpp deleted file mode 100644 index 0fa0c0f..0000000 --- a/old/common/debug.hpp +++ /dev/null @@ -1,435 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -// Debug level configuration - controls verbosity of logging output -#ifndef EQDEBUG - #define EQDEBUG 1 -#endif - -#ifndef ThrowError - void CatchSignal(int); - #if defined(CATCH_CRASH) || defined(_EQDEBUG) - #define ThrowError(errstr) { std::cout << "Fatal error: " << errstr << " (" << __FILE__ << ", line " << __LINE__ << ")" << std::endl; LogWrite(WORLD__ERROR, 0, "Debug", "Thrown Error: %s (%s:%i)", errstr, __FILE__, __LINE__); throw errstr; } - #else - #define ThrowError(errstr) { std::cout << "Fatal error: " << errstr << " (" << __FILE__ << ", line " << __LINE__ << ")" << std::endl; LogWrite(WORLD__ERROR, 0, "Debug", "Thrown Error: %s (%s:%i)", errstr, __FILE__, __LINE__); CatchSignal(0); } - #endif -#endif - -/** - * Main logging system for EQ2Emulator - * Provides file and console logging with multiple log levels and categories - */ -class EQEMuLog -{ -public: - // Log category enumeration - defines different types of log messages - enum LogIDs { - Status = 0, // Server status messages (must stay first) - Normal, // General operational messages - Error, // Error conditions and failures - Debug, // Debug information for development - Quest, // Quest system specific messages - Commands, // Player command execution logs - MaxLogID // Total number of log categories - }; - - // Callback function types for custom log handling - using msgCallbackBuf = std::function; - using msgCallbackFmt = std::function; - - /** - * Constructor - initializes logging system with default settings - * Sets up file pointers, log status flags, and callback handlers - */ - EQEMuLog() - { - for (int i = 0; i < MaxLogID; i++) { - fp[i] = nullptr; -#if EQDEBUG >= 2 - pLogStatus[i] = 1 | 2; // Enable file and console output for debug builds -#else - pLogStatus[i] = 0; // Disable logging for release builds -#endif - logCallbackFmt[i] = nullptr; - logCallbackBuf[i] = nullptr; - } -#if EQDEBUG < 2 - // Configure specific log levels for release builds - pLogStatus[Status] = 3; // File + console - pLogStatus[Error] = 3; // File + console - pLogStatus[Debug] = 3; // File + console - pLogStatus[Quest] = 2; // Console only - pLogStatus[Commands] = 2; // Console only -#endif - } - - /** - * Destructor - closes all open log files - * Ensures proper cleanup of file handles - */ - ~EQEMuLog() - { - for (int i = 0; i < MaxLogID; i++) { - if (fp[i]) { - fp[i]->close(); - delete fp[i]; - } - } - } - - /** - * Sets callback function for all log categories (formatted version) - * @param proc Callback function to handle formatted log messages - */ - void SetAllCallbacks(msgCallbackFmt proc) - { - for (int r = Status; r < MaxLogID; r++) { - SetCallback(static_cast(r), proc); - } - } - - /** - * Sets callback function for all log categories (buffer version) - * @param proc Callback function to handle raw buffer log messages - */ - void SetAllCallbacks(msgCallbackBuf proc) - { - for (int r = Status; r < MaxLogID; r++) { - SetCallback(static_cast(r), proc); - } - } - - /** - * Sets callback function for specific log category (formatted version) - * @param id Log category to set callback for - * @param proc Callback function to handle formatted messages - */ - void SetCallback(LogIDs id, msgCallbackFmt proc) - { - if (id >= MaxLogID) { - return; - } - logCallbackFmt[id] = proc; - } - - /** - * Sets callback function for specific log category (buffer version) - * @param id Log category to set callback for - * @param proc Callback function to handle buffer messages - */ - void SetCallback(LogIDs id, msgCallbackBuf proc) - { - if (id >= MaxLogID) { - return; - } - logCallbackBuf[id] = proc; - } - - /** - * Writes raw buffer data to log with timestamp - * @param id Log category to write to - * @param buf Raw data buffer to write - * @param size Size of each element in buffer - * @param count Number of elements to write - * @return True if write was successful - */ - bool writebuf(LogIDs id, const char *buf, int8_t size, int32_t count) - { - if (id >= MaxLogID) { - return false; - } - - bool dofile = false; - if (pLogStatus[id] & 1) { - dofile = open(id); - } - if (!(dofile || pLogStatus[id] & 2)) { - return false; - } - - std::lock_guard lock(MLog[id]); - - auto now = std::chrono::system_clock::now(); - auto time_t = std::chrono::system_clock::to_time_t(now); - auto tm = *std::localtime(&time_t); - - if (dofile) { - *fp[id] << std::format("{:04d} [{:04d}{:02d}{:02d} {:02d}:{:02d}:{:02d}] ", - getpid(), tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec); - fp[id]->write(buf, size * count); - *fp[id] << "\n"; - fp[id]->flush(); - } - - if (logCallbackBuf[id]) { - logCallbackBuf[id](id, buf, size, count); - } - - if (pLogStatus[id] & 2) { - auto& stream = (pLogStatus[id] & 8) ? std::cerr : std::cout; - stream << "[" << LogNames[id] << "] "; - stream.write(buf, size * count); - stream << "\n"; - } - - return true; - } - - /** - * Writes formatted message to log with timestamp - * @param id Log category to write to - * @param fmt Printf-style format string - * @param ... Variable arguments for format string - * @return True if write was successful - */ - template - bool write(LogIDs id, const std::string& fmt, Args&&... args) - { - if (id >= MaxLogID) { - return false; - } - - bool dofile = false; - if (pLogStatus[id] & 1) { - dofile = open(id); - } - if (!(dofile || pLogStatus[id] & 2)) { - return false; - } - - std::lock_guard lock(MLog[id]); - - auto now = std::chrono::system_clock::now(); - auto time_t = std::chrono::system_clock::to_time_t(now); - auto tm = *std::localtime(&time_t); - - std::string buffer; - if constexpr (sizeof...(args) > 0) { - buffer = std::vformat(fmt, std::make_format_args(args...)); - } else { - buffer = fmt; - } - - if (dofile) { - *fp[id] << std::format("[{:04d}{:02d}{:02d} {:02d}:{:02d}:{:02d}] {}\n", - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec, buffer); - fp[id]->flush(); - } - - if (pLogStatus[id] & 2) { - auto& stream = (pLogStatus[id] & 8) ? std::cerr : std::cout; - stream << std::format("[{:04d}{:02d}{:02d} {:02d}:{:02d}:{:02d}] [{}] {}\n", - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec, LogNames[id], buffer); - } - - return true; - } - - /** - * Dumps binary data in hexadecimal format with ASCII representation - * @param id Log category to write to - * @param data Binary data to dump - * @param size Number of bytes to dump - * @param cols Number of columns per row (default 16) - * @param skip Number of bytes to skip at beginning (default 0) - * @return True if dump was successful - */ - bool Dump(LogIDs id, const int8_t* data, int32_t size, int32_t cols = 16, int32_t skip = 0) - { - if (size == 0) { - return true; - } - if (id >= MaxLogID) { - return false; - } - - bool dofile = false; - if (pLogStatus[id] & 1) { - dofile = open(id); - } - if (!(dofile || pLogStatus[id] & 2)) { - return false; - } - - std::lock_guard lock(MLog[id]); - write(id, "Dumping Packet: {}", size); - - // Output data in hexadecimal format with ASCII representation - std::string ascii(cols, '\0'); - int j = 0; - - for (int32_t i = skip; i < size; i++) { - if ((i - skip) % cols == 0) { - if (i != skip) { - writeNTS(id, dofile, " | {}\n", ascii); - } - writeNTS(id, dofile, "{:4}: ", i - skip); - ascii.assign(cols, '\0'); - j = 0; - } else if ((i - skip) % (cols / 2) == 0) { - writeNTS(id, dofile, "- "); - } - - writeNTS(id, dofile, "{:02X} ", static_cast(data[i])); - - if (data[i] >= 32 && data[i] < 127) { - ascii[j++] = data[i]; - } else { - ascii[j++] = '.'; - } - } - - // Pad final line if necessary - int32_t k = ((size - skip) - 1) % cols; - if (k < 8) { - writeNTS(id, dofile, " "); - } - for (int32_t h = k + 1; h < cols; h++) { - writeNTS(id, dofile, " "); - } - writeNTS(id, dofile, " | {}\n", ascii.substr(0, j)); - - if (dofile) { - fp[id]->flush(); - } - - return true; - } - -private: - /** - * Opens log file for specified category - * @param id Log category to open file for - * @return True if file opened successfully - */ - bool open(LogIDs id) - { - if (id >= MaxLogID) { - return false; - } - - std::lock_guard lock(MOpen); - - if (pLogStatus[id] & 4) { // File error state - return false; - } - if (fp[id]) { // Already open - return true; - } - - std::string exename; -#if defined(WORLD) - exename = "_world"; -#elif defined(ZONE) - exename = "_zone"; -#endif - - std::string filename = std::format("{}{}_{:04d}.log", FileNames[id], exename, getpid()); - - fp[id] = new std::ofstream(filename, std::ios::app); - if (!fp[id]->is_open()) { - std::cerr << "Failed to open log file: " << filename << std::endl; - pLogStatus[id] |= 4; // Set error state - delete fp[id]; - fp[id] = nullptr; - return false; - } - - *fp[id] << "---------------------------------------------\n"; - return true; - } - - /** - * Internal write function without timestamp or locking (Not Thread Safe) - * @param id Log category to write to - * @param dofile Whether to write to file - * @param fmt Format string - * @param args Format arguments - */ - template - void writeNTS(LogIDs id, bool dofile, const std::string& fmt, Args&&... args) - { - std::string buffer; - if constexpr (sizeof...(args) > 0) { - buffer = std::vformat(fmt, std::make_format_args(args...)); - } else { - buffer = fmt; - } - - if (dofile) { - *fp[id] << buffer; - } - if (pLogStatus[id] & 2) { - auto& stream = (pLogStatus[id] & 8) ? std::cerr : std::cout; - stream << buffer; - } - } - - // File management - std::mutex MOpen; // Mutex for file opening operations - std::mutex MLog[MaxLogID]; // Per-category logging mutexes - std::ofstream* fp[MaxLogID]; // File pointers for each log category - - // Log status flags (bitwise) - // 1 = output to file, 2 = output to stdout, 4 = file error, 8 = use stderr - int8_t pLogStatus[MaxLogID]; - - // Callback function pointers for custom log handling - msgCallbackFmt logCallbackFmt[MaxLogID]; // Formatted message callbacks - msgCallbackBuf logCallbackBuf[MaxLogID]; // Buffer message callbacks - - // Static configuration arrays - static constexpr const char* FileNames[MaxLogID] = { - "logs/eq2emu", "logs/eq2emu", "logs/eq2emu_error", - "logs/eq2emu_debug", "logs/eq2emu_quest", "logs/eq2emu_commands" - }; - - static constexpr const char* LogNames[MaxLogID] = { - "Status", "Normal", "Error", "Debug", "Quest", "Command" - }; -}; - -#ifdef _EQDEBUG -/** - * Performance monitoring utility for debug builds - * Measures execution time between construction and destruction - */ -class PerformanceMonitor -{ -public: - /** - * Constructor - starts timing measurement - * @param ip Pointer to variable to store elapsed time - */ - PerformanceMonitor(int64_t* ip) : p(ip), start_time(std::chrono::high_resolution_clock::now()) - { - } - - /** - * Destructor - calculates and stores elapsed time - */ - ~PerformanceMonitor() - { - auto end_time = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end_time - start_time); - *p += duration.count(); - } - -private: - std::chrono::high_resolution_clock::time_point start_time; // Start time measurement - int64_t* p; // Pointer to elapsed time storage -}; -#endif \ No newline at end of file diff --git a/old/common/emu_opcodes.cpp b/old/common/emu_opcodes.cpp new file mode 100644 index 0000000..a07135e --- /dev/null +++ b/old/common/emu_opcodes.cpp @@ -0,0 +1,39 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ + +#include "emu_opcodes.h" + +const char *OpcodeNames[_maxEmuOpcode+1] = { + "OP_Unknown", + +//a preprocessor hack so we dont have to maintain two lists +#define N(x) #x +#if !defined(LOGIN) + #include "emu_oplist.h" +#endif +#ifdef LOGIN + #include "login_oplist.h" +#endif +#undef N + + "" +}; + + diff --git a/old/common/emu_opcodes.h b/old/common/emu_opcodes.h new file mode 100644 index 0000000..9011de1 --- /dev/null +++ b/old/common/emu_opcodes.h @@ -0,0 +1,56 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef EMU_OPCODES_H +#define EMU_OPCODES_H + +//this is the highest opcode possibly used in the regular EQ protocol +#define MAX_EQ_OPCODE 0xFFFF +/* + + +the list of opcodes is in emu_oplist.h + +we somewhat rely on the fact that we have more than 255 opcodes, +so we know the enum type for the opcode defines must be at least +16 bits, so we can use the protocol flags on them. + +*/ + +typedef enum { //EQEmu internal opcodes list + OP_Unknown=0, + +//a preprocessor hack so we dont have to maintain two lists +#define N(x) x +#if !defined(LOGIN) + #include "emu_oplist.h" +#endif +#ifdef LOGIN + #include "login_oplist.h" +#endif +#undef N + + _maxEmuOpcode +} EmuOpcode; + +extern const char *OpcodeNames[_maxEmuOpcode+1]; + +#endif + + diff --git a/old/common/opcodes/emu_oplist.hpp b/old/common/emu_oplist.h similarity index 77% rename from old/common/opcodes/emu_oplist.hpp rename to old/common/emu_oplist.h index aa436a6..1784af6 100644 --- a/old/common/opcodes/emu_oplist.hpp +++ b/old/common/emu_oplist.h @@ -1,19 +1,25 @@ -// EQ2Emulator © 2007 EQ2EMulator Development Team, GPL v3+ License +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) -#pragma once + This file is part of EQ2Emulator. -/** - * Operation code definitions for EQ2Emulator network protocol - * This file contains all operation codes used for client-server communication - * Each entry uses the N() macro to define protocol message types - */ + EQ2Emulator 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. -// Login and authentication operations + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ N(OP_LoginReplyMsg), N(OP_LoginByNumRequestMsg), N(OP_WSLoginRequestMsg), - -// Server initialization and zone management N(OP_ESInitMsg), N(OP_ESReadyForClientsMsg), N(OP_CreateZoneInstanceMsg), @@ -22,71 +28,47 @@ N(OP_ZoneInstanceDestroyedMsg), N(OP_ExpectClientAsCharacterRequest), N(OP_ExpectClientAsCharacterReplyMs), N(OP_ZoneInfoMsg), - -// Character creation and loading N(OP_CreateCharacterRequestMsg), N(OP_DoneLoadingZoneResourcesMsg), N(OP_DoneSendingInitialEntitiesMsg), N(OP_DoneLoadingEntityResourcesMsg), N(OP_DoneLoadingUIResourcesMsg), - -// Game state updates N(OP_PredictionUpdateMsg), N(OP_RemoteCmdMsg), N(OP_SetRemoteCmdsMsg), N(OP_GameWorldTimeMsg), N(OP_MOTDMsg), N(OP_ZoneMOTDMsg), - -// Guild recruitment system N(OP_GuildRecruitingMemberInfo), N(OP_GuildRecruiting), N(OP_GuildRecruitingDetails), N(OP_GuildRecruitingImage), - -// Avatar lifecycle N(OP_AvatarCreatedMsg), N(OP_AvatarDestroyedMsg), - -// Camping and logout N(OP_RequestCampMsg), N(OP_MapRequest), N(OP_CampStartedMsg), N(OP_CampAbortedMsg), - -// Player queries and monitoring N(OP_WhoQueryRequestMsg), N(OP_WhoQueryReplyMsg), N(OP_MonitorReplyMsg), N(OP_MonitorCharacterListMsg), N(OP_MonitorCharacterListRequestMsg), - -// Command dispatching N(OP_ClientCmdMsg), N(OP_Lottery), N(OP_DispatchClientCmdMsg), N(OP_DispatchESMsg), - -// Target and opportunity updates N(OP_UpdateTargetMsg), N(OP_UpdateOpportunityMsg), N(OP_UpdateTargetLocMsg), - -// Character sheet and inventory updates N(OP_UpdateCharacterSheetMsg), N(OP_UpdateSpellBookMsg), N(OP_UpdateInventoryMsg), - -// Recipe and crafting system N(OP_UpdateRecipeBookMsg), N(OP_RequestRecipeDetailsMsg), N(OP_RecipeDetailsMsg), - -// Skills system N(OP_UpdateSkillBookMsg), N(OP_UpdateSkillsMsg), - -// Zone transitions and teleportation N(OP_ChangeZoneMsg), N(OP_ClientTeleportRequestMsg), N(OP_TeleportWithinZoneMsg), @@ -94,8 +76,6 @@ N(OP_TeleportWithinZoneNoReloadMsg), N(OP_MigrateClientToZoneRequestMsg), N(OP_MigrateClientToZoneReplyMsg), N(OP_ReadyToZoneMsg), - -// Group management //N(OP_AddClientToGroupMsg), //N(OP_AddGroupToGroupMsg), N(OP_RemoveClientFromGroupMsg), @@ -108,47 +88,31 @@ N(OP_GroupMemberRemovedMsg), N(OP_GroupRemovedFromGroupMsg), N(OP_GroupLeaderChangedMsg), N(OP_GroupSettingsChangedMsg), - -// Data synchronization N(OP_SendLatestRequestMsg), N(OP_ClearDataMsg), N(OP_SetSocialMsg), - -// Server status monitoring N(OP_ESStatusMsg), N(OP_ESZoneInstanceStatusMsg), N(OP_ZonesStatusRequestMsg), N(OP_ZonesStatusMsg), - -// Weather system N(OP_ESWeatherRequestMsg), N(OP_ESWeatherRequestEndMsg), //N(OP_WSWeatherUpdateMsg), - -// Dialog system N(OP_DialogSelectMsg), N(OP_DialogCloseMsg), - -// Spell effects and concentration N(OP_RemoveSpellEffectMsg), N(OP_RemoveConcentrationMsg), - -// Quest journal system N(OP_QuestJournalOpenMsg), N(OP_QuestJournalInspectMsg), //N(OP_SkillSlotMapping), N(OP_QuestJournalSetVisibleMsg), N(OP_QuestJournalWaypointMsg), - -// Guild management N(OP_CreateGuildRequestMsg), N(OP_CreateGuildReplyMsg), N(OP_GuildsayMsg), //N(OP_GuildKickMsg), N(OP_GuildUpdateMsg), N(OP_FellowshipExpMsg), - -// Consignment and trading N(OP_ConsignmentCloseStoreMsg), N(OP_ConsignItemRequestMsg), N(OP_ConsignItemResponseMsg), @@ -158,8 +122,6 @@ N(OP_QuestReward), //N(OP_PurchaseConsignmentResponseMsg), //N(OP_ProcessScriptMsg), //N(OP_ProcessWorkspaceMsg), - -// Housing system N(OP_HouseDeletedRemotelyMsg), N(OP_UpdateHouseDataMsg), N(OP_UpdateHouseAccessDataMsg), @@ -174,51 +136,33 @@ N(OP_CollectAllHouseItemsMsg), N(OP_RelinquishHouseMsg), N(OP_EnterHouseMsg), N(OP_ExitHouseMsg), - -// Object examination and placement N(OP_ExamineConsignmentRequestMsg), N(OP_MoveableObjectPlacementCriteri), N(OP_EnterMoveObjectModeMsg), N(OP_PositionMoveableObject), N(OP_CancelMoveObjectModeMsg), - -// Visual customization N(OP_ShaderCustomizationMsg), N(OP_ReplaceableSubMeshesMsg), N(OP_ExamineConsignmentResponseMsg), - -// House access control N(OP_HouseDefaultAccessSetMsg), N(OP_HouseAccessSetMsg), N(OP_HouseAccessRemoveMsg), N(OP_PayHouseUpkeepMsg), - -// UI customization and settings N(OP_TintWidgetsMsg), N(OP_UISettingsResponseMsg), N(OP_UIResetMsg), - -// Keymap management N(OP_KeymapLoadMsg), N(OP_KeymapNoneMsg), N(OP_KeymapDataMsg), N(OP_KeymapSaveMsg), - -// Spell commands N(OP_DispatchSpellCmdMsg), - -// House customization N(OP_HouseCustomizationScreenMsg), N(OP_CustomizationPurchaseRequestMs), N(OP_CustomizationSetRequestMsg), N(OP_CustomizationReplyMsg), - -// Entity interaction N(OP_EntityVerbsRequestMsg), N(OP_EntityVerbsReplyMsg), N(OP_EntityVerbsVerbMsg), - -// Chat system N(OP_ChatRelationshipUpdateMsg), N(OP_ChatCreateChannelMsg), N(OP_ChatJoinChannelMsg), @@ -231,27 +175,18 @@ N(OP_ChatToggleIgnoreMsg), N(OP_ChatSendFriendsMsg), N(OP_ChatSendIgnoresMsg), N(OP_ChatFiltersMsg), - -// Looting system N(OP_LootItemsRequestMsg), N(OP_StoppedLootingMsg), - -// Character positioning N(OP_SitMsg), N(OP_StandMsg), N(OP_SatMsg), N(OP_StoodMsg), - //N(OP_QuickbarAddMsg), - -// Group options and interface N(OP_DefaultGroupOptionsRequestMsg), N(OP_DefaultGroupOptionsMsg), N(OP_GroupOptionsMsg), N(OP_DisplayGroupOptionsScreenMsg), N(OP_DisplayInnVisitScreenMsg), - -// System debugging and monitoring N(OP_DumpSchedulerMsg), //N(OP_LSRequestPlayerDescMsg), N(OP_LSCheckAcctLockMsg), @@ -259,19 +194,13 @@ N(OP_WSAcctLockStatusMsg), N(OP_RequestHelpRepathMsg), N(OP_UpdateMotdMsg), N(OP_RequestTargetLocMsg), - -// Combat effects N(OP_PerformPlayerKnockbackMsg), N(OP_PerformCameraShakeMsg), - -// Skills and abilities N(OP_PopulateSkillMapsMsg), N(OP_CancelledFeignMsg), N(OP_SignalMsg), N(OP_SkillInfoRequest), N(OP_SkillInfoResponse), - -// Crafting interface N(OP_ShowCreateFromRecipeUIMsg), N(OP_CancelCreateFromRecipeMsg), N(OP_BeginItemCreationMsg), @@ -280,12 +209,8 @@ N(OP_ShowItemCreationProcessUIMsg), N(OP_UpdateItemCreationProcessUIMsg), N(OP_DisplayTSEventReactionMsg), N(OP_ShowRecipeBookMsg), - -// Knowledge base and help system N(OP_KnowledgebaseRequestMsg), N(OP_KnowledgebaseResponseMsg), - -// Customer service ticket system N(OP_CSTicketHeaderRequestMsg), N(OP_CSTicketInfoMsg), N(OP_CSTicketCommentRequestMsg), @@ -294,57 +219,35 @@ N(OP_CSTicketCreateMsg), N(OP_CSTicketAddCommentMsg), N(OP_CSTicketDeleteMsg), N(OP_CSTicketChangeNotificationMsg), - -// World data management N(OP_WorldDataUpdateMsg), N(OP_WorldDataChangeMsg), N(OP_KnownLanguagesMsg), - -// Logging system N(OP_LsRequestClientCrashLogMsg), N(OP_LsClientBaselogReplyMsg), N(OP_LsClientCrashlogReplyMsg), N(OP_LsClientAlertlogReplyMsg), N(OP_LsClientVerifylogReplyMsg), - -// Client control and prediction N(OP_ClientTeleportToLocationMsg), N(OP_UpdateClientPredFlagsMsg), N(OP_ChangeServerControlFlagMsg), - -// Customer service tools N(OP_CSToolsRequestMsg), N(OP_CSToolsResponseMsg), - -// Boat transport system N(OP_CreateBoatTransportsMsg), N(OP_PositionBoatTransportMsg), N(OP_MigrateBoatTransportMsg), N(OP_MigrateBoatTransportReplyMsg), - -// Debugging and examination N(OP_DisplayDebugNLLPointsMsg), N(OP_ExamineInfoRequestMsg), - -// Quickbar and macro management N(OP_QuickbarInitMsg), N(OP_QuickbarUpdateMsg), N(OP_MacroInitMsg), N(OP_MacroUpdateMsg), - -// Questionnaire and surveys N(OP_QuestionnaireMsg), - -// Character progression N(OP_LevelChangedMsg), N(OP_SpellGainedMsg), N(OP_EncounterBrokenMsg), - -// UI notifications N(OP_OnscreenMsgMsg), N(OP_DisplayWarningMsg), - -// Guild management extended N(OP_ModifyGuildMsg), N(OP_GuildEventMsg), N(OP_GuildEventAddMsg), @@ -356,119 +259,66 @@ N(OP_RequestGuildBankEventDetailsMs), N(OP_GuildBankUpdateMsg), N(OP_RewardPackMsg), N(OP_RenameGuildMsg), - -// Friend system and travel N(OP_ZoneToFriendRequestMsg), N(OP_ZoneToFriendReplyMsg), - -// Waypoint system N(OP_WaypointRequestMsg), N(OP_WaypointReplyMsg), N(OP_WaypointSelectMsg), N(OP_WaypointUpdateMsg), - -// Character name changes N(OP_CharNameChangedMsg), - -// Zone teleportation N(OP_ShowZoneTeleporterDestinations), N(OP_SelectZoneTeleporterDestinatio), - -// Localization N(OP_ReloadLocalizedTxtMsg), - -// Guild membership N(OP_RequestGuildMembershipMsg), N(OP_GuildMembershipResponseMsg), N(OP_LeaveGuildNotifyMsg), N(OP_JoinGuildNotifyMsg), N(OP_RequestGuildInfoMsg), N(OP_GuildBankEventListMsg), - -// Avatar and biography N(OP_AvatarUpdateMsg), N(OP_BioUpdateMsg), N(OP_InspectPlayerMsg), - -// Server management N(OP_WSServerLockMsg), N(OP_WSServerHideMsg), N(OP_LSServerLockMsg), - -// Customer service categories N(OP_CsCategoryRequestMsg), N(OP_CsCategoryResponseMsg), - -// Knowledge window N(OP_KnowledgeWindowSlotMappingMsg), - -// Player status flags N(OP_LFGUpdateMsg), N(OP_AFKUpdateMsg), N(OP_AnonUpdateMsg), - -// Zone management N(OP_UpdateActivePublicZonesMsg), N(OP_UnknownNpcMsg), - -// Promotional flags N(OP_PromoFlagsDetailsMsg), - -// Consignment view system N(OP_ConsignViewCreateMsg), N(OP_ConsignViewGetPageMsg), N(OP_ConsignViewReleaseMsg), N(OP_UpdateDebugRadiiMsg), N(OP_ConsignRemoveItemsMsg), - -// Reporting and raid management //N(OP_SnoopMsg), N(OP_ReportMsg), N(OP_UpdateRaidMsg), N(OP_ConsignViewSortMsg), - -// Title and flight systems N(OP_TitleUpdateMsg), N(OP_FlightPathsMsg), - -// Client state notifications N(OP_ClientFellMsg), N(OP_ClientInDeathRegionMsg), N(OP_CampClientMsg), - -// Customer service tool access N(OP_GetAvatarAccessRequestForCSToo), N(OP_CSToolAccessResponseMsg), - -// Guild deletion N(OP_DeleteGuildMsg), - -// Tracking system N(OP_TrackingUpdateMsg), N(OP_BeginTrackingMsg), N(OP_StopTrackingMsg), - -// Character advancement N(OP_AdvancementRequestMsg), - -// Map fog system N(OP_MapFogDataInitMsg), N(OP_MapFogDataUpdateMsg), - //N(OP_UpdateAvgFrameTimeMsg), - -// Group invitation management N(OP_CloseGroupInviteWindowMsg), N(OP_UpdateGroupMemberDataMsg), - -// World ping and movement N(OP_WorldPingMsg), N(OP_MoveLogUpdateMsg), - -// Quest offers N(OP_OfferQuestMsg), - -// Mail system //N(OP_MailGetHeadersMsg), N(OP_MailGetMessageMsg), N(OP_MailSendMessageMsg), @@ -479,24 +329,14 @@ N(OP_MailSendMessageReplyMsg), N(OP_MailCommitSendMessageMsg), N(OP_MailSendSystemMessageMsg), N(OP_MailRemoveAttachFromMailMsg), - -// Server shutdown and client idle N(OP_WorldShutdownUpdateMsg), N(OP_ClientIdleBeginMsg), N(OP_ClientIdleEndMsg), N(OP_DisplayMailScreenMsg), - -// Mentoring system N(OP_NotifyApprenticeStoppedMentori), - -// Client corruption and notifications N(OP_CorruptedClientMsg), N(OP_MailEventNotificationMsg), - -// Zone restart N(OP_RestartZoneMsg), - -// Character transfer system N(OP_CharTransferStartRequestMsg), N(OP_CharTransferStartReplyMsg), N(OP_CharTransferRequestMsg), @@ -509,12 +349,8 @@ N(OP_GetCharacterSerializedRequestM), N(OP_GetCharacterSerializedReplyMsg), N(OP_CreateCharFromCBBRequestMsg), N(OP_CreateCharFromCBBReplyMsg), - -// Housing data management N(OP_HousingDataChangedMsg), N(OP_HousingRestoreMsg), - -// Auction system N(OP_AuctionItem), N(OP_AuctionItemReply), N(OP_AuctionCoin), @@ -523,21 +359,13 @@ N(OP_AuctionCharacter), N(OP_AuctionCharacterReply), N(OP_AuctionCommitMsg), N(OP_AuctionAbortMsg), - -// Character validation and restrictions N(OP_CharTransferValidateRequestMsg), N(OP_CharTransferValidateReplyMsg), N(OP_CharacterLinkdeadMsg), N(OP_RaceRestrictionMsg), - -// Instance management N(OP_SetInstanceDisplayNameMsg), - -// EverQuest specific commands - Audio and visual N(OP_EqHearChatCmd), N(OP_EqDisplayTextCmd), - -// EverQuest specific commands - Ghost and widget management N(OP_EqCreateGhostCmd), N(OP_EqCreateWidgetCmd), N(OP_EqCreateSignWidgetCmd), @@ -545,20 +373,14 @@ N(OP_EqDestroyGhostCmd), N(OP_EqUpdateGhostCmd), N(OP_EqSetControlGhostCmd), N(OP_EqSetPOVGhostCmd), - -// EverQuest specific commands - Combat audio N(OP_EqHearCombatCmd), N(OP_EqHearSpellCastCmd), N(OP_EqHearSpellInterruptCmd), N(OP_EqHearSpellFizzleCmd), N(OP_EqHearConsiderCmd), - -// EverQuest specific commands - Class and UI management N(OP_EqUpdateSubClassesCmd), N(OP_EqCreateListBoxCmd), N(OP_EqSetDebugPathPointsCmd), - -// EverQuest specific commands - Emotes and audio N(OP_EqCannedEmoteCmd), N(OP_EqStateCmd), N(OP_EqPlaySoundCmd), @@ -566,108 +388,68 @@ N(OP_EqPlaySound3DCmd), N(OP_EqPlayVoiceCmd), N(OP_EqHearDrowningCmd), N(OP_EqHearDeathCmd), - -// EverQuest specific commands - Group and effects N(OP_EqGroupMemberRemovedCmd), N(OP_EqHearChainEffectCmd), N(OP_EqReceiveOfferCmd), N(OP_EqInspectPCResultsCmd), N(OP_EqDrawablePathGraphCmd), - -// EverQuest specific commands - Dialog system N(OP_EqDialogOpenCmd), N(OP_EqDialogCloseCmd), - -// EverQuest specific commands - Collections N(OP_EqCollectionUpdateCmd), N(OP_EqCollectionFilterCmd), N(OP_EqCollectionItemCmd), - -// EverQuest specific commands - Quest system N(OP_EqQuestJournalUpdateCmd), N(OP_EqQuestJournalReplyCmd), N(OP_EqQuestGroupCmd), - -// EverQuest specific commands - Commerce N(OP_EqUpdateMerchantCmd), N(OP_EqUpdateStoreCmd), N(OP_EqUpdatePlayerTradeCmd), - -// EverQuest specific commands - Navigation N(OP_EqHelpPathCmd), N(OP_EqHelpPathClearCmd), - -// EverQuest specific commands - Banking and examination N(OP_EqUpdateBankCmd), N(OP_EqExamineInfoCmd), N(OP_EqCloseWindowCmd), - -// EverQuest specific commands - Looting N(OP_EqUpdateLootCmd), N(OP_EqJunctionListCmd), - -// EverQuest specific commands - Death and resurrection N(OP_EqShowDeathWindowCmd), N(OP_EqDisplaySpellFailCmd), N(OP_EqSpellCastStartCmd), N(OP_EqSpellCastEndCmd), N(OP_EqResurrectedCmd), - -// EverQuest specific commands - UI windows N(OP_EqChoiceWinCmd), N(OP_EqSetDefaultVerbCmd), N(OP_EqInstructionWindowCmd), N(OP_EqInstructionWindowCloseCmd), N(OP_EqInstructionWindowGoalCmd), N(OP_EqInstructionWindowTaskCmd), - -// EverQuest specific commands - Game events and windows N(OP_EqEnableGameEventCmd), N(OP_EqShowWindowCmd), N(OP_EqEnableWindowCmd), N(OP_EqFlashWindowCmd), - -// EverQuest specific commands - Flavor and books N(OP_EqHearPlayFlavorCmd), N(OP_EqUpdateSignWidgetCmd), N(OP_EqDebugPVDCmd), N(OP_EqShowBookCmd), - -// EverQuest specific commands - Questionnaire and healing N(OP_EqQuestionnaireCmd), N(OP_EqGetProbsCmd), N(OP_EqHearHealCmd), - -// EverQuest specific commands - Chat channels N(OP_EqChatChannelUpdateCmd), N(OP_EqWhoChannelQueryReplyCmd), N(OP_EqAvailWorldChannelsCmd), - -// Arena system N(OP_ArenaGameTypesMsg), - -// EverQuest specific commands - Target and consignment N(OP_EqUpdateTargetCmd), N(OP_EqConsignmentItemsCmd), N(OP_EqStartBrokerCmd), - -// EverQuest specific commands - Map and mail N(OP_EqMapExplorationCmd), N(OP_EqStoreLogCmd), N(OP_EqSpellMoveToRangeAndRetryCmd), N(OP_EqUpdatePlayerMailCmd), - -// EverQuest specific commands - Faction updates N(OP_EqFactionUpdateCmd), - -// Position and combat updates N(OP_UpdateTitleCmd), N(OP_UpdatePositionMsg), N(OP_AttackNotAllowed), N(OP_AttackAllowed), N(OP_CancelSpellCast), - -// Miscellaneous systems N(OP_BadLanguageFilter), N(OP_DressingRoom), N(OP_TraitsList), @@ -701,8 +483,6 @@ N(OP_MarketAddFundsRequest), N(OP_ZoneBgInstanceList), N(OP_UIEvent), N(OP_Launchpad), - -// Additional EverQuest commands N(OP_EQHearThreatCmd), N(OP_EqHearSpellNoLandCmd), N(OP_Weakness), diff --git a/old/common/eq_common_structs.hpp b/old/common/eq_common_structs.hpp deleted file mode 100644 index bdcf69c..0000000 --- a/old/common/eq_common_structs.hpp +++ /dev/null @@ -1,387 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GNU GPL v3 - -#pragma once - -#include - -// Equipment slot indices for character gear -#define SPAWN_PACKET_SIZE 895 -#define EQUIPMENT_L_WEAPON_INDEX 0 // Character's left hand weapon -#define EQUIPMENT_R_WEAPON_INDEX 1 // Character's right hand weapon -#define EQUIPMENT_HELMET 2 // Character's helmet slot - -#pragma pack(1) - -// Key generation structure for authentication -struct KeyGen_Struct -{ - int32_t size; // Size of the key data -}; - -// End structure for key generation process -struct KeyGen_End_Struct -{ - int32_t exponent_len; // Length of the exponent - int8_t exponent; // Exponent value for key calculation -}; - -// Login request structure for account authentication -struct LoginByNumRequest_Struct -{ - int32_t account_id; // Account identifier - int32_t access_code; // Access code for authentication - int16_t version; // Client version - int32_t unknown2[5]; // Reserved fields -}; - -// Login server response structure -struct LS_LoginResponse -{ - int8_t reply_code; // 0 = granted, 1 = denied - int16_t unknown01; // Reserved field - int8_t unknown02; // Reserved field - int32_t unknown03; // -1 = denied, 0 = granted - int32_t unknown04; // Reserved field - int32_t unknown05; // Reserved field - int32_t unknown06; // Reserved field - int8_t unknown07; // Reserved field - int8_t unknown08; // Reserved field - int8_t unknown09; // Reserved field - int8_t unknown10; // Reserved field - int32_t unknown11; // Reserved field - int32_t accountid; // Account identifier - int16_t unknown12; // Reserved field -}; - -#pragma pack() - -// Equipment slot enumeration for character gear placement -enum EQ2_EquipmentSlot { - slot_primary = 0, // Primary weapon slot - slot_secondary = 1, // Secondary weapon slot - slot_head = 2, // Head/helmet slot - slot_chest = 3, // Chest armor slot - slot_shoulders = 4, // Shoulder armor slot - slot_forearms = 5, // Forearm armor slot - slot_hands = 6, // Hand/glove armor slot - slot_legs = 7, // Leg armor slot - slot_feet = 8, // Foot armor slot - slot_left_ring = 9, // Left ring slot - slot_right_ring = 10, // Right ring slot - slot_ears = 11, // Earring slot - slot_neck = 12, // Necklace slot - slot_left_wrist = 13, // Left wrist slot - slot_right_wrist = 14, // Right wrist slot - slot_ranged = 15, // Ranged weapon slot - slot_ammo = 16, // Ammunition slot - slot_waist = 17, // Belt/waist slot - slot_activate1 = 18, // First activatable item slot - slot_activate2 = 19, // Second activatable item slot - slot_textures = 20, // Texture overlay slot - slot_hair = 21, // Hair style slot - slot_beard = 22, // Beard style slot - slot_naked_chest = 23, // Naked chest appearance slot - slot_naked_legs = 24 // Naked legs appearance slot -}; - -// Individual equipment item with visual properties -struct EQ2_EquipmentItem -{ - int16_t type; // Item type identifier - EQ2_Color color; // Primary color of the item - EQ2_Color highlight; // Highlight color of the item -}; - -// Complete equipment set for a character -struct EQ2_Equipment -{ - int16_t equip_id[25]; // Equipment item IDs for each slot - EQ2_Color color[25]; // Primary colors for each equipment slot - EQ2_Color highlight[25]; // Highlight colors for each equipment slot -}; - -#pragma pack(1) - -// Character appearance features and customization options -struct CharFeatures -{ - int16_t hair_type; // Hair style type - int16_t hair_face_type; // Facial hair type - int16_t wing_type; // Wing type (for races with wings) - int16_t chest_type; // Chest appearance type - int16_t legs_type; // Leg appearance type - int8_t eye_type[3]; // Eye shape variations - int8_t ear_type[3]; // Ear shape variations - int8_t eye_brow_type[3]; // Eyebrow shape variations - int8_t cheek_type[3]; // Cheek shape variations - int8_t lip_type[3]; // Lip shape variations - int8_t chin_type[3]; // Chin shape variations - int8_t nose_type[3]; // Nose shape variations - int8_t body_size; // Body size modifier - int8_t body_age; // Body age appearance - int8_t soga_eye_type[3]; // SOGA eye shape variations - int8_t soga_ear_type[3]; // SOGA ear shape variations - int8_t soga_eye_brow_type[3]; // SOGA eyebrow shape variations - int8_t soga_cheek_type[3]; // SOGA cheek shape variations - int16_t soga_chest_type; // SOGA chest appearance type - int16_t soga_legs_type; // SOGA leg appearance type - int8_t soga_lip_type[3]; // SOGA lip shape variations - int8_t soga_chin_type[3]; // SOGA chin shape variations - int8_t soga_nose_type[3]; // SOGA nose shape variations - int8_t soga_body_size; // SOGA body size modifier - int8_t soga_body_age; // SOGA body age appearance - int16_t soga_hair_type; // SOGA hair style type - int16_t soga_hair_face_type; // SOGA facial hair type - int16_t combat_voice; // Combat voice type - int16_t emote_voice; // Emote voice type - int16_t mount_model_type; // Mount model type - - // Color customization for various character features - EQ2_Color mount_saddle_color; // Mount saddle color - EQ2_Color mount_color; // Mount body color - EQ2_Color skin_color; // Skin color - EQ2_Color eye_color; // Eye color - EQ2_Color hair_type_color; // Primary hair color - EQ2_Color hair_type_highlight_color; // Hair highlight color - EQ2_Color hair_face_color; // Facial hair color - EQ2_Color hair_face_highlight_color; // Facial hair highlight color - EQ2_Color hair_highlight_color; // Hair highlight color - EQ2_Color wing_color1; // Wing primary color - EQ2_Color wing_color2; // Wing secondary color - EQ2_Color shirt_color; // Shirt color - EQ2_Color pants_color; // Pants color - EQ2_Color hair_color1; // Hair primary color - EQ2_Color hair_color2; // Hair secondary color - EQ2_Color soga_skin_color; // SOGA skin color - EQ2_Color soga_eye_color; // SOGA eye color - EQ2_Color soga_hair_color1; // SOGA hair primary color - EQ2_Color soga_hair_color2; // SOGA hair secondary color - EQ2_Color soga_hair_type_color; // SOGA primary hair color - EQ2_Color soga_hair_type_highlight_color; // SOGA hair highlight color - EQ2_Color soga_hair_face_color; // SOGA facial hair color - EQ2_Color soga_hair_face_highlight_color; // SOGA facial hair highlight color - EQ2_Color soga_hair_highlight_color; // SOGA hair highlight color - EQ2_Color model_color; // Model color - EQ2_Color soga_model_color; // SOGA model color -}; - -// Position and movement data for entities -struct PositionData -{ - int32_t grid_id; // Current grid identifier - int32_t bad_grid_id; // Invalid grid identifier - int8_t Speed1; // Primary speed value - int8_t Speed2; // Secondary speed value - int16_t Dir1; // Primary direction - int16_t Dir2; // Secondary direction - int16_t Pitch1; // Primary pitch angle - int16_t Pitch2; // Secondary pitch angle - int16_t Roll; // Roll angle - float X; // X coordinate - float Y; // Y coordinate - float Z; // Z coordinate - float X2; // Secondary X coordinate - float Y2; // Secondary Y coordinate - float Z2; // Secondary Z coordinate - float X3; // Tertiary X coordinate - float Y3; // Tertiary Y coordinate - float Z3; // Tertiary Z coordinate - float SpawnOrigX; // Original spawn X coordinate - float SpawnOrigY; // Original spawn Y coordinate - float SpawnOrigZ; // Original spawn Z coordinate - float SpawnOrigHeading; // Original spawn heading - float SpawnOrigPitch; // Original spawn pitch - float SpawnOrigRoll; // Original spawn roll - float SpeedX; // X-axis speed - float SpeedY; // Y-axis speed - float SpeedZ; // Z-axis speed - float SideSpeed; // Lateral movement speed - float VertSpeed; // Vertical movement speed - float ClientHeading1; // Client-side heading (primary) - float ClientHeading2; // Client-side heading (secondary) - float ClientPitch; // Client-side pitch - int16_t collision_radius; // Collision detection radius - int16_t state; // Current movement state -}; - -// Visual appearance and display properties for entities -struct AppearanceData -{ - PositionData pos; // Position and movement data - int16_t model_type; // 3D model type identifier - int16_t soga_model_type; // SOGA model type identifier - int16_t activity_status; // Current activity status - int16_t visual_state; // Visual state flags - int16_t action_state; // Action state flags - int16_t mood_state; // Mood/emotion state - int16_t emote_state; // Current emote state - int8_t attackable; // Whether entity can be attacked - int8_t icon; // Icon type to display - int8_t hide_hood; // Whether to hide hood graphics - int8_t show_level; // Whether to show level - - int8_t locked_no_loot; // Locked status with no loot - int8_t quest_flag; // Quest-related flag - int8_t heroic_flag; // Heroic opportunity flag - int8_t show_command_icon; // Whether to show command icon - int8_t display_hand_icon; // Whether to show hand cursor icon - int8_t player_flag; // Player character flag - int8_t targetable; // Whether entity can be targeted - int8_t display_name; // Whether to display name - char sub_title[255]; // Subtitle text (Guild name) - int32_t display_hp; // Health percentage (0 = 100%) - int32_t power_left; // Power remaining (bar hidden if >=100) - int8_t adventure_class; // Adventure class identifier - int8_t tradeskill_class; // Tradeskill class identifier - int8_t level; // Character level - int8_t tradeskill_level; // Tradeskill level - int8_t min_level; // Minimum level for encounters - int8_t max_level; // Maximum level for encounters - int8_t difficulty; // Encounter difficulty rating - int16_t visible; // Visibility state (02 = normal, 15 = shadow) - char name[128]; // Entity name - char last_name[64]; // Last name (for players) - char prefix_title[128]; // Title prefix - char suffix_title[128]; // Title suffix - int8_t race; // Race identifier - int8_t gender; // Gender identifier - int32_t randomize; // Randomization seed - int8_t lua_race_id; // Lua script race identifier -}; - -// Player movement update packet structure -struct Player_Update -{ -/*0000*/ int32_t activity; // Activity state -/*0004*/ float unknown2; // Unknown value (typically 1) -/*0008*/ float direction1; // Primary direction -/*0012*/ float unknown3[8]; // Unknown array of values -/*0044*/ float speed; // Movement speed -/*0048*/ float side_speed; // Lateral movement speed -/*0052*/ float vert_speed; // Vertical movement speed -/*0056*/ float orig_x; // Original X position -/*0060*/ float orig_y; // Original Y position -/*0064*/ float orig_z; // Original Z position -/*0068*/ float orig_x2; // Secondary original X position -/*0072*/ float orig_y2; // Secondary original Y position -/*0076*/ float orig_z2; // Secondary original Z position -/*0080*/ float unknown5[3]; // Unknown array of values -/*0092*/ int32_t unknown6; // Unknown value -/*0096*/ float unknown7[3]; // Unknown array of values -/*0108*/ int32_t unknown8; // Unknown value -/*0112*/ int32_t grid_location; // Grid location identifier -/*0116*/ float x; // Current X position -/*0120*/ float y; // Current Y position -/*0124*/ float z; // Current Z position -/*0128*/ float direction2; // Secondary direction -/*0132*/ float pitch; // Pitch angle -/*0136*/ float unknown10; // Unknown value -/*0140*/ float speed_x; // X-axis velocity -/*0144*/ float speed_y; // Y-axis velocity -/*0148*/ float speed_z; // Z-axis velocity -}; - -// Enhanced player movement update for version 283 -struct Player_Update283 -{ -/*0000*/ int32_t activity; // Activity state -/*0004*/ int32_t movement_mode; // Movement mode (typically 1) -/*0008*/ float direction1; // Primary direction -/*0012*/ float desiredpitch; // Desired pitch angle -/*0016*/ float desired_heading_speed; // Desired heading change speed -/*0020*/ float desired_pitch_speed; // Desired pitch change speed -/*0024*/ float collision_radius; // Collision detection radius -/*0028*/ float collision_scale; // Collision scale factor -/*0032*/ float temp_scale; // Temporary scale factor -/*0036*/ float speed_modifier; // Speed modification factor -/*0040*/ float swim_speed_modifier; // Swimming speed modifier -/*0044*/ float speed; // Movement speed -/*0048*/ float side_speed; // Lateral movement speed -/*0052*/ float vert_speed; // Vertical movement speed -/*0056*/ float orig_x; // Original X position -/*0060*/ float orig_y; // Original Y position -/*0064*/ float orig_z; // Original Z position -/*0068*/ float orig_x2; // Secondary original X position -/*0072*/ float orig_y2; // Secondary original Y position -/*0076*/ float orig_z2; // Secondary original Z position -/*0080*/ int32_t face_actor_id; // ID of actor to face -/*0084*/ int32_t face_actor_range; // Range for facing actor -/*0088*/ int32_t grid_location; // Grid location identifier -/*0092*/ float x; // Current X position -/*0096*/ float y; // Current Y position -/*0100*/ float z; // Current Z position -/*0104*/ float direction2; // Secondary direction -/*0108*/ float pitch; // Pitch angle -/*0112*/ float roll; // Roll angle -/*0116*/ float speed_x; // X-axis velocity -/*0120*/ float speed_y; // Y-axis velocity -/*0124*/ float speed_z; // Z-axis velocity -}; // Total size: 0128 - -// Player movement update for version 1096 -struct Player_Update1096 -{ -/*0000*/ int32_t activity; // Activity state -/*0004*/ float unknown2; // Unknown value (typically 1) -/*0008*/ float direction1; // Primary direction -/*0012*/ float unknown3[8]; // Unknown array of values -/*0044*/ float unk_speed; // Unknown speed value -/*0048*/ float speed; // Movement speed -/*0052*/ float side_speed; // Lateral movement speed -/*0056*/ float vert_speed; // Vertical movement speed -/*0060*/ float orig_x; // Original X position -/*0064*/ float orig_y; // Original Y position -/*0068*/ float orig_z; // Original Z position -/*0072*/ float orig_x2; // Secondary original X position -/*0076*/ float orig_y2; // Secondary original Y position -/*0080*/ float orig_z2; // Secondary original Z position -/*0092*/ float unknown5[3]; // Unknown array of values -/*0096*/ int32_t unknown6; // Unknown value -/*0108*/ float unknown7[3]; // Unknown array of values -/*0112*/ int32_t unknown8; // Unknown value -/*0116*/ int32_t grid_location; // Grid location identifier -/*0120*/ float x; // Current X position -/*0124*/ float y; // Current Y position -/*0128*/ float z; // Current Z position -/*0132*/ float direction2; // Secondary direction -/*0136*/ float pitch; // Pitch angle -/*0140*/ float unknown10; // Unknown value -/*0144*/ float speed_x; // X-axis velocity -/*0148*/ float speed_y; // Y-axis velocity -/*0152*/ float speed_z; // Z-axis velocity -}; - -// Player movement update for version 1144 -struct Player_Update1144 -{ -/*0000*/ int32_t activity; // Activity state -/*0004*/ float unknown2; // Unknown value (typically 1) -/*0008*/ float direction1; // Primary direction -/*0012*/ float unknown3[12]; // Extended unknown array of values -/*0044*/ float unk_speed; // Unknown speed value -/*0048*/ float speed; // Movement speed -/*0052*/ float side_speed; // Lateral movement speed -/*0056*/ float vert_speed; // Vertical movement speed -/*0060*/ float orig_x; // Original X position -/*0064*/ float orig_y; // Original Y position -/*0068*/ float orig_z; // Original Z position -/*0072*/ float orig_x2; // Secondary original X position -/*0076*/ float orig_y2; // Secondary original Y position -/*0080*/ float orig_z2; // Secondary original Z position -/*0092*/ float unknown5[3]; // Unknown array of values -/*0096*/ int32_t unknown6; // Unknown value -/*0108*/ float unknown7[3]; // Unknown array of values -/*0112*/ int32_t unknown8; // Unknown value -/*0116*/ int32_t grid_location; // Grid location identifier -/*0120*/ float x; // Current X position -/*0124*/ float y; // Current Y position -/*0128*/ float z; // Current Z position -/*0132*/ float direction2; // Secondary direction -/*0136*/ float pitch; // Pitch angle -/*0140*/ float unknown10; // Unknown value -/*0144*/ float speed_x; // X-axis velocity -/*0148*/ float speed_y; // Y-axis velocity -/*0152*/ float speed_z; // Z-axis velocity -}; - -#pragma pack() \ No newline at end of file diff --git a/old/common/eq_emu_error.hpp b/old/common/eq_emu_error.hpp deleted file mode 100644 index d76058a..0000000 --- a/old/common/eq_emu_error.hpp +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team, GPL v3 - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "types.hpp" - -enum eEQEMuError -{ - EQEMuError_NoError, - EQEMuError_Mysql_1405, - EQEMuError_Mysql_2003, - EQEMuError_Mysql_2005, - EQEMuError_Mysql_2007, - EQEMuError_MaxErrorID -}; - -// Error message lookup table for predefined error codes -const char* EQEMuErrorText[EQEMuError_MaxErrorID] = { - "ErrorID# 0, No Error", - "MySQL Error #1405 or #2001 means your mysql server rejected the username and password you presented it.", - "MySQL Error #2003 means you were unable to connect to the mysql server.", - "MySQL Error #2005 means you there are too many connections on the mysql server. The server is overloaded.", - "MySQL Error #2007 means you the server is out of memory. The server is overloaded.", -}; - -// Error storage structure to handle both enum and string errors -struct ErrorEntry -{ - bool is_enum_error; // True if this is an enum error, false for string error - int32 error_id; // Used only for enum errors - std::string message; // Used for string errors or can store enum converted to string - - ErrorEntry(eEQEMuError error) : is_enum_error(true), error_id(error) {} - ErrorEntry(const std::string& msg) : is_enum_error(false), error_id(0), message(msg) {} -}; - -// Global error list and mutex for thread-safe error collection -std::vector g_error_list; -std::mutex g_error_mutex; - -// Forward declaration for signal handling -void CatchSignal(int sig_num); - -// Retrieves the error text for a given error ID -// Returns the corresponding error message or out of range message -const char* GetErrorText(int32 iError) -{ - if (iError >= EQEMuError_MaxErrorID) - return "ErrorID# out of range"; - else - return EQEMuErrorText[iError]; -} - -// Adds an enumerated error to the error list with optional immediate exit -// Checks for duplicate enum errors before adding to prevent spam -void AddEQEMuError(eEQEMuError iError, bool iExitNow = false) -{ - if (!iError) - return; - - std::lock_guard lock(g_error_mutex); - - // Check for duplicate enum errors - auto it = std::find_if(g_error_list.begin(), g_error_list.end(), - [iError](const ErrorEntry& entry) { - return entry.is_enum_error && entry.error_id == iError; - }); - - if (it != g_error_list.end()) - return; // Duplicate found, don't add - - g_error_list.emplace_back(iError); - - if (iExitNow) - CatchSignal(2); -} - -// Adds a string-based error to the error list with optional immediate exit -// String errors are not deduplicated as they may contain unique information -void AddEQEMuError(const char* iError, bool iExitNow = false) -{ - if (!iError) - return; - - std::lock_guard lock(g_error_mutex); - g_error_list.emplace_back(std::string(iError)); - - if (iExitNow) - CatchSignal(2); -} - -// Checks and displays all accumulated errors, then clears the error list -// Returns the number of errors that were found and displayed -int32 CheckEQEMuError() -{ - std::lock_guard lock(g_error_mutex); - - if (g_error_list.empty()) - return 0; - - bool header_printed = false; - int32 error_count = 0; - - for (const auto& entry : g_error_list) { - if (!header_printed) { - std::fprintf(stdout, "===============================\nRuntime errors:\n\n"); - header_printed = true; - } - - if (entry.is_enum_error) { - std::fprintf(stdout, "%s\n", GetErrorText(entry.error_id)); - } else { - std::fprintf(stdout, "%s\n\n", entry.message.c_str()); - } - error_count++; - } - - g_error_list.clear(); - return error_count; -} - -// Checks for errors and pauses for user input if any errors were found -// Useful for keeping console windows open to display error messages -void CheckEQEMuErrorAndPause() -{ - if (CheckEQEMuError()) { - std::fprintf(stdout, "Hit any key to exit\n"); - std::getchar(); - } -} \ No newline at end of file diff --git a/old/common/global_headers.hpp b/old/common/global_headers.hpp deleted file mode 100644 index 3552eb5..0000000 --- a/old/common/global_headers.hpp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GNU GPL v3+ - http://www.eq2emulator.net - -#pragma once - -// Character creation reply codes - returned by character creation validation -constexpr int UNKNOWNERROR_REPLY = 0; // Unknown error occurred during creation -constexpr int CREATESUCCESS_REPLY = 1; // Character created successfully -constexpr int NOSERVERSAVAIL_REPLY = 2; // No servers available for character creation -constexpr int CREATEPENDING_REPLY = 3; // Character creation is pending -constexpr int MAXCHARSALLOWED_REPLY = 4; // Maximum characters allowed on account reached -constexpr int INVALIDRACE_REPLY = 5; // Invalid race selected for character -constexpr int INVALIDCITY_REPLY = 6; // Invalid starting city selected -constexpr int INVALIDCLASS_REPLY = 7; // Invalid class selected for character -constexpr int INVALIDGENDER_REPLY = 8; // Invalid gender selection -constexpr int INVALIDFIRST_LVL_REPLY = 9; // Invalid first level configuration -constexpr int BADNAMELENGTH_REPLY = 10; // Character name length invalid -constexpr int NAMEINVALID_REPLY = 11; // Character name contains invalid characters -constexpr int NAMEFILTER_REPLY = 12; // Name contains filtered words or blocked content -constexpr int NAMETAKEN_REPLY = 13; // Character name already taken -constexpr int OVERLOADEDSERVER_REPLY = 14; // Server overloaded, cannot create character -constexpr int UNKNOWNERROR_REPLY2 = 15; // Secondary unknown error code -constexpr int INVALIDFEATURES1_REPLY = 16; // Invalid character features (primary) -constexpr int INVALIDFEATURES2_REPLY = 17; // Invalid character features (secondary) -constexpr int INVALIDRACE_APPEARANCE_REPLY = 18; // Invalid race appearance combination - -// Play error codes - returned when attempting to enter game world -constexpr int PLAY_ERROR_PROBLEM = 0; // General problem entering game -constexpr int PLAY_ERROR_ZONE_DOWN = 4; // Target zone server is down -constexpr int PLAY_ERROR_CHAR_NOT_LOADED = 5; // Character data not loaded -constexpr int PLAY_ERROR_CHAR_NOT_FOUND = 6; // Character not found in database -constexpr int PLAY_ERROR_ACCOUNT_IN_USE = 7; // Account already logged in elsewhere -constexpr int PLAY_ERROR_SERVER_TIMEOUT = 8; // Server connection timeout -constexpr int PLAY_ERROR_SERVER_SHUTDOWN = 9; // Server shutting down -constexpr int PLAY_ERROR_LOADING_ERROR = 10; // Error loading character data -constexpr int PLAY_ERROR_EXCHANGE_SERVER = 11; // Exchange server unavailable -constexpr int PLAY_ERROR_REGION_SERVER = 12; // Region server unavailable -constexpr int PLAY_ERROR_CLASS_INVALID = 13; // Character class validation failed -constexpr int PLAY_ERROR_TOO_MANY_CHARACTERS = 14; // Too many characters on server -constexpr int PLAY_ERROR_EOF_EXP_NOT_FOUND = 15; // Expansion data not found -constexpr int PLAY_ERROR_UNKNOWN_RESPONSE = 16; // Unknown response from server -constexpr int PLAY_ERROR_UNKNOWN = 17; // Unknown play error -constexpr int PLAY_ERROR_ACCOUNT_BANNED = 18; // Account has been banned -constexpr int PLAY_ERROR_PROHIBITED = 19; // Action prohibited \ No newline at end of file diff --git a/old/common/json_parser.hpp b/old/common/json_parser.hpp deleted file mode 100644 index d901b7d..0000000 --- a/old/common/json_parser.hpp +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright (c) 2024 MIT License All rights reserved - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class JsonParser -{ -public: - // Constructor that loads and parses JSON from specified file - explicit JsonParser(const std::string_view filename); - - // Retrieves value for given dot-separated path, returns empty string if not found - std::string getValue(const std::string_view path) const - { - std::string lower_path{path}; - std::transform(lower_path.begin(), lower_path.end(), lower_path.begin(), ::tolower); - - if (auto it = values.find(lower_path); it != values.end()) { - return it->second; - } - return ""; - } - - // Converts string to unsigned char with range validation - static bool convertStringToUnsignedChar(const std::string_view str, unsigned char& result); - - // Converts string to unsigned short with range validation - static bool convertStringToUnsignedShort(const std::string_view str, unsigned short& result); - - // Converts string to unsigned int with range validation - static bool convertStringToUnsignedInt(const std::string_view str, unsigned int& result); - - // Converts string to unsigned long with range validation - static bool convertStringToUnsignedLong(const std::string_view str, unsigned long& result); - - // Returns whether the JSON file was successfully loaded and parsed - constexpr bool IsLoaded() const noexcept { return is_loaded; } - -private: - std::map values; // Flat map of dot-separated paths to string values - bool is_loaded{false}; // Success flag for file loading and parsing - - // Recursively parses JSON object and builds flat key-value map with dot notation - void parseObject(const std::string& json, size_t& pos, const std::string& path); - - // Skips whitespace characters in JSON string starting from given position - size_t skipWhitespace(const std::string& json, size_t pos) const noexcept; - - // Parses JSON string literal and returns unescaped content - std::string parseString(const std::string& json, size_t& pos); - - // Parses JSON number and returns string representation - std::string parseNumber(const std::string& json, size_t& pos); - - // Parses JSON value (string, number, object) and stores in values map - void parseValue(const std::string& json, size_t& pos, const std::string& key); - - // Helper template for string to unsigned integer conversion with type safety - template - static bool convertStringToUnsigned(const std::string_view str, T& result); -}; - -// Constructor implementation - loads and parses JSON file -JsonParser::JsonParser(const std::string_view filename) -{ - std::ifstream file{std::string{filename}}; - if (!file.is_open()) { - std::cerr << "Error: Cannot open JSON file: " << filename << std::endl; - return; - } - - std::ostringstream buffer; - buffer << file.rdbuf(); - std::string json_content = buffer.str(); - - try { - size_t pos = 0; - parseObject(json_content, pos, ""); - is_loaded = true; - } catch (const std::exception& e) { - std::cerr << "Error parsing JSON: " << e.what() << std::endl; - } -} - -// Template implementation for safe string to unsigned conversion -template -bool JsonParser::convertStringToUnsigned(const std::string_view str, T& result) -{ - try { - unsigned long ul = std::stoul(std::string{str}); - if (ul > std::numeric_limits::max()) { - return false; - } - result = static_cast(ul); - return true; - } catch (const std::invalid_argument&) { - return false; - } catch (const std::out_of_range&) { - return false; - } -} - -// Converts string to unsigned char with validation -bool JsonParser::convertStringToUnsignedChar(const std::string_view str, unsigned char& result) -{ - return convertStringToUnsigned(str, result); -} - -// Converts string to unsigned short with validation -bool JsonParser::convertStringToUnsignedShort(const std::string_view str, unsigned short& result) -{ - return convertStringToUnsigned(str, result); -} - -// Converts string to unsigned int with validation -bool JsonParser::convertStringToUnsignedInt(const std::string_view str, unsigned int& result) -{ - return convertStringToUnsigned(str, result); -} - -// Converts string to unsigned long with validation -bool JsonParser::convertStringToUnsignedLong(const std::string_view str, unsigned long& result) -{ - return convertStringToUnsigned(str, result); -} - -// Skips whitespace characters and returns next non-whitespace position -size_t JsonParser::skipWhitespace(const std::string& json, size_t pos) const noexcept -{ - while (pos < json.length() && std::isspace(json[pos])) { - ++pos; - } - return pos; -} - -// Parses JSON string literal, handling escape sequences -std::string JsonParser::parseString(const std::string& json, size_t& pos) -{ - if (json[pos] != '"') { - throw std::runtime_error("Expected '\"' at start of string"); - } - ++pos; // Skip opening quote - - std::string result; - while (pos < json.length() && json[pos] != '"') { - if (json[pos] == '\\') { - ++pos; - if (pos >= json.length()) { - throw std::runtime_error("Unterminated escape sequence"); - } - // Handle basic escape sequences - switch (json[pos]) { - case '"': result += '"'; break; - case '\\': result += '\\'; break; - case '/': result += '/'; break; - case 'b': result += '\b'; break; - case 'f': result += '\f'; break; - case 'n': result += '\n'; break; - case 'r': result += '\r'; break; - case 't': result += '\t'; break; - default: result += json[pos]; break; - } - } else { - result += json[pos]; - } - ++pos; - } - - if (pos >= json.length() || json[pos] != '"') { - throw std::runtime_error("Unterminated string"); - } - ++pos; // Skip closing quote - return result; -} - -// Parses JSON number and returns string representation -std::string JsonParser::parseNumber(const std::string& json, size_t& pos) -{ - size_t start = pos; - - // Handle optional minus sign - if (json[pos] == '-') { - ++pos; - } - - // Parse digits - while (pos < json.length() && std::isdigit(json[pos])) { - ++pos; - } - - // Handle decimal point and fractional part - if (pos < json.length() && json[pos] == '.') { - ++pos; - while (pos < json.length() && std::isdigit(json[pos])) { - ++pos; - } - } - - // Handle exponent - if (pos < json.length() && (json[pos] == 'e' || json[pos] == 'E')) { - ++pos; - if (pos < json.length() && (json[pos] == '+' || json[pos] == '-')) { - ++pos; - } - while (pos < json.length() && std::isdigit(json[pos])) { - ++pos; - } - } - - return json.substr(start, pos - start); -} - -// Parses JSON value and stores it in the values map -void JsonParser::parseValue(const std::string& json, size_t& pos, const std::string& key) -{ - pos = skipWhitespace(json, pos); - - if (pos >= json.length()) { - throw std::runtime_error("Unexpected end of JSON"); - } - - if (json[pos] == '"') { - // String value - store directly - std::string value = parseString(json, pos); - std::string lower_key = key; - std::transform(lower_key.begin(), lower_key.end(), lower_key.begin(), ::tolower); - values[lower_key] = value; - } else if (json[pos] == '{') { - // Nested object - recurse with current key as path prefix - parseObject(json, pos, key); - } else if (std::isdigit(json[pos]) || json[pos] == '-') { - // Number value - store as string - std::string value = parseNumber(json, pos); - std::string lower_key = key; - std::transform(lower_key.begin(), lower_key.end(), lower_key.begin(), ::tolower); - values[lower_key] = value; - } else { - // Skip other JSON types (true, false, null, arrays) - while (pos < json.length() && json[pos] != ',' && json[pos] != '}') { - ++pos; - } - } -} - -// Recursively parses JSON object and builds flat key-value structure -void JsonParser::parseObject(const std::string& json, size_t& pos, const std::string& path) -{ - pos = skipWhitespace(json, pos); - - if (pos >= json.length() || json[pos] != '{') { - throw std::runtime_error("Expected '{' at start of object"); - } - ++pos; // Skip opening brace - - pos = skipWhitespace(json, pos); - - // Handle empty object - if (pos < json.length() && json[pos] == '}') { - ++pos; - return; - } - - while (pos < json.length()) { - pos = skipWhitespace(json, pos); - - // Parse key - if (json[pos] != '"') { - throw std::runtime_error("Expected string key in object"); - } - std::string key = parseString(json, pos); - - pos = skipWhitespace(json, pos); - - // Expect colon - if (pos >= json.length() || json[pos] != ':') { - throw std::runtime_error("Expected ':' after object key"); - } - ++pos; - - // Build full path with dot notation - std::string full_key = path.empty() ? key : path + "." + key; - - // Parse value - parseValue(json, pos, full_key); - - pos = skipWhitespace(json, pos); - - // Check for continuation or end - if (pos < json.length() && json[pos] == ',') { - ++pos; // Skip comma - continue; - } else if (pos < json.length() && json[pos] == '}') { - ++pos; // Skip closing brace - break; - } else { - throw std::runtime_error("Expected ',' or '}' in object"); - } - } -} \ No newline at end of file diff --git a/old/common/linked_list.h b/old/common/linked_list.h new file mode 100644 index 0000000..023a9d2 --- /dev/null +++ b/old/common/linked_list.h @@ -0,0 +1,445 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef LINKEDLIST_H +#define LINKEDLIST_H + +#include "types.h" + +enum direction{FORWARD,BACKWARD}; + +template class LinkedListIterator; + +template +class ListElement +{ +private: + + TYPE data; + ListElement* next; + ListElement* prev; +public: + ListElement (); + ListElement (const TYPE&); + ListElement (const ListElement&); + + ~ListElement (); + + ListElement& operator= (const ListElement&); + + ListElement* GetLast () + { + ListElement* tmp = this; + while (tmp->GetNext()) { + tmp = tmp->GetNext(); + } + return tmp; + } + ListElement* GetNext () const { return next ; } + ListElement* GetPrev () const { return prev ; } + + inline TYPE& GetData () { return data ; } + inline const TYPE& GetData () const { return data ; } + + void SetData ( const TYPE& d ) { data = d ; } // Quagmire - this may look like a mem leak, but dont change it, this behavior is expected where it's called + void SetLastNext ( ListElement* p ) + { + GetLast()->SetNext(p); + } + void SetNext (ListElement* n) { next = n ; } + void SetPrev (ListElement* p) { prev = p ; } + + void ReplaceData(const TYPE&); +}; + +template +class LinkedList +{ +private: + int32 count; + ListElement* first; + bool list_destructor_invoked; + +public: + + LinkedList(); + ~LinkedList(); + bool dont_delete; + LinkedList& operator= (const LinkedList&); + + void Append (const TYPE&); + void Insert (const TYPE&); + TYPE Pop(); + TYPE PeekTop(); + void Clear(); + void LCount() { count--; } + void ResetCount() { count=0; } + int32 Count() { return count; } + friend class LinkedListIterator; +}; + +template +class LinkedListIterator +{ +private: + LinkedList& list; + ListElement* current_element; + direction dir; + +public: + LinkedListIterator(LinkedList& l,direction d = FORWARD) : list(l), dir(d) {}; + + void Advance(); + const TYPE& GetData(); + bool IsFirst() + { + if (current_element->GetPrev() == 0) + return true; + else + return false; + } + bool IsLast() + { + if (current_element->GetNext() == 0) + return true; + else + return false; + } + bool MoreElements(); + void MoveFirst(); + void MoveLast(); + void RemoveCurrent(bool DeleteData = true); + void Replace(const TYPE& new_data); + void Reset(); + void SetDir(direction); +}; + +template +void LinkedListIterator::Advance() +{ + if (current_element == 0) + { + return; + } + if (dir == FORWARD) + { + current_element = current_element->GetNext(); + } + else + { + current_element = current_element->GetPrev(); + } + + if (list.list_destructor_invoked) + { + while(current_element && current_element->GetData() == 0) + { +// if (current_element == 0) +// { +// return; +// } + if (dir == FORWARD) + { + current_element = current_element->GetNext(); + } + else + { + current_element = current_element->GetPrev(); + } + } + } +} + +template +bool LinkedListIterator::MoreElements() +{ + if (current_element == 0) + return false; + return true; +} + +template +const TYPE& LinkedListIterator::GetData() +{ + return current_element->GetData(); +} + +template +void LinkedListIterator::MoveFirst() +{ + ListElement* prev = current_element->GetPrev(); + ListElement* next = current_element->GetNext(); + + if (prev == 0) + { + return; + } + +// if (prev != 0) +// { + prev->SetNext(next); +// } + if (next != 0) + { + next->SetPrev(prev); + } + current_element->SetPrev(0); + current_element->SetNext(list.first); + list.first->SetPrev(current_element); + list.first = current_element; +} + + +template +void LinkedListIterator::MoveLast() +{ + ListElement* prev = current_element->GetPrev(); + ListElement* next = current_element->GetNext(); + + if (next == 0) + { + return; + } + + if (prev != 0) + { + prev->SetNext(next); + } + else + { + list.first = next; + } +// if (next != 0) +// { + next->SetPrev(prev); +// } + current_element->SetNext(0); + current_element->SetPrev(next->GetLast()); + next->GetLast()->SetNext(current_element); +} + +template +void LinkedListIterator::RemoveCurrent(bool DeleteData) +{ + ListElement* save; + + if (list.first == current_element) + { + list.first = current_element->GetNext(); + } + + if (current_element->GetPrev() != 0) + { + current_element->GetPrev()->SetNext(current_element->GetNext()); + } + if (current_element->GetNext() != 0) + { + current_element->GetNext()->SetPrev(current_element->GetPrev()); + } + if (dir == FORWARD) + { + save = current_element->GetNext(); + } + else + { + save = current_element->GetPrev(); + } + current_element->SetNext(0); + current_element->SetPrev(0); + if (!DeleteData) + current_element->SetData(0); + safe_delete(current_element); + current_element = save; + list.LCount(); +} + +template +void LinkedListIterator::Replace(const TYPE& new_data) +{ + current_element->ReplaceData(new_data); +} + +template +void LinkedListIterator::Reset() +{ + if (!(&list)) + { + current_element=0; + return; + } + + if (dir == FORWARD) + { + current_element = list.first; + } + else + { + if (list.first == 0) + { + current_element = 0; + } + else + { + current_element = list.first->GetLast(); + } + } + + if (list.list_destructor_invoked) + { + while(current_element && current_element->GetData() == 0) + { +// if (current_element == 0) +// { +// return; +// } + if (dir == FORWARD) + { + current_element = current_element->GetNext(); + } + else + { + current_element = current_element->GetPrev(); + } + } + } +} + +template +void LinkedListIterator::SetDir(direction d) +{ + dir = d; +} + +template +ListElement::ListElement(const TYPE& d) +{ + data = d; + next = 0; + prev = 0; +} + +template +ListElement::~ListElement() +{ +// cout << "ListElement::~ListElement()" << endl; + + if (data != 0) + safe_delete(data); + data = 0; + if (next != 0) + { + safe_delete(next); + next = 0; + } +} + +template +void ListElement::ReplaceData(const TYPE& new_data) +{ + if (data != 0) + safe_delete(data); + data = new_data; +} + +template +LinkedList::LinkedList() +{ + list_destructor_invoked = false; + first = 0; + count = 0; + dont_delete = false; +} + +template +LinkedList::~LinkedList() +{ + list_destructor_invoked = true; + if(!dont_delete) + Clear(); +} + +template +void LinkedList::Clear() { + while (first) { + ListElement* tmp = first; + first = tmp->GetNext(); + tmp->SetNext(0); + safe_delete(tmp); + } + ResetCount(); +} + +template +void LinkedList::Append(const TYPE& data) +{ + ListElement* new_element = new ListElement(data); + + if (first == 0) + { + first = new_element; + } + else + { + new_element->SetPrev(first->GetLast()); + first->SetLastNext(new_element); + } + count++; +} + +template +void LinkedList::Insert(const TYPE& data) +{ + ListElement* new_element = new ListElement(data); + + new_element->SetNext(first); + if (first != 0) + { + first->SetPrev(new_element); + } + first = new_element; + count++; +} + +template +TYPE LinkedList::Pop() { + TYPE ret = 0; + if (first) { + ListElement* tmpdel = first; + first = tmpdel->GetNext(); + if (first) + first->SetPrev(0); + ret = tmpdel->GetData(); + tmpdel->SetData(0); + tmpdel->SetNext(0); + safe_delete(tmpdel); + count--; + } + return ret; +} + +template +TYPE LinkedList::PeekTop() { + if (first) + return first->GetData(); + return 0; +} + +#endif + + diff --git a/old/common/linked_list.hpp b/old/common/linked_list.hpp deleted file mode 100644 index 3dbf43a..0000000 --- a/old/common/linked_list.hpp +++ /dev/null @@ -1,524 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team, GPL v3+ - -#pragma once - -#include - -#include "types.hpp" - -// Safe deletion utility for backwards compatibility -template -inline void safe_delete(T*& ptr) -{ - delete ptr; - ptr = nullptr; -} - -// Direction enumeration for iterator traversal -enum direction -{ - FORWARD, - BACKWARD -}; - -template class LinkedListIterator; - -// Individual element in the linked list containing data and navigation pointers -template -class ListElement -{ -private: - TYPE data; // The actual data stored in this element - ListElement* next; // Pointer to the next element in the list - ListElement* prev; // Pointer to the previous element in the list - -public: - // Default constructor - initializes pointers to null - ListElement(); - - // Constructor with data - creates element with specified data - ListElement(const TYPE& d); - - // Copy constructor - creates element as copy of another - ListElement(const ListElement& other); - - // Destructor - handles cleanup of data and recursive deletion - ~ListElement(); - - // Assignment operator - copies data and pointers from another element - ListElement& operator=(const ListElement& other); - - // Traverses to the last element in the chain starting from this element - ListElement* GetLast() - { - ListElement* tmp = this; - while (tmp->GetNext()) { - tmp = tmp->GetNext(); - } - return tmp; - } - - // Returns pointer to the next element - ListElement* GetNext() const { return next; } - - // Returns pointer to the previous element - ListElement* GetPrev() const { return prev; } - - // Returns reference to the data stored in this element - inline TYPE& GetData() { return data; } - - // Returns const reference to the data stored in this element - inline const TYPE& GetData() const { return data; } - - // Sets the data for this element - expected behavior for pointer management - void SetData(const TYPE& d) { data = d; } - - // Sets the next pointer of the last element in the chain - void SetLastNext(ListElement* p) - { - GetLast()->SetNext(p); - } - - // Sets the next element pointer - void SetNext(ListElement* n) { next = n; } - - // Sets the previous element pointer - void SetPrev(ListElement* p) { prev = p; } - - // Replaces the data in this element, properly deleting old data - void ReplaceData(const TYPE& new_data); -}; - -// Doubly-linked list container with automatic memory management -template -class LinkedList -{ -private: - int32 count; // Number of elements in the list - ListElement* first; // Pointer to the first element - bool list_destructor_invoked; // Flag to track destructor state - -public: - bool dont_delete; // Flag to prevent automatic deletion - - // Default constructor - initializes empty list - LinkedList(); - - // Destructor - cleans up all elements unless dont_delete is set - ~LinkedList(); - - // Assignment operator - copies entire list from another - LinkedList& operator=(const LinkedList& other); - - // Adds element to the end of the list - void Append(const TYPE& data); - - // Inserts element at the beginning of the list - void Insert(const TYPE& data); - - // Removes and returns the first element from the list - TYPE Pop(); - - // Returns the data from the first element without removing it - TYPE PeekTop(); - - // Removes all elements from the list - void Clear(); - - // Decrements the element count - void LCount() { count--; } - - // Resets the element count to zero - void ResetCount() { count = 0; } - - // Returns the current number of elements in the list - int32 Count() { return count; } - - friend class LinkedListIterator; -}; - -// Iterator for traversing and manipulating linked list elements -template -class LinkedListIterator -{ -private: - LinkedList& list; // Reference to the list being iterated - ListElement* current_element; // Pointer to current element - direction dir; // Direction of iteration - -public: - // Constructor - creates iterator for specified list and direction - LinkedListIterator(LinkedList& l, direction d = FORWARD) : list(l), dir(d) {} - - // Advances the iterator to the next element in the specified direction - void Advance(); - - // Returns reference to the data at the current iterator position - const TYPE& GetData(); - - // Checks if current element is the first in the list - bool IsFirst() - { - if (current_element->GetPrev() == nullptr) - return true; - else - return false; - } - - // Checks if current element is the last in the list - bool IsLast() - { - if (current_element->GetNext() == nullptr) - return true; - else - return false; - } - - // Checks if there are more elements to iterate over - bool MoreElements(); - - // Moves current element to the beginning of the list - void MoveFirst(); - - // Moves current element to the end of the list - void MoveLast(); - - // Removes current element from list with optional data deletion - void RemoveCurrent(bool DeleteData = true); - - // Replaces data in current element with new data - void Replace(const TYPE& new_data); - - // Resets iterator to the beginning or end based on direction - void Reset(); - - // Sets the iteration direction - void SetDir(direction d); -}; - -// Implementation of LinkedListIterator methods - -// Advances iterator to next element, skipping null data during destruction -template -void LinkedListIterator::Advance() -{ - if (current_element == nullptr) { - return; - } - - if (dir == FORWARD) { - current_element = current_element->GetNext(); - } else { - current_element = current_element->GetPrev(); - } - - if (list.list_destructor_invoked) { - while (current_element && current_element->GetData() == 0) { - if (dir == FORWARD) { - current_element = current_element->GetNext(); - } else { - current_element = current_element->GetPrev(); - } - } - } -} - -// Checks if iterator has more elements to process -template -bool LinkedListIterator::MoreElements() -{ - if (current_element == nullptr) - return false; - return true; -} - -// Returns reference to data at current iterator position -template -const TYPE& LinkedListIterator::GetData() -{ - return current_element->GetData(); -} - -// Moves current element to the front of the list -template -void LinkedListIterator::MoveFirst() -{ - ListElement* prev = current_element->GetPrev(); - ListElement* next = current_element->GetNext(); - - if (prev == nullptr) { - return; - } - - prev->SetNext(next); - if (next != nullptr) { - next->SetPrev(prev); - } - current_element->SetPrev(nullptr); - current_element->SetNext(list.first); - list.first->SetPrev(current_element); - list.first = current_element; -} - -// Moves current element to the end of the list -template -void LinkedListIterator::MoveLast() -{ - ListElement* prev = current_element->GetPrev(); - ListElement* next = current_element->GetNext(); - - if (next == nullptr) { - return; - } - - if (prev != nullptr) { - prev->SetNext(next); - } else { - list.first = next; - } - next->SetPrev(prev); - current_element->SetNext(nullptr); - current_element->SetPrev(next->GetLast()); - next->GetLast()->SetNext(current_element); -} - -// Removes current element from list and optionally deletes its data -template -void LinkedListIterator::RemoveCurrent(bool DeleteData) -{ - ListElement* save; - - if (list.first == current_element) { - list.first = current_element->GetNext(); - } - - if (current_element->GetPrev() != nullptr) { - current_element->GetPrev()->SetNext(current_element->GetNext()); - } - if (current_element->GetNext() != nullptr) { - current_element->GetNext()->SetPrev(current_element->GetPrev()); - } - - if (dir == FORWARD) { - save = current_element->GetNext(); - } else { - save = current_element->GetPrev(); - } - - current_element->SetNext(nullptr); - current_element->SetPrev(nullptr); - if (!DeleteData) - current_element->SetData(0); - safe_delete(current_element); - current_element = save; - list.LCount(); -} - -// Replaces data in current element with new data -template -void LinkedListIterator::Replace(const TYPE& new_data) -{ - current_element->ReplaceData(new_data); -} - -// Resets iterator to start position based on direction -template -void LinkedListIterator::Reset() -{ - if (!(&list)) { - current_element = nullptr; - return; - } - - if (dir == FORWARD) { - current_element = list.first; - } else { - if (list.first == nullptr) { - current_element = nullptr; - } else { - current_element = list.first->GetLast(); - } - } - - if (list.list_destructor_invoked) { - while (current_element && current_element->GetData() == 0) { - if (dir == FORWARD) { - current_element = current_element->GetNext(); - } else { - current_element = current_element->GetPrev(); - } - } - } -} - -// Sets the iteration direction for this iterator -template -void LinkedListIterator::SetDir(direction d) -{ - dir = d; -} - -// Implementation of ListElement methods - -// Default constructor initializes all members to safe defaults -template -ListElement::ListElement() -{ - data = 0; - next = nullptr; - prev = nullptr; -} - -// Constructor with data initializes element with provided data -template -ListElement::ListElement(const TYPE& d) -{ - data = d; - next = nullptr; - prev = nullptr; -} - -// Copy constructor creates identical copy of another element -template -ListElement::ListElement(const ListElement& other) -{ - data = other.data; - next = nullptr; - prev = nullptr; -} - -// Destructor handles cleanup of data and recursive deletion of chain -template -ListElement::~ListElement() -{ - if (data != 0) - safe_delete(data); - data = 0; - if (next != nullptr) { - safe_delete(next); - next = nullptr; - } -} - -// Assignment operator copies data and pointers from another element -template -ListElement& ListElement::operator=(const ListElement& other) -{ - if (this != &other) { - data = other.data; - next = other.next; - prev = other.prev; - } - return *this; -} - -// Replaces current data with new data, properly cleaning up old data -template -void ListElement::ReplaceData(const TYPE& new_data) -{ - if (data != 0) - safe_delete(data); - data = new_data; -} - -// Implementation of LinkedList methods - -// Default constructor initializes empty list -template -LinkedList::LinkedList() -{ - list_destructor_invoked = false; - first = nullptr; - count = 0; - dont_delete = false; -} - -// Destructor cleans up all elements unless dont_delete flag is set -template -LinkedList::~LinkedList() -{ - list_destructor_invoked = true; - if (!dont_delete) - Clear(); -} - -// Assignment operator creates deep copy of another list -template -LinkedList& LinkedList::operator=(const LinkedList& other) -{ - if (this != &other) { - Clear(); - // Deep copy implementation would go here if needed - } - return *this; -} - -// Removes all elements from the list and resets count -template -void LinkedList::Clear() -{ - while (first) { - ListElement* tmp = first; - first = tmp->GetNext(); - tmp->SetNext(nullptr); - safe_delete(tmp); - } - ResetCount(); -} - -// Adds new element to the end of the list -template -void LinkedList::Append(const TYPE& data) -{ - ListElement* new_element = new ListElement(data); - - if (first == nullptr) { - first = new_element; - } else { - new_element->SetPrev(first->GetLast()); - first->SetLastNext(new_element); - } - count++; -} - -// Inserts new element at the beginning of the list -template -void LinkedList::Insert(const TYPE& data) -{ - ListElement* new_element = new ListElement(data); - - new_element->SetNext(first); - if (first != nullptr) { - first->SetPrev(new_element); - } - first = new_element; - count++; -} - -// Removes and returns first element from the list -template -TYPE LinkedList::Pop() -{ - TYPE ret = 0; - if (first) { - ListElement* tmpdel = first; - first = tmpdel->GetNext(); - if (first) - first->SetPrev(nullptr); - ret = tmpdel->GetData(); - tmpdel->SetData(0); - tmpdel->SetNext(nullptr); - safe_delete(tmpdel); - count--; - } - return ret; -} - -// Returns data from first element without removing it -template -TYPE LinkedList::PeekTop() -{ - if (first) - return first->GetData(); - return 0; -} \ No newline at end of file diff --git a/old/common/log.hpp b/old/common/log.hpp deleted file mode 100644 index 7c03f39..0000000 --- a/old/common/log.hpp +++ /dev/null @@ -1,656 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "types.hpp" -#include "xml_parser.hpp" -#include "../WorldServer/World.h" -#include "../WorldServer/client.h" -#include "../WorldServer/zoneserver.h" - -extern ZoneList zone_list; - -#define LOG_BUFFER_SIZE 4096 - -// Log output destinations -#define LOG_LOGFILE 1 -#define LOG_CONSOLE 2 -#define LOG_CLIENT 4 - -// Console color definitions using ANSI escape codes -#define FOREGROUND_WHITE 37 -#define FOREGROUND_WHITE_BOLD 137 -#define FOREGROUND_RED 31 -#define FOREGROUND_RED_BOLD 131 -#define FOREGROUND_GREEN 32 -#define FOREGROUND_GREEN_BOLD 132 -#define FOREGROUND_BLUE 34 -#define FOREGROUND_BLUE_BOLD 134 -#define FOREGROUND_YELLOW 33 -#define FOREGROUND_YELLOW_BOLD 133 -#define FOREGROUND_CYAN 36 -#define FOREGROUND_CYAN_BOLD 136 -#define FOREGROUND_MAGENTA 35 -#define FOREGROUND_MAGENTA_BOLD 135 - -#define LOG_CATEGORY(category) LOG_ ##category , -enum LogCategory -{ - #include "log_types.hpp" - NUMBER_OF_LOG_CATEGORIES -}; - -#define LOG_TYPE(category, type, level, color, enabled, logfile, console, client, str) category##__##type , -enum LogType -{ - #include "log_types.hpp" - NUMBER_OF_LOG_TYPES -}; - -#define LOG_CATEGORY(category) #category, -const char *log_category_names[NUMBER_OF_LOG_CATEGORIES] = { - #include "log_types.hpp" -}; - -// Structure to hold log type configuration and status -struct LogTypeStatus -{ - int8_t level; // Minimum log level for this type - int color; // Console color code - bool enabled; // Whether this log type is active - bool logfile; // Write to log file - bool console; // Write to console - bool client; // Send to connected clients - LogCategory category; // Category this log type belongs to - const char *name; // Internal name identifier - const char *display_name; // Human-readable display name -}; - -#define LOG_TYPE(category, type, level, color, enabled, logfile, console, client, str) { level, color, enabled, logfile, console, client, LOG_ ##category, #category "__" #type, ( strlen(str)>0 ) ? str : #category "__" #type }, -static LogTypeStatus real_log_type_info[NUMBER_OF_LOG_TYPES+1] = -{ - #include "log_types.hpp" - { 0, 0, false, false, false, false, NUMBER_OF_LOG_CATEGORIES, "BAD TYPE", "Bad Name" } /* dummy trailing record */ -}; - -LogTypeStatus *log_type_info = real_log_type_info; - -// Logging system configuration constants -#define LOG_CYCLE 100 // milliseconds between each batch of log writes -#define LOGS_PER_CYCLE 50 // amount of logs to write per cycle -#define LOG_DIR "logs" // directory for log files -#define DATE_MAX 8 // maximum date string length -#define LOG_NAME_MAX 32 // maximum log name length - -// Determine executable name for log file naming -#if defined LOGIN -#define EXE_NAME "login" -#elif defined WORLD -#define EXE_NAME "world" -#elif defined PARSER -#define EXE_NAME "parser" -#elif defined PATCHER -#define EXE_NAME "patcher" -#else -#define EXE_NAME "unknown" -#endif - -// Structure for queued log entries in doubly-linked list -struct logq_t -{ - LogType log_type; // Type of log entry - char date[DATE_MAX + 1]; // Timestamp string - char name[LOG_NAME_MAX + 1]; // Log category name - char *text; // Log message text - struct logq_t *next; // Next entry in queue - struct logq_t *prev; // Previous entry in queue -}; - -// Global logging system state -static logq_t head; // Head of log queue -static logq_t tail; // Tail of log queue -static std::atomic num_logqs{0}; // Number of queued logs -static std::mutex log_mutex; // Mutex for log queue access -static std::atomic looping{false}; // Thread loop control -static std::atomic start_called{false}; // Initialization flag - -extern const char* log_category_names[NUMBER_OF_LOG_CATEGORIES]; -extern LogTypeStatus* log_type_info; - -// Function declarations -void LogStart(); -void LogStop(); -int8_t GetLoggerLevel(LogType type); -void LogWrite(LogType type, int8_t log_level, const char *cat_text, const char *fmt, ...); -bool LogParseConfigs(); - -#ifdef PARSER -void ColorizeLog(int color, char *date, const char *display_name, const char *category, std::string buffer); -#endif - -// Set console text color using ANSI escape codes -static void SetConsoleColor(int color) -{ - switch (color) { - case FOREGROUND_WHITE: - case FOREGROUND_WHITE_BOLD: - case FOREGROUND_RED: - case FOREGROUND_RED_BOLD: - case FOREGROUND_GREEN: - case FOREGROUND_GREEN_BOLD: - case FOREGROUND_BLUE: - case FOREGROUND_BLUE_BOLD: - case FOREGROUND_YELLOW: - case FOREGROUND_YELLOW_BOLD: - case FOREGROUND_CYAN: - case FOREGROUND_CYAN_BOLD: - case FOREGROUND_MAGENTA: - case FOREGROUND_MAGENTA_BOLD: - printf("\033[%i;%i;40m", color > 100 ? 1 : 0, color > 100 ? color - 100 : color); - break; - default: - printf("\033[0;37;40m"); - break; - } -} - -// Open a log file for writing with date-based naming -static FILE * OpenLogFile() -{ - char file[FILENAME_MAX + 1]; - struct stat st; - struct tm *tm; - time_t now; - FILE *f; - - now = time(nullptr); - tm = localtime(&now); - - // Create logs directory if it doesn't exist - if (stat(LOG_DIR, &st) != 0) { - if (mkdir(LOG_DIR, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) { - fprintf(stderr, "Unable to create directory '%s': %s\n", LOG_DIR, strerror(errno)); - return stderr; - } - } - - // Generate filename with date and process ID -#ifdef NO_PIDLOG - snprintf(file, FILENAME_MAX, LOG_DIR"/%04i-%02i-%02i_eq2" EXE_NAME ".log", - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); -#else - snprintf(file, FILENAME_MAX, LOG_DIR"/%04i-%02i-%02i_eq2" EXE_NAME "_%04i.log", - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, getpid()); -#endif - - if ((f = fopen(file, "a")) == nullptr) { - fprintf(stderr, "Could not open '%s' for writing: %s\n", file, strerror(errno)); - return stderr; - } - - return f; -} - -// Write a batch of queued log entries to their destinations -static void WriteQueuedLogs(int count) -{ - logq_t pending_head, pending_tail, *logq, *tmp; - int i = 0; - FILE *f; - - // Initialize temporary list for processing - pending_head.next = &pending_tail; - pending_tail.prev = &pending_head; - - // Move logs from main queue to temporary list for processing - { - std::lock_guard lock(log_mutex); - - while (head.next != &tail) { - // Remove from main list - logq = head.next; - logq->next->prev = &head; - head.next = logq->next; - - // Add to temporary list - tmp = pending_tail.prev; - tmp->next = logq; - logq->prev = tmp; - logq->next = &pending_tail; - pending_tail.prev = logq; - --num_logqs; - - // Check count limit - if (count > 0 && ++i == count) - break; - } - } - - // Process logs from temporary list - logq = pending_head.next; - if (logq == &pending_tail) - return; - - while (logq != &pending_tail) { - // Write to console if enabled - if (log_type_info[logq->log_type].console) { - SetConsoleColor(FOREGROUND_WHITE_BOLD); - printf("%s ", logq->date); - SetConsoleColor(log_type_info[logq->log_type].color); - printf("%s ", log_type_info[logq->log_type].display_name); - SetConsoleColor(FOREGROUND_WHITE_BOLD); - printf("%-10s: ", logq->name); - SetConsoleColor(log_type_info[logq->log_type].color); - printf("%s\n", logq->text); - SetConsoleColor(-1); - fflush(stdout); - } - - // Write to log file if enabled - if (log_type_info[logq->log_type].logfile) { - f = OpenLogFile(); - - if (f != stderr || (f == stderr && !log_type_info[logq->log_type].console)) { - fprintf(f, "%s %s %s: %s\n", logq->date, - log_type_info[logq->log_type].display_name, logq->name, logq->text); - fflush(f); - if (f != stderr) - fclose(f); - } - } - -#if defined WORLD - // Send to subscribed clients if enabled - if (log_type_info[logq->log_type].client) { - // TODO: Implement client logging subscription system - } -#endif - - // Move to next log entry and clean up current - tmp = logq; - logq = logq->next; - free(tmp->text); - free(tmp); - } -} - -// Main logging thread function - processes queued logs continuously -void LogLoop() -{ - while (looping.load()) { - WriteQueuedLogs(LOGS_PER_CYCLE); - std::this_thread::sleep_for(std::chrono::milliseconds(LOG_CYCLE)); - } -} - -// Initialize the logging system and start the logging thread -void LogStart() -{ - if (start_called.load()) - return; - - // Initialize doubly-linked list - head.prev = nullptr; - head.next = &tail; - tail.prev = &head; - tail.next = nullptr; - - looping.store(true); - - // Start logging thread - std::thread log_thread(LogLoop); - log_thread.detach(); - - start_called.store(true); -} - -// Stop the logging system and flush remaining logs -void LogStop() -{ - looping.store(false); - WriteQueuedLogs(-1); // Write all remaining logs - start_called.store(false); -} - -// Add a log entry to the queue for processing -static void LogQueueAdd(LogType log_type, char *text, int len, const char *cat_text = nullptr) -{ - logq_t *logq; - struct tm *tm; - time_t now; - - // Allocate log queue entry - if ((logq = static_cast(calloc(1, sizeof(logq_t)))) == nullptr) { - free(text); - fprintf(stderr, "%s: %u: Unable to allocate %zu bytes\n", __FUNCTION__, __LINE__, sizeof(logq_t)); - return; - } - - // Allocate text buffer - if ((logq->text = static_cast(calloc(len + 1, sizeof(char)))) == nullptr) { - free(text); - free(logq); - fprintf(stderr, "%s: %u: Unable to allocate %i bytes\n", __FUNCTION__, __LINE__, len + 1); - return; - } - - // Set timestamp - now = time(nullptr); - tm = localtime(&now); - - // Initialize log entry - logq->log_type = log_type; - snprintf(logq->date, DATE_MAX + 1, "%02i:%02i:%02i", tm->tm_hour, tm->tm_min, tm->tm_sec); - strncpy(logq->name, cat_text == nullptr || cat_text[0] == '\0' ? - log_type_info[log_type].name : cat_text, LOG_NAME_MAX); - strncpy(logq->text, text, len); - free(text); - - // Ensure logging system is started - if (!start_called.load()) - LogStart(); - - // Add to queue - { - std::lock_guard lock(log_mutex); - tail.prev->next = logq; - logq->prev = tail.prev; - logq->next = &tail; - tail.prev = logq; - ++num_logqs; - } -} - -// Get the minimum log level for a specific log type -int8_t GetLoggerLevel(LogType type) -{ - return log_type_info[type].level; -} - -#ifndef PARSER -// Main logging function - formats message and queues for output -void LogWrite(LogType type, int8_t log_level, const char *cat_text, const char *fmt, ...) -{ - int count, size = 64; - char *buf; - va_list ap; - - // Check if logging is enabled and level is appropriate - if (!log_type_info[type].enabled || (log_level > 0 && log_type_info[type].level < log_level)) - return; - - // Dynamically allocate buffer for formatted message - while (true) { - if ((buf = static_cast(malloc(size))) == nullptr) { - fprintf(stderr, "%s: %i: Unable to allocate %i bytes\n", __FUNCTION__, __LINE__, size); - return; - } - - va_start(ap, fmt); - count = vsnprintf(buf, size, fmt, ap); - va_end(ap); - - if (count > -1 && count < size) - break; - - free(buf); - if (count > 1) - size = count + 1; - else - size *= 2; - } - - LogQueueAdd(type, buf, count, cat_text); -} - -#else -// Parser-specific logging function - writes directly without queueing -void LogWrite(LogType type, int8_t log_level, const char *cat_text, const char *format, ...) -{ - // Check if logging should proceed - if (!format || !log_type_info[type].enabled || - (log_level > 0 && log_type_info[type].level < log_level)) - return; - - time_t clock; - struct tm *tm; - char buffer[LOG_BUFFER_SIZE], date[32]; - va_list args; - FILE *f; - size_t cat_text_len = 0; - - memset(buffer, 0, sizeof(buffer)); - memset(date, 0, sizeof(date)); - - // Format the log message - va_start(args, format); - vsnprintf(buffer, sizeof(buffer) - 1, format, args); - va_end(args); - - // Generate timestamp - time(&clock); - tm = localtime(&clock); - snprintf(date, sizeof(date)-1, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); - - cat_text_len = strlen(cat_text); - - // Write to log file if enabled - if (log_type_info[type].logfile) { - char exename[200] = ""; - -#ifdef LOGIN - snprintf(exename, sizeof(exename), "login"); -#elif defined WORLD - snprintf(exename, sizeof(exename), "world"); -#elif defined PARSER - snprintf(exename, sizeof(exename), "parser"); -#elif defined PATCHER - snprintf(exename, sizeof(exename), "patcher"); -#endif - - char filename[200], log_header[200] = ""; - - // Generate filename with or without PID -#ifndef NO_PIDLOG - snprintf(filename, sizeof(filename)-1, "logs/%04d-%02d-%02d_eq2%s_%04i.log", - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, exename, getpid()); -#else - snprintf(filename, sizeof(filename)-1, "logs/%04d-%02d-%02d_eq2%s.log", - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, exename); -#endif - - // Check if new log file and create header - f = fopen(filename, "r"); - if (!f) - snprintf(log_header, sizeof(log_header), "===[ New log '%s' started ]===\n\n", filename); - else - fclose(f); - - // Write log entry - f = fopen(filename, "a"); - if (f) { - if (strlen(log_header) > 0) - fprintf(f, "%s\n", log_header); - fprintf(f, "%s %s %s: %s\n", date, log_type_info[type].display_name, cat_text, buffer); - fclose(f); - } - } - - // Write to console if enabled - if (log_type_info[type].console) { - printf("%s %s %s: %s\n", date, log_type_info[type].display_name, - cat_text_len == 0 ? log_type_info[type].name : cat_text, buffer); - } -} - -// Colorize console output for parser (simplified for Linux) -void ColorizeLog(int color, char *date, const char *display_name, const char *category, std::string buffer) -{ - printf("%s ", date); - SetConsoleColor(color); - printf("%s ", display_name); - SetConsoleColor(FOREGROUND_WHITE_BOLD); - printf("%s: ", category); - SetConsoleColor(color); - printf("%s\n", buffer.c_str()); - SetConsoleColor(FOREGROUND_WHITE); -} -#endif - -// Find log type status by category and type name -LogTypeStatus* GetLogTypeStatus(const char *category, const char *type) -{ - char combined[256]; - int i; - - memset(combined, 0, sizeof(combined)); - snprintf(combined, sizeof(combined) - 1, "%s__%s", category, type); - - for (i = 0; i < NUMBER_OF_LOG_TYPES; i++) { - if (strcasecmp(log_type_info[i].name, combined) == 0) - return &log_type_info[i]; - } - - return &log_type_info[NUMBER_OF_LOG_TYPES]; -} - -// Process XML configuration for a log category -void ProcessLogConfig(XMLNode node) -{ - int i; - const char *category, *type, *level, *color, *enabled, *logs; - LogTypeStatus *lfs; - XMLNode child; - - // Get category attribute - category = node.getAttribute("Category"); - if (!category) { - LogWrite(MISC__WARNING, 0, "Misc", "Error parsing log config. Config missing a Category"); - return; - } - - // Process each ConfigType child node - for (i = 0; i < node.nChildNode("ConfigType"); i++) { - child = node.getChildNode("ConfigType", i); - type = child.getAttribute("Type"); - - if (!type) { - LogWrite(MISC__WARNING, 0, "Misc", "Error parsing log config. Config missing a Type"); - continue; - } - - // Get log type status and configuration attributes - lfs = GetLogTypeStatus(category, type); - level = child.getAttribute("Level"); - enabled = child.getAttribute("Enabled"); - color = child.getAttribute("Color"); - logs = child.getAttribute("Logs"); - - // Validate logs attribute - if (!logs) { - LogWrite(MISC__WARNING, 0, "Misc", - "Error parsing log config. Config missing 'Logs' attribute to specify which log(s) to write to"); - continue; - } - if (!IsNumber(logs)) { - LogWrite(MISC__WARNING, 0, "Misc", - "Error parsing log config. Attribute 'Logs' must be a number. See LogTypes.h for the valid types."); - continue; - } - - // Set enabled status - if (enabled) { - if (!strcasecmp("true", enabled) || !strcasecmp("on", enabled)) - lfs->enabled = true; - else if (!strcasecmp("false", enabled) || !strcasecmp("off", enabled)) - lfs->enabled = false; - else - LogWrite(MISC__WARNING, 0, "Misc", - "Error parsing log config. Log setting 'Enabled' has invalid value '%s'. " - "'true'/'on' or 'false'/'off' are valid values", enabled); - } - - // Set log level - if (IsNumber(level)) - lfs->level = atoi(level); - else - lfs->level = 0; - - // Set color - if (color) { - if (IsNumber(color)) - lfs->color = atoi(color); - else if (!strcasecmp("White", color)) - lfs->color = FOREGROUND_WHITE; - else if (!strcasecmp("Green", color)) - lfs->color = FOREGROUND_GREEN; - else if (!strcasecmp("Yellow", color)) - lfs->color = FOREGROUND_YELLOW; - else if (!strcasecmp("Red", color)) - lfs->color = FOREGROUND_RED; - else if (!strcasecmp("Blue", color)) - lfs->color = FOREGROUND_BLUE; - else if (!strcasecmp("Cyan", color)) - lfs->color = FOREGROUND_CYAN; - else if (!strcasecmp("Magenta", color)) - lfs->color = FOREGROUND_MAGENTA; - else if (!strcasecmp("WhiteBold", color)) - lfs->color = FOREGROUND_WHITE_BOLD; - else if (!strcasecmp("GreenBold", color)) - lfs->color = FOREGROUND_GREEN_BOLD; - else if (!strcasecmp("YellowBold", color)) - lfs->color = FOREGROUND_YELLOW_BOLD; - else if (!strcasecmp("RedBold", color)) - lfs->color = FOREGROUND_RED_BOLD; - else if (!strcasecmp("BlueBold", color)) - lfs->color = FOREGROUND_BLUE_BOLD; - else if (!strcasecmp("CyanBold", color)) - lfs->color = FOREGROUND_CYAN_BOLD; - else if (!strcasecmp("MagentaBold", color)) - lfs->color = FOREGROUND_MAGENTA_BOLD; - else - LogWrite(MISC__WARNING, 0, "Misc", - "Error parsing log config. Log setting 'Color' has invalid value '%s'", color); - } - - // Set output destinations - lfs->logfile = (atoi(logs) & LOG_LOGFILE); - lfs->console = (atoi(logs) & LOG_CONSOLE); - lfs->client = (atoi(logs) & LOG_CLIENT); - } -} - -// Parse log configuration from XML file -bool LogParseConfigs() -{ - XMLNode main_node; - int i; - - main_node = XMLNode::openFileHelper("log_config.xml", "EQ2EmuLogConfigs"); - if (main_node.isEmpty()) { - LogWrite(MISC__WARNING, 0, "Misc", - "Unable to parse the file 'log_config.xml' or it does not exist. Default values will be used"); - return false; - } - - // Process each LogConfig node - for (i = 0; i < main_node.nChildNode("LogConfig"); i++) - ProcessLogConfig(main_node.getChildNode("LogConfig", i)); - - return true; -} \ No newline at end of file diff --git a/old/common/login_oplist.h b/old/common/login_oplist.h new file mode 100644 index 0000000..c8bd536 --- /dev/null +++ b/old/common/login_oplist.h @@ -0,0 +1,61 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#if defined(LOGIN) || defined(MINILOGIN) +N(OP_LoginRequestMsg), +N(OP_LoginByNumRequestMsg), +N(OP_WSLoginRequestMsg), +N(OP_ESLoginRequestMsg), +N(OP_LoginReplyMsg), +N(OP_WorldListMsg), +N(OP_WorldStatusChangeMsg), +N(OP_AllWSDescRequestMsg), +N(OP_WSStatusReplyMsg), +N(OP_AllCharactersDescRequestMsg), +N(OP_AllCharactersDescReplyMsg), +N(OP_CreateCharacterRequestMsg), +N(OP_ReskinCharacterRequestMsg), +N(OP_CreateCharacterReplyMsg), +N(OP_WSCreateCharacterRequestMsg), +N(OP_WSCreateCharacterReplyMsg), +N(OP_DeleteCharacterRequestMsg), +N(OP_DeleteCharacterReplyMsg), +N(OP_PlayCharacterRequestMsg), +N(OP_PlayCharacterReplyMsg), +N(OP_ServerPlayCharacterRequestMsg), +N(OP_ServerPlayCharacterReplyMsg), +N(OP_KeymapLoadMsg), +N(OP_KeymapNoneMsg), +N(OP_KeymapDataMsg), +N(OP_KeymapSaveMsg), +//N(OP_LSRequestPlayerDescMsg), +N(OP_LSCheckAcctLockMsg), +N(OP_WSAcctLockStatusMsg), +N(OP_LsRequestClientCrashLogMsg), +N(OP_LsClientBaselogReplyMsg), +N(OP_LsClientCrashlogReplyMsg), +N(OP_LsClientAlertlogReplyMsg), +N(OP_LsClientVerifylogReplyMsg), +N(OP_BadLanguageFilter), +N(OP_WSServerLockMsg), +N(OP_WSServerHideMsg), +N(OP_LSServerLockMsg), +N(OP_UpdateCharacterSheetMsg), +N(OP_UpdateInventoryMsg), +#endif diff --git a/old/common/md5.cpp b/old/common/md5.cpp new file mode 100644 index 0000000..1244c8c --- /dev/null +++ b/old/common/md5.cpp @@ -0,0 +1,281 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include /* for memcpy() */ +#include "../common/md5.h" +#include "../common/MiscFunctions.h" +#include "../common/seperator.h" + +MD5::MD5() { + memset(pMD5, 0, 16); +} + +MD5::MD5(const uchar* buf, uint32 len) { + Generate(buf, len, pMD5); +} + +MD5::MD5(const char* buf, uint32 len) { + Generate((const uchar*) buf, len, pMD5); +} + +MD5::MD5(const int8 buf[16]) { + Set(buf); +} + +MD5::MD5(const char* iMD5String) { + Set(iMD5String); +} + +void MD5::Generate(const char* iString) { + Generate((const uchar*) iString, strlen(iString)); +} + +void MD5::Generate(const int8* buf, uint32 len) { + Generate(buf, len, pMD5); +} + +bool MD5::Set(const int8 buf[16]) { + memcpy(pMD5, buf, 16); + return true; +} + +bool MD5::Set(const char* iMD5String) { + char tmp[5] = { '0', 'x', 0, 0, 0 }; + for (int i=0; i<16; i++) { + tmp[2] = iMD5String[i*2]; + tmp[3] = iMD5String[(i*2) + 1]; + if (!Seperator::IsHexNumber(tmp)) + return false; + pMD5[i] = hextoi(tmp); + } + return true; +} + +MD5::operator const char* () { + snprintf(pMD5String, sizeof(pMD5String), "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", pMD5[0], pMD5[1], pMD5[2], pMD5[3], pMD5[4], pMD5[5], pMD5[6], pMD5[7], pMD5[8], pMD5[9], pMD5[10], pMD5[11], pMD5[12], pMD5[13], pMD5[14], pMD5[15]); + return pMD5String; +} + +bool MD5::operator== (const MD5& iMD5) { + if (memcmp(pMD5, iMD5.pMD5, 16) == 0) + return true; + else + return false; +} + +bool MD5::operator== (const int8* iMD5) { + if (memcmp(pMD5, iMD5, 16) == 0) + return true; + else + return false; +} + +bool MD5::operator== (const char* iMD5String) { + char tmp[5] = { '0', 'x', 0, 0, 0 }; + for (int i=0; i<16; i++) { + tmp[2] = iMD5String[i*2]; + tmp[3] = iMD5String[(i*2) + 1]; + if (pMD5[i] != hextoi(tmp)) + return false; + } + return true; +} + +MD5& MD5::operator= (const MD5& iMD5) { + memcpy(pMD5, iMD5.pMD5, 16); + return *this; +} + +MD5* MD5::operator= (const MD5* iMD5) { + memcpy(pMD5, iMD5->pMD5, 16); + return this; +} + +/* Byte-swap an array of words to little-endian. (Byte-sex independent) */ +void MD5::byteSwap(uint32 *buf, uint32 words) { + int8 *p = (int8 *)buf; + do { + *buf++ = (uint32)((uint32)p[3]<<8 | p[2]) << 16 | + ((uint32)p[1]<<8 | p[0]); + p += 4; + } while (--words); +} + +void MD5::Generate(const int8* buf, uint32 len, int8 digest[16]) { + MD5Context ctx; + Init(&ctx); + Update(&ctx, buf, len); + Final(digest, &ctx); +} + +/* Start MD5 accumulation. */ +void MD5::Init(struct MD5Context *ctx) { + ctx->hash[0] = 0x67452301; + ctx->hash[1] = 0xefcdab89; + ctx->hash[2] = 0x98badcfe; + ctx->hash[3] = 0x10325476; + ctx->bytes[1] = ctx->bytes[0] = 0; +} + +/* Update ctx to reflect the addition of another buffer full of bytes. */ +void MD5::Update(struct MD5Context *ctx, int8 const *buf, uint32 len) { + uint32 t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) /* Update 64-bit byte count */ + ctx->bytes[1]++; /* Carry from low to high */ + + + + t = 64 - (t & 0x3f); /* Bytes available in ctx->input (>= 1) */ + if (t > len) { + memcpy((int8*)ctx->input+64-t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((int8*)ctx->input+64-t, buf, t); + byteSwap(ctx->input, 16); + Transform(ctx->hash, ctx->input); + buf += t; + len -= t; + /* Process data in 64-byte chunks */ + while (len >= 64) { + memcpy(ctx->input, buf, 64); + byteSwap(ctx->input, 16); + Transform(ctx->hash, ctx->input); + buf += 64; + len -= 64; + } + /* Buffer any remaining bytes of data */ + memcpy(ctx->input, buf, len); +} + +/* Final wrapup - pad to 64-byte boundary with the bit pattern +* 1 0* (64-bit count of bits processed, LSB-first) */ +void MD5::Final(int8 digest[16], MD5Context *ctx) { + int count = ctx->bytes[0] & 0x3F; /* Bytes mod 64 */ + int8 *p = (int8*)ctx->input + count; + /* Set the first byte of padding to 0x80. There is always room. */ + *p++ = 0x80; + /* Bytes of zero padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, count+8); + byteSwap(ctx->input, 16); + Transform(ctx->hash, ctx->input); + p = (int8*)ctx->input; + count = 56; + } + memset(p, 0, count); + byteSwap(ctx->input, 14); + /* Append 8 bytes of length in *bits* and transform */ + ctx->input[14] = ctx->bytes[0] << 3; + + ctx->input[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + Transform(ctx->hash, ctx->input); + byteSwap(ctx->hash, 4); + memcpy(digest, ctx->hash, 16); + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ +} + +/* The four core functions */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) (w += f(x,y,z)+in, w = (w<>(32-s)) + x) + + + +/* The heart of the MD5 algorithm. */ +void MD5::Transform(uint32 hash[4], const uint32 input[16]) { + uint32 a = hash[0], b = hash[1], c = hash[2], d = hash[3]; + + MD5STEP(F1, a, b, c, d, input[ 0]+0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, input[ 1]+0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, input[ 2]+0x242070db, 17); + MD5STEP(F1, b, c, d, a, input[ 3]+0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, input[ 4]+0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, input[ 5]+0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, input[ 6]+0xa8304613, 17); + MD5STEP(F1, b, c, d, a, input[ 7]+0xfd469501, 22); + MD5STEP(F1, a, b, c, d, input[ 8]+0x698098d8, 7); + MD5STEP(F1, d, a, b, c, input[ 9]+0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, input[10]+0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, input[11]+0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, input[12]+0x6b901122, 7); + MD5STEP(F1, d, a, b, c, input[13]+0xfd987193, 12); + MD5STEP(F1, c, d, a, b, input[14]+0xa679438e, 17); + MD5STEP(F1, b, c, d, a, input[15]+0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, input[ 1]+0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, input[ 6]+0xc040b340, 9); + MD5STEP(F2, c, d, a, b, input[11]+0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, input[ 0]+0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, input[ 5]+0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, input[10]+0x02441453, 9); + MD5STEP(F2, c, d, a, b, input[15]+0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, input[ 4]+0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, input[ 9]+0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, input[14]+0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, input[ 3]+0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, input[ 8]+0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, input[13]+0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, input[ 2]+0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, input[ 7]+0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, input[12]+0x8d2a4c8a, 20); + + + + + MD5STEP(F3, a, b, c, d, input[ 5]+0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, input[ 8]+0x8771f681, 11); + MD5STEP(F3, c, d, a, b, input[11]+0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, input[14]+0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, input[ 1]+0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, input[ 4]+0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, input[ 7]+0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, input[10]+0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, input[13]+0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, input[ 0]+0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, input[ 3]+0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, input[ 6]+0x04881d05, 23); + MD5STEP(F3, a, b, c, d, input[ 9]+0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, input[12]+0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, input[15]+0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, input[ 2]+0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, input[ 0]+0xf4292244, 6); + MD5STEP(F4, d, a, b, c, input[ 7]+0x432aff97, 10); + MD5STEP(F4, c, d, a, b, input[14]+0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, input[ 5]+0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, input[12]+0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, input[ 3]+0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, input[10]+0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, input[ 1]+0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, input[ 8]+0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, input[15]+0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, input[ 6]+0xa3014314, 15); + MD5STEP(F4, b, c, d, a, input[13]+0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, input[ 4]+0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, input[11]+0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, input[ 2]+0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, input[ 9]+0xeb86d391, 21); + + hash[0] += a; hash[1] += b; hash[2] += c; hash[3] += d; +} diff --git a/old/common/md5.h b/old/common/md5.h new file mode 100644 index 0000000..6c54f94 --- /dev/null +++ b/old/common/md5.h @@ -0,0 +1,64 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef MD5_H +#define MD5_H +#include "../common/types.h" + + +class MD5 { +public: + struct MD5Context { + uint32 hash[4]; + uint32 bytes[2]; + uint32 input[16]; + }; + static void Generate(const int8* buf, uint32 len, int8 digest[16]); + + static void Init(struct MD5Context *context); + static void Update(struct MD5Context *context, const int8 *buf, uint32 len); + static void Final(int8 digest[16], struct MD5Context *context); + + MD5(); + MD5(const uchar* buf, uint32 len); + MD5(const char* buf, uint32 len); + MD5(const int8 buf[16]); + MD5(const char* iMD5String); + + void Generate(const char* iString); + void Generate(const int8* buf, uint32 len); + bool Set(const int8 buf[16]); + bool Set(const char* iMD5String); + + bool operator== (const MD5& iMD5); + bool operator== (const int8 iMD5[16]); + bool operator== (const char* iMD5String); + + MD5& operator= (const MD5& iMD5); + MD5* operator= (const MD5* iMD5); + MD5* operator= (const int8* iMD5); + operator const char* (); +protected: + int8 pMD5[16]; +private: + static void byteSwap(uint32 *buf, uint32 words); + static void Transform(uint32 hash[4], const int32 input[16]); + char pMD5String[33]; +}; +#endif diff --git a/old/common/md5.hpp b/old/common/md5.hpp deleted file mode 100644 index 6e5d809..0000000 --- a/old/common/md5.hpp +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team, GPL v3 - -#pragma once - -#include -#include -#include -#include - -class MD5 -{ -public: - // MD5 computation context structure - struct MD5Context - { - std::uint32_t hash[4]; // Current hash state - std::uint32_t bytes[2]; // Number of bytes processed (64-bit counter) - std::uint32_t input[16]; // Input buffer for 64-byte blocks - }; - - // Static method to generate MD5 hash from buffer - static void Generate(const std::int8_t* buf, std::uint32_t len, std::int8_t digest[16]); - - // Initialize MD5 context for incremental hashing - static void Init(struct MD5Context *context); - - // Update MD5 context with additional data - static void Update(struct MD5Context *context, const std::int8_t *buf, std::uint32_t len); - - // Finalize MD5 computation and produce digest - static void Final(std::int8_t digest[16], struct MD5Context *context); - - // Default constructor - initializes to zero hash - MD5(); - - // Constructor from unsigned char buffer - MD5(const unsigned char* buf, std::uint32_t len); - - // Constructor from char buffer - MD5(const char* buf, std::uint32_t len); - - // Constructor from 16-byte binary hash - MD5(const std::int8_t buf[16]); - - // Constructor from hex string representation - MD5(const char* iMD5String); - - // Generate MD5 from null-terminated string - void Generate(const char* iString); - - // Generate MD5 from buffer - void Generate(const std::int8_t* buf, std::uint32_t len); - - // Set MD5 from 16-byte binary data - bool Set(const std::int8_t buf[16]); - - // Set MD5 from 32-character hex string - bool Set(const char* iMD5String); - - // Equality comparison operators - bool operator== (const MD5& iMD5); - bool operator== (const std::int8_t iMD5[16]); - bool operator== (const char* iMD5String); - - // Assignment operators - MD5& operator= (const MD5& iMD5); - MD5* operator= (const MD5* iMD5); - - // Conversion to hex string - operator const char* (); - -private: - // Helper function to convert bytes to little-endian format - static void byteSwap(std::uint32_t *buf, std::uint32_t words); - - // Core MD5 transformation function - static void Transform(std::uint32_t hash[4], const std::uint32_t input[16]); - - // Helper function to check if character is valid hex digit - static bool IsHexDigit(char c) { - return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); - } - - // Helper function to convert hex string to integer - static std::uint32_t HexToInt(const char* hex) { - std::uint32_t result = 0; - for (int i = 0; hex[i] != '\0'; ++i) { - result *= 16; - if (hex[i] >= '0' && hex[i] <= '9') { - result += hex[i] - '0'; - } else if (hex[i] >= 'a' && hex[i] <= 'f') { - result += hex[i] - 'a' + 10; - } else if (hex[i] >= 'A' && hex[i] <= 'F') { - result += hex[i] - 'A' + 10; - } - } - return result; - } - - std::int8_t pMD5[16]; // 16-byte MD5 hash storage - char pMD5String[33]; // 32-character hex string representation + null terminator -}; - -// Default constructor - zero out hash -MD5::MD5() { - std::memset(pMD5, 0, 16); -} - -// Constructor from unsigned char buffer -MD5::MD5(const unsigned char* buf, std::uint32_t len) { - Generate(reinterpret_cast(buf), len, pMD5); -} - -// Constructor from char buffer -MD5::MD5(const char* buf, std::uint32_t len) { - Generate(reinterpret_cast(buf), len, pMD5); -} - -// Constructor from 16-byte binary hash -MD5::MD5(const std::int8_t buf[16]) { - Set(buf); -} - -// Constructor from hex string -MD5::MD5(const char* iMD5String) { - Set(iMD5String); -} - -// Generate MD5 from null-terminated string -void MD5::Generate(const char* iString) { - Generate(reinterpret_cast(iString), std::strlen(iString)); -} - -// Generate MD5 from buffer using instance method -void MD5::Generate(const std::int8_t* buf, std::uint32_t len) { - Generate(buf, len, pMD5); -} - -// Set MD5 from 16-byte binary data -bool MD5::Set(const std::int8_t buf[16]) { - std::memcpy(pMD5, buf, 16); - return true; -} - -// Set MD5 from 32-character hex string -bool MD5::Set(const char* iMD5String) { - char tmp[5] = { '0', 'x', 0, 0, 0 }; - for (int i = 0; i < 16; i++) { - tmp[2] = iMD5String[i * 2]; - tmp[3] = iMD5String[(i * 2) + 1]; - - // Validate hex characters - if (!IsHexDigit(tmp[2]) || !IsHexDigit(tmp[3])) - return false; - - pMD5[i] = static_cast(HexToInt(tmp)); - } - return true; -} - -// Convert MD5 to hex string representation -MD5::operator const char* () { - std::snprintf(pMD5String, sizeof(pMD5String), - "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - static_cast(pMD5[0]), static_cast(pMD5[1]), - static_cast(pMD5[2]), static_cast(pMD5[3]), - static_cast(pMD5[4]), static_cast(pMD5[5]), - static_cast(pMD5[6]), static_cast(pMD5[7]), - static_cast(pMD5[8]), static_cast(pMD5[9]), - static_cast(pMD5[10]), static_cast(pMD5[11]), - static_cast(pMD5[12]), static_cast(pMD5[13]), - static_cast(pMD5[14]), static_cast(pMD5[15])); - return pMD5String; -} - -// Equality comparison with another MD5 object -bool MD5::operator== (const MD5& iMD5) { - return std::memcmp(pMD5, iMD5.pMD5, 16) == 0; -} - -// Equality comparison with 16-byte array -bool MD5::operator== (const std::int8_t* iMD5) { - return std::memcmp(pMD5, iMD5, 16) == 0; -} - -// Equality comparison with hex string -bool MD5::operator== (const char* iMD5String) { - char tmp[5] = { '0', 'x', 0, 0, 0 }; - for (int i = 0; i < 16; i++) { - tmp[2] = iMD5String[i * 2]; - tmp[3] = iMD5String[(i * 2) + 1]; - if (pMD5[i] != static_cast(HexToInt(tmp))) - return false; - } - return true; -} - -// Assignment from another MD5 object -MD5& MD5::operator= (const MD5& iMD5) { - std::memcpy(pMD5, iMD5.pMD5, 16); - return *this; -} - -// Assignment from MD5 pointer -MD5* MD5::operator= (const MD5* iMD5) { - std::memcpy(pMD5, iMD5->pMD5, 16); - return this; -} - -// Byte-swap array of 32-bit words to little-endian format -void MD5::byteSwap(std::uint32_t *buf, std::uint32_t words) { - std::int8_t *p = reinterpret_cast(buf); - do { - *buf++ = (static_cast(static_cast(p[3]) << 8 | p[2]) << 16) | - (static_cast(p[1]) << 8 | p[0]); - p += 4; - } while (--words); -} - -// Generate MD5 digest from buffer using static method -void MD5::Generate(const std::int8_t* buf, std::uint32_t len, std::int8_t digest[16]) { - MD5Context ctx; - Init(&ctx); - Update(&ctx, buf, len); - Final(digest, &ctx); -} - -// Initialize MD5 context with standard initial values -void MD5::Init(struct MD5Context *ctx) { - ctx->hash[0] = 0x67452301; - ctx->hash[1] = 0xefcdab89; - ctx->hash[2] = 0x98badcfe; - ctx->hash[3] = 0x10325476; - ctx->bytes[1] = ctx->bytes[0] = 0; -} - -// Update MD5 context with new data -void MD5::Update(struct MD5Context *ctx, std::int8_t const *buf, std::uint32_t len) { - std::uint32_t t = ctx->bytes[0]; - if ((ctx->bytes[0] = t + len) < t) // Update 64-bit byte count - ctx->bytes[1]++; // Carry from low to high - - t = 64 - (t & 0x3f); // Bytes available in ctx->input (>= 1) - if (t > len) { - std::memcpy(reinterpret_cast(ctx->input) + 64 - t, buf, len); - return; - } - - // First chunk is an odd size - std::memcpy(reinterpret_cast(ctx->input) + 64 - t, buf, t); - byteSwap(ctx->input, 16); - Transform(ctx->hash, ctx->input); - buf += t; - len -= t; - - // Process data in 64-byte chunks - while (len >= 64) { - std::memcpy(ctx->input, buf, 64); - byteSwap(ctx->input, 16); - Transform(ctx->hash, ctx->input); - buf += 64; - len -= 64; - } - - // Buffer any remaining bytes of data - std::memcpy(ctx->input, buf, len); -} - -// Finalize MD5 computation with padding and length -void MD5::Final(std::int8_t digest[16], MD5Context *ctx) { - int count = ctx->bytes[0] & 0x3F; // Bytes mod 64 - std::int8_t *p = reinterpret_cast(ctx->input) + count; - - // Set the first byte of padding to 0x80. There is always room. - *p++ = 0x80; - - // Bytes of zero padding needed to make 56 bytes (-8..55) - count = 56 - 1 - count; - if (count < 0) { // Padding forces an extra block - std::memset(p, 0, count + 8); - byteSwap(ctx->input, 16); - Transform(ctx->hash, ctx->input); - p = reinterpret_cast(ctx->input); - count = 56; - } - std::memset(p, 0, count); - byteSwap(ctx->input, 14); - - // Append 8 bytes of length in *bits* and transform - ctx->input[14] = ctx->bytes[0] << 3; - ctx->input[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; - Transform(ctx->hash, ctx->input); - byteSwap(ctx->hash, 4); - std::memcpy(digest, ctx->hash, 16); - std::memset(ctx, 0, sizeof(*ctx)); // Clear sensitive data -} - -// MD5 transformation constants and helper functions -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -// Central step in MD5 algorithm -#define MD5STEP(f,w,x,y,z,in,s) (w += f(x,y,z)+in, w = (w<>(32-s)) + x) - -// Core MD5 transformation function - processes one 64-byte block -void MD5::Transform(std::uint32_t hash[4], const std::uint32_t input[16]) { - std::uint32_t a = hash[0], b = hash[1], c = hash[2], d = hash[3]; - - // Round 1 - F1 function - MD5STEP(F1, a, b, c, d, input[ 0]+0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, input[ 1]+0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, input[ 2]+0x242070db, 17); - MD5STEP(F1, b, c, d, a, input[ 3]+0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, input[ 4]+0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, input[ 5]+0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, input[ 6]+0xa8304613, 17); - MD5STEP(F1, b, c, d, a, input[ 7]+0xfd469501, 22); - MD5STEP(F1, a, b, c, d, input[ 8]+0x698098d8, 7); - MD5STEP(F1, d, a, b, c, input[ 9]+0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, input[10]+0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, input[11]+0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, input[12]+0x6b901122, 7); - MD5STEP(F1, d, a, b, c, input[13]+0xfd987193, 12); - MD5STEP(F1, c, d, a, b, input[14]+0xa679438e, 17); - MD5STEP(F1, b, c, d, a, input[15]+0x49b40821, 22); - - // Round 2 - F2 function - MD5STEP(F2, a, b, c, d, input[ 1]+0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, input[ 6]+0xc040b340, 9); - MD5STEP(F2, c, d, a, b, input[11]+0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, input[ 0]+0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, input[ 5]+0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, input[10]+0x02441453, 9); - MD5STEP(F2, c, d, a, b, input[15]+0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, input[ 4]+0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, input[ 9]+0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, input[14]+0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, input[ 3]+0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, input[ 8]+0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, input[13]+0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, input[ 2]+0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, input[ 7]+0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, input[12]+0x8d2a4c8a, 20); - - // Round 3 - F3 function - MD5STEP(F3, a, b, c, d, input[ 5]+0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, input[ 8]+0x8771f681, 11); - MD5STEP(F3, c, d, a, b, input[11]+0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, input[14]+0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, input[ 1]+0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, input[ 4]+0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, input[ 7]+0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, input[10]+0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, input[13]+0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, input[ 0]+0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, input[ 3]+0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, input[ 6]+0x04881d05, 23); - MD5STEP(F3, a, b, c, d, input[ 9]+0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, input[12]+0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, input[15]+0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, input[ 2]+0xc4ac5665, 23); - - // Round 4 - F4 function - MD5STEP(F4, a, b, c, d, input[ 0]+0xf4292244, 6); - MD5STEP(F4, d, a, b, c, input[ 7]+0x432aff97, 10); - MD5STEP(F4, c, d, a, b, input[14]+0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, input[ 5]+0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, input[12]+0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, input[ 3]+0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, input[10]+0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, input[ 1]+0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, input[ 8]+0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, input[15]+0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, input[ 6]+0xa3014314, 15); - MD5STEP(F4, b, c, d, a, input[13]+0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, input[ 4]+0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, input[11]+0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, input[ 2]+0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, input[ 9]+0xeb86d391, 21); - - // Add the working variables back into the hash state - hash[0] += a; - hash[1] += b; - hash[2] += c; - hash[3] += d; -} \ No newline at end of file diff --git a/old/common/misc.cpp b/old/common/misc.cpp new file mode 100644 index 0000000..6f5daa0 --- /dev/null +++ b/old/common/misc.cpp @@ -0,0 +1,305 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifdef WIN32 + // VS6 doesn't like the length of STL generated names: disabling + #pragma warning(disable:4786) +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "misc.h" +#include "types.h" +using namespace std; + +#define ENC(c) (((c) & 0x3f) + ' ') +#define DEC(c) (((c) - ' ') & 0x3f) + +map DBFieldNames; + +#ifndef WIN32 +#ifdef FREEBSD +int print_stacktrace() +{ + printf("Insert stack trace here...\n"); + return(0); +} +#else //!WIN32 && !FREEBSD == linux +#include +int print_stacktrace() +{ + void *ba[20]; + int n = backtrace (ba, 20); + if (n != 0) + { + char **names = backtrace_symbols (ba, n); + if (names != NULL) + { + int i; + cerr << "called from " << (char*)names[0] << endl; + for (i = 1; i < n; ++i) + cerr << " " << (char*)names[i] << endl; + free (names); + } + } + return(0); +} +#endif //!FREEBSD +#endif //!WIN32 + +int Deflate(unsigned char* in_data, int in_length, unsigned char* out_data, int max_out_length) +{ +z_stream zstream; +int zerror; + + zstream.next_in = in_data; + zstream.avail_in = in_length; + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + deflateInit(&zstream, Z_FINISH); + zstream.next_out = out_data; + zstream.avail_out = max_out_length; + zerror = deflate(&zstream, Z_FINISH); + + if (zerror == Z_STREAM_END) + { + deflateEnd(&zstream); + return zstream.total_out; + } + else + { + cout << "Error: Deflate: deflate() returned " << zerror << " '"; + if (zstream.msg) + cout << zstream.msg; + cout << "'" << endl; + zerror = deflateEnd(&zstream); + return 0; + } +} + +int Inflate(unsigned char* indata, int indatalen, unsigned char* outdata, int outdatalen, bool iQuiet) +{ +z_stream zstream; +int zerror = 0; +int i; + + zstream.next_in = indata; + zstream.avail_in = indatalen; + zstream.next_out = outdata; + zstream.avail_out = outdatalen; + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + + i = inflateInit2( &zstream, 15 ); + if (i != Z_OK) { + return 0; + } + + zerror = inflate( &zstream, Z_FINISH ); + + if(zerror == Z_STREAM_END) { + inflateEnd( &zstream ); + return zstream.total_out; + } + else { + if (!iQuiet) { + cout << "Error: Inflate: inflate() returned " << zerror << " '"; + if (zstream.msg) + cout << zstream.msg; + cout << "'" << endl; + } + + if (zerror == Z_DATA_ERROR || zerror == Z_ERRNO) + return -1; + + if (zerror == Z_MEM_ERROR && zstream.msg == 0) + { + return 0; + } + + zerror = inflateEnd( &zstream ); + return 0; + } +} + +void dump_message_column(unsigned char *buffer, unsigned long length, string leader, FILE *to) +{ +unsigned long i,j; +unsigned long rows,offset=0; + rows=(length/16)+1; + for(i=0;i= 0x41 && val <=0x5A) || (val >= 0x61 && val <=0x7A)) + return true; + else + return false; +} + +unsigned int GetSpellNameCrc(const char* src) { + if (!src) + return 0; + uLong crc = crc32(0L, Z_NULL, 0); + return crc32(crc, (unsigned const char*)src, strlen(src)); +} + +int GetItemNameCrc(string item_name){ + const char *src = item_name.c_str(); + uLong crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, (unsigned const char *)src,strlen(src)) + 1; + return sint32(crc) * -1; +} + +unsigned int GetNameCrc(string name) { + const char* src = name.c_str(); + uLong crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, (unsigned const char*)src, strlen(src)) + 1; + return int32(crc)-1; +} diff --git a/old/common/misc.h b/old/common/misc.h new file mode 100644 index 0000000..4107eb9 --- /dev/null +++ b/old/common/misc.h @@ -0,0 +1,65 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef _MISC_H + +#define _MISC_H +#include +#include +#include + +using namespace std; + +#define ITEMFIELDCOUNT 116 + +void Unprotect(string &s, char what); + +void Protect(string &s, char what); + +bool ItemParse(const char *data, int length, map > &items, int id_pos, int name_pos, int max_field, int level=0); + +int Tokenize(string s, map & tokens, char delim='|'); + +void LoadItemDBFieldNames(); + +void encode_length(unsigned long length, char *out); +unsigned long decode_length(char *in); +unsigned long encode(char *in, unsigned long length, char *out); +void decode(char *in, char *out); +void encode_chunk(char *in, int len, char *out); +void decode_chunk(char *in, char *out); + +int Deflate(unsigned char* in_data, int in_length, unsigned char* out_data, int max_out_length); +int Inflate(unsigned char* indata, int indatalen, unsigned char* outdata, int outdatalen, bool iQuiet=true); +#ifndef WIN32 +int print_stacktrace(); +#endif + +bool alpha_check(unsigned char val); + +void dump_message_column(unsigned char *buffer, unsigned long length, string leader="", FILE *to = stdout); +string string_from_time(string pattern, time_t now=0); +string timestamp(time_t now=0); +string long2ip(unsigned long ip); +string pop_arg(string &s, string seps, bool obey_quotes); +int EQsprintf(char *buffer, const char *pattern, const char *arg1, const char *arg2, const char *arg3, const char *arg4, const char *arg5, const char *arg6, const char *arg7, const char *arg8, const char *arg9); +unsigned int GetSpellNameCrc(const char* src); +int GetItemNameCrc(string item_name); +unsigned int GetNameCrc(string name); +#endif diff --git a/old/common/misc.hpp b/old/common/misc.hpp deleted file mode 100644 index 600ace7..0000000 --- a/old/common/misc.hpp +++ /dev/null @@ -1,327 +0,0 @@ -// EQ2Emulator: Everquest II Server Emulator, Copyright (C) 2007 EQ2EMulator Development Team, GPL v3+ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std; - -#define ITEMFIELDCOUNT 116 -#define ENC(c) (((c) & 0x3f) + ' ') -#define DEC(c) (((c) - ' ') & 0x3f) - -// Global map for database field names -extern map DBFieldNames; - -// Removes protection characters from string -void Unprotect(string &s, char what); - -// Adds protection characters to string -void Protect(string &s, char what); - -// Parses item data from raw string into structured map -bool ItemParse(const char *data, int length, map> &items, int id_pos, int name_pos, int max_field, int level = 0); - -// Tokenizes string using delimiter into numbered map -int Tokenize(string s, map &tokens, char delim = '|'); - -// Loads item database field names into global map -void LoadItemDBFieldNames(); - -// Encodes length value into buffer -void encode_length(unsigned long length, char *out); - -// Decodes length value from buffer -unsigned long decode_length(char *in); - -// Encodes input buffer to output with specified length -unsigned long encode(char *in, unsigned long length, char *out); - -// Decodes input buffer to output -void decode(char *in, char *out); - -// Encodes chunk of specified length -void encode_chunk(char *in, int len, char *out); - -// Decodes chunk from input to output -void decode_chunk(char *in, char *out); - -// Compresses data using zlib deflate algorithm -int Deflate(unsigned char* in_data, int in_length, unsigned char* out_data, int max_out_length) -{ - z_stream zstream; - int zerror; - - zstream.next_in = in_data; - zstream.avail_in = in_length; - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - deflateInit(&zstream, Z_FINISH); - zstream.next_out = out_data; - zstream.avail_out = max_out_length; - zerror = deflate(&zstream, Z_FINISH); - - if (zerror == Z_STREAM_END) { - deflateEnd(&zstream); - return zstream.total_out; - } - else { - cout << "Error: Deflate: deflate() returned " << zerror << " '"; - if (zstream.msg) - cout << zstream.msg; - cout << "'" << endl; - zerror = deflateEnd(&zstream); - return 0; - } -} - -// Decompresses data using zlib inflate algorithm -int Inflate(unsigned char* indata, int indatalen, unsigned char* outdata, int outdatalen, bool iQuiet = true) -{ - z_stream zstream; - int zerror = 0; - int i; - - zstream.next_in = indata; - zstream.avail_in = indatalen; - zstream.next_out = outdata; - zstream.avail_out = outdatalen; - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - - i = inflateInit2(&zstream, 15); - if (i != Z_OK) { - return 0; - } - - zerror = inflate(&zstream, Z_FINISH); - - if (zerror == Z_STREAM_END) { - inflateEnd(&zstream); - return zstream.total_out; - } - else { - if (!iQuiet) { - cout << "Error: Inflate: inflate() returned " << zerror << " '"; - if (zstream.msg) - cout << zstream.msg; - cout << "'" << endl; - } - - if (zerror == Z_DATA_ERROR || zerror == Z_ERRNO) - return -1; - - if (zerror == Z_MEM_ERROR && zstream.msg == 0) { - return 0; - } - - zerror = inflateEnd(&zstream); - return 0; - } -} - -// Prints stack trace for debugging purposes (Linux only) -int print_stacktrace() -{ - void *ba[20]; - int n = backtrace(ba, 20); - if (n != 0) { - char **names = backtrace_symbols(ba, n); - if (names != NULL) { - int i; - cerr << "called from " << (char*)names[0] << endl; - for (i = 1; i < n; ++i) - cerr << " " << (char*)names[i] << endl; - free(names); - } - } - return 0; -} - -// Checks if character is alphabetic (A-Z, a-z) -bool alpha_check(unsigned char val) -{ - if ((val >= 0x41 && val <= 0x5A) || (val >= 0x61 && val <= 0x7A)) - return true; - else - return false; -} - -// Dumps binary data in hexadecimal column format for debugging -void dump_message_column(unsigned char *buffer, unsigned long length, string leader = "", FILE *to = stdout) -{ - unsigned long i, j; - unsigned long rows, offset = 0; - rows = (length / 16) + 1; - for (i = 0; i < rows; i++) { - fprintf(to, "%s%05ld: ", leader.c_str(), i * 16); - for (j = 0; j < 16; j++) { - if (j == 8) - fprintf(to, "- "); - if (offset + j < length) - fprintf(to, "%02x ", *(buffer + offset + j)); - else - fprintf(to, " "); - } - fprintf(to, "| "); - for (j = 0; j < 16; j++, offset++) { - if (offset < length) { - char c = *(buffer + offset); - fprintf(to, "%c", isprint(c) ? c : '.'); - } - } - fprintf(to, "\n"); - } -} - -// Converts unsigned long IP address to dotted decimal string format -string long2ip(unsigned long ip) -{ - char temp[16]; - union { - unsigned long ip; - struct { - unsigned char a, b, c, d; - } octet; - } ipoctet; - - ipoctet.ip = ip; - sprintf(temp, "%d.%d.%d.%d", ipoctet.octet.a, ipoctet.octet.b, ipoctet.octet.c, ipoctet.octet.d); - - return string(temp); -} - -// Formats time using pattern string, returns formatted time string -string string_from_time(string pattern, time_t now = 0) -{ - struct tm *now_tm; - char time_string[51]; - - if (!now) - time(&now); - now_tm = localtime(&now); - - strftime(time_string, 51, pattern.c_str(), now_tm); - - return string(time_string); -} - -// Returns timestamp string in format [YYYYMMDD.HHMMSS] -string timestamp(time_t now = 0) -{ - return string_from_time("[%Y%m%d.%H%M%S] ", now); -} - -// Extracts and returns first argument from string, removes it from source -string pop_arg(string &s, string seps, bool obey_quotes = false) -{ - string ret; - unsigned long i; - bool in_quote = false; - - unsigned long length = s.length(); - for (i = 0; i < length; i++) { - char c = s[i]; - if (c == '"' && obey_quotes) { - in_quote = !in_quote; - } - if (in_quote) - continue; - if (seps.find(c) != string::npos) { - break; - } - } - - if (i == length) { - ret = s; - s = ""; - } - else { - ret = s.substr(0, i); - s.erase(0, i + 1); - } - - return ret; -} - -// Custom sprintf implementation supporting %1-%9 argument substitution -int EQsprintf(char *buffer, const char *pattern, const char *arg1, const char *arg2, const char *arg3, const char *arg4, const char *arg5, const char *arg6, const char *arg7, const char *arg8, const char *arg9) -{ - const char *args[9], *ptr; - char *bptr; - args[0] = arg1; - args[1] = arg2; - args[2] = arg3; - args[3] = arg4; - args[4] = arg5; - args[5] = arg6; - args[6] = arg7; - args[7] = arg8; - args[8] = arg9; - for (ptr = pattern, bptr = buffer; *ptr;) { - switch (*ptr) { - case '%': - ptr++; - switch (*ptr) { - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - strcpy(bptr, args[*ptr - '0' - 1]); - bptr += strlen(args[*ptr - '0' - 1]); - break; - } - break; - default: - *bptr = *ptr; - bptr++; - } - ptr++; - } - - *bptr = 0; - return (bptr - buffer); -} - -// Calculates CRC32 checksum for spell name string -unsigned int GetSpellNameCrc(const char* src) -{ - if (!src) - return 0; - uLong crc = crc32(0L, Z_NULL, 0); - return crc32(crc, (unsigned const char*)src, strlen(src)); -} - -// Calculates negative CRC32 checksum for item name string -int GetItemNameCrc(string item_name) -{ - const char *src = item_name.c_str(); - uLong crc = crc32(0L, Z_NULL, 0); - crc = crc32(crc, (unsigned const char *)src, strlen(src)) + 1; - return int32_t(crc) * -1; -} - -// Calculates CRC32 checksum for general name string -unsigned int GetNameCrc(string name) -{ - const char* src = name.c_str(); - uLong crc = crc32(0L, Z_NULL, 0); - crc = crc32(crc, (unsigned const char*)src, strlen(src)) + 1; - return int32_t(crc) - 1; -} \ No newline at end of file diff --git a/old/common/misc_functions.hpp b/old/common/misc_functions.hpp deleted file mode 100644 index 186f3ac..0000000 --- a/old/common/misc_functions.hpp +++ /dev/null @@ -1,1154 +0,0 @@ -// EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) - GPL v3 - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "log.hpp" -#include "types.hpp" -#include "debug.hpp" -#include "timer.hpp" -#include "separator.hpp" -#include "packet/packet_dump.hpp" - -#ifndef ERRBUF_SIZE -#define ERRBUF_SIZE 1024 -#endif - -#ifndef _ITOA_BUFLEN -#define _ITOA_BUFLEN 25 -#endif - -#ifndef PATCHER -extern std::map EQOpcodeVersions; -#endif - -using namespace std; - -// Function declarations -int32 hextoi(char* num); -int64 hextoi64(char* num); -sint32 filesize(FILE* fp); -int32 ResolveIP(const char* hostname, char* errbuf = 0); -void CoutTimestamp(bool ms = true); -string loadInt32String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_32BitString* eq_string = nullptr); -string loadInt16String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_16BitString* eq_string = nullptr); -string loadInt8String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_8BitString* eq_string = nullptr); -sint16 storeInt32String(uchar* buffer, int16 buffer_size, string in_str); -sint16 storeInt16String(uchar* buffer, int16 buffer_size, string in_str); -sint16 storeInt8String(uchar* buffer, int16 buffer_size, string in_str); -int MakeRandomInt(int low, int high); -float MakeRandomFloat(float low, float high); -float TransformToFloat(sint16 data, int8 bits); -sint16 TransformFromFloat(float data, int8 bits); -int32 GenerateEQ2Color(float* r, float* g, float* b); -int32 GenerateEQ2Color(float* rgb[3]); -void SetColor(EQ2_Color* color, long data); -int8 MakeInt8(float* input); -bool Unpack(int32 srcLen, uchar* data, uchar* dst, int16 dstLen, int16 version = 0, bool reverse = true); -bool Unpack(uchar* data, uchar* dst, int16 dstLen, int16 version = 0, bool reverse = true); -int32 Pack(uchar* data, uchar* src, int16 srcLen, int16 dstLen, int16 version = 0, bool reverse = true); -void Reverse(uchar* input, int32 srcLen); -void Encode(uchar* dst, uchar* src, int16 len); -void Decode(uchar* dst, uchar* src, int16 len); -string ToUpper(string input); -string ToLower(string input); -int32 ParseIntValue(string input); -int64 ParseLongLongValue(string input); -std::map TranslateBrokerRequest(string request); -void MovementDecode(uchar* dst, uchar* newval, uchar* orig, int16 len); -vector* SplitString(string str, char delim); -int8 DoOverLoad(int32 val, uchar* data); -int8 CheckOverLoadSize(int32 val); -int32 CountWordsInString(const char* text); -bool IsNumber(const char *num); -void PrintSep(Seperator *sep, const char *name = 0); -string GetDeviceName(string device); -int32 GetDeviceID(string device); -int16 GetItemPacketType(int32 version); -int16 GetOpcodeVersion(int16 version); -void SleepMS(int32 milliseconds); -size_t strlcpy(char *dst, const char *src, size_t size); -float short_to_float(const ushort x); -uint32 float_to_int(const float x); -uint32 as_uint(const float x); -float as_float(const uint32 x); -int64 getCurrentTimestamp(); -std::tuple convertTimestampDuration(int64 total_milliseconds); -bool INIReadBool(FILE *f, const char *section, const char *property, bool *out); -bool INIReadInt(FILE *f, const char *section, const char *property, int *out); -const char* itoa(int value); -char* itoa(int value, char *result, int base); - -// Template function definitions -template -void AddData(Type input, string* datastring) -{ - if(datastring) - datastring->append((char*)&input, sizeof(input)); -} - -template -void AddData(Type input, int32 array_size, string* datastring) -{ - if(array_size > 0) { - for(int32 i = 0; i < array_size; i++) - AddData(input[i], datastring); - } - else - AddData(input, datastring); -} - -template -class AutoDelete -{ -public: - AutoDelete(T** iVar, T* iSetTo = 0) - { - init(iVar, iSetTo); - } - - AutoDelete() {} - - void init(T** iVar, T* iSetTo = 0) - { - pVar = iVar; - if (iSetTo) - *pVar = iSetTo; - } - - ~AutoDelete() - { - safe_delete(*pVar); - } - -private: - T** pVar; // Pointer to the variable being managed -}; - -class VersionRange -{ -public: - VersionRange(int32 in_min_version, int32 in_max_version) - { - min_version = in_min_version; - max_version = in_max_version; - } - - int32 GetMinVersion() { return min_version; } - int32 GetMaxVersion() { return max_version; } - -private: - int32 min_version; // Minimum supported version - int32 max_version; // Maximum supported version -}; - -// Utility function to check if IP address is private -static bool IsPrivateAddress(uint32_t ip) -{ - uint8_t b1, b2; - b1 = (uint8_t)(ip >> 24); - b2 = (uint8_t)((ip >> 16) & 0x0ff); - - // 10.x.y.z - if (b1 == 10) - return true; - - // 172.16.0.0 - 172.31.255.255 - if ((b1 == 172) && (b2 >= 16) && (b2 <= 31)) - return true; - - // 192.168.0.0 - 192.168.255.255 - if ((b1 == 192) && (b2 == 168)) - return true; - - return false; -} - -// Implementation - -// Outputs current timestamp to cout with optional millisecond precision -void CoutTimestamp(bool ms) -{ - time_t rawtime; - struct tm* gmt_t; - time(&rawtime); - gmt_t = gmtime(&rawtime); - - struct timeval read_time; - gettimeofday(&read_time, 0); - - cout << (gmt_t->tm_year + 1900) << "/" << setw(2) << setfill('0') << (gmt_t->tm_mon + 1) << "/" << setw(2) << setfill('0') << gmt_t->tm_mday << " " << setw(2) << setfill('0') << gmt_t->tm_hour << ":" << setw(2) << setfill('0') << gmt_t->tm_min << ":" << setw(2) << setfill('0') << gmt_t->tm_sec; - if (ms) - cout << "." << setw(3) << setfill('0') << (read_time.tv_usec / 1000); - cout << " GMT"; -} - -// Loads a 32-bit length prefixed string from buffer at specified position -string loadInt32String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_32BitString* eq_string) -{ - buffer += *pos; - int32 size = *(int32*)buffer; - if((size + *pos + sizeof(int16)) > buffer_size) { - cout << "Error in loadInt32String: Corrupt packet.\n"; - return string(""); - } - buffer += sizeof(int32); - string ret((char*)buffer, 0, size); - if(eq_string) { - eq_string->size = size; - eq_string->data = ret; - } - *pos += (size + sizeof(int32)); - return ret; -} - -// Loads a 16-bit length prefixed string from buffer at specified position -string loadInt16String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_16BitString* eq_string) -{ - buffer += *pos; - int16 size = *(int16*)buffer; - if((size + *pos + sizeof(int16)) > buffer_size) { - cout << "Error in loadInt16String: Corrupt packet.\n"; - return string(""); - } - buffer += sizeof(int16); - string ret((char*)buffer, 0, size); - if(eq_string) { - eq_string->size = size; - eq_string->data = ret; - } - *pos += (size + sizeof(int16)); - return ret; -} - -// Loads an 8-bit length prefixed string from buffer at specified position -string loadInt8String(uchar* buffer, int16 buffer_size, int16* pos, EQ2_8BitString* eq_string) -{ - buffer += *pos; - int8 size = *(int8*)buffer; - if((size + *pos + sizeof(int16)) > buffer_size) { - cout << "Error in loadInt8String: Corrupt packet.\n"; - return string(""); - } - buffer += sizeof(int8); - string ret((char*)buffer, 0, size); - if(eq_string) { - eq_string->size = size; - eq_string->data = ret; - } - *pos += (size + sizeof(int8)); - return ret; -} - -// Stores a string with 32-bit length prefix into buffer -sint16 storeInt32String(uchar* buffer, int16 buffer_size, string in_str) -{ - sint16 string_size = in_str.length(); - if((string_size + sizeof(int32)) > buffer_size) - return -1; - memcpy(buffer, &string_size, sizeof(int32)); - buffer += sizeof(int32); - memcpy(buffer, in_str.c_str(), string_size); - buffer += string_size; - return (buffer_size - (string_size + sizeof(int32))); -} - -// Stores a string with 16-bit length prefix into buffer -sint16 storeInt16String(uchar* buffer, int16 buffer_size, string in_str) -{ - sint16 string_size = in_str.length(); - if((string_size + sizeof(int16)) > buffer_size) - return -1; - memcpy(buffer, &string_size, sizeof(int16)); - buffer += sizeof(int16); - memcpy(buffer, in_str.c_str(), string_size); - buffer += string_size; - return (buffer_size - (string_size + sizeof(int16))); -} - -// Stores a string with 8-bit length prefix into buffer -sint16 storeInt8String(uchar* buffer, int16 buffer_size, string in_str) -{ - sint16 string_size = in_str.length(); - if((string_size + sizeof(int8)) > buffer_size) - return -1; - memcpy(buffer, &string_size, sizeof(int8)); - buffer += sizeof(int8); - memcpy(buffer, in_str.c_str(), string_size); - buffer += string_size; - return (buffer_size - (string_size + sizeof(int8))); -} - -// Returns file size for given file pointer -sint32 filesize(FILE* fp) -{ - struct stat file_stat; - fstat(fileno(fp), &file_stat); - return (sint32) file_stat.st_size; -} - -// Resolves hostname to IP address, returns 0 on failure -int32 ResolveIP(const char* hostname, char* errbuf) -{ - if (errbuf) - errbuf[0] = 0; - if (hostname == 0) { - if (errbuf) - snprintf(errbuf, ERRBUF_SIZE, "ResolveIP(): hostname == 0"); - return 0; - } - - struct sockaddr_in server_sin; - struct hostent *phostent = nullptr; - - server_sin.sin_family = AF_INET; - if ((phostent = gethostbyname(hostname)) == nullptr) { - if (errbuf) - snprintf(errbuf, ERRBUF_SIZE, "Unable to get the host name. Error: %s", strerror(errno)); - return 0; - } - - memcpy((char*)&(server_sin.sin_addr), phostent->h_addr, phostent->h_length); - return server_sin.sin_addr.s_addr; -} - -// Converts integer to string representation -const char* itoa(int value) -{ - static char temp[_ITOA_BUFLEN]; - memset(temp, 0, _ITOA_BUFLEN); - snprintf(temp, _ITOA_BUFLEN, "%d", value); - return temp; -} - -// Converts integer to string representation with specified base -char* itoa(int value, char *result, int base) -{ - char *ptr1, *ptr2; - char c; - int tmp_value; - - // Need a valid base - if (base < 2 || base > 36) { - *result = '\0'; - return result; - } - - ptr1 = ptr2 = result; - do { - tmp_value = value; - value /= base; - *ptr1++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; - } - while (value > 0); - - // Apply a negative sign if needed - if (tmp_value < 0) - *ptr1++ = '-'; - - *ptr1-- = '\0'; - while (ptr2 < ptr1) { - c = *ptr1; - *ptr1-- = *ptr2; - *ptr2++ = c; - } - - return result; -} - -// Generate a random integer in the range low-high (inclusive) -int MakeRandomInt(int low, int high) -{ - return (int)MakeRandomFloat((double)low, (double)high + 0.999); -} - -// Converts hexadecimal string to 32-bit integer -int32 hextoi(char* num) -{ - int len = strlen(num); - if (len < 3) - return 0; - - if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) - return 0; - - int32 ret = 0; - int mul = 1; - for (int i = len - 1; i >= 2; i--) { - if (num[i] >= 'A' && num[i] <= 'F') - ret += ((num[i] - 'A') + 10) * mul; - else if (num[i] >= 'a' && num[i] <= 'f') - ret += ((num[i] - 'a') + 10) * mul; - else if (num[i] >= '0' && num[i] <= '9') - ret += (num[i] - '0') * mul; - else - return 0; - mul *= 16; - } - return ret; -} - -// Converts hexadecimal string to 64-bit integer -int64 hextoi64(char* num) -{ - int len = strlen(num); - if (len < 3) - return 0; - - if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) - return 0; - - int64 ret = 0; - int mul = 1; - for (int i = len - 1; i >= 2; i--) { - if (num[i] >= 'A' && num[i] <= 'F') - ret += ((num[i] - 'A') + 10) * mul; - else if (num[i] >= 'a' && num[i] <= 'f') - ret += ((num[i] - 'a') + 10) * mul; - else if (num[i] >= '0' && num[i] <= '9') - ret += (num[i] - '0') * mul; - else - return 0; - mul *= 16; - } - return ret; -} - -// Generate a random float between low and high values with thread-safe random generator -float MakeRandomFloat(float low, float high) -{ - // Handle edge case where range is zero or inverted - float diff = high - low; - if(!diff) return low; - - if (low == high) return low; - if (low > high) std::swap(low, high); - - // Use a thread-local random generator for thread safety - thread_local std::mt19937 generator(std::random_device{}()); // Seed once per thread - std::uniform_real_distribution distribution(low, high); - - return distribution(generator); -} - -// Generates EQ2 color value from RGB float components -int32 GenerateEQ2Color(float* r, float* g, float* b) -{ - int8 rgb[4] = {0}; - rgb[0] = (int8)((*r) * 255); - rgb[1] = (int8)((*b) * 255); - rgb[2] = (int8)((*g) * 255); - int32 color = 0; - memcpy(&color, rgb, sizeof(int32)); - return color; -} - -// Generates EQ2 color value from RGB float array -int32 GenerateEQ2Color(float* rgb[3]) -{ - return GenerateEQ2Color(rgb[0], rgb[1], rgb[2]); -} - -// Converts float input to 8-bit integer value -int8 MakeInt8(float* input) -{ - float input2 = *input; - if(input2 < 0) - input2 *= -1; - return (int8)(input2 * 255); -} - -// Splits string by delimiter and returns vector of substrings -vector* SplitString(string str, char delim) -{ - vector* results = new vector; - int32 pos; - while((pos = str.find_first_of(delim)) != str.npos) { - if(pos > 0) { - results->push_back(str.substr(0, pos)); - } - if(str.length() > pos) - str = str.substr(pos + 1); - else - break; - } - if(str.length() > 0) - results->push_back(str); - return results; -} - -// Unpacks compressed data with optional reversal -bool Unpack(uchar* data, uchar* dst, int16 dstLen, int16 version, bool reverse) -{ - int32 srcLen = 0; - memcpy(&srcLen, data, sizeof(int32)); - return Unpack(srcLen, data + 4, dst, dstLen, version, reverse); -} - -// Unpacks compressed data from source buffer to destination buffer -bool Unpack(int32 srcLen, uchar* data, uchar* dst, int16 dstLen, int16 version, bool reverse) -{ - if(reverse) - Reverse(data, srcLen); - int16 pos = 0; - int16 real_pos = 0; - while(srcLen && pos < dstLen) { - if(srcLen >= 0 && !srcLen--) - return false; - int8 code = data[real_pos++]; - - if(code >= 128) { - for(int8 index = 0; index < 7; index++) { - if(code & 1) { - if(pos >= dstLen) - return false; - if(srcLen >= 0 && !srcLen--) - return false; - dst[pos++] = data[real_pos++]; - } else { - if(pos < dstLen) dst[pos++] = 0; - } - code >>= 1; - } - } else { - if(pos + code > dstLen) - return false; - memset(dst + pos, 0, code); - pos += code; - } - } - return srcLen <= 0; -} - -// Packs source data into compressed format -int32 Pack(uchar* data, uchar* src, int16 srcLen, int16 dstLen, int16 version, bool reverse) -{ - int16 real_pos = 4; - int32 pos = 0; - int32 code = 0; - int codePos = 0; - int codeLen = 0; - int8 zeroLen = 0; - memset(data, 0, dstLen); - if (version > 1 && version <= 374) - reverse = false; - while(pos < srcLen) { - if(src[pos] || codeLen) { - if(!codeLen) { - if (zeroLen) { - data[real_pos++] = zeroLen; - zeroLen = 0; - } - codePos = real_pos; - code = 0; - data[real_pos++] = 0; - } - if(src[pos]) { - data[real_pos++] = src[pos]; - code |= 0x80; - } - code >>= 1; - codeLen++; - - if(codeLen == 7) { - data[codePos] = int8(0x80 | code); - codeLen = 0; - } - } else { - if(zeroLen == 0x7F) { - data[real_pos++] = zeroLen; - zeroLen = 0; - } - zeroLen++; - } - pos++; - } - if(codeLen) { - code >>= (7 - codeLen); - data[codePos] = int8(0x80 | code); - } else if(zeroLen) { - data[real_pos++] = zeroLen; - } - if(reverse) - Reverse(data + 4, real_pos - 4); - int32 dataLen = real_pos - 4; - memcpy(&data[0], &dataLen, sizeof(int32)); - return dataLen + 4; -} - -// Reverses byte order in packed data segments -void Reverse(uchar* input, int32 srcLen) -{ - int16 real_pos = 0; - int16 orig_pos = 0; - int8 reverse_count = 0; - while(srcLen > 0 && srcLen < 0xFFFFFFFF) { // XXX it was >=0 before. but i think it was a bug - int8 code = input[real_pos++]; - srcLen--; - if(code >= 128) { - for(int8 index = 0; index < 7; index++) { - if(code & 1) { - if(srcLen >= 0 && !srcLen--) - return; - real_pos++; - reverse_count++; - } - code >>= 1; - } - } - if(reverse_count > 0) { - int8 tmp_data[8] = {0}; - for(int8 i = 0; i < reverse_count; i++) { - tmp_data[i] = input[orig_pos + reverse_count - i]; - } - memcpy(input + orig_pos + 1, tmp_data, reverse_count); - reverse_count = 0; - } - orig_pos = real_pos; - } -} - -// Decodes movement data using XOR operation -void MovementDecode(uchar* dst, uchar* newval, uchar* orig, int16 len) -{ - int16 pos = len; - while(pos--) - dst[pos] = newval[pos] ^ orig[pos]; -} - -// Decodes data using XOR operation and updates source -void Decode(uchar* dst, uchar* src, int16 len) -{ - int16 pos = len; - while(pos--) - dst[pos] ^= src[pos]; - memcpy(src, dst, len); -} - -// Encodes data using XOR operation -void Encode(uchar* dst, uchar* src, int16 len) -{ - uchar* data = new uchar[len]; - int16 pos = len; - while(pos--) - data[pos] = int8(src[pos] ^ dst[pos]); - memcpy(src, dst, len); - memcpy(dst, data, len); - safe_delete_array(data); -} - -// Transforms 16-bit integer to float using bit shifting -float TransformToFloat(sint16 data, int8 bits) -{ - return (float)(data / (float)(1 << bits)); -} - -// Transforms float to 16-bit integer using bit shifting -sint16 TransformFromFloat(float data, int8 bits) -{ - return (sint16)(data * (1 << bits)); -} - -// Sets EQ2 color structure from long data value -void SetColor(EQ2_Color* color, long data) -{ - memcpy(color, &data, sizeof(EQ2_Color)); -} - -// Converts string to uppercase -string ToUpper(string input) -{ - string ret = input; - transform(input.begin(), input.end(), ret.begin(), ::toupper); - return ret; -} - -// Converts string to lowercase -string ToLower(string input) -{ - string ret = input; - transform(input.begin(), input.end(), ret.begin(), ::tolower); - return ret; -} - -// Parses string to integer value, returns 0xFFFFFFFF on error -int32 ParseIntValue(string input) -{ - int32 ret = 0xFFFFFFFF; - try { - if(input.length() > 0) { - ret = atoul(input.c_str()); - } - } - catch(...) {} - return ret; -} - -// Parses string to 64-bit integer value -int64 ParseLongLongValue(string input) -{ - int64 ret = 0xFFFFFFFFFFFFFFFF; - try { - if(input.length() > 0) { - ret = strtoull(input.c_str(), 0, 10); - } - } - catch(...) {} - return ret; -} - -// Translates broker request string into key-value map -std::map TranslateBrokerRequest(string request) -{ - std::map ret; - string key; - string value; - int32 start_pos = 0; - int32 end_pos = 0; - int32 pos = request.find("="); - bool str_val = false; - while(pos < 0xFFFFFFFF) { - str_val = false; - key = request.substr(start_pos, pos - start_pos); - if(request.find("|", pos) == pos + 1) { - pos++; - end_pos = request.find("|", pos + 1); - str_val = true; - } - else - end_pos = request.find(" ", pos); - if(end_pos < 0xFFFFFFFF) { - value = request.substr(pos + 1, end_pos - pos - 1); - start_pos = end_pos + 1; - if(str_val) { - start_pos++; - ret[key] = ToLower(value); - } - else - ret[key] = value; - pos = request.find("=", start_pos); - } - else { - value = request.substr(pos + 1); - if(str_val) { - start_pos++; - ret[key] = ToLower(value); - } - else - ret[key] = value; - break; - } - } - return ret; -} - -// Checks size needed for overload encoding -int8 CheckOverLoadSize(int32 val) -{ - int8 ret = 1; - if(val >= 0xFFFF) // int32 - ret = sizeof(int16) + sizeof(int32); - else if(val >= 0xFF) - ret = sizeof(int8) + sizeof(int16); - return ret; -} - -// Encodes value using overload format -int8 DoOverLoad(int32 val, uchar* data) -{ - int8 ret = 1; - if(val >= 0xFFFF) { // int32 - memset(data, 0xFF, sizeof(int16)); - memcpy(data + sizeof(int16), &val, sizeof(int32)); - ret = sizeof(int16) + sizeof(int32); - } - else if(val >= 0xFF) { // int16 - memset(data, 0xFF, sizeof(int8)); - memcpy(data + sizeof(int8), &val, sizeof(int16)); - ret = sizeof(int8) + sizeof(int16); - } - else - memcpy(data, &val, sizeof(int8)); - return ret; -} - -// Counts words in text string, treating contiguous spaces as one space -int32 CountWordsInString(const char* text) -{ - int32 words = 0; - if (text && strlen(text) > 0) { - bool on_word = false; - for (int32 i = 0; i < strlen(text); i++) { - char letter = text[i]; - if (on_word && !((letter >= 48 && letter <= 57) || (letter >= 65 && letter <= 90) || (letter >= 97 && letter <= 122))) - on_word = false; - else if (!on_word && ((letter >= 48 && letter <= 57) || (letter >= 65 && letter <= 90) || (letter >= 97 && letter <= 122))) { - on_word = true; - words++; - } - } - } - return words; -} - -// Checks if string contains only numeric characters -bool IsNumber(const char *num) -{ - size_t len, i; - - if (!num) - return false; - - len = strlen(num); - if (len == 0) - return false; - - for (i = 0; i < len; i++) { - if (!isdigit(num[i])) - return false; - } - - return true; -} - -// Prints separator contents for debugging -void PrintSep(Seperator *sep, const char *name) -{ - int32 i = 0; - - LogWrite(MISC__DEBUG, 0, "Misc", "Printing sep %s", name ? name : "No Name"); - if (!sep) - LogWrite(MISC__DEBUG, 0, "Misc", "\tSep is null"); - else { - while (sep->arg[i] && strlen(sep->arg[i]) > 0) { - LogWrite(MISC__DEBUG, 0, "Misc", "\t%i => %s", i, sep->arg[i]); - i++; - } - } -} - -#define INI_IGNORE(c) (c == '\n' || c == '\r' || c == '#') - -// Seeks to specified section in INI file -static bool INIGoToSection(FILE *f, const char *section) -{ - size_t size = strlen(section) + 3; - char line[256], *buf, *tmp; - bool found = false; - - if ((buf = (char *)malloc(size)) == nullptr) { - fprintf(stderr, "%s: %u: Unable to allocate %zu bytes\n", __FUNCTION__, __LINE__, size); - return false; - } - - sprintf(buf, "[%s]", section); - - while (fgets(line, sizeof(line), f) != nullptr) { - if (INI_IGNORE(line[0])) - continue; - - if (line[0] == '[') { - if ((tmp = strstr(line, "\n")) != nullptr) - *tmp = '\0'; - if ((tmp = strstr(line, "\r")) != nullptr) - *tmp = '\0'; - - if (strcasecmp(buf, line) == 0) { - found = true; - break; - } - } - } - - free(buf); - return found; -} - -// Finds property value in INI file section -static char* INIFindValue(FILE *f, const char *section, const char *property) -{ - char line[256], *key, *val; - - if (section != nullptr && !INIGoToSection(f, section)) - return nullptr; - - while (fgets(line, sizeof(line), f) != nullptr) { - if (INI_IGNORE(line[0])) - continue; - - if (section != nullptr && line[0] == '[') - return nullptr; - - if ((key = strtok(line, "=")) == nullptr) - continue; - - if (strcasecmp(key, property) == 0) { - val = strtok(nullptr, "\n\r"); - - if (val == nullptr) - return nullptr; - - return strdup(val); - } - } - - return nullptr; -} - -// Reads integer value from INI file -bool INIReadInt(FILE *f, const char *section, const char *property, int *out) -{ - char *value; - - rewind(f); - - if ((value = INIFindValue(f, section, property)) == nullptr) - return false; - - if (!IsNumber(value)) { - free(value); - return false; - } - - *out = atoi(value); - free(value); - - return true; -} - -// Reads boolean value from INI file -bool INIReadBool(FILE *f, const char *section, const char *property, bool *out) -{ - char *value; - - rewind(f); - - if ((value = INIFindValue(f, section, property)) == nullptr) - return false; - - *out = (strcasecmp(value, "1") == 0 || strcasecmp(value, "true") == 0 || strcasecmp(value, "on") == 0 || strcasecmp(value, "yes") == 0); - free(value); - - return true; -} - -// Converts device string to display name -string GetDeviceName(string device) -{ - if (device == "chemistry_table") - device = "Chemistry Table"; - else if (device == "work_desk") - device = "Engraved Desk"; - else if (device == "forge") - device = "Forge"; - else if (device == "stove and keg") - device = "Stove & Keg"; - else if (device == "sewing_table") - device = "Sewing Table & Mannequin"; - else if (device == "woodworking_table") - device = "Woodworking Table"; - else if (device == "work_bench") - device = "Work Bench"; - else if (device == "crafting_intro_anvil") - device = "Mender's Anvil"; - return device; -} - -// Gets numeric device ID from device name -int32 GetDeviceID(string device) -{ - if (device == "Chemistry Table") - return 3; - else if (device == "Engraved Desk") - return 4; - else if (device == "Forge") - return 2; - else if (device == "Stove & Keg") - return 7; - else if (device == "Sewing Table & Mannequin") - return 1; - else if (device == "Woodworking Table") - return 6; - else if (device == "Work Bench") - return 5; - else if (device == "Mender's Anvil") - return 0xFFFFFFFF; - return 0; -} - -// Gets item packet type for specified client version -int16 GetItemPacketType(int32 version) -{ - int16 item_version; - if (version >= 64707) - item_version = 0x5CFE; - else if (version >= 63119) - item_version = 0x56FE; - else if (version >= 60024) - item_version = 0x51FE; - else if (version >= 57107) - item_version = 0x4CFE; - else if (version >= 57048) - item_version = 0x48FE; - else if (version >= 1199) - item_version = 0x44FE; - else if (version >= 1195) - item_version = 0x40FE; - else if (version >= 1193) - item_version = 0x3FFE; - else if (version >= 1190) - item_version = 0x3EFE; - else if (version >= 1188) - item_version = 0x3DFE; - else if (version >= 1096) - item_version = 0x35FE; - else if (version >= 1027) - item_version = 0x31FE; - else if (version >= 1008) - item_version = 0x2CFE; - else if (version >= 927) - item_version = 0x23FE; - else if (version >= 893) - item_version = 0x22FE; - else if (version >= 860) - item_version = 0x20FE; - else if (version > 546) - item_version = 0x1CFE; - else - item_version = 0; - - return item_version; -} - -#ifndef PATCHER -// Gets opcode version range for specified client version -int16 GetOpcodeVersion(int16 version) -{ - int16 ret = version; - int16 version1 = 0; - int16 version2 = 0; - std::map::iterator itr; - for (itr = EQOpcodeVersions.begin(); itr != EQOpcodeVersions.end(); itr++) { - version1 = itr->first; - version2 = itr->second; - if (version >= version1 && version <= version2) { - ret = version1; - break; - } - } - - return ret; -} -#endif - -// Cross-platform sleep function -void SleepMS(int32 milliseconds) -{ - usleep(milliseconds * 1000); -} - -// Safe string copy with size limit -size_t strlcpy(char *dst, const char *src, size_t size) -{ - char *d = dst; - const char *s = src; - size_t n = size; - - if (n != 0 && --n != 0) { - do { - if ((*d++ = *s++) == 0) - break; - } while (--n != 0); - } - - if (n == 0) { - if (size != 0) - *d = '\0'; - while (*s++) - ; - } - - return(s - src - 1); -} - -// Converts 16-bit float to 32-bit float (IEEE-754 format) -float short_to_float(const ushort x) -{ - const uint32 e = (x & 0x7C00) >> 10; // exponent - const uint32 m = (x & 0x03FF) << 13; // mantissa - const uint32 v = as_uint((float)m) >> 23; // evil log2 bit hack to count leading zeros in denormalized format - return as_float((x & 0x8000) << 16 | (e != 0) * ((e + 112) << 23 | m) | ((e == 0) & (m != 0)) * ((v - 37) << 23 | ((m << (150 - v)) & 0x007FE000))); // sign : normalized : denormalized -} - -// Converts 32-bit float to 16-bit representation -uint32 float_to_int(const float x) -{ - const uint32 b = as_uint(x) + 0x00001000; // round-to-nearest-even: add last bit after truncated mantissa - const uint32 e = (b & 0x7F800000) >> 23; // exponent - const uint32 m = b & 0x007FFFFF; // mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding - return (b & 0x80000000) >> 16 | (e > 112) * ((((e - 112) << 10) & 0x7C00) | m >> 13) | ((e < 113) & (e > 101)) * ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) | (e > 143) * 0x7FFF; // sign : normalized : denormalized : saturate -} - -// Type punning: interprets float as uint32 -uint32 as_uint(const float x) -{ - return *(uint32*)&x; -} - -// Type punning: interprets uint32 as float -float as_float(const uint32 x) -{ - return *(float*)&x; -} - -// Gets current timestamp in milliseconds using steady clock -int64 getCurrentTimestamp() -{ - auto now = std::chrono::steady_clock::now(); - auto duration = std::chrono::duration_cast(now.time_since_epoch()); - return duration.count(); -} - -// Converts timestamp duration to days, hours, minutes, seconds -std::tuple convertTimestampDuration(int64 total_milliseconds) -{ - std::chrono::milliseconds duration(total_milliseconds); - // Convert to days, hours, minutes, and seconds - auto hours = std::chrono::duration_cast(duration); - duration -= hours; - - auto days = hours / 24; - hours -= days * 24; - - auto minutes = std::chrono::duration_cast(duration); - duration -= minutes; - - auto seconds = std::chrono::duration_cast(duration); - - // Return the result as a tuple - return std::make_tuple(days.count(), hours.count(), minutes.count(), seconds.count()); -} \ No newline at end of file diff --git a/old/common/op_codes.h b/old/common/op_codes.h new file mode 100644 index 0000000..c25e9a0 --- /dev/null +++ b/old/common/op_codes.h @@ -0,0 +1,44 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef _OP_CODES_H + +#define _OP_CODES_H + +static const char OP_SessionRequest = 0x01; +static const char OP_SessionResponse = 0x02; +static const char OP_Combined = 0x03; +static const char OP_SessionDisconnect = 0x05; +static const char OP_KeepAlive = 0x06; +static const char OP_ServerKeyRequest = 0x07; +static const char OP_SessionStatResponse= 0x08; +static const char OP_Packet = 0x09; +static const char OP_Fragment = 0x0d; +static const char OP_OutOfOrderAck = 0x11; +static const char OP_Ack = 0x15; +static const char OP_AppCombined = 0x19; +static const char OP_OutOfSession = 0x1d; + +#if defined(LOGIN) || defined(CHAT) + #define APP_OPCODE_SIZE 1 +#else + #define APP_OPCODE_SIZE 2 +#endif + +#endif diff --git a/old/common/opcodemgr.cpp b/old/common/opcodemgr.cpp new file mode 100644 index 0000000..6d57921 --- /dev/null +++ b/old/common/opcodemgr.cpp @@ -0,0 +1,350 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include "debug.h" +#include +#include +#include "opcodemgr.h" +//#include "debug.h" +#include "emu_opcodes.h" +#include "../common/Log.h" + +#if defined(SHARED_OPCODES) && !defined(EQ2) + #include "EMuShareMem.h" + extern LoadEMuShareMemDLL EMuShareMemDLL; +#endif + +#include +#include +using namespace std; + + +//#define DEBUG_TRANSLATE + + +OpcodeManager::OpcodeManager() { + loaded = false; +} +bool OpcodeManager::LoadOpcodesMap(map* eq, OpcodeSetStrategy *s, std::string* missingOpcodes){ + //do the mapping and store them in the shared memory array + bool ret = true; + EmuOpcode emu_op; + map::iterator res; + //stupid enum wont let me ++ on it... + + + for(emu_op = (EmuOpcode)(0); emu_op < _maxEmuOpcode; emu_op=(EmuOpcode)(emu_op+1)) { + //get the name of this emu opcode + const char *op_name = OpcodeNames[emu_op]; + if(op_name[0] == '\0') { + break; + } + + //find the opcode in the file + res = eq->find(op_name); + if(res == eq->end()) { + if(missingOpcodes) { + if(missingOpcodes->size() < 1) { + missingOpcodes->append(op_name); + } + else { + missingOpcodes->append(", " + std::string(op_name)); + } + } + else { + LogWrite(OPCODE__WARNING, 1, "Opcode", "Opcode %s is missing from the opcodes table.", op_name); + } + s->Set(emu_op, 0xFFFF); + continue; //continue to give them a list of all missing opcodes + } + + //ship the mapping off to shared mem. + s->Set(emu_op, res->second); + } + return ret; +} +bool OpcodeManager::LoadOpcodesFile(const char *filename, OpcodeSetStrategy *s) { + FILE *opf = fopen(filename, "r"); + if(opf == NULL) { + LogWrite(OPCODE__ERROR, 0, "Opcode", "Unable to open opcodes file '%s'. Thats bad.", filename); + return(false); + } + + map eq; + + //load the opcode file into eq, could swap in a nice XML parser here + char line[2048]; + int lineno = 0; + uint16 curop; + while(!feof(opf)) { + lineno++; + line[0] = '\0'; //for blank line at end of file + if(fgets(line, sizeof(line), opf) == NULL) + break; + + //ignore any line that dosent start with OP_ + if(line[0] != 'O' || line[1] != 'P' || line[2] != '_') + continue; + + char *num = line+3; //skip OP_ + //look for the = sign + while(*num != '=' && *num != '\0') { + num++; + } + //make sure we found = + if(*num != '=') { + LogWrite(OPCODE__ERROR, 0, "Opcode", "Malformed opcode line at %s:%d\n", filename, lineno); + continue; + } + *num = '\0'; //null terminate the name + num++; //num should point to the opcode + + //read the opcode + if(sscanf(num, "0x%hx", &curop) != 1) { + LogWrite(OPCODE__ERROR, 0, "Opcode", "Malformed opcode at %s:%d\n", filename, lineno); + continue; + } + + //we have a name and our opcode... stick it in the map + eq[line] = curop; + } + fclose(opf); + return LoadOpcodesMap(&eq, s); +} + +//convenience routines +const char *OpcodeManager::EmuToName(const EmuOpcode emu_op) { + if(emu_op > _maxEmuOpcode) + return "OP_Unknown"; + + return(OpcodeNames[emu_op]); +} + +const char *OpcodeManager::EQToName(const uint16 eq_op) { + //first must resolve the eq op to an emu op + EmuOpcode emu_op = EQToEmu(eq_op); + if(emu_op > _maxEmuOpcode) + return "OP_Unknown"; + + return(OpcodeNames[emu_op]); +} + +EmuOpcode OpcodeManager::NameSearch(const char *name) { + EmuOpcode emu_op; + //stupid enum wont let me ++ on it... + for(emu_op = (EmuOpcode)(0); emu_op < _maxEmuOpcode; emu_op=(EmuOpcode)(emu_op+1)) { + //get the name of this emu opcode + const char *op_name = OpcodeNames[emu_op]; + if(!strcasecmp(op_name, name)) { + return(emu_op); + } + } + return(OP_Unknown); +} + +RegularOpcodeManager::RegularOpcodeManager() +: MutableOpcodeManager() +{ + emu_to_eq = NULL; + eq_to_emu = NULL; + EQOpcodeCount = 0; + EmuOpcodeCount = 0; +} + +RegularOpcodeManager::~RegularOpcodeManager() { + safe_delete_array(emu_to_eq); + safe_delete_array(eq_to_emu); +} + +bool RegularOpcodeManager::LoadOpcodes(map* eq, std::string* missingOpcodes) { + NormalMemStrategy s; + s.it = this; + MOpcodes.lock(); + + loaded = true; + eq_to_emu = new EmuOpcode[MAX_EQ_OPCODE]; + emu_to_eq = new uint16[_maxEmuOpcode]; + EQOpcodeCount = MAX_EQ_OPCODE; + EmuOpcodeCount = _maxEmuOpcode; + + //dont need to set eq_to_emu cause every element should get a value + memset(eq_to_emu, 0, sizeof(EmuOpcode)*MAX_EQ_OPCODE); + memset(emu_to_eq, 0xCD, sizeof(uint16)*_maxEmuOpcode); + + bool ret = LoadOpcodesMap(eq, &s, missingOpcodes); + MOpcodes.unlock(); + return ret; +} + +bool RegularOpcodeManager::LoadOpcodes(const char *filename) { + NormalMemStrategy s; + s.it = this; + MOpcodes.lock(); + + loaded = true; + eq_to_emu = new EmuOpcode[MAX_EQ_OPCODE]; + emu_to_eq = new uint16[_maxEmuOpcode]; + EQOpcodeCount = MAX_EQ_OPCODE; + EmuOpcodeCount = _maxEmuOpcode; + + //dont need to set eq_to_emu cause every element should get a value + memset(eq_to_emu, 0, sizeof(EmuOpcode)*MAX_EQ_OPCODE); + memset(emu_to_eq, 0xCD, sizeof(uint16)*_maxEmuOpcode); + + bool ret = LoadOpcodesFile(filename, &s); + MOpcodes.unlock(); + return ret; +} + +bool RegularOpcodeManager::ReloadOpcodes(const char *filename) { + if(!loaded) + return(LoadOpcodes(filename)); + + NormalMemStrategy s; + s.it = this; + MOpcodes.lock(); + + memset(eq_to_emu, 0, sizeof(EmuOpcode)*MAX_EQ_OPCODE); + + bool ret = LoadOpcodesFile(filename, &s); + + MOpcodes.unlock(); + return(ret); +} + + +uint16 RegularOpcodeManager::EmuToEQ(const EmuOpcode emu_op) { + //opcode is checked for validity in GetEQOpcode + uint16 res; + MOpcodes.lock(); + + if(emu_op > _maxEmuOpcode) + res = 0; + else + res = emu_to_eq[emu_op]; + + MOpcodes.unlock(); +#ifdef _DEBUG_TRANSLATE + fprintf(stderr, "M Translate Emu %s (%d) to EQ 0x%.4x\n", OpcodeNames[emu_op], emu_op, res); +#endif + return(res); +} + +EmuOpcode RegularOpcodeManager::EQToEmu(const uint16 eq_op) { + //opcode is checked for validity in GetEmuOpcode +//Disabled since current live EQ uses the entire uint16 bitspace for opcodes +// if(eq_op > MAX_EQ_OPCODE) +// return(OP_Unknown); + EmuOpcode res; + MOpcodes.lock(); + if(eq_op >= MAX_EQ_OPCODE) + res = OP_Unknown; + else + res = eq_to_emu[eq_op]; + MOpcodes.unlock(); +#ifdef _DEBUG_TRANSLATE + fprintf(stderr, "M Translate EQ 0x%.4x to Emu %s (%d)\n", eq_op, OpcodeNames[res], res); +#endif + return(res); +} + +void RegularOpcodeManager::SetOpcode(EmuOpcode emu_op, uint16 eq_op) { + + //clear out old mapping + uint16 oldop = 0; + + if(emu_op <= _maxEmuOpcode) + oldop = emu_to_eq[emu_op]; + + if(oldop != 0 && oldop < MAX_EQ_OPCODE) + eq_to_emu[oldop] = OP_Unknown; + + //use our strategy, since we have it + NormalMemStrategy s; + s.it = this; + s.Set(emu_op, eq_op); +} + + +void RegularOpcodeManager::NormalMemStrategy::Set(EmuOpcode emu_op, uint16 eq_op) { + if(uint32(emu_op) >= it->EmuOpcodeCount || eq_op >= it->EQOpcodeCount) + return; + it->emu_to_eq[emu_op] = eq_op; + it->eq_to_emu[eq_op] = emu_op; +} + +NullOpcodeManager::NullOpcodeManager() +: MutableOpcodeManager() { +} + +bool NullOpcodeManager::LoadOpcodes(map* eq, std::string* missingOpcodes) { + return(true); +} + +bool NullOpcodeManager::LoadOpcodes(const char *filename) { + return(true); +} + +bool NullOpcodeManager::ReloadOpcodes(const char *filename) { + return(true); +} + +uint16 NullOpcodeManager::EmuToEQ(const EmuOpcode emu_op) { + return(0); +} + +EmuOpcode NullOpcodeManager::EQToEmu(const uint16 eq_op) { + return(OP_Unknown); +} + +EmptyOpcodeManager::EmptyOpcodeManager() +: MutableOpcodeManager() { +} + + +bool EmptyOpcodeManager::LoadOpcodes(const char *filename) { + return(true); +} + +bool EmptyOpcodeManager::LoadOpcodes(map* eq, std::string* missingOpcodes) { + return(true); +} + +bool EmptyOpcodeManager::ReloadOpcodes(const char *filename) { + return(true); +} + +uint16 EmptyOpcodeManager::EmuToEQ(const EmuOpcode emu_op) { + map::iterator f; + f = emu_to_eq.find(emu_op); + return(f == emu_to_eq.end()? 0 : f->second); +} + +EmuOpcode EmptyOpcodeManager::EQToEmu(const uint16 eq_op) { + map::iterator f; + f = eq_to_emu.find(eq_op); + return(f == eq_to_emu.end()?OP_Unknown:f->second); +} + +void EmptyOpcodeManager::SetOpcode(EmuOpcode emu_op, uint16 eq_op) { + emu_to_eq[emu_op] = eq_op; + eq_to_emu[eq_op] = emu_op; +} + + diff --git a/old/common/opcodemgr.h b/old/common/opcodemgr.h new file mode 100644 index 0000000..fe487f0 --- /dev/null +++ b/old/common/opcodemgr.h @@ -0,0 +1,162 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef OPCODE_MANAGER_H +#define OPCODE_MANAGER_H + +#include "types.h" +#include "Mutex.h" +#include "emu_opcodes.h" + +#include +using namespace std; + +class OpcodeManager { +public: + OpcodeManager(); + virtual ~OpcodeManager() {} + + virtual bool Mutable() { return(false); } + virtual bool LoadOpcodes(const char *filename) = 0; + virtual bool LoadOpcodes(map* eq, std::string* missingOpcodes = nullptr) = 0; + virtual bool ReloadOpcodes(const char *filename) = 0; + + virtual uint16 EmuToEQ(const EmuOpcode emu_op) = 0; + virtual EmuOpcode EQToEmu(const uint16 eq_op) = 0; + + static const char *EmuToName(const EmuOpcode emu_op); + const char *EQToName(const uint16 emu_op); + EmuOpcode NameSearch(const char *name); + + //This has to be public for stupid visual studio + class OpcodeSetStrategy { + public: + virtual void Set(EmuOpcode emu_op, uint16 eq_op) = 0; + virtual ~OpcodeSetStrategy(){} + }; + +protected: + bool loaded; //true if all opcodes loaded + Mutex MOpcodes; //this only protects the local machine + //in a shared manager, this dosent protect others + + static bool LoadOpcodesFile(const char *filename, OpcodeSetStrategy *s); + static bool LoadOpcodesMap(map* eq, OpcodeSetStrategy *s, std::string* missingOpcodes = nullptr); +}; + +class MutableOpcodeManager : public OpcodeManager { +public: + MutableOpcodeManager() : OpcodeManager() {} + virtual bool Mutable() { return(true); } + virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op) = 0; +}; + +#ifdef SHARED_OPCODES //quick toggle since only world and zone should possibly use this +//keeps opcodes in shared memory +class SharedOpcodeManager : public OpcodeManager { +public: + virtual ~SharedOpcodeManager() {} + + virtual bool LoadOpcodes(const char *filename); + virtual bool LoadOpcodes(map* eq, std::string* missingOpcodes = nullptr); + virtual bool ReloadOpcodes(const char *filename); + + virtual uint16 EmuToEQ(const EmuOpcode emu_op); + virtual EmuOpcode EQToEmu(const uint16 eq_op); + +protected: + class SharedMemStrategy : public OpcodeManager::OpcodeSetStrategy { + public: + void Set(EmuOpcode emu_op, uint16 eq_op); + }; + static bool DLLLoadOpcodesCallback(const char *filename); +}; +#endif //SHARED_OPCODES + +//keeps opcodes in regular heap memory +class RegularOpcodeManager : public MutableOpcodeManager { +public: + RegularOpcodeManager(); + virtual ~RegularOpcodeManager(); + + virtual bool Editable() { return(true); } + virtual bool LoadOpcodes(const char *filename); + virtual bool LoadOpcodes(map* eq, std::string* missingOpcodes = nullptr); + virtual bool ReloadOpcodes(const char *filename); + + virtual uint16 EmuToEQ(const EmuOpcode emu_op); + virtual EmuOpcode EQToEmu(const uint16 eq_op); + + //implement our editing interface + virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op); + +protected: + class NormalMemStrategy : public OpcodeManager::OpcodeSetStrategy { + public: + RegularOpcodeManager *it; + void Set(EmuOpcode emu_op, uint16 eq_op); + }; + friend class NormalMemStrategy; + + uint16 *emu_to_eq; + EmuOpcode *eq_to_emu; + uint32 EQOpcodeCount; + uint32 EmuOpcodeCount; +}; + +//always resolves everything to 0 or OP_Unknown +class NullOpcodeManager : public MutableOpcodeManager { +public: + NullOpcodeManager(); + + virtual bool LoadOpcodes(const char *filename); + virtual bool LoadOpcodes(map* eq, std::string* missingOpcodes = nullptr); + virtual bool ReloadOpcodes(const char *filename); + + virtual uint16 EmuToEQ(const EmuOpcode emu_op); + virtual EmuOpcode EQToEmu(const uint16 eq_op); + + //fake it, just used for testing anyways + virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op) {} +}; + +//starts as NullOpcodeManager, but remembers any mappings set +//could prolly have been implemented with an extension to regular, +//by overriding its load methods to be empty. +class EmptyOpcodeManager : public MutableOpcodeManager { +public: + EmptyOpcodeManager(); + + virtual bool LoadOpcodes(const char *filename); + virtual bool LoadOpcodes(map* eq, std::string* missingOpcodes = nullptr); + virtual bool ReloadOpcodes(const char *filename); + + virtual uint16 EmuToEQ(const EmuOpcode emu_op); + virtual EmuOpcode EQToEmu(const uint16 eq_op); + + //fake it, just used for testing anyways + virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op); +protected: + map emu_to_eq; + map eq_to_emu; +}; + +#endif + + diff --git a/old/common/opcodes/emu_opcodes.hpp b/old/common/opcodes/emu_opcodes.hpp deleted file mode 100644 index f569bf1..0000000 --- a/old/common/opcodes/emu_opcodes.hpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GNU GPL v3+ License - -#pragma once - -// Maximum opcode value used in the regular EQ protocol -#define MAX_EQ_OPCODE 0xFFFF - -/* - * EQEmu internal opcodes enumeration - * - * The opcode list is maintained in emu_oplist.h or login_oplist.h depending on build configuration. - * We rely on having more than 255 opcodes to ensure the enum type is at least 16 bits, - * allowing protocol flags to be used with opcode values. - */ -typedef enum -{ - OP_Unknown = 0, // Default opcode for unrecognized packets - - // Preprocessor hack to avoid maintaining duplicate opcode lists - #define N(x) x - #if !defined(LOGIN) - #include "emu_oplist.hpp" - #endif - #ifdef LOGIN - #include "login_oplist.hpp" - #endif - #undef N - - _maxEmuOpcode // Sentinel value marking the end of valid opcodes -} EmuOpcode; - -/* - * String names corresponding to each opcode value - * Used for debugging and logging purposes to provide human-readable opcode names - */ -const char *OpcodeNames[_maxEmuOpcode+1] = { - "OP_Unknown", - - // Preprocessor hack to generate string literals from opcode names - #define N(x) #x - #if !defined(LOGIN) - #include "emu_oplist.hpp" - #endif - #ifdef LOGIN - #include "login_oplist.hpp" - #endif - #undef N - - "" // Empty string terminator -}; \ No newline at end of file diff --git a/old/common/opcodes/login_oplist.hpp b/old/common/opcodes/login_oplist.hpp deleted file mode 100644 index 05895c4..0000000 --- a/old/common/opcodes/login_oplist.hpp +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GNU GPL v3+ - http://www.eq2emulator.net - -#pragma once - -// Login server operation codes - defines network message types for login/character operations -// Core login operations -N(OP_LoginRequestMsg), // Initial login request from client -N(OP_LoginByNumRequestMsg), // Login request using account number -N(OP_WSLoginRequestMsg), // World server login request -N(OP_ESLoginRequestMsg), // EverQuest station login request -N(OP_LoginReplyMsg), // Server response to login attempt - -// World server operations -N(OP_WorldListMsg), // List of available world servers -N(OP_WorldStatusChangeMsg), // World server status update notification -N(OP_AllWSDescRequestMsg), // Request all world server descriptions -N(OP_WSStatusReplyMsg), // World server status response - -// Character management operations -N(OP_AllCharactersDescRequestMsg), // Request descriptions of all characters -N(OP_AllCharactersDescReplyMsg), // Response with character descriptions -N(OP_CreateCharacterRequestMsg), // Client character creation request -N(OP_ReskinCharacterRequestMsg), // Character appearance modification request -N(OP_CreateCharacterReplyMsg), // Server response to character creation -N(OP_WSCreateCharacterRequestMsg), // World server character creation request -N(OP_WSCreateCharacterReplyMsg), // World server character creation response -N(OP_DeleteCharacterRequestMsg), // Character deletion request -N(OP_DeleteCharacterReplyMsg), // Character deletion confirmation -N(OP_PlayCharacterRequestMsg), // Request to enter game with character -N(OP_PlayCharacterReplyMsg), // Response to character play request -N(OP_ServerPlayCharacterRequestMsg),// Server-side character play request -N(OP_ServerPlayCharacterReplyMsg), // Server-side character play response - -// Key mapping operations -N(OP_KeymapLoadMsg), // Load saved key mappings -N(OP_KeymapNoneMsg), // No key mappings available -N(OP_KeymapDataMsg), // Key mapping data transmission -N(OP_KeymapSaveMsg), // Save current key mappings - -// Account security operations -//N(OP_LSRequestPlayerDescMsg), // Request player description (disabled) -N(OP_LSCheckAcctLockMsg), // Check account lock status -N(OP_WSAcctLockStatusMsg), // Account lock status update - -// Logging and crash reporting operations -N(OP_LsRequestClientCrashLogMsg), // Request client crash log -N(OP_LsClientBaselogReplyMsg), // Base log response -N(OP_LsClientCrashlogReplyMsg), // Crash log response -N(OP_LsClientAlertlogReplyMsg), // Alert log response -N(OP_LsClientVerifylogReplyMsg), // Verification log response - -// Server administration operations -N(OP_BadLanguageFilter), // Bad language filter message -N(OP_WSServerLockMsg), // World server lock message -N(OP_WSServerHideMsg), // World server hide message -N(OP_LSServerLockMsg), // Login server lock message - -// Character data updates -N(OP_UpdateCharacterSheetMsg), // Character sheet data update -N(OP_UpdateInventoryMsg), // Character inventory update \ No newline at end of file diff --git a/old/common/opcodes/opcode_manager.hpp b/old/common/opcodes/opcode_manager.hpp deleted file mode 100644 index aaffcdb..0000000 --- a/old/common/opcodes/opcode_manager.hpp +++ /dev/null @@ -1,578 +0,0 @@ -// EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) - GPL v3 - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include "emu_opcodes.hpp" -#include "../debug.hpp" -#include "../types.hpp" -#include "../log.hpp" - -#if defined(SHARED_OPCODES) && !defined(EQ2) - #include "EMuShareMem.h" - extern LoadEMuShareMemDLL EMuShareMemDLL; -#endif - -using namespace std; - -// Base opcode manager class providing common functionality for opcode translation -class OpcodeManager -{ -public: - // Constructor initializes the manager to an unloaded state - OpcodeManager(); - virtual ~OpcodeManager() {} - - // Returns whether this manager supports opcode modification at runtime - virtual bool Mutable() { return false; } - - // Load opcodes from a file containing opcode definitions - virtual bool LoadOpcodes(const char *filename) = 0; - - // Load opcodes from a pre-parsed map of opcode names to values - virtual bool LoadOpcodes(map* eq, std::string* missingOpcodes = nullptr) = 0; - - // Reload opcodes from file, typically used for runtime updates - virtual bool ReloadOpcodes(const char *filename) = 0; - - // Convert emulator opcode to EverQuest client opcode - virtual uint16 EmuToEQ(const EmuOpcode emu_op) = 0; - - // Convert EverQuest client opcode to emulator opcode - virtual EmuOpcode EQToEmu(const uint16 eq_op) = 0; - - // Convert emulator opcode to human-readable name - static const char *EmuToName(const EmuOpcode emu_op); - - // Convert EverQuest opcode to human-readable name via emulator opcode lookup - const char *EQToName(const uint16 emu_op); - - // Find emulator opcode by name (case-insensitive search) - EmuOpcode NameSearch(const char *name); - - // Strategy pattern interface for setting opcodes during loading - class OpcodeSetStrategy - { - public: - // Set mapping between emulator and EverQuest opcodes - virtual void Set(EmuOpcode emu_op, uint16 eq_op) = 0; - virtual ~OpcodeSetStrategy() {} - }; - -protected: - bool loaded; // True when opcodes have been successfully loaded - std::mutex MOpcodes; // Mutex protecting opcode data structures - - // Load opcodes from file using specified strategy for storage - static bool LoadOpcodesFile(const char *filename, OpcodeSetStrategy *s); - - // Load opcodes from map using specified strategy for storage - static bool LoadOpcodesMap(map* eq, OpcodeSetStrategy *s, std::string* missingOpcodes = nullptr); -}; - -// Opcode manager that supports runtime modification of opcode mappings -class MutableOpcodeManager : public OpcodeManager -{ -public: - MutableOpcodeManager() : OpcodeManager() {} - - // Returns true indicating this manager supports runtime modifications - virtual bool Mutable() { return true; } - - // Set individual opcode mapping at runtime - virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op) = 0; -}; - -#ifdef SHARED_OPCODES -// Opcode manager using shared memory for inter-process opcode sharing -class SharedOpcodeManager : public OpcodeManager -{ -public: - virtual ~SharedOpcodeManager() {} - - // Load opcodes from file into shared memory - virtual bool LoadOpcodes(const char *filename); - - // Load opcodes from map into shared memory - virtual bool LoadOpcodes(map* eq, std::string* missingOpcodes = nullptr); - - // Reload opcodes from file into shared memory - virtual bool ReloadOpcodes(const char *filename); - - // Convert emulator opcode to EverQuest opcode using shared memory lookup - virtual uint16 EmuToEQ(const EmuOpcode emu_op); - - // Convert EverQuest opcode to emulator opcode using shared memory lookup - virtual EmuOpcode EQToEmu(const uint16 eq_op); - -protected: - // Strategy for storing opcodes in shared memory - class SharedMemStrategy : public OpcodeManager::OpcodeSetStrategy - { - public: - // Store opcode mapping in shared memory - void Set(EmuOpcode emu_op, uint16 eq_op); - }; - - // Callback for DLL-based opcode loading - static bool DLLLoadOpcodesCallback(const char *filename); -}; -#endif - -// Standard opcode manager using heap memory for opcode storage -class RegularOpcodeManager : public MutableOpcodeManager -{ -public: - // Constructor allocates memory for opcode translation tables - RegularOpcodeManager(); - - // Destructor cleans up allocated memory - virtual ~RegularOpcodeManager(); - - // Returns true indicating opcodes can be edited - virtual bool Editable() { return true; } - - // Load opcodes from file into memory arrays - virtual bool LoadOpcodes(const char *filename); - - // Load opcodes from map into memory arrays - virtual bool LoadOpcodes(map* eq, std::string* missingOpcodes = nullptr); - - // Reload opcodes from file, preserving existing mappings where possible - virtual bool ReloadOpcodes(const char *filename); - - // Convert emulator opcode to EverQuest opcode using array lookup - virtual uint16 EmuToEQ(const EmuOpcode emu_op); - - // Convert EverQuest opcode to emulator opcode using array lookup - virtual EmuOpcode EQToEmu(const uint16 eq_op); - - // Set individual opcode mapping, updating both translation arrays - virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op); - -protected: - // Strategy for storing opcodes in regular memory arrays - class NormalMemStrategy : public OpcodeManager::OpcodeSetStrategy - { - public: - RegularOpcodeManager *it; // Pointer to parent manager - - // Store opcode mapping in memory arrays - void Set(EmuOpcode emu_op, uint16 eq_op); - }; - friend class NormalMemStrategy; - -private: - uint16 *emu_to_eq; // Array mapping emulator opcodes to EverQuest opcodes - EmuOpcode *eq_to_emu; // Array mapping EverQuest opcodes to emulator opcodes - uint32 EQOpcodeCount; // Size of EverQuest opcode array - uint32 EmuOpcodeCount; // Size of emulator opcode array -}; - -// Null opcode manager that always returns default values (for testing) -class NullOpcodeManager : public MutableOpcodeManager -{ -public: - NullOpcodeManager(); - - // Always succeeds but loads no actual opcodes - virtual bool LoadOpcodes(const char *filename); - - // Always succeeds but loads no actual opcodes - virtual bool LoadOpcodes(map* eq, std::string* missingOpcodes = nullptr); - - // Always succeeds but reloads no actual opcodes - virtual bool ReloadOpcodes(const char *filename); - - // Always returns 0 (invalid opcode) - virtual uint16 EmuToEQ(const EmuOpcode emu_op); - - // Always returns OP_Unknown - virtual EmuOpcode EQToEmu(const uint16 eq_op); - - // No-op implementation for testing compatibility - virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op) {} -}; - -// Empty opcode manager that starts with no mappings but remembers what is set -class EmptyOpcodeManager : public MutableOpcodeManager -{ -public: - EmptyOpcodeManager(); - - // No-op load operation - manager starts empty - virtual bool LoadOpcodes(const char *filename); - - // No-op load operation - manager starts empty - virtual bool LoadOpcodes(map* eq, std::string* missingOpcodes = nullptr); - - // No-op reload operation - virtual bool ReloadOpcodes(const char *filename); - - // Lookup emulator to EverQuest opcode in stored mappings - virtual uint16 EmuToEQ(const EmuOpcode emu_op); - - // Lookup EverQuest to emulator opcode in stored mappings - virtual EmuOpcode EQToEmu(const uint16 eq_op); - - // Store opcode mapping in internal maps - virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op); - -private: - map emu_to_eq; // Map of emulator to EverQuest opcodes - map eq_to_emu; // Map of EverQuest to emulator opcodes -}; - -// Implementation - -// Initialize opcode manager in unloaded state -OpcodeManager::OpcodeManager() -{ - loaded = false; -} - -// Load opcodes from map using specified storage strategy -bool OpcodeManager::LoadOpcodesMap(map* eq, OpcodeSetStrategy *s, std::string* missingOpcodes) -{ - bool ret = true; - EmuOpcode emu_op; - map::iterator res; - - // Iterate through all emulator opcodes to build translation mappings - for(emu_op = (EmuOpcode)(0); emu_op < _maxEmuOpcode; emu_op=(EmuOpcode)(emu_op+1)) { - // Get human-readable name for this emulator opcode - const char *op_name = OpcodeNames[emu_op]; - if(op_name[0] == '\0') { - break; - } - - // Find corresponding EverQuest opcode in the provided map - res = eq->find(op_name); - if(res == eq->end()) { - // Handle missing opcode by either logging or collecting for batch report - if(missingOpcodes) { - if(missingOpcodes->size() < 1) { - missingOpcodes->append(op_name); - } - else { - missingOpcodes->append(", " + std::string(op_name)); - } - } - else { - LogWrite(OPCODE__WARNING, 1, "Opcode", "Opcode %s is missing from the opcodes table.", op_name); - } - // Set invalid mapping to indicate missing opcode - s->Set(emu_op, 0xFFFF); - continue; - } - - // Store valid mapping using the provided strategy - s->Set(emu_op, res->second); - } - return ret; -} - -// Parse opcode file and load mappings using specified storage strategy -bool OpcodeManager::LoadOpcodesFile(const char *filename, OpcodeSetStrategy *s) -{ - FILE *opf = fopen(filename, "r"); - if(opf == NULL) { - LogWrite(OPCODE__ERROR, 0, "Opcode", "Unable to open opcodes file '%s'. Thats bad.", filename); - return false; - } - - map eq; - - // Parse the opcode file line by line - char line[2048]; - int lineno = 0; - uint16 curop; - while(!feof(opf)) { - lineno++; - line[0] = '\0'; - if(fgets(line, sizeof(line), opf) == NULL) - break; - - // Skip lines that don't start with OP_ (comments, blank lines, etc.) - if(line[0] != 'O' || line[1] != 'P' || line[2] != '_') - continue; - - char *num = line+3; - // Find the equals sign separating name from value - while(*num != '=' && *num != '\0') { - num++; - } - // Ensure we found a proper assignment - if(*num != '=') { - LogWrite(OPCODE__ERROR, 0, "Opcode", "Malformed opcode line at %s:%d\n", filename, lineno); - continue; - } - *num = '\0'; // Null terminate the opcode name - num++; // Point to the opcode value - - // Parse hexadecimal opcode value - if(sscanf(num, "0x%hx", &curop) != 1) { - LogWrite(OPCODE__ERROR, 0, "Opcode", "Malformed opcode at %s:%d\n", filename, lineno); - continue; - } - - // Store parsed opcode mapping in temporary map - eq[line] = curop; - } - fclose(opf); - return LoadOpcodesMap(&eq, s); -} - -// Convert emulator opcode to human-readable name -const char *OpcodeManager::EmuToName(const EmuOpcode emu_op) -{ - if(emu_op > _maxEmuOpcode) - return "OP_Unknown"; - - return OpcodeNames[emu_op]; -} - -// Convert EverQuest opcode to human-readable name via emulator opcode lookup -const char *OpcodeManager::EQToName(const uint16 eq_op) -{ - // First resolve EverQuest opcode to emulator opcode - EmuOpcode emu_op = EQToEmu(eq_op); - if(emu_op > _maxEmuOpcode) - return "OP_Unknown"; - - return OpcodeNames[emu_op]; -} - -// Search for emulator opcode by name (case-insensitive) -EmuOpcode OpcodeManager::NameSearch(const char *name) -{ - EmuOpcode emu_op; - // Linear search through all opcode names - for(emu_op = (EmuOpcode)(0); emu_op < _maxEmuOpcode; emu_op=(EmuOpcode)(emu_op+1)) { - const char *op_name = OpcodeNames[emu_op]; - if(!strcasecmp(op_name, name)) { - return emu_op; - } - } - return OP_Unknown; -} - -// Initialize regular opcode manager with null arrays -RegularOpcodeManager::RegularOpcodeManager() -: MutableOpcodeManager() -{ - emu_to_eq = nullptr; - eq_to_emu = nullptr; - EQOpcodeCount = 0; - EmuOpcodeCount = 0; -} - -// Clean up allocated translation arrays -RegularOpcodeManager::~RegularOpcodeManager() -{ - delete[] emu_to_eq; - delete[] eq_to_emu; -} - -// Load opcodes from map into memory arrays -bool RegularOpcodeManager::LoadOpcodes(map* eq, std::string* missingOpcodes) -{ - NormalMemStrategy s; - s.it = this; - std::lock_guard lock(MOpcodes); - - loaded = true; - eq_to_emu = new EmuOpcode[MAX_EQ_OPCODE]; - emu_to_eq = new uint16[_maxEmuOpcode]; - EQOpcodeCount = MAX_EQ_OPCODE; - EmuOpcodeCount = _maxEmuOpcode; - - // Initialize arrays with default values - memset(eq_to_emu, 0, sizeof(EmuOpcode)*MAX_EQ_OPCODE); - memset(emu_to_eq, 0xCD, sizeof(uint16)*_maxEmuOpcode); - - bool ret = LoadOpcodesMap(eq, &s, missingOpcodes); - return ret; -} - -// Load opcodes from file into memory arrays -bool RegularOpcodeManager::LoadOpcodes(const char *filename) -{ - NormalMemStrategy s; - s.it = this; - std::lock_guard lock(MOpcodes); - - loaded = true; - eq_to_emu = new EmuOpcode[MAX_EQ_OPCODE]; - emu_to_eq = new uint16[_maxEmuOpcode]; - EQOpcodeCount = MAX_EQ_OPCODE; - EmuOpcodeCount = _maxEmuOpcode; - - // Initialize arrays with default values - memset(eq_to_emu, 0, sizeof(EmuOpcode)*MAX_EQ_OPCODE); - memset(emu_to_eq, 0xCD, sizeof(uint16)*_maxEmuOpcode); - - bool ret = LoadOpcodesFile(filename, &s); - return ret; -} - -// Reload opcodes from file, preserving existing array allocations -bool RegularOpcodeManager::ReloadOpcodes(const char *filename) -{ - if(!loaded) - return LoadOpcodes(filename); - - NormalMemStrategy s; - s.it = this; - std::lock_guard lock(MOpcodes); - - // Clear existing EverQuest to emulator mappings - memset(eq_to_emu, 0, sizeof(EmuOpcode)*MAX_EQ_OPCODE); - - bool ret = LoadOpcodesFile(filename, &s); - return ret; -} - -// Convert emulator opcode to EverQuest opcode using array lookup -uint16 RegularOpcodeManager::EmuToEQ(const EmuOpcode emu_op) -{ - uint16 res; - std::lock_guard lock(MOpcodes); - - if(emu_op > _maxEmuOpcode) - res = 0; - else - res = emu_to_eq[emu_op]; - - return res; -} - -// Convert EverQuest opcode to emulator opcode using array lookup -EmuOpcode RegularOpcodeManager::EQToEmu(const uint16 eq_op) -{ - EmuOpcode res; - std::lock_guard lock(MOpcodes); - - if(eq_op >= MAX_EQ_OPCODE) - res = OP_Unknown; - else - res = eq_to_emu[eq_op]; - - return res; -} - -// Set individual opcode mapping, updating both translation arrays -void RegularOpcodeManager::SetOpcode(EmuOpcode emu_op, uint16 eq_op) -{ - // Clear existing mapping for this emulator opcode - uint16 oldop = 0; - - if(emu_op <= _maxEmuOpcode) - oldop = emu_to_eq[emu_op]; - - if(oldop != 0 && oldop < MAX_EQ_OPCODE) - eq_to_emu[oldop] = OP_Unknown; - - // Set new mapping using our strategy - NormalMemStrategy s; - s.it = this; - s.Set(emu_op, eq_op); -} - -// Store opcode mapping in memory arrays -void RegularOpcodeManager::NormalMemStrategy::Set(EmuOpcode emu_op, uint16 eq_op) -{ - if(uint32(emu_op) >= it->EmuOpcodeCount || eq_op >= it->EQOpcodeCount) - return; - it->emu_to_eq[emu_op] = eq_op; - it->eq_to_emu[eq_op] = emu_op; -} - -// Initialize null opcode manager -NullOpcodeManager::NullOpcodeManager() -: MutableOpcodeManager() -{ -} - -// No-op load from map - always succeeds but loads nothing -bool NullOpcodeManager::LoadOpcodes(map* eq, std::string* missingOpcodes) -{ - return true; -} - -// No-op load from file - always succeeds but loads nothing -bool NullOpcodeManager::LoadOpcodes(const char *filename) -{ - return true; -} - -// No-op reload - always succeeds but reloads nothing -bool NullOpcodeManager::ReloadOpcodes(const char *filename) -{ - return true; -} - -// Always return invalid opcode -uint16 NullOpcodeManager::EmuToEQ(const EmuOpcode emu_op) -{ - return 0; -} - -// Always return unknown opcode -EmuOpcode NullOpcodeManager::EQToEmu(const uint16 eq_op) -{ - return OP_Unknown; -} - -// Initialize empty opcode manager -EmptyOpcodeManager::EmptyOpcodeManager() -: MutableOpcodeManager() -{ -} - -// No-op load from file - manager starts empty -bool EmptyOpcodeManager::LoadOpcodes(const char *filename) -{ - return true; -} - -// No-op load from map - manager starts empty -bool EmptyOpcodeManager::LoadOpcodes(map* eq, std::string* missingOpcodes) -{ - return true; -} - -// No-op reload operation -bool EmptyOpcodeManager::ReloadOpcodes(const char *filename) -{ - return true; -} - -// Lookup emulator to EverQuest opcode in stored mappings -uint16 EmptyOpcodeManager::EmuToEQ(const EmuOpcode emu_op) -{ - map::iterator f; - f = emu_to_eq.find(emu_op); - return (f == emu_to_eq.end() ? 0 : f->second); -} - -// Lookup EverQuest to emulator opcode in stored mappings -EmuOpcode EmptyOpcodeManager::EQToEmu(const uint16 eq_op) -{ - map::iterator f; - f = eq_to_emu.find(eq_op); - return (f == eq_to_emu.end() ? OP_Unknown : f->second); -} - -// Store opcode mapping in internal maps -void EmptyOpcodeManager::SetOpcode(EmuOpcode emu_op, uint16 eq_op) -{ - emu_to_eq[emu_op] = eq_op; - eq_to_emu[eq_op] = emu_op; -} \ No newline at end of file diff --git a/old/common/opcodes/opcodes.hpp b/old/common/opcodes/opcodes.hpp deleted file mode 100644 index b3d0284..0000000 --- a/old/common/opcodes/opcodes.hpp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License - -#pragma once - -// Network protocol operation codes for EQ2Emulator session management -// These constants define the packet types used in the custom UDP protocol - -// Session establishment and management opcodes -static const char OP_SessionRequest = 0x01; // Initial connection request from client -static const char OP_SessionResponse = 0x02; // Server response to session request -static const char OP_SessionDisconnect = 0x05; // Clean session termination -static const char OP_SessionStatResponse = 0x08; // Session statistics response -static const char OP_OutOfSession = 0x1d; // Packet received outside valid session - -// Data transmission opcodes -static const char OP_Combined = 0x03; // Multiple packets combined into single transmission -static const char OP_Packet = 0x09; // Standard data packet -static const char OP_Fragment = 0x0d; // Large packet fragmented for transmission -static const char OP_AppCombined = 0x19; // Application-level combined packets - -// Connection maintenance opcodes -static const char OP_KeepAlive = 0x06; // Heartbeat to maintain connection -static const char OP_ServerKeyRequest = 0x07; // Request for server encryption key - -// Acknowledgment and ordering opcodes -static const char OP_Ack = 0x15; // Standard packet acknowledgment -static const char OP_OutOfOrderAck = 0x11; // Acknowledgment for out-of-sequence packet - -// Application opcode size configuration -// LOGIN and CHAT servers use 1-byte opcodes, game servers use 2-byte opcodes -#if defined(LOGIN) || defined(CHAT) - #define APP_OPCODE_SIZE 1 -#else - #define APP_OPCODE_SIZE 2 -#endif \ No newline at end of file diff --git a/old/common/packet/data_struct.hpp b/old/common/packet/data_struct.hpp deleted file mode 100644 index e2daa16..0000000 --- a/old/common/packet/data_struct.hpp +++ /dev/null @@ -1,453 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team, GPL v3+ - -#pragma once - -#include -#include - -#include "../log.hpp" -#include "../types.hpp" -#include "../misc_functions.hpp" - -#define DATA_STRUCT_NONE 0 -#define DATA_STRUCT_INT8 1 -#define DATA_STRUCT_INT16 2 -#define DATA_STRUCT_INT32 3 -#define DATA_STRUCT_INT64 4 -#define DATA_STRUCT_FLOAT 5 -#define DATA_STRUCT_DOUBLE 6 -#define DATA_STRUCT_COLOR 7 -#define DATA_STRUCT_SINT8 8 -#define DATA_STRUCT_SINT16 9 -#define DATA_STRUCT_SINT32 10 -#define DATA_STRUCT_CHAR 11 -#define DATA_STRUCT_EQ2_8BIT_STRING 12 -#define DATA_STRUCT_EQ2_16BIT_STRING 13 -#define DATA_STRUCT_EQ2_32BIT_STRING 14 -#define DATA_STRUCT_EQUIPMENT 15 -#define DATA_STRUCT_ARRAY 16 -#define DATA_STRUCT_ITEM 17 -#define DATA_STRUCT_SINT64 18 - -class DataStruct -{ -public: - // Default constructor - initializes all member variables to default values - DataStruct() - { - item_size = 0; - type = 0; - type2 = 0; - length = 1; - if_flag_set = false; - if_flag_not_set = false; - if_set = false; - if_not_set = false; - if_not_equals = false; - if_equals = false; - is_set = false; - optional = false; - oversized = 0; - oversized_byte = 0; - add = false; - addType = 0; - maxArraySize = 0; - default_value = 0; - } - - // Copy constructor - creates new DataStruct from existing one - DataStruct(DataStruct* data_struct) - { - type = data_struct->GetType(); - type2 = data_struct->GetType2(); - length = data_struct->GetLength(); - name = data_struct->GetName(); - array_size_variable = data_struct->array_size_variable; - default_value = data_struct->default_value; - add = true; - addType = type; - oversized = data_struct->GetOversized(); - oversized_byte = data_struct->GetOversizedByte(); - maxArraySize = data_struct->GetMaxArraySize(); - if_flag_set = false; - if_flag_not_set = false; - if_set = false; - if_not_set = false; - if_not_equals = false; - if_equals = false; - optional = false; - if (data_struct->GetIfSet()) - SetIfSetVariable(data_struct->GetIfSetVariable()); - if (data_struct->GetIfNotSet()) - SetIfNotSetVariable(data_struct->GetIfNotSetVariable()); - if (data_struct->GetIfNotEquals()) - SetIfNotEqualsVariable(data_struct->GetIfNotEqualsVariable()); - if (data_struct->GetIfEquals()) - SetIfEqualsVariable(data_struct->GetIfEqualsVariable()); - if (data_struct->GetIfFlagSet()) - SetIfFlagSetVariable(data_struct->GetIfFlagSetVariable()); - if (data_struct->GetIfFlagNotSet()) - SetIfFlagNotSetVariable(data_struct->GetIfFlagNotSetVariable()); - item_size = 0; - is_set = false; - } - - // Constructor with name and type strings - DataStruct(const char* new_name, const char* new_type, int32 new_length, const char* new_type2) - { - name = std::string(new_name); - type = 0; - type2 = 0; - SetType(new_type, &type); - if (new_type2) - SetType(new_type2, &type2); - length = new_length; - add = true; - addType = type; - if_set = false; - if_not_set = false; - is_set = false; - if_not_equals = false; - item_size = 0; - } - - // Constructor with name and length only - DataStruct(const char* new_name, int32 new_length) - { - name = std::string(new_name); - length = new_length; - if_set = false; - if_not_set = false; - is_set = false; - item_size = 0; - } - - // Constructor with name, type integers, and length - DataStruct(const char* new_name, int8 new_type, int32 new_length, int8 new_type2) - { - name = std::string(new_name); - type = new_type; - length = new_length; - type2 = new_type2; - addType = type; - if_set = false; - if_not_set = false; - is_set = false; - item_size = 0; - } - - // Converts type string to numeric type constant - void SetType(const char* new_type, int8* output_type) - { - if (strlen(new_type) > 3 && strncasecmp("int", new_type, 3) == 0) { - if (strncasecmp("int8", new_type, 4) == 0) - *output_type = DATA_STRUCT_INT8; - else if (strncasecmp("int16", new_type, 5) == 0) - *output_type = DATA_STRUCT_INT16; - else if (strncasecmp("int32", new_type, 5) == 0) - *output_type = DATA_STRUCT_INT32; - else if (strncasecmp("int64", new_type, 5) == 0) - *output_type = DATA_STRUCT_INT64; - } - else if (strlen(new_type) > 4 && strncasecmp("sint", new_type, 4) == 0) { - if (strncasecmp("sint8", new_type, 5) == 0) - *output_type = DATA_STRUCT_SINT8; - else if (strncasecmp("sint16", new_type, 6) == 0) - *output_type = DATA_STRUCT_SINT16; - else if (strncasecmp("sint32", new_type, 6) == 0) - *output_type = DATA_STRUCT_SINT32; - else if (strncasecmp("sint64", new_type, 6) == 0) - *output_type = DATA_STRUCT_SINT64; - } - else if (strlen(new_type) == 4 && strncasecmp("char", new_type, 4) == 0) - *output_type = DATA_STRUCT_CHAR; - else if (strlen(new_type) == 5 && strncasecmp("float", new_type, 5) == 0) - *output_type = DATA_STRUCT_FLOAT; - else if (strlen(new_type) == 6 && strncasecmp("double", new_type, 6) == 0) - *output_type = DATA_STRUCT_DOUBLE; - else if (strlen(new_type) >= 5 && strncasecmp("EQ2_", new_type, 4) == 0) { - if (strncasecmp("EQ2_8", new_type, 5) == 0) - *output_type = DATA_STRUCT_EQ2_8BIT_STRING; - else if (strncasecmp("EQ2_16", new_type, 6) == 0) - *output_type = DATA_STRUCT_EQ2_16BIT_STRING; - else if (strncasecmp("EQ2_32", new_type, 6) == 0) - *output_type = DATA_STRUCT_EQ2_32BIT_STRING; - else if (strncasecmp("EQ2_E", new_type, 5) == 0) - *output_type = DATA_STRUCT_EQUIPMENT; - else if (strncasecmp("EQ2_C", new_type, 5) == 0) - *output_type = DATA_STRUCT_COLOR; - else if (strncasecmp("EQ2_I", new_type, 5) == 0) - *output_type = DATA_STRUCT_ITEM; - } - else if (strlen(new_type) >= 5) { - if (strncasecmp("Array", new_type, 5) == 0) - *output_type = DATA_STRUCT_ARRAY; - } - else - LogWrite(PACKET__ERROR, 0, "Packet", "Invalid Type: %s", new_type); - } - - // Setter methods - void SetType(int8 new_type) { type = new_type; addType = type; } - void SetMaxArraySize(int8 size) { maxArraySize = size; } - void SetOversized(int8 val) { oversized = val; } - void SetDefaultValue(int8 new_val) { default_value = new_val; } - void SetName(const char* new_name) { name = std::string(new_name); } - void SetLength(int32 new_length) { length = new_length; } - void SetOversizedByte(int8 val) { oversized_byte = val; } - void SetAddToStruct(bool val) { add = val; } - void SetAddType(int8 new_type) { addType = new_type; } - void SetArraySizeVariable(const char* new_name) { array_size_variable = std::string(new_name); } - void SetIsSet(bool val) { is_set = val; } - void SetIsOptional(bool val) { optional = val; } - - // Sets item size in bytes and marks as set if size > 0 - void SetItemSize(int32 val) - { - item_size = val; - if (item_size) - is_set = true; - else - is_set = false; - } - - // Conditional variable setters - void SetIfEqualsVariable(const char* variable) - { - if (variable) { - if_equals = true; - if_equals_variable = std::string(variable); - } - else - if_equals = false; - } - - void SetIfNotEqualsVariable(const char* variable) - { - if (variable) { - if_not_equals = true; - if_not_equals_variable = std::string(variable); - } - else - if_not_equals = false; - } - - void SetIfFlagNotSetVariable(const char* variable) - { - if (variable) { - if_flag_not_set = true; - if_flag_not_set_variable = std::string(variable); - } - else - if_flag_not_set = false; - } - - void SetIfFlagSetVariable(const char* variable) - { - if (variable) { - if_flag_set = true; - if_flag_set_variable = std::string(variable); - } - else - if_flag_set = false; - } - - void SetIfSetVariable(const char* variable) - { - if (variable) { - if_set = true; - if_set_variable = std::string(variable); - } - else - if_set = false; - } - - void SetIfNotSetVariable(const char* variable) - { - if (variable) { - if_not_set = true; - if_not_set_variable = std::string(variable); - } - else - if_not_set = false; - } - - // Getter methods - const char* GetArraySizeVariable() { return array_size_variable.c_str(); } - int32 GetItemSize() { return item_size; } - bool GetIfSet() { return if_set; } - bool GetIfNotSet() { return if_not_set; } - bool GetIfEquals() { return if_equals; } - bool GetIfNotEquals() { return if_not_equals; } - bool GetIfFlagSet() { return if_flag_set; } - bool GetIfFlagNotSet() { return if_flag_not_set; } - bool IsSet() { return is_set; } - bool IsOptional() { return optional; } - bool AddToStruct() { return add; } - int8 GetDefaultValue() { return default_value; } - int8 GetType() { return type; } - int8 GetType2() { return type2; } - int8 GetOversized() { return oversized; } - int8 GetOversizedByte() { return oversized_byte; } - int8 GetMaxArraySize() { return maxArraySize; } - int8 GetAddType() { return addType; } - int32 GetLength() { return length; } - const char* GetName() { return name.c_str(); } - std::string GetStringName() { return name; } - - // Conditional variable getters - const char* GetIfSetVariable() - { - if (if_set_variable.length() > 0) - return if_set_variable.c_str(); - return 0; - } - - const char* GetIfNotSetVariable() - { - if (if_not_set_variable.length() > 0) - return if_not_set_variable.c_str(); - return 0; - } - - const char* GetIfEqualsVariable() - { - if (if_equals_variable.length() > 0) - return if_equals_variable.c_str(); - return 0; - } - - const char* GetIfNotEqualsVariable() - { - if (if_not_equals_variable.length() > 0) - return if_not_equals_variable.c_str(); - return 0; - } - - const char* GetIfFlagSetVariable() - { - if (if_flag_set_variable.length() > 0) - return if_flag_set_variable.c_str(); - return 0; - } - - const char* GetIfFlagNotSetVariable() - { - if (if_flag_not_set_variable.length() > 0) - return if_flag_not_set_variable.c_str(); - return 0; - } - - // Appends variable name to existing variable list, avoiding duplicates - std::string AppendVariable(std::string orig, const char* val) - { - if (!val) - return orig; - if (orig.length() == 0) - return std::string(val); - if (orig.find(",") < 0xFFFFFFFF) { // has more than one already - std::string valstr = std::string(val); - std::vector* varnames = SplitString(orig, ','); - if (varnames) { - for (int32 i = 0; i < varnames->size(); i++) { - if (valstr.compare(varnames->at(i)) == 0) { - return orig; // already in the variable, no need to append - } - } - safe_delete(varnames); - } - } - return orig.append(",").append(val); - } - - // Calculates total data size in bytes based on type and length - int32 GetDataSizeInBytes() - { - int32 ret = 0; - switch (type) { - case DATA_STRUCT_INT8: - ret = length * sizeof(int8); - break; - case DATA_STRUCT_INT16: - ret = length * sizeof(int16); - break; - case DATA_STRUCT_INT32: - ret = length * sizeof(int32); - break; - case DATA_STRUCT_INT64: - ret = length * sizeof(int64); - break; - case DATA_STRUCT_SINT8: - ret = length * sizeof(sint8); - break; - case DATA_STRUCT_SINT16: - ret = length * sizeof(sint16); - break; - case DATA_STRUCT_SINT32: - ret = length * sizeof(sint32); - break; - case DATA_STRUCT_SINT64: - ret = length * sizeof(sint64); - break; - case DATA_STRUCT_FLOAT: - ret = length * sizeof(float); - break; - case DATA_STRUCT_DOUBLE: - ret = length * sizeof(double); - break; - case DATA_STRUCT_ARRAY: - // Array elements won't have a size so get out now to avoid the warning. - break; - default: - LogWrite(PACKET__WARNING, 0, "DataStruct", "Tried retrieving a data size from an unsupported data struct type in GetDataSizeInBytes()"); - break; - } - return ret; - } - - // Adds variable to if-set condition list - void AddIfSetVariable(const char* val) - { - if (val) { - if_set_variable = AppendVariable(if_set_variable, val); - is_set = true; - } - } - - // Adds variable to if-not-set condition list - void AddIfNotSetVariable(const char* val) - { - if (val) { - if_not_set_variable = AppendVariable(if_not_set_variable, val); - if_not_set = true; - } - } - -private: - bool is_set; // Whether this data structure contains data - bool if_not_set; // Conditional inclusion based on variable not set - bool optional; // Whether this field is optional - bool if_set; // Conditional inclusion based on variable set - bool if_not_equals; // Conditional inclusion based on variable not equals - bool if_equals; // Conditional inclusion based on variable equals - bool if_flag_set; // Conditional inclusion based on flag set - bool if_flag_not_set; // Conditional inclusion based on flag not set - std::string if_flag_not_set_variable; // Variable name for flag not set condition - std::string if_flag_set_variable; // Variable name for flag set condition - std::string if_not_equals_variable; // Variable name for not equals condition - std::string if_equals_variable; // Variable name for equals condition - std::string if_not_set_variable; // Variable name for not set condition - std::string if_set_variable; // Variable name for set condition - int8 oversized; // Threshold for oversized data handling - int8 oversized_byte; // Byte marker for oversized data - bool add; // Whether to include in packet structure - int8 addType; // Type to use when adding to packet - int8 maxArraySize; // Maximum allowed array size - std::string array_size_variable; // Variable name containing array size - std::string name; // Name identifier of this data structure - int8 type; // Primary data type - int8 default_value; // Default value for initialization - int8 type2; // Secondary data type (for special cases) - int32 length; // Number of elements/length - int32 item_size; // Size in bytes for item data -}; \ No newline at end of file diff --git a/old/common/packet/eq_packet.hpp b/old/common/packet/eq_packet.hpp deleted file mode 100644 index 004a37b..0000000 --- a/old/common/packet/eq_packet.hpp +++ /dev/null @@ -1,891 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3+ License - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "packet_dump.hpp" -#include "../log.hpp" -#include "../misc.hpp" -#include "../debug.hpp" -#include "../types.hpp" -#include "../crc16.hpp" -#include "../opcodes/opcodes.hpp" -#include "../opcodes/emu_opcodes.hpp" -#include "../opcodes/opcode_manager.hpp" - -using namespace std; -extern mapEQOpcodeManager; - -class OpcodeManager; -class EQStream; - -// Base packet class for all EverQuest protocol packets -class EQPacket -{ - friend class EQStream; - -public: - unsigned char *pBuffer; // Raw packet data buffer - uint32 size; // Size of packet data - uint32 src_ip, dst_ip; // Source and destination IP addresses - uint16 src_port, dst_port; // Source and destination ports - uint32 priority; // Packet priority for queuing - timeval timestamp; // Timestamp when packet was created/received - int16 version; // Protocol version - - // Destructor - cleans up allocated buffer - ~EQPacket() - { - safe_delete_array(pBuffer); - pBuffer = nullptr; - } - - // Dumps packet header with timestamp information to file - void DumpRawHeader(uint16 seq = 0xffff, FILE* to = stdout) const - { - DumpRawHeaderNoTime(seq, to); - } - - // Dumps packet header without timestamp information to file - void DumpRawHeaderNoTime(uint16 seq = 0xffff, FILE* to = stdout) const - { - if (src_ip) { - string sIP = long2ip(src_ip); - string dIP = long2ip(dst_ip); - fprintf(to, "[%s:%d->%s:%d] ", sIP.c_str(), src_port, dIP.c_str(), dst_port); - } - - if (seq != 0xffff) - fprintf(to, "[Seq=%u] ", seq); - - string name; - int16 OpcodeVersion = GetOpcodeVersion(version); - if (EQOpcodeManager.count(OpcodeVersion) > 0) - name = EQOpcodeManager[OpcodeVersion]->EQToName(opcode); - - fprintf(to, "[OpCode 0x%04x (%s) Size=%u]\n", opcode, name.c_str(), size); - } - - // Dumps complete packet with header and data in hex format - void DumpRaw(FILE* to = stdout) const - { - DumpRawHeader(); - if (pBuffer && size) - dump_message_column(pBuffer, size, " ", to); - fprintf(to, "\n"); - } - - // Gets human-readable name for packet opcode - const char* GetOpcodeName() - { - int16 OpcodeVersion = GetOpcodeVersion(version); - if (EQOpcodeManager.count(OpcodeVersion) > 0) - return EQOpcodeManager[OpcodeVersion]->EQToName(opcode); - else - return nullptr; - } - - // Sets protocol version for this packet - void setVersion(int16 new_version) { version = new_version; } - - // Sets source IP and port information - void setSrcInfo(uint32 sip, uint16 sport) { src_ip = sip; src_port = sport; } - - // Sets destination IP and port information - void setDstInfo(uint32 dip, uint16 dport) { dst_ip = dip; dst_port = dport; } - - // Sets timestamp information for packet timing - void setTimeInfo(uint32 ts_sec, uint32 ts_usec) { timestamp.tv_sec = ts_sec; timestamp.tv_usec = ts_usec; } - - // Copies connection and timing info from another packet - void copyInfo(const EQPacket* p) - { - src_ip = p->src_ip; - src_port = p->src_port; - dst_ip = p->dst_ip; - dst_port = p->dst_port; - timestamp.tv_sec = p->timestamp.tv_sec; - timestamp.tv_usec = p->timestamp.tv_usec; - } - - // Returns total packet size including opcode header - uint32 Size() const { return size + 2; } - - // Gets raw opcode value without translation - uint16 GetRawOpcode() const { return opcode; } - - // Comparison operator for timestamp-based sorting - inline bool operator<(const EQPacket& rhs) - { - return (timestamp.tv_sec < rhs.timestamp.tv_sec || - (timestamp.tv_sec == rhs.timestamp.tv_sec && timestamp.tv_usec < rhs.timestamp.tv_usec)); - } - - // Sets the protocol-level opcode - void SetProtocolOpcode(int16 new_opcode) { opcode = new_opcode; } - -protected: - uint16 opcode; // Packet opcode identifier - - // Constructor for creating packet with opcode and data buffer - EQPacket(const uint16 op, const unsigned char* buf, uint32 len) - { - opcode = op; - pBuffer = nullptr; - size = 0; - version = 0; - setTimeInfo(0, 0); - - if (len > 0) { - size = len; - pBuffer = new unsigned char[size]; - if (buf) { - memcpy(pBuffer, buf, size); - } else { - memset(pBuffer, 0, size); - } - } - } - - // Copy constructor - disabled to prevent accidental copies - EQPacket(const EQPacket& p) { version = 0; } - - // Default constructor - EQPacket() - { - opcode = 0; - pBuffer = nullptr; - size = 0; - version = 0; - setTimeInfo(0, 0); - } -}; - -class EQApplicationPacket; - -// Protocol-level packet handling EverQuest network protocol specifics -class EQProtocolPacket : public EQPacket -{ -public: - bool eq2_compressed; // Flag indicating if packet is compressed - bool packet_prepared; // Flag indicating if packet has been prepared for sending - bool packet_encrypted; // Flag indicating if packet is encrypted - bool acked; // Flag indicating if packet has been acknowledged - int32 sent_time; // Timestamp when packet was sent - int8 attempt_count; // Number of send attempts for this packet - int32 sequence; // Sequence number for ordering - - // Constructor with opcode and data buffer - EQProtocolPacket(uint16 op, const unsigned char* buf, uint32 len) : EQPacket(op, buf, len) - { - eq2_compressed = false; - packet_prepared = false; - packet_encrypted = false; - sequence = 0; - sent_time = 0; - attempt_count = 0; - acked = false; - } - - // Constructor from raw buffer with optional opcode override - EQProtocolPacket(const unsigned char* buf, uint32 len, int in_opcode = -1) - { - uint32 offset = 0; - if (in_opcode >= 0) { - opcode = in_opcode; - } else { - // Ensure there are at least 2 bytes for the opcode - if (len < 2 || buf == nullptr) { - opcode = 0; - offset = len; - } else { - offset = 2; - opcode = ntohs(*(const uint16*)buf); - } - } - - // Check that there is payload data after the header - if (len > offset) { - size = len - offset; - pBuffer = new unsigned char[size]; - if (buf) - memcpy(pBuffer, buf + offset, size); - else - memset(pBuffer, 0, size); - } else { - pBuffer = nullptr; - size = 0; - } - - version = 0; - eq2_compressed = false; - packet_prepared = false; - packet_encrypted = false; - sent_time = 0; - attempt_count = 0; - sequence = 0; - } - - // Attempts to combine this packet with another protocol packet for efficiency - bool combine(const EQProtocolPacket* rhs) - { - bool result = false; - - // Check if this is already a combined packet and we can add more - if (opcode == OP_Combined && size + rhs->size + 5 < 256) { - auto tmpbuffer = new unsigned char[size + rhs->size + 3]; - memcpy(tmpbuffer, pBuffer, size); - uint32 offset = size; - tmpbuffer[offset++] = rhs->Size(); - offset += rhs->serialize(tmpbuffer + offset); - size = offset; - delete[] pBuffer; - pBuffer = tmpbuffer; - result = true; - } - // Check if we can create a new combined packet from two individual packets - else if (size + rhs->size + 7 < 256) { - auto tmpbuffer = new unsigned char[size + rhs->size + 6]; - uint32 offset = 0; - tmpbuffer[offset++] = Size(); - offset += serialize(tmpbuffer + offset); - tmpbuffer[offset++] = rhs->Size(); - offset += rhs->serialize(tmpbuffer + offset); - size = offset; - delete[] pBuffer; - pBuffer = tmpbuffer; - opcode = OP_Combined; - result = true; - } - return result; - } - - // Serializes packet data to destination buffer with optional offset - uint32 serialize(unsigned char* dest, int8 offset = 0) const - { - if (opcode > 0xff) { - *(uint16*)dest = opcode; - } else { - *(dest) = 0; - *(dest + 1) = opcode; - } - memcpy(dest + 2, pBuffer + offset, size - offset); - - return size + 2; - } - - // Creates a deep copy of this protocol packet - EQProtocolPacket* Copy() - { - EQProtocolPacket* new_packet = new EQProtocolPacket(opcode, pBuffer, size); - new_packet->eq2_compressed = this->eq2_compressed; - new_packet->packet_prepared = this->packet_prepared; - new_packet->packet_encrypted = this->packet_encrypted; - return new_packet; - } - - // Converts protocol packet to application packet for higher-level processing - EQApplicationPacket* MakeApplicationPacket(uint8 opcode_size = 0) const; - - // Validates CRC checksum on received packet data - static bool ValidateCRC(const unsigned char* buffer, int length, uint32 Key) - { - bool valid = false; - - // OP_SessionRequest, OP_SessionResponse, OP_OutOfSession are not CRC'd - if (buffer[0] == 0x00 && (buffer[1] == OP_SessionRequest || buffer[1] == OP_SessionResponse || buffer[1] == OP_OutOfSession)) { - valid = true; - } else if (buffer[2] == 0x00 && buffer[3] == 0x19) { - valid = true; - } else { - uint16 comp_crc = CRC16(buffer, length - 2, Key); - uint16 packet_crc = ntohs(*(const uint16*)(buffer + length - 2)); - valid = (!packet_crc || comp_crc == packet_crc); - } - return valid; - } - - // Decompresses packet data using zlib inflation - static uint32 Decompress(const unsigned char* buffer, const uint32 length, unsigned char* newbuf, uint32 newbufsize) - { - uint32 newlen = 0; - uint32 flag_offset = 0; - newbuf[0] = buffer[0]; - - if (buffer[0] == 0x00) { - flag_offset = 2; - newbuf[1] = buffer[1]; - } else { - flag_offset = 1; - } - - if (length > 2 && buffer[flag_offset] == 0x5a) { - LogWrite(PACKET__DEBUG, 0, "Packet", "In Decompress 1"); - newlen = Inflate(const_cast(buffer + flag_offset + 1), length - (flag_offset + 1) - 2, newbuf + flag_offset, newbufsize - flag_offset) + 2; - - // something went bad with zlib - if (newlen == -1) { - LogWrite(PACKET__ERROR, 0, "Packet", "Debug Bad Inflate!"); - DumpPacket(buffer, length); - memcpy(newbuf, buffer, length); - return length; - } - - newbuf[newlen++] = buffer[length - 2]; - newbuf[newlen++] = buffer[length - 1]; - } else if (length > 2 && buffer[flag_offset] == 0xa5) { - LogWrite(PACKET__DEBUG, 0, "Packet", "In Decompress 2"); - memcpy(newbuf + flag_offset, buffer + flag_offset + 1, length - (flag_offset + 1)); - newlen = length - 1; - } else { - memcpy(newbuf, buffer, length); - newlen = length; - } - - return newlen; - } - - // Compresses packet data using zlib deflation - static uint32 Compress(const unsigned char* buffer, const uint32 length, unsigned char* newbuf, uint32 newbufsize) - { - uint32 flag_offset = 1, newlength; - newbuf[0] = buffer[0]; - - if (buffer[0] == 0) { - flag_offset = 2; - newbuf[1] = buffer[1]; - } - - if (length > 30) { - newlength = Deflate(const_cast(buffer + flag_offset), length - flag_offset, newbuf + flag_offset + 1, newbufsize); - *(newbuf + flag_offset) = 0x5a; - newlength += flag_offset + 1; - } else { - memmove(newbuf + flag_offset + 1, buffer + flag_offset, length - flag_offset); - *(newbuf + flag_offset) = 0xa5; - newlength = length + 1; - } - - return newlength; - } - - // Decodes chat message encryption - static void ChatDecode(unsigned char* buffer, int size, int DecodeKey) - { - if (buffer[1] != 0x01 && buffer[0] != 0x02 && buffer[0] != 0x1d) { - int Key = DecodeKey; - unsigned char* test = (unsigned char*)malloc(size); - buffer += 2; - size -= 2; - - int i; - for (i = 0; i + 4 <= size; i += 4) { - int pt = (*(int*)&buffer[i]) ^ (Key); - Key = (*(int*)&buffer[i]); - *(int*)&test[i] = pt; - } - unsigned char KC = Key & 0xFF; - for (; i < size; i++) { - test[i] = buffer[i] ^ KC; - } - memcpy(buffer, test, size); - free(test); - } - } - - // Encodes chat message with encryption - static void ChatEncode(unsigned char* buffer, int size, int EncodeKey) - { - if (buffer[1] != 0x01 && buffer[0] != 0x02 && buffer[0] != 0x1d) { - int Key = EncodeKey; - char* test = (char*)malloc(size); - int i; - buffer += 2; - size -= 2; - for (i = 0; i + 4 <= size; i += 4) { - int pt = (*(int*)&buffer[i]) ^ (Key); - Key = pt; - *(int*)&test[i] = pt; - } - unsigned char KC = Key & 0xFF; - for (; i < size; i++) { - test[i] = buffer[i] ^ KC; - } - memcpy(buffer, test, size); - free(test); - } - } - - // Determines if buffer contains a valid protocol packet - static bool IsProtocolPacket(const unsigned char* in_buff, uint32_t len, bool bTrimCRC) - { - bool ret = false; - uint16_t opcode = ntohs(*(uint16_t*)in_buff); - - switch (opcode) { - case OP_SessionRequest: - case OP_SessionDisconnect: - case OP_KeepAlive: - case OP_SessionStatResponse: - case OP_Packet: - case OP_Combined: - case OP_Fragment: - case OP_Ack: - case OP_OutOfOrderAck: - case OP_OutOfSession: - ret = true; - break; - } - - return ret; - } - -private: - // Copy constructor disabled to prevent accidental copies - EQProtocolPacket(const EQProtocolPacket& p) {} -}; - -// EverQuest 2 specific packet handling with login opcodes -class EQ2Packet : public EQProtocolPacket -{ -public: - EmuOpcode login_op; // EmuOpcode for this packet - - // Constructor with login opcode and data buffer - EQ2Packet(const EmuOpcode in_login_op, const unsigned char* buf, uint32 len) : EQProtocolPacket(OP_Packet, buf, len) - { - login_op = in_login_op; - eq2_compressed = false; - packet_prepared = false; - packet_encrypted = false; - } - - // Attempts to combine this EQ2 packet with another for transmission efficiency - bool AppCombine(EQ2Packet* rhs) - { - bool result = false; - uchar* tmpbuffer = nullptr; - bool over_sized_packet = false; - int32 new_size = 0; - - // If this is already a combined packet and we can add more - if (opcode == OP_AppCombined && ((size + rhs->size + 3) < 255)) { - int16 tmp_size = rhs->size - 2; - if (tmp_size >= 255) { - new_size = size + tmp_size + 3; - over_sized_packet = true; - } else { - new_size = size + tmp_size + 1; - } - - tmpbuffer = new uchar[new_size]; - uchar* ptr = tmpbuffer; - memcpy(ptr, pBuffer, size); - ptr += size; - - if (over_sized_packet) { - memset(ptr, 255, sizeof(int8)); - ptr += sizeof(int8); - tmp_size = htons(tmp_size); - memcpy(ptr, &tmp_size, sizeof(int16)); - ptr += sizeof(int16); - } else { - memcpy(ptr, &tmp_size, sizeof(int8)); - ptr += sizeof(int8); - } - - memcpy(ptr, rhs->pBuffer + 2, rhs->size - 2); - delete[] pBuffer; - size = new_size; - pBuffer = tmpbuffer; - safe_delete(rhs); - result = true; - } - // Create a new combined packet from two individual packets - else if (rhs->size > 2 && size > 2 && (size + rhs->size + 6) < 255) { - int32 tmp_size = size - 2; - int32 tmp_size2 = rhs->size - 2; - opcode = OP_AppCombined; - bool over_sized_packet2 = false; - new_size = size; - - if (tmp_size >= 255) { - new_size += 5; - over_sized_packet = true; - } else { - new_size += 3; - } - - if (tmp_size2 >= 255) { - new_size += tmp_size2 + 3; - over_sized_packet2 = true; - } else { - new_size += tmp_size2 + 1; - } - - tmpbuffer = new uchar[new_size]; - tmpbuffer[2] = 0; - tmpbuffer[3] = 0x19; - uchar* ptr = tmpbuffer + 4; - - if (over_sized_packet) { - memset(ptr, 255, sizeof(int8)); - ptr += sizeof(int8); - tmp_size = htons(tmp_size); - memcpy(ptr, &tmp_size, sizeof(int16)); - ptr += sizeof(int16); - } else { - memcpy(ptr, &tmp_size, sizeof(int8)); - ptr += sizeof(int8); - } - - memcpy(ptr, pBuffer + 2, size - 2); - ptr += (size - 2); - - if (over_sized_packet2) { - memset(ptr, 255, sizeof(int8)); - ptr += sizeof(int8); - tmp_size2 = htons(tmp_size2); - memcpy(ptr, &tmp_size2, sizeof(int16)); - ptr += sizeof(int16); - } else { - memcpy(ptr, &tmp_size2, sizeof(int8)); - ptr += sizeof(int8); - } - - memcpy(ptr, rhs->pBuffer + 2, rhs->size - 2); - size = new_size; - delete[] pBuffer; - pBuffer = tmpbuffer; - safe_delete(rhs); - result = true; - } - - return result; - } - - // Creates a deep copy of this EQ2 packet - EQ2Packet* Copy() - { - EQ2Packet* new_packet = new EQ2Packet(login_op, pBuffer, size); - new_packet->eq2_compressed = this->eq2_compressed; - new_packet->packet_prepared = this->packet_prepared; - new_packet->packet_encrypted = this->packet_encrypted; - return new_packet; - } - - // Prepares packet for transmission by adding headers and opcodes - int8 PreparePacket(int16 MaxLen) - { - int16 OpcodeVersion = GetOpcodeVersion(version); - - // stops a crash for incorrect version - if (EQOpcodeManager.count(OpcodeVersion) == 0) { - LogWrite(PACKET__ERROR, 0, "Packet", "Version %i is not listed in the opcodes table.", version); - return -1; - } - - packet_prepared = true; - - int16 login_opcode = EQOpcodeManager[OpcodeVersion]->EmuToEQ(login_op); - if (login_opcode == 0xcdcd) { - LogWrite(PACKET__ERROR, 0, "Packet", "Version %i is not listed in the opcodes table for opcode %s", version, EQOpcodeManager[OpcodeVersion]->EmuToName(login_op)); - return -1; - } - - int16 orig_opcode = login_opcode; - int8 offset = 0; - // one of the int16s is for the seq, other is for the EQ2 opcode and compressed flag (OP_Packet is the header, not the opcode) - int32 new_size = size + sizeof(int16) + sizeof(int8); - bool oversized = false; - - if (login_opcode != 2) { - new_size += sizeof(int8); // for opcode - if (login_opcode >= 255) { - new_size += sizeof(int16); - oversized = true; - } else { - login_opcode = ntohs(login_opcode); - } - } - - uchar* new_buffer = new uchar[new_size]; - memset(new_buffer, 0, new_size); - uchar* ptr = new_buffer + sizeof(int16); // sequence is first - - if (login_opcode != 2) { - if (oversized) { - ptr += sizeof(int8); // compressed flag - int8 addon = 0xff; - memcpy(ptr, &addon, sizeof(int8)); - ptr += sizeof(int8); - } - memcpy(ptr, &login_opcode, sizeof(int16)); - ptr += sizeof(int16); - } else { - memcpy(ptr, &login_opcode, sizeof(int8)); - ptr += sizeof(int8); - } - - memcpy(ptr, pBuffer, size); - - safe_delete_array(pBuffer); - pBuffer = new_buffer; - offset = new_size - size - 1; - size = new_size; - - return offset; - } - - // Gets human-readable name for the login opcode - const char* GetOpcodeName() - { - int16 OpcodeVersion = GetOpcodeVersion(version); - if (EQOpcodeManager.count(OpcodeVersion) > 0) - return EQOpcodeManager[OpcodeVersion]->EmuToName(login_op); - else - return nullptr; - } -}; - -// Application-level packet handling for game logic -class EQApplicationPacket : public EQPacket -{ - friend class EQProtocolPacket; - friend class EQStream; - -public: - static uint8 default_opcode_size; // Default size for opcodes in bytes - - // Default constructor - EQApplicationPacket() : EQPacket(0, nullptr, 0) - { - emu_opcode = OP_Unknown; - app_opcode_size = default_opcode_size; - } - - // Constructor with opcode only - EQApplicationPacket(const EmuOpcode op) : EQPacket(0, nullptr, 0) - { - SetOpcode(op); - app_opcode_size = default_opcode_size; - } - - // Constructor with opcode and length - EQApplicationPacket(const EmuOpcode op, const uint32 len) : EQPacket(0, nullptr, len) - { - SetOpcode(op); - app_opcode_size = default_opcode_size; - } - - // Constructor with opcode, buffer and length - EQApplicationPacket(const EmuOpcode op, const unsigned char* buf, const uint32 len) : EQPacket(0, buf, len) - { - SetOpcode(op); - app_opcode_size = default_opcode_size; - } - - // Attempts to combine this application packet with another (currently not implemented) - bool combine(const EQApplicationPacket* rhs) - { - cout << "CALLED AP COMBINE!!!!\n"; - return false; - } - - // Serializes application packet to destination buffer - uint32 serialize(unsigned char* dest) const - { - uint8 OpCodeBytes = app_opcode_size; - - if (app_opcode_size == 1) { - *(unsigned char*)dest = opcode; - } else { - // Application opcodes with a low order byte of 0x00 require an extra 0x00 byte inserting prior to the opcode. - if ((opcode & 0x00ff) == 0) { - *(uint8*)dest = 0; - *(uint16*)(dest + 1) = opcode; - ++OpCodeBytes; - } else { - *(uint16*)dest = opcode; - } - } - - memcpy(dest + app_opcode_size, pBuffer, size); - - return size + OpCodeBytes; - } - - // Returns total packet size including opcode - uint32 Size() const { return size + app_opcode_size; } - - // Creates a deep copy of this application packet - EQApplicationPacket* Copy() const - { - EQApplicationPacket* it = new EQApplicationPacket; - try { - it->pBuffer = new unsigned char[size]; - memcpy(it->pBuffer, pBuffer, size); - it->size = size; - it->opcode = opcode; - it->emu_opcode = emu_opcode; - it->version = version; - return it; - } - catch (bad_alloc& ba) { - cout << ba.what() << endl; - if (it != nullptr) - delete it; - } - return nullptr; - } - - // Sets the opcode size for this packet - void SetOpcodeSize(uint8 s) { app_opcode_size = s; } - - // Sets the opcode for this packet using EmuOpcode - void SetOpcode(EmuOpcode emu_op) - { - if (emu_op == OP_Unknown) { - opcode = 0; - emu_opcode = OP_Unknown; - return; - } - - opcode = EQOpcodeManager[GetOpcodeVersion(version)]->EmuToEQ(emu_op); - - if (opcode == OP_Unknown) { - LogWrite(PACKET__DEBUG, 0, "Packet", "Unable to convert Emu opcode %s (%d) into an EQ opcode.", OpcodeNames[emu_op], emu_op); - } - - // save the emu opcode we just set. - emu_opcode = emu_op; - } - - // Gets the constant opcode value for this packet - const EmuOpcode GetOpcodeConst() const - { - if (emu_opcode != OP_Unknown) { - return emu_opcode; - } - if (opcode == 10000) { - return OP_Unknown; - } - - EmuOpcode emu_op; - emu_op = EQOpcodeManager[GetOpcodeVersion(version)]->EQToEmu(opcode); - if (emu_op == OP_Unknown) { - LogWrite(PACKET__DEBUG, 1, "Packet", "Unable to convert EQ opcode 0x%.4X (%i) to an emu opcode (%s)", opcode, opcode, __FUNCTION__); - } - - return emu_op; - } - - // Gets the opcode for this packet (const version) - inline const EmuOpcode GetOpcode() const { return GetOpcodeConst(); } - - // Gets the opcode for this packet (caching version) - inline const EmuOpcode GetOpcode() { EmuOpcode r = GetOpcodeConst(); emu_opcode = r; return r; } - -protected: - EmuOpcode emu_opcode; // Cached emu opcode to avoid repeated lookups - -private: - uint8 app_opcode_size; // Size of opcode in bytes - - // Constructor from raw buffer - used internally by EQProtocolPacket - EQApplicationPacket(const unsigned char* buf, uint32 len, uint8 opcode_size = 0) - { - uint32 offset = 0; - app_opcode_size = (opcode_size == 0) ? EQApplicationPacket::default_opcode_size : opcode_size; - - if (app_opcode_size == 1) { - opcode = *(const unsigned char*)buf; - offset++; - } else { - opcode = *(const uint16*)buf; - offset += 2; - } - - if ((len - offset) > 0) { - pBuffer = new unsigned char[len - offset]; - memcpy(pBuffer, buf + offset, len - offset); - size = len - offset; - } else { - pBuffer = nullptr; - size = 0; - } - - emu_opcode = OP_Unknown; - } - - // Copy constructor disabled to prevent accidental copies - EQApplicationPacket(const EQApplicationPacket& p) { emu_opcode = OP_Unknown; app_opcode_size = default_opcode_size; } -}; - -// Implementation of MakeApplicationPacket that was forward declared -inline EQApplicationPacket* EQProtocolPacket::MakeApplicationPacket(uint8 opcode_size) const -{ - EQApplicationPacket* res = new EQApplicationPacket; - res->app_opcode_size = (opcode_size == 0) ? EQApplicationPacket::default_opcode_size : opcode_size; - - if (res->app_opcode_size == 1) { - res->pBuffer = new unsigned char[size + 1]; - memcpy(res->pBuffer + 1, pBuffer, size); - *(res->pBuffer) = htons(opcode) & 0xff; - res->opcode = opcode & 0xff; - res->size = size + 1; - } else { - res->pBuffer = new unsigned char[size]; - memcpy(res->pBuffer, pBuffer, size); - res->opcode = opcode; - res->size = size; - } - res->copyInfo(this); - return res; -} - -// Global packet dumping functions for debugging - -// Dumps application packet data in hexadecimal format -void DumpPacketHex(const EQApplicationPacket* app) -{ - DumpPacketHex(app->pBuffer, app->size); -} - -// Dumps application packet data in ASCII format -void DumpPacketAscii(const EQApplicationPacket* app) -{ - DumpPacketAscii(app->pBuffer, app->size); -} - -// Dumps protocol packet data in hexadecimal format -void DumpPacket(const EQProtocolPacket* app) -{ - DumpPacketHex(app->pBuffer, app->size); -} - -// Dumps application packet with optional info display -void DumpPacket(const EQApplicationPacket* app, bool iShowInfo = false) -{ - if (iShowInfo) { - cout << "Dumping Applayer: 0x" << hex << setfill('0') << setw(4) << app->GetOpcode() << dec; - cout << " size:" << app->size << endl; - } - DumpPacketHex(app->pBuffer, app->size); -} - -// Dumps application packet data in binary format -void DumpPacketBin(const EQApplicationPacket* app) -{ - DumpPacketBin(app->pBuffer, app->size); -} \ No newline at end of file diff --git a/old/common/packet/packet_dump.hpp b/old/common/packet/packet_dump.hpp deleted file mode 100644 index a514ea1..0000000 --- a/old/common/packet/packet_dump.hpp +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GNU GPL v3+ License - -#pragma once - -#include -#include -#include -#include - -#include "eq_packet.hpp" -#include "../debug.hpp" -#include "../types.hpp" -#include "../servertalk.hpp" -#include "../stream/eq_stream.hpp" - -using namespace std; - -class ServerPacket; - -// Function declarations -void DumpPacketAscii(const uchar* buf, int32 size, int32 cols = 16, int32 skip = 0); -void DumpPacketHex(const uchar* buf, int32 size, int32 cols = 16, int32 skip = 0); -void DumpPacketBin(const void* data, int32 len); -void DumpPacket(const uchar* buf, int32 size); -void DumpPacket(const ServerPacket* pack, bool iShowInfo = false); -void DumpPacketBin(const ServerPacket* pack); -void DumpPacketBin(int32 data); -void DumpPacketBin(int16 data); -void DumpPacketBin(int8 data); - -// Outputs packet buffer as ASCII characters with formatted columns -// Non-printable characters are displayed as dots -void DumpPacketAscii(const uchar* buf, int32 size, int32 cols, int32 skip) -{ - for(int32 i = skip; i < size; i++) { - if ((i - skip) % cols == 0) { - cout << endl << setw(3) << setfill(' ') << i - skip << ":"; - } - else if ((i - skip) % (cols / 2) == 0) { - cout << " - "; - } - - if (buf[i] > 32 && buf[i] < 127) { - cout << buf[i]; - } - else { - cout << '.'; - } - } - cout << endl << endl; -} - -// Outputs packet buffer as hexadecimal with ASCII representation sidebar -// Includes offset addresses and formatted column layout -void DumpPacketHex(const uchar* buf, int32 size, int32 cols, int32 skip) -{ - if (size == 0 || size > 39565) - return; - - char output[4]; - int j = 0; - auto ascii = std::make_unique(cols + 1); - memset(ascii.get(), 0, cols + 1); - - int32 i; - for(i = skip; i < size; i++) { - if ((i - skip) % cols == 0) { - if (i != skip) - cout << " | " << ascii.get() << endl; - cout << setw(4) << setfill(' ') << i - skip << ": "; - memset(ascii.get(), 0, cols + 1); - j = 0; - } - else if ((i - skip) % (cols / 2) == 0) { - cout << "- "; - } - - sprintf(output, "%02X ", static_cast(buf[i])); - cout << output; - - if (buf[i] >= 32 && buf[i] < 127) { - ascii[j++] = buf[i]; - } - else { - ascii[j++] = '.'; - } - } - - int32 k = ((i - skip) - 1) % cols; - if (k < 8) - cout << " "; - for (int32 h = k + 1; h < cols; h++) { - cout << " "; - } - cout << " | " << ascii.get() << endl; -} - -// Simple wrapper to dump packet in hexadecimal format -void DumpPacket(const uchar* buf, int32 size) -{ - DumpPacketHex(buf, size); -} - -// Dumps ServerPacket with optional header information display -// Shows opcode and size when iShowInfo is true -void DumpPacket(const ServerPacket* pack, bool iShowInfo) -{ - if (iShowInfo) { - cout << "Dumping ServerPacket: 0x" << hex << setfill('0') << setw(4) << pack->opcode << dec; - cout << " size:" << pack->size << endl; - } - DumpPacketHex(pack->pBuffer, pack->size); -} - -// Wrapper to dump ServerPacket buffer in binary format -void DumpPacketBin(const ServerPacket* pack) -{ - DumpPacketBin(pack->pBuffer, pack->size); -} - -// Dumps 32-bit integer in binary format -void DumpPacketBin(int32 data) -{ - DumpPacketBin(reinterpret_cast(&data), sizeof(int32)); -} - -// Dumps 16-bit integer in binary format -void DumpPacketBin(int16 data) -{ - DumpPacketBin(reinterpret_cast(&data), sizeof(int16)); -} - -// Dumps 8-bit integer in binary format -void DumpPacketBin(int8 data) -{ - DumpPacketBin(reinterpret_cast(&data), sizeof(int8)); -} - -// Outputs data buffer in binary format with hexadecimal values sidebar -// Each byte is displayed as 8 binary digits with offset addressing -void DumpPacketBin(const void* iData, int32 len) -{ - if (!len) - return; - - const int8* data = static_cast(iData); - int32 k = 0; - - for (k = 0; k < len; k++) { - if (k % 4 == 0) { - if (k != 0) { - cout << " | " << hex << setw(2) << setfill('0') << static_cast(data[k-4]) << dec; - cout << " " << hex << setw(2) << setfill('0') << static_cast(data[k-3]) << dec; - cout << " " << hex << setw(2) << setfill('0') << static_cast(data[k-2]) << dec; - cout << " " << hex << setw(2) << setfill('0') << static_cast(data[k-1]) << dec; - cout << endl; - } - cout << setw(4) << setfill('0') << k << ":"; - } - else if (k % 2 == 0) - cout << " "; - - cout << " "; - - // Output each bit of the byte - for (int bit = 0; bit < 8; bit++) { - cout << ((data[k] & (1 << bit)) ? "1" : "0"); - } - } - - // Handle final line formatting - int8 tmp = (k % 4); - if (!tmp) - tmp = 4; - if (tmp <= 3) - cout << " "; - if (tmp <= 2) - cout << " "; - if (tmp <= 1) - cout << " "; - - cout << " | " << hex << setw(2) << setfill('0') << static_cast(data[k-4]) << dec; - if (tmp > 1) - cout << " " << hex << setw(2) << setfill('0') << static_cast(data[k-3]) << dec; - if (tmp > 2) - cout << " " << hex << setw(2) << setfill('0') << static_cast(data[k-2]) << dec; - if (tmp > 3) - cout << " " << hex << setw(2) << setfill('0') << static_cast(data[k-1]) << dec; - cout << endl; -} \ No newline at end of file diff --git a/old/common/packet/packet_functions.hpp b/old/common/packet/packet_functions.hpp deleted file mode 100644 index 02f4536..0000000 --- a/old/common/packet/packet_functions.hpp +++ /dev/null @@ -1,376 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team, GPL v3 License - -#pragma once - -#include -#include -#include -#include -#include - -#include "eq_packet.hpp" -#include "packet_dump.hpp" -#include "../types.hpp" -#include "../stream/eq_stream.hpp" - -#define eqemu_alloc_func Z_NULL -#define eqemu_free_func Z_NULL - -// Forward declarations for encryption functions (implementations not provided) -void EncryptProfilePacket(EQApplicationPacket* app); -void EncryptProfilePacket(uchar* pBuffer, int32 size); -void EncryptZoneSpawnPacket(EQApplicationPacket* app); -void EncryptZoneSpawnPacket(uchar* pBuffer, int32 size); -#define EncryptSpawnPacket EncryptZoneSpawnPacket - -/** - * Compresses packet data using zlib deflate algorithm - * @param in_data Input data buffer to compress - * @param in_length Length of input data in bytes - * @param out_data Output buffer for compressed data - * @param max_out_length Maximum size of output buffer - * @return Number of bytes written to output buffer, or 0 on failure - */ -int DeflatePacket(unsigned char* in_data, int in_length, unsigned char* out_data, int max_out_length) -{ -#ifdef REUSE_ZLIB - static bool inited = false; - static z_stream zstream; - int zerror; - - // Special cleanup state - deallocate zlib resources - if (in_data == nullptr && out_data == nullptr && in_length == 0 && max_out_length == 0) { - deflateEnd(&zstream); - return 0; - } - - // Initialize zlib stream on first use - if (!inited) { - zstream.zalloc = eqemu_alloc_func; - zstream.zfree = eqemu_free_func; - zstream.opaque = Z_NULL; - deflateInit(&zstream, Z_FINISH); - inited = true; - } - - // Configure input and output buffers - zstream.next_in = in_data; - zstream.avail_in = in_length; - zstream.next_out = out_data; - zstream.avail_out = max_out_length; - - // Perform compression - zerror = deflate(&zstream, Z_FINISH); - deflateReset(&zstream); - - if (zerror == Z_STREAM_END) { - return zstream.total_out; - } - else { - return 0; - } -#else - // Return early if no input data provided - if (in_data == nullptr) { - return 0; - } - - z_stream zstream; - int zerror; - - // Initialize zlib stream for single-use compression - zstream.next_in = in_data; - zstream.avail_in = in_length; - zstream.zalloc = eqemu_alloc_func; - zstream.zfree = eqemu_free_func; - zstream.opaque = Z_NULL; - deflateInit(&zstream, Z_FINISH); - zstream.next_out = out_data; - zstream.avail_out = max_out_length; - - // Perform compression - zerror = deflate(&zstream, Z_FINISH); - - if (zerror == Z_STREAM_END) { - deflateEnd(&zstream); - return zstream.total_out; - } - else { - deflateEnd(&zstream); - return 0; - } -#endif -} - -/** - * Decompresses packet data using zlib inflate algorithm - * @param indata Input compressed data buffer - * @param indatalen Length of compressed input data - * @param outdata Output buffer for decompressed data - * @param outdatalen Maximum size of output buffer - * @param iQuiet Suppress error messages if true - * @return Number of bytes written to output buffer, or 0 on failure - */ -uint32 InflatePacket(uchar* indata, uint32 indatalen, uchar* outdata, uint32 outdatalen, bool iQuiet = false) -{ -#ifdef REUSE_ZLIB - static bool inited = false; - static z_stream zstream; - int zerror; - - // Special cleanup state - deallocate zlib resources - if (indata == nullptr && outdata == nullptr && indatalen == 0 && outdatalen == 0) { - inflateEnd(&zstream); - return 0; - } - - // Initialize zlib stream on first use - if (!inited) { - zstream.zalloc = eqemu_alloc_func; - zstream.zfree = eqemu_free_func; - zstream.opaque = Z_NULL; - inflateInit2(&zstream, 15); - inited = true; - } - - // Configure input and output buffers - zstream.next_in = indata; - zstream.avail_in = indatalen; - zstream.next_out = outdata; - zstream.avail_out = outdatalen; - - // Perform decompression - zerror = inflate(&zstream, Z_FINISH); - inflateReset(&zstream); - - if (zerror == Z_STREAM_END) { - return zstream.total_out; - } - else { - // Output error information unless quiet mode is enabled - if (!iQuiet) { - std::cout << "Error: InflatePacket: inflate() returned " << zerror << " '"; - if (zstream.msg) - std::cout << zstream.msg; - std::cout << "'" << std::endl; - } - - // Handle specific error condition - if (zerror == -4 && zstream.msg == nullptr) { - return 0; - } - - return 0; - } -#else - // Return early if no input data provided - if (indata == nullptr) - return 0; - - z_stream zstream; - int zerror = 0; - - // Initialize zlib stream for single-use decompression - zstream.next_in = indata; - zstream.avail_in = indatalen; - zstream.next_out = outdata; - zstream.avail_out = outdatalen; - zstream.zalloc = eqemu_alloc_func; - zstream.zfree = eqemu_free_func; - zstream.opaque = Z_NULL; - - // Initialize inflate stream - int init_result = inflateInit2(&zstream, 15); - if (init_result != Z_OK) { - return 0; - } - - // Perform decompression - zerror = inflate(&zstream, Z_FINISH); - - if (zerror == Z_STREAM_END) { - inflateEnd(&zstream); - return zstream.total_out; - } - else { - // Output error information unless quiet mode is enabled - if (!iQuiet) { - std::cout << "Error: InflatePacket: inflate() returned " << zerror << " '"; - if (zstream.msg) - std::cout << zstream.msg; - std::cout << "'" << std::endl; - } - - // Handle specific error condition - if (zerror == -4 && zstream.msg == nullptr) { - inflateEnd(&zstream); - return 0; - } - - inflateEnd(&zstream); - return 0; - } -#endif -} - -/** - * Performs left bit rotation on 32-bit integer - * @param in Input value to rotate - * @param bits Number of bit positions to rotate left - * @return Rotated value - */ -int32 roll(int32 in, int8 bits) -{ - return ((in << bits) | (in >> (32 - bits))); -} - -/** - * Performs left bit rotation on 64-bit integer - * @param in Input value to rotate - * @param bits Number of bit positions to rotate left - * @return Rotated value - */ -int64 roll(int64 in, int8 bits) -{ - return ((in << bits) | (in >> (64 - bits))); -} - -/** - * Performs right bit rotation on 32-bit integer - * @param in Input value to rotate - * @param bits Number of bit positions to rotate right - * @return Rotated value - */ -int32 rorl(int32 in, int8 bits) -{ - return ((in >> bits) | (in << (32 - bits))); -} - -/** - * Performs right bit rotation on 64-bit integer - * @param in Input value to rotate - * @param bits Number of bit positions to rotate right - * @return Rotated value - */ -int64 rorl(int64 in, int8 bits) -{ - return ((in >> bits) | (in << (64 - bits))); -} - -/** - * Generates CRC lookup table value for given index - * Uses recursive algorithm to compute CRC polynomial values - * @param idx Index value for CRC lookup table - * @return CRC value for the given index - */ -int32 CRCLookup(uchar idx) -{ - // Handle base cases - if (idx == 0) - return 0x00000000; - - if (idx == 1) - return 0x77073096; - - if (idx == 2) - return roll(CRCLookup(1), 1); - - if (idx == 4) - return 0x076DC419; - - // Find highest set bit and compute CRC recursively - for (uchar b = 7; b > 0; b--) { - uchar bv = 1 << b; - - // Check if this bit is the only one set - if (!(idx ^ bv)) { - return roll(CRCLookup(4), b - 2); - } - - // If bit is set, use XOR with lower bits - if (idx & bv) { - return CRCLookup(bv) ^ CRCLookup(idx & (bv - 1)); - } - } - - // Should not reach here - return failure indicator - return 0; -} - -/** - * Generates CRC32 checksum for given buffer using custom lookup function - * @param b Initial CRC seed value - * @param bufsize Size of buffer in bytes - * @param buf Pointer to data buffer - * @return Generated CRC32 checksum in network byte order - */ -uint32 GenerateCRC(int32 b, int32 bufsize, uchar* buf) -{ - int32 CRC = (b ^ 0xFFFFFFFF); - int32 bufremain = bufsize; - uchar* bufptr = buf; - - // Process each byte in the buffer - while (bufremain--) { - CRC = CRCLookup(static_cast(*(bufptr++) ^ (CRC & 0xFF))) ^ (CRC >> 8); - } - - return htonl(CRC ^ 0xFFFFFFFF); -} - -// Pre-computed CRC32 lookup table for standard polynomial -constexpr long int CRCArray[] = { - 0, 1996959894, 3993919788, 2567524794, 124634137, 1886057615, 3915621685, 2657392035, - 249268274, 2044508324, 3772115230, 2547177864, 162941995, 2125561021, 3887607047, 2428444049, - 498536548, 1789927666, 4089016648, 2227061214, 450548861, 1843258603, 4107580753, 2211677639, - 325883990, 1684777152, 4251122042, 2321926636, 335633487, 1661365465, 4195302755, 2366115317, - 997073096, 1281953886, 3579855332, 2724688242, 1006888145, 1258607687, 3524101629, 2768942443, - 901097722, 1119000684, 3686517206, 2898065728, 853044451, 1172266101, 3705015759, 2882616665, - 651767980, 1373503546, 3369554304, 3218104598, 565507253, 1454621731, 3485111705, 3099436303, - 671266974, 1594198024, 3322730930, 2970347812, 795835527, 1483230225, 3244367275, 3060149565, - 1994146192, 31158534, 2563907772, 4023717930, 1907459465, 112637215, 2680153253, 3904427059, - 2013776290, 251722036, 2517215374, 3775830040, 2137656763, 141376813, 2439277719, 3865271297, - 1802195444, 476864866, 2238001368, 4066508878, 1812370925, 453092731, 2181625025, 4111451223, - 1706088902, 314042704, 2344532202, 4240017532, 1658658271, 366619977, 2362670323, 4224994405, - 1303535960, 984961486, 2747007092, 3569037538, 1256170817, 1037604311, 2765210733, 3554079995, - 1131014506, 879679996, 2909243462, 3663771856, 1141124467, 855842277, 2852801631, 3708648649, - 1342533948, 654459306, 3188396048, 3373015174, 1466479909, 544179635, 3110523913, 3462522015, - 1591671054, 702138776, 2966460450, 3352799412, 1504918807, 783551873, 3082640443, 3233442989, - 3988292384, 2596254646, 62317068, 1957810842, 3939845945, 2647816111, 81470997, 1943803523, - 3814918930, 2489596804, 225274430, 2053790376, 3826175755, 2466906013, 167816743, 2097651377, - 4027552580, 2265490386, 503444072, 1762050814, 4150417245, 2154129355, 426522225, 1852507879, - 4275313526, 2312317920, 282753626, 1742555852, 4189708143, 2394877945, 397917763, 1622183637, - 3604390888, 2714866558, 953729732, 1340076626, 3518719985, 2797360999, 1068828381, 1219638859, - 3624741850, 2936675148, 906185462, 1090812512, 3747672003, 2825379669, 829329135, 1181335161, - 3412177804, 3160834842, 628085408, 1382605366, 3423369109, 3138078467, 570562233, 1426400815, - 3317316542, 2998733608, 733239954, 1555261956, 3268935591, 3050360625, 752459403, 1541320221, - 2607071920, 3965973030, 1969922972, 40735498, 2617837225, 3943577151, 1913087877, 83908371, - 2512341634, 3803740692, 2075208622, 213261112, 2463272603, 3855990285, 2094854071, 198958881, - 2262029012, 4057260610, 1759359992, 534414190, 2176718541, 4139329115, 1873836001, 414664567, - 2282248934, 4279200368, 1711684554, 285281116, 2405801727, 4167216745, 1634467795, 376229701, - 2685067896, 3608007406, 1308918612, 956543938, 2808555105, 3495958263, 1231636301, 1047427035, - 2932959818, 3654703836, 1088359270, 936918000, 2847714899, 3736837829, 1202900863, 817233897, - 3183342108, 3401237130, 1404277552, 615818150, 3134207493, 3453421203, 1423857449, 601450431, - 3009837614, 3294710456, 1567103746, 711928724, 3020668471, 3272380065, 1510334235, 755167117 -}; - -/** - * Generates CRC32 checksum using pre-computed lookup table - * More efficient than custom CRCLookup function for standard CRC32 calculations - * @param initial Initial CRC seed value (typically 0xFFFFFFFF) - * @param buf Pointer to data buffer - * @param len Length of data buffer in bytes - * @return Generated CRC32 checksum - */ -uint32 GenerateCRCRecipe(uint32 initial, void* buf, uint32 len) -{ - uint32 c = 0xFFFFFFFF; - sint8* u = static_cast(buf); - - // Process each byte using lookup table - for (size_t i = 0; i < len; ++i) { - c = CRCArray[(c ^ u[i]) & 0xFF] ^ (c >> 8); - } - - return c; -} \ No newline at end of file diff --git a/old/common/packet/packet_struct.hpp b/old/common/packet/packet_struct.hpp deleted file mode 100644 index e70f888..0000000 --- a/old/common/packet/packet_struct.hpp +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team, GPL v3+ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#include "data_struct.hpp" -#include "../log.hpp" -#include "../debug.hpp" -#include "../data_buffer.hpp" -#include "../config_reader.hpp" -#include "../misc_functions.hpp" -#include "../opcodes/opcode_manager.hpp" - -#ifdef WORLD - #include "../../WorldServer/Items/Items.h" - #include "../../WorldServer/Player.h" - #include "../../WorldServer/World.h" - class Item; - class Player; -#endif - -extern ConfigReader configReader; -extern std::map EQOpcodeManager; - -class PacketStruct : public DataBuffer -{ -public: - // Default constructor - initializes empty packet structure - PacketStruct() - { - parent = 0; - opcode = OP_Unknown; - opcode_type = std::string(""); - } - - // Copy constructor from existing packet with sub-packet flag - PacketStruct(PacketStruct* packet, bool sub) - { - std::vector::iterator itr2; - - for (itr2 = packet->structs.begin(); itr2 != packet->structs.end(); itr2++) { - add(new DataStruct(*itr2)); - } - std::vector::iterator itr; - for (itr = packet->flags.begin(); itr != packet->flags.end(); itr++) { - AddFlag((*itr).c_str()); - } - sub_packet = sub; - opcode = packet->opcode; - version = packet->version; - opcode_type = packet->opcode_type; - name = packet->name; - sub_packet_size = 0; - parent = 0; - } - - // Copy constructor with client version specification - PacketStruct(PacketStruct* packet, int16 in_client_version) - { - parent = packet->parent; - client_version = in_client_version; - std::vector::iterator itr2; - name = packet->name; - - for (itr2 = packet->structs.begin(); itr2 != packet->structs.end(); itr2++) { - add(new DataStruct(*itr2)); - } - std::vector::iterator itr; - for (itr = packet->flags.begin(); itr != packet->flags.end(); itr++) { - AddFlag((*itr).c_str()); - } - sub_packet = false; - opcode = packet->opcode; - version = packet->version; - opcode_type = packet->opcode_type; - sub_packet_size = 1; - - addPacketArrays(packet); - } - - // Destructor - cleans up all allocated data structures and arrays - ~PacketStruct() - { - deleteDataStructs(&structs); - deleteDataStructs(&orig_structs); - deletePacketArrays(this); - struct_map.clear(); - struct_data.clear(); - flags.clear(); - } - - // Core packet management functions - void add(DataStruct* data); - void add(PacketStruct* packet_struct); - void remove(DataStruct* data); - void remove(const char* name); - DataStruct* findStruct(const char* name, int32 index); - DataStruct* findStruct(const char* name, int32 index1, int32 index2); - - // String manipulation functions - void setSmallStringByName(const char* name, const char* text, int32 index = 0); - void setMediumStringByName(const char* name, const char* text, int32 index = 0); - void setLargeStringByName(const char* name, const char* text, int32 index = 0); - void setSmallString(DataStruct* data_struct, const char* text, int32 index = 0); - void setMediumString(DataStruct* data_struct, const char* text, int32 index = 0); - void setLargeString(DataStruct* data_struct, const char* text, int32 index = 0); - - // Color manipulation functions - void setColorByName(const char* name, int8 red, int8 green, int8 blue, int32 index = 0); - void setColorByName(const char* name, EQ2_Color* data, int32 index = 0); - void setColorByName(const char* name, EQ2_Color data, int32 index = 0); - void setColor(DataStruct* data, int8 red, int8 green, int8 blue, int32 index = 0); - void setColor(DataStruct* data_struct, EQ2_Color data, int32 index = 0); - - // Equipment manipulation functions - void setEquipmentByName(const char* name, EQ2_EquipmentItem data, int32 index = 0); - void setEquipmentByName(const char* name, EQ2_EquipmentItem* data, int32 size); - void setEquipmentByName(const char* name, int32 type, int8 c_red, int8 c_blue, int8 c_green, int8 h_red, int8 h_blue, int8 h_green, int32 index = 0); - void setEquipmentByName(DataStruct* data_struct, EQ2_EquipmentItem data, int32 index = 0); - void setEquipment(DataStruct* data, int16 type, int8 c_red, int8 c_blue, int8 c_green, int8 h_red, int8 h_blue, int8 h_green, int32 index); - - // Array management functions - void reAddAll(int32 length); - void renameSubstructArray(const char* substruct, int32 index); - int32 GetArraySize(DataStruct* data_struct, int32 index); - int32 GetArraySize(const char* name, int32 index); - int32 GetArraySizeByName(const char* name, int32 index); - void UpdateArrayByArrayLength(DataStruct* data_struct, int32 index, int32 size); - void UpdateArrayByArrayLengthName(const char* name, int32 index, int32 size); - - // Packet structure utility functions - void setAddToPacketByName(const char* name, bool new_val, int32 index = 0); - void setArrayAddToPacketByName(const char* name, bool new_val, int32 index1 = 0, int32 index2 = 0); - void setAddTypePacketByName(const char* name, int8 new_val, int32 index = 0); - - // Type checking functions - bool IsStringValueType(std::string in_name, int32 index); - bool IsColorValueType(std::string in_name, int32 index); - - // Variable state functions - bool GetVariableIsSet(const char* name); - bool GetVariableIsNotSet(const char* name); - - // Flag management functions - bool CheckFlagExists(const char* name); - void AddFlag(const char* name); - - // Serialization functions - void serializePacket(bool clear = true); - std::string* serializeString(); - EQ2Packet* serialize(); - EQ2Packet* serializeCountPacket(int16 version, int8 offset = 0, uchar* orig_packet = 0, uchar* xor_packet = 0); - void AddSerializedData(DataStruct* data, int32 index = 0, std::string* datastring = 0); - - // Data loading functions - bool LoadPacketData(uchar* data, int32 data_len, bool create_color = false); - bool StructLoadData(DataStruct* data_struct, void* data, int32 len, bool useType2 = false, bool create_color = false); - void LoadFromPacketStruct(PacketStruct* packet, char* substruct_name = 0); - - // Opcode management functions - bool SetOpcode(const char* new_opcode); - int16 GetOpcodeValue(int16 client_version); - const char* GetOpcodeType(); - void SetOpcodeType(const char* opcodeType); - - // Utility accessor functions - std::vector* getStructs() { return &structs; } - int32 GetVersion() { return version; } - void SetVersion(int32 in_version) { version = in_version; } - EmuOpcode GetOpcode() { return opcode; } - const char* GetName() { return name.c_str(); } - void SetName(const char* in_name) { name = std::string(in_name); } - bool LoadedSuccessfully() { return loadedSuccessfully; } - bool IsSubPacket() { return sub_packet; } - void IsSubPacket(bool new_val) { sub_packet = new_val; } - int32 GetSubPacketSize() { return sub_packet_size; } - void SetSubPacketSize(int32 new_size) { sub_packet_size = new_size; } - int32 GetTotalPacketSize(); - - // Advanced functions - PacketStruct* GetPacketStructByName(const char* name); - void* GetStructPointer(DataStruct* data_struct, bool erase = false); - std::vector GetDataStructs(); - void PrintPacket(); - -#ifdef WORLD - // World server specific functions - void setItem(DataStruct* ds, Item* item, Player* player, int32 index, sint8 offset = 0, bool loot_item = false, bool make_empty_item_packet = false, bool inspect = false); - void setItemByName(const char* name, Item* item, Player* player, int32 index = 0, sint8 offset = 0, bool loot_item = false, bool make_empty_item_packet = false, bool inspect = false); - void setItemArrayDataByName(const char* name, Item* item, Player* player, int32 index1 = 0, int32 index2 = 0, sint8 offset = 0, bool loot_item = false, bool make_empty_item_packet = false, bool inspect = false); - void ResetData(); -#endif - - // Include data getter/setter template functions - #include "packet_struct_data.hpp" - -private: - // Memory management functions - void deleteDataStructs(std::vector* data_structs); - void deletePacketArrays(PacketStruct* packet); - void addPacketArrays(PacketStruct* packet); - - // Core data setting functions - void setDataType(DataStruct* data_struct, char data, int32 index); - void setDataType(DataStruct* data_struct, int8 data, int32 index); - void setDataType(DataStruct* data_struct, int16 data, int32 index); - void setDataType(DataStruct* data_struct, int32 data, int32 index); - void setDataType(DataStruct* data_struct, int64 data, int32 index); - void setDataType(DataStruct* data_struct, sint8 data, int32 index); - void setDataType(DataStruct* data_struct, sint16 data, int32 index); - void setDataType(DataStruct* data_struct, sint32 data, int32 index); - void setDataType(DataStruct* data_struct, sint64 data, int32 index); - void setDataType(DataStruct* data_struct, float data, int32 index); - void setDataType(DataStruct* data_struct, double data, int32 index); - void setData(DataStruct* data_struct, EQ2_8BitString* input_string, int32 index, bool use_second_type = false); - void setData(DataStruct* data_struct, EQ2_16BitString* input_string, int32 index, bool use_second_type = false); - void setData(DataStruct* data_struct, EQ2_32BitString* input_string, int32 index, bool use_second_type = false); - - // Member variables - PacketStruct* parent; // Parent packet structure for sub-packets - int32 sub_packet_size; // Size of sub-packet array - std::string opcode_type; // String representation of opcode type - bool sub_packet; // Whether this is a sub-packet - bool loadedSuccessfully; // Whether data loaded successfully - std::string name; // Name of this packet structure - EmuOpcode opcode; // Opcode enumeration value - int16 version; // Packet structure version - int16 client_version; // Client version for compatibility - std::vector arrays; // Sub-packet arrays - std::vector flags; // Packet flags - std::map struct_data; // Data structure to memory mapping - std::map packed_data; // Packed data storage - std::map struct_map; // Name to data structure mapping - std::vector structs; // Main data structures - std::vector orig_structs; // Original structures for reAddAll - std::vector orig_packets; // Original packets for reAddAll -}; \ No newline at end of file diff --git a/old/common/packet/packet_struct_data.hpp b/old/common/packet/packet_struct_data.hpp deleted file mode 100644 index 2c9015f..0000000 --- a/old/common/packet/packet_struct_data.hpp +++ /dev/null @@ -1,447 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team, GPL v3+ - -// Template data getter/setter functions for PacketStruct - -// Template functions for setting generic data with conditional type handling -template void setData(DataStruct* data_struct, Data* data, int32 index, bool use_second_type = false) -{ - if (!data_struct) - return; - data_struct->SetIsOptional(false); - int8 type_to_use = (use_second_type) ? data_struct->GetType2() : data_struct->GetType(); - if (type_to_use >= DATA_STRUCT_EQ2_8BIT_STRING && type_to_use <= DATA_STRUCT_EQ2_32BIT_STRING) { - if (type_to_use == DATA_STRUCT_EQ2_8BIT_STRING) { - setSmallString(data_struct, data, index); - } - else if (type_to_use == DATA_STRUCT_EQ2_16BIT_STRING) { - setMediumString(data_struct, data, index); - } - else { - setLargeString(data_struct, data, index); - } - } - else { - if (data_struct && index == 0 && data_struct->GetLength() > 1) { - if (type_to_use == DATA_STRUCT_CHAR) { - for (int32 i = 0; data && i < data_struct->GetLength() && i < strlen(data); i++) - setData(data_struct, data[i], i); - } - else { - for (int32 i = 0; i < data_struct->GetLength(); i++) - setData(data_struct, data[i], i); - } - } - else - setData(data_struct, *data, index); - } -} - -// Template function for setting data by value with type conversion -template void setData(DataStruct* data_struct, Data data, int32 index, bool use_second_type = false) -{ - if (data_struct && index < data_struct->GetLength()) { - data_struct->SetIsOptional(false); - int8 type_to_use = (use_second_type) ? data_struct->GetType2() : data_struct->GetType(); - if (use_second_type) { - data_struct->SetType(type_to_use); - } - switch (type_to_use) { - case DATA_STRUCT_INT8: - setDataType(data_struct, (int8)data, index); - break; - case DATA_STRUCT_INT16: - setDataType(data_struct, (int16)data, index); - break; - case DATA_STRUCT_INT32: - setDataType(data_struct, (int32)data, index); - break; - case DATA_STRUCT_INT64: - setDataType(data_struct, (int64)data, index); - break; - case DATA_STRUCT_SINT8: - setDataType(data_struct, (sint8)data, index); - break; - case DATA_STRUCT_SINT16: - setDataType(data_struct, (sint16)data, index); - break; - case DATA_STRUCT_SINT32: - setDataType(data_struct, (sint32)data, index); - break; - case DATA_STRUCT_SINT64: - setDataType(data_struct, (sint64)data, index); - break; - case DATA_STRUCT_CHAR: - setDataType(data_struct, (char)data, index); - break; - case DATA_STRUCT_FLOAT: - setDataType(data_struct, (float)data, index); - break; - case DATA_STRUCT_DOUBLE: - setDataType(data_struct, (double)data, index); - break; - case DATA_STRUCT_COLOR: - setColor(data_struct, *((EQ2_Color*)&data), index); - break; - case DATA_STRUCT_EQUIPMENT: - setEquipmentByName(data_struct, *((EQ2_EquipmentItem*)&data), index); - break; - case DATA_STRUCT_ITEM: - break; - } - } -} - -// Template functions for setting data by name -template void setSubstructSubstructDataByName(const char* substruct_name1, const char* substruct_name2, const char* name, Data data, int32 substruct_index1 = 0, int32 substruct_index2 = 0, int32 index = 0) -{ - char tmp[15] = { 0 }; - sprintf(tmp, "_%i_%i", substruct_index1, substruct_index2); - std::string name2 = std::string(substruct_name1).append("_").append(substruct_name2).append("_").append(name).append(tmp); - setData(findStruct(name2.c_str(), index), data, index); -} - -template void setSubstructDataByName(const char* substruct_name, const char* name, Data data, int32 substruct_index = 0, int32 index = 0) -{ - char tmp[10] = { 0 }; - sprintf(tmp, "_%i", substruct_index); - std::string name2 = std::string(substruct_name).append("_").append(name).append(tmp); - setData(findStruct(name2.c_str(), index), data, index); -} - -template void setSubstructColorByName(const char* substruct_name, const char* name, Data data, int32 substruct_index = 0, int32 index = 0) -{ - char tmp[10] = { 0 }; - sprintf(tmp, "_%i", substruct_index); - std::string name2 = std::string(substruct_name).append("_").append(name).append(tmp); - setColor(findStruct(name2.c_str(), index), data, index); -} - -template void setSubstructArrayDataByName(const char* substruct_name, const char* name, Data data, int32 index = 0, int32 substruct_index = 0) -{ - char tmp[10] = { 0 }; - sprintf(tmp, "_%i", substruct_index); - std::string name2 = std::string(substruct_name).append("_").append(name).append(tmp); - setData(findStruct(name2.c_str(), substruct_index, index), data, index); -} - -template void setSubstructArrayColorByName(const char* substruct_name, const char* name, Data data, int32 substruct_index = 0, int32 index = 0) -{ - char tmp[10] = { 0 }; - sprintf(tmp, "_%i", substruct_index); - std::string name2 = std::string(substruct_name).append("_").append(name).append(tmp); - setColor(findStruct(name2.c_str(), index, substruct_index), data, index); -} - -template void setDataByName(const char* name, Data data, int32 index = 0, bool use_second_type = false) -{ - setData(findStruct(name, index), data, index, use_second_type); -} - -template void setDataByName(const char* name, Data* data, int32 index = 0, bool use_second_type = false) -{ - setData(findStruct(name, index), data, index, use_second_type); -} - -template void setSubArrayDataByName(const char* name, Data data, int32 index1 = 0, int32 index2 = 0, int32 index3 = 0) -{ - char tmp[20] = { 0 }; - sprintf(tmp, "%i_%i", index1, index2); - std::string name2 = std::string(name).append(tmp); - setData(findStruct(name2.c_str(), index2, index3), data, index3); -} - -template void setArrayDataByName(const char* name, Data data, int32 index1 = 0, int32 index2 = 0, bool use_second_type = false) -{ - char tmp[10] = { 0 }; - sprintf(tmp, "_%i", index1); - std::string name2 = std::string(name).append(tmp); - setData(findStruct(name2.c_str(), index1, index2), data, index2, use_second_type); -} - -template void setSubArrayLengthByName(const char* name, Data data, int32 index1 = 0, int32 index2 = 0) -{ - char tmp[10] = { 0 }; - sprintf(tmp, "_%i", index1); - std::string name2 = std::string(name).append(tmp); - DataStruct* data_struct = findStruct(name2.c_str(), index2); - setData(data_struct, data, index2); - UpdateArrayByArrayLength(data_struct, index2, data); -} - -template void setArrayLengthByName(const char* name, Data data, int32 index = 0) -{ - DataStruct* data_struct = findStruct(name, index); - setData(data_struct, data, index); - UpdateArrayByArrayLength(data_struct, index, data); -} - -template void setSubstructArrayLengthByName(const char* substruct_name, const char* name, Data data, int32 substruct_index = 0, int32 index = 0) -{ - char tmp[10] = { 0 }; - sprintf(tmp, "_%i", substruct_index); - std::string name2 = std::string(substruct_name).append("_").append(name).append(tmp); - - DataStruct* data_struct = findStruct(name2.c_str(), index); - setData(data_struct, data, index); - UpdateArrayByArrayLength(data_struct, index, data); -} - -// Getter functions by name for all data types -int8 getType_int8_ByName(const char* name, int32 index = 0, bool force = false) -{ - DataStruct* data_struct = findStruct(name, index); - return getType_int8(data_struct, index, force); -} - -int16 getType_int16_ByName(const char* name, int32 index = 0, bool force = false) -{ - DataStruct* data_struct = findStruct(name, index); - return getType_int16(data_struct, index, force); -} - -int32 getType_int32_ByName(const char* name, int32 index = 0, bool force = false) -{ - DataStruct* data_struct = findStruct(name, index); - return getType_int32(data_struct, index, force); -} - -int64 getType_int64_ByName(const char* name, int32 index = 0, bool force = false) -{ - DataStruct* data_struct = findStruct(name, index); - return getType_int64(data_struct, index, force); -} - -sint8 getType_sint8_ByName(const char* name, int32 index = 0, bool force = false) -{ - DataStruct* data_struct = findStruct(name, index); - return getType_sint8(data_struct, index, force); -} - -sint16 getType_sint16_ByName(const char* name, int32 index = 0, bool force = false) -{ - DataStruct* data_struct = findStruct(name, index); - return getType_sint16(data_struct, index, force); -} - -sint32 getType_sint32_ByName(const char* name, int32 index = 0, bool force = false) -{ - DataStruct* data_struct = findStruct(name, index); - return getType_sint32(data_struct, index, force); -} - -sint64 getType_sint64_ByName(const char* name, int32 index = 0, bool force = false) -{ - DataStruct* data_struct = findStruct(name, index); - return getType_sint64(data_struct, index, force); -} - -float getType_float_ByName(const char* name, int32 index = 0, bool force = false) -{ - DataStruct* data_struct = findStruct(name, index); - return getType_float(data_struct, index, force); -} - -double getType_double_ByName(const char* name, int32 index = 0, bool force = false) -{ - DataStruct* data_struct = findStruct(name, index); - return getType_double(data_struct, index, force); -} - -char getType_char_ByName(const char* name, int32 index = 0, bool force = false) -{ - DataStruct* data_struct = findStruct(name, index); - return getType_char(data_struct, index, force); -} - -EQ2_8BitString getType_EQ2_8BitString_ByName(const char* name, int32 index = 0, bool force = false) -{ - DataStruct* data_struct = findStruct(name, index); - return getType_EQ2_8BitString(data_struct, index, force); -} - -EQ2_16BitString getType_EQ2_16BitString_ByName(const char* name, int32 index = 0, bool force = false) -{ - DataStruct* data_struct = findStruct(name, index); - return getType_EQ2_16BitString(data_struct, index, force); -} - -EQ2_32BitString getType_EQ2_32BitString_ByName(const char* name, int32 index = 0, bool force = false) -{ - DataStruct* data_struct = findStruct(name, index); - return getType_EQ2_32BitString(data_struct, index, force); -} - -EQ2_Color getType_EQ2_Color_ByName(const char* name, int32 index = 0, bool force = false) -{ - DataStruct* data_struct = findStruct(name, index); - return getType_EQ2_Color(data_struct, index, force); -} - -EQ2_EquipmentItem getType_EQ2_EquipmentItem_ByName(const char* name, int32 index = 0, bool force = false) -{ - DataStruct* data_struct = findStruct(name, index); - return getType_EQ2_EquipmentItem(data_struct, index, force); -} - -// Getter functions by data structure for all data types -int8 getType_int8(DataStruct* data_struct, int32 index = 0, bool force = false) -{ - if (data_struct && ((data_struct->GetType() == DATA_STRUCT_INT8) || force)) { - int8* ptr = (int8*)GetStructPointer(data_struct); - return ptr[index]; - } - return 0; -} - -int16 getType_int16(DataStruct* data_struct, int32 index = 0, bool force = false) -{ - if (data_struct && ((data_struct->GetType() == DATA_STRUCT_INT16) || force)) { - int16* ptr = (int16*)GetStructPointer(data_struct); - return ptr[index]; - } - return 0; -} - -int32 getType_int32(DataStruct* data_struct, int32 index = 0, bool force = false) -{ - if (data_struct && ((data_struct->GetType() == DATA_STRUCT_INT32) || force)) { - int32* ptr = (int32*)GetStructPointer(data_struct); - return ptr[index]; - } - return 0; -} - -int64 getType_int64(DataStruct* data_struct, int32 index = 0, bool force = false) -{ - if (data_struct && ((data_struct->GetType() == DATA_STRUCT_INT64) || force)) { - int64* ptr = (int64*)GetStructPointer(data_struct); - return ptr[index]; - } - return 0; -} - -sint8 getType_sint8(DataStruct* data_struct, int32 index = 0, bool force = false) -{ - if (data_struct && ((data_struct->GetType() == DATA_STRUCT_SINT8) || force)) { - sint8* ptr = (sint8*)GetStructPointer(data_struct); - return ptr[index]; - } - return 0; -} - -sint16 getType_sint16(DataStruct* data_struct, int32 index = 0, bool force = false) -{ - if (data_struct && ((data_struct->GetType() == DATA_STRUCT_SINT16) || force)) { - sint16* ptr = (sint16*)GetStructPointer(data_struct); - return ptr[index]; - } - return 0; -} - -sint32 getType_sint32(DataStruct* data_struct, int32 index = 0, bool force = false) -{ - if (data_struct && ((data_struct->GetType() == DATA_STRUCT_SINT32) || force)) { - sint32* ptr = (sint32*)GetStructPointer(data_struct); - return ptr[index]; - } - return 0; -} - -sint64 getType_sint64(DataStruct* data_struct, int32 index = 0, bool force = false) -{ - if (data_struct && ((data_struct->GetType() == DATA_STRUCT_SINT64) || force)) { - sint64* ptr = (sint64*)GetStructPointer(data_struct); - return ptr[index]; - } - return 0; -} - -float getType_float(DataStruct* data_struct, int32 index = 0, bool force = false) -{ - if (data_struct && ((data_struct->GetType() == DATA_STRUCT_FLOAT) || force)) { - float* ptr = (float*)GetStructPointer(data_struct); - return ptr[index]; - } - return 0; -} - -double getType_double(DataStruct* data_struct, int32 index = 0, bool force = false) -{ - if (data_struct && ((data_struct->GetType() == DATA_STRUCT_DOUBLE) || force)) { - double* ptr = (double*)GetStructPointer(data_struct); - return ptr[index]; - } - return 0; -} - -char getType_char(DataStruct* data_struct, int32 index = 0, bool force = false) -{ - if (data_struct && ((data_struct->GetType() == DATA_STRUCT_CHAR) || force)) { - char* ptr = (char*)GetStructPointer(data_struct); - return ptr[index]; - } - return 0; -} - -EQ2_8BitString getType_EQ2_8BitString(DataStruct* data_struct, int32 index = 0, bool force = false) -{ - if (data_struct && ((data_struct->GetType() == DATA_STRUCT_EQ2_8BIT_STRING) || force)) { - EQ2_8BitString* ptr = (EQ2_8BitString*)GetStructPointer(data_struct); - return ptr[index]; - } - EQ2_8BitString ret; - ret.size = 0; - return ret; -} - -EQ2_16BitString getType_EQ2_16BitString(DataStruct* data_struct, int32 index = 0, bool force = false) -{ - if (data_struct && ((data_struct->GetType() == DATA_STRUCT_EQ2_16BIT_STRING) || force)) { - EQ2_16BitString* ptr = (EQ2_16BitString*)GetStructPointer(data_struct); - return ptr[index]; - } - EQ2_16BitString ret; - ret.size = 0; - return ret; -} - -EQ2_32BitString getType_EQ2_32BitString(DataStruct* data_struct, int32 index = 0, bool force = false) -{ - if (data_struct && ((data_struct->GetType() == DATA_STRUCT_EQ2_32BIT_STRING) || force)) { - EQ2_32BitString* ptr = (EQ2_32BitString*)GetStructPointer(data_struct); - return ptr[index]; - } - EQ2_32BitString ret; - ret.size = 0; - return ret; -} - -EQ2_Color getType_EQ2_Color(DataStruct* data_struct, int32 index = 0, bool force = false) -{ - if (data_struct && ((data_struct->GetType() == DATA_STRUCT_COLOR) || force)) { - EQ2_Color* ptr = (EQ2_Color*)GetStructPointer(data_struct); - return ptr[index]; - } - EQ2_Color ret; - ret.blue = 0; - ret.red = 0; - ret.green = 0; - return ret; -} - -EQ2_EquipmentItem getType_EQ2_EquipmentItem(DataStruct* data_struct, int32 index = 0, bool force = false) -{ - if (data_struct && ((data_struct->GetType() == DATA_STRUCT_EQUIPMENT) || force)) { - EQ2_EquipmentItem* ptr = (EQ2_EquipmentItem*)GetStructPointer(data_struct); - return ptr[index]; - } - EQ2_EquipmentItem ret; - ret.color.blue = 0; - ret.color.red = 0; - ret.color.green = 0; - ret.highlight.blue = 0; - ret.highlight.red = 0; - ret.highlight.green = 0; - ret.type = 0; - return ret; -} \ No newline at end of file diff --git a/old/common/packet_dump.cpp b/old/common/packet_dump.cpp new file mode 100644 index 0000000..27cb0a9 --- /dev/null +++ b/old/common/packet_dump.cpp @@ -0,0 +1,195 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include "../common/debug.h" +#include +#include +#include + +using namespace std; + +#include "packet_dump.h" +#include "EQStream.h" +#include "../common/servertalk.h" + +void DumpPacketAscii(const uchar* buf, int32 size, int32 cols, int32 skip) { + // Output as ASCII + for(int32 i=skip; i 32 && buf[i] < 127) + { + cout << buf[i]; + } + else + { + cout << '.'; + } + } + cout << endl << endl; +} + +void DumpPacketHex(const uchar* buf, int32 size, int32 cols, int32 skip) { + if (size == 0 || size > 39565) + return; + // Output as HEX + char output[4]; + int j = 0; char* ascii = new char[cols+1]; memset(ascii, 0, cols+1); + int32 i; + for(i=skip; i= 32 && buf[i] < 127) { + ascii[j++] = buf[i]; + } + else { + ascii[j++] = '.'; + } +// cout << setfill(0) << setw(2) << hex << (int)buf[i] << " "; + } + int32 k = ((i-skip)-1)%cols; + if (k < 8) + cout << " "; + for (int32 h = k+1; h < cols; h++) { + cout << " "; + } + cout << " | " << ascii << endl; + safe_delete_array(ascii); +} + +void DumpPacket(const uchar* buf, int32 size) +{ + DumpPacketHex(buf, size); +// DumpPacketAscii(buf,size); +} + +void DumpPacket(const ServerPacket* pack, bool iShowInfo) { + if (iShowInfo) { + cout << "Dumping ServerPacket: 0x" << hex << setfill('0') << setw(4) << pack->opcode << dec; + cout << " size:" << pack->size << endl; + } + DumpPacketHex(pack->pBuffer, pack->size); +} + +void DumpPacketBin(const ServerPacket* pack) { + DumpPacketBin(pack->pBuffer, pack->size); +} + +void DumpPacketBin(int32 data) { + DumpPacketBin((uchar*)&data, sizeof(int32)); +} + +void DumpPacketBin(int16 data) { + DumpPacketBin((uchar*)&data, sizeof(int16)); +} + +void DumpPacketBin(int8 data) { + DumpPacketBin((uchar*)&data, sizeof(int8)); +} + + +void DumpPacketBin(const void* iData, int32 len) { + if (!len) + return; + const int8* data = (const int8*) iData; + int32 k=0; + for (k=0; k 1) + cout << " " << hex << setw(2) << setfill('0') << (int) data[k-3] << dec; + if (tmp > 2) + cout << " " << hex << setw(2) << setfill('0') << (int) data[k-2] << dec; + if (tmp > 3) + cout << " " << hex << setw(2) << setfill('0') << (int) data[k-1] << dec; + cout << endl; +} diff --git a/old/common/packet_dump.h b/old/common/packet_dump.h new file mode 100644 index 0000000..42fe2ef --- /dev/null +++ b/old/common/packet_dump.h @@ -0,0 +1,41 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef PACKET_DUMP_H +#define PACKET_DUMP_H + +#include +using namespace std; + +#include "../common/types.h" +#include "EQPacket.h" + +class ServerPacket; + +void DumpPacketAscii(const uchar* buf, int32 size, int32 cols=16, int32 skip=0); +void DumpPacketHex(const uchar* buf, int32 size, int32 cols=16, int32 skip=0); +void DumpPacketBin(const void* data, int32 len); +void DumpPacket(const uchar* buf, int32 size); +void DumpPacket(const ServerPacket* pack, bool iShowInfo = false); +void DumpPacketBin(const ServerPacket* pack); +void DumpPacketBin(int32 data); +void DumpPacketBin(int16 data); +void DumpPacketBin(int8 data); + +#endif diff --git a/old/common/packet_functions.cpp b/old/common/packet_functions.cpp new file mode 100644 index 0000000..a7e6a6c --- /dev/null +++ b/old/common/packet_functions.cpp @@ -0,0 +1,537 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include "../common/debug.h" +#include +#include +#include +#include +#include "packet_dump.h" +#include "EQStream.h" +#include "packet_functions.h" + +#ifndef WIN32 + #include +#endif + +using namespace std; + +#define eqemu_alloc_func Z_NULL +#define eqemu_free_func Z_NULL + + +int DeflatePacket(unsigned char* in_data, int in_length, unsigned char* out_data, int max_out_length) { +#ifdef REUSE_ZLIB + static bool inited = false; + static z_stream zstream; + int zerror; + + if(in_data == NULL && out_data == NULL && in_length == 0 && max_out_length == 0) { + //special delete state + deflateEnd(&zstream); + return(0); + } + if(!inited) { + zstream.zalloc = eqemu_alloc_func; + zstream.zfree = eqemu_free_func; + zstream.opaque = Z_NULL; + deflateInit(&zstream, Z_FINISH); + } + + zstream.next_in = in_data; + zstream.avail_in = in_length; +/* zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + deflateInit(&zstream, Z_FINISH);*/ + zstream.next_out = out_data; + zstream.avail_out = max_out_length; + zerror = deflate(&zstream, Z_FINISH); + + deflateReset(&zstream); + + if (zerror == Z_STREAM_END) + { +// deflateEnd(&zstream); + return zstream.total_out; + } + else + { +// zerror = deflateEnd(&zstream); + return 0; + } +#else + if(in_data == NULL) { + return(0); + } + + z_stream zstream; + int zerror; + + zstream.next_in = in_data; + zstream.avail_in = in_length; + zstream.zalloc = eqemu_alloc_func; + zstream.zfree = eqemu_free_func; + zstream.opaque = Z_NULL; + deflateInit(&zstream, Z_FINISH); + zstream.next_out = out_data; + zstream.avail_out = max_out_length; + zerror = deflate(&zstream, Z_FINISH); + + if (zerror == Z_STREAM_END) + { + deflateEnd(&zstream); + return zstream.total_out; + } + else + { + zerror = deflateEnd(&zstream); + return 0; + } +#endif +} + +uint32 InflatePacket(uchar* indata, uint32 indatalen, uchar* outdata, uint32 outdatalen, bool iQuiet) { +#ifdef REUSE_ZLIB + static bool inited = false; + static z_stream zstream; + int zerror; + + if(indata == NULL && outdata == NULL && indatalen == 0 && outdatalen == 0) { + //special delete state + inflateEnd(&zstream); + return(0); + } + if(!inited) { + zstream.zalloc = eqemu_alloc_func; + zstream.zfree = eqemu_free_func; + zstream.opaque = Z_NULL; + inflateInit2(&zstream, 15); + } + + zstream.next_in = indata; + zstream.avail_in = indatalen; + zstream.next_out = outdata; + zstream.avail_out = outdatalen; + zstream.zalloc = eqemu_alloc_func; + zstream.zfree = eqemu_free_func; + zstream.opaque = Z_NULL; + + i = inflateInit2( &zstream, 15 ); + if (i != Z_OK) { + return 0; + } + + zerror = inflate( &zstream, Z_FINISH ); + + inflateReset(&zstream); + + if(zerror == Z_STREAM_END) { + return zstream.total_out; + } + else { + if (!iQuiet) { + cout << "Error: InflatePacket: inflate() returned " << zerror << " '"; + if (zstream.msg) + cout << zstream.msg; + cout << "'" << endl; + //DumpPacket(indata-16, indatalen+16); + } + + if (zerror == -4 && zstream.msg == 0) + { + return 0; + } + + return 0; + } +#else + if(indata == NULL) + return(0); + + z_stream zstream; + int zerror = 0; + int i; + + zstream.next_in = indata; + zstream.avail_in = indatalen; + zstream.next_out = outdata; + zstream.avail_out = outdatalen; + zstream.zalloc = eqemu_alloc_func; + zstream.zfree = eqemu_free_func; + zstream.opaque = Z_NULL; + + i = inflateInit2( &zstream, 15 ); + if (i != Z_OK) { + return 0; + } + + zerror = inflate( &zstream, Z_FINISH ); + + if(zerror == Z_STREAM_END) { + inflateEnd( &zstream ); + return zstream.total_out; + } + else { + if (!iQuiet) { + cout << "Error: InflatePacket: inflate() returned " << zerror << " '"; + if (zstream.msg) + cout << zstream.msg; + cout << "'" << endl; + //DumpPacket(indata-16, indatalen+16); + } + + if (zerror == -4 && zstream.msg == 0) + { + return 0; + } + + zerror = inflateEnd( &zstream ); + return 0; + } +#endif +} + +int32 roll(int32 in, int8 bits) { + return ((in << bits) | (in >> (32-bits))); +} + +int64 roll(int64 in, int8 bits) { + return ((in << bits) | (in >> (64-bits))); +} + +int32 rorl(int32 in, int8 bits) { + return ((in >> bits) | (in << (32-bits))); +} + +int64 rorl(int64 in, int8 bits) { + return ((in >> bits) | (in << (64-bits))); +} + +int32 CRCLookup(uchar idx) { + if (idx == 0) + return 0x00000000; + + if (idx == 1) + return 0x77073096; + + if (idx == 2) + return roll(CRCLookup(1), 1); + + if (idx == 4) + return 0x076DC419; + + for (uchar b=7; b>0; b--) { + uchar bv = 1 << b; + + if (!(idx ^ bv)) { + // bit is only one set + return ( roll(CRCLookup (4), b - 2) ); + } + + if (idx&bv) { + // bit is set + return( CRCLookup(bv) ^ CRCLookup(idx&(bv - 1)) ); + } + } + + //Failure + return false; +} + +uint32 GenerateCRC(int32 b, int32 bufsize, uchar *buf) { + int32 CRC = (b ^ 0xFFFFFFFF); + int32 bufremain = bufsize; + uchar* bufptr = buf; + + while (bufremain--) { + CRC = CRCLookup((uchar)(*(bufptr++)^ (CRC&0xFF))) ^ (CRC >> 8); + } + + return (htonl (CRC ^ 0xFFFFFFFF)); +} + +long int CRCArray[] = { + 0, +1996959894, +3993919788, +2567524794, +124634137, +1886057615, +3915621685, +2657392035, +249268274, +2044508324, +3772115230, +2547177864, +162941995, +2125561021, +3887607047, +2428444049, +498536548, +1789927666, +4089016648, +2227061214, +450548861, +1843258603, +4107580753, +2211677639, +325883990, +1684777152, +4251122042, +2321926636, +335633487, +1661365465, +4195302755, +2366115317, +997073096, +1281953886, +3579855332, +2724688242, +1006888145, +1258607687, +3524101629, +2768942443, +901097722, +1119000684, +3686517206, +2898065728, +853044451, +1172266101, +3705015759, +2882616665, +651767980, +1373503546, +3369554304, +3218104598, +565507253, +1454621731, +3485111705, +3099436303, +671266974, +1594198024, +3322730930, +2970347812, +795835527, +1483230225, +3244367275, +3060149565, +1994146192, +31158534, +2563907772, +4023717930, +1907459465, +112637215, +2680153253, +3904427059, +2013776290, +251722036, +2517215374, +3775830040, +2137656763, +141376813, +2439277719, +3865271297, +1802195444, +476864866, +2238001368, +4066508878, +1812370925, +453092731, +2181625025, +4111451223, +1706088902, +314042704, +2344532202, +4240017532, +1658658271, +366619977, +2362670323, +4224994405, +1303535960, +984961486, +2747007092, +3569037538, +1256170817, +1037604311, +2765210733, +3554079995, +1131014506, +879679996, +2909243462, +3663771856, +1141124467, +855842277, +2852801631, +3708648649, +1342533948, +654459306, +3188396048, +3373015174, +1466479909, +544179635, +3110523913, +3462522015, +1591671054, +702138776, +2966460450, +3352799412, +1504918807, +783551873, +3082640443, +3233442989, +3988292384, +2596254646, +62317068, +1957810842, +3939845945, +2647816111, +81470997, +1943803523, +3814918930, +2489596804, +225274430, +2053790376, +3826175755, +2466906013, +167816743, +2097651377, +4027552580, +2265490386, +503444072, +1762050814, +4150417245, +2154129355, +426522225, +1852507879, +4275313526, +2312317920, +282753626, +1742555852, +4189708143, +2394877945, +397917763, +1622183637, +3604390888, +2714866558, +953729732, +1340076626, +3518719985, +2797360999, +1068828381, +1219638859, +3624741850, +2936675148, +906185462, +1090812512, +3747672003, +2825379669, +829329135, +1181335161, +3412177804, +3160834842, +628085408, +1382605366, +3423369109, +3138078467, +570562233, +1426400815, +3317316542, +2998733608, +733239954, +1555261956, +3268935591, +3050360625, +752459403, +1541320221, +2607071920, +3965973030, +1969922972, +40735498, +2617837225, +3943577151, +1913087877, +83908371, +2512341634, +3803740692, +2075208622, +213261112, +2463272603, +3855990285, +2094854071, +198958881, +2262029012, +4057260610, +1759359992, +534414190, +2176718541, +4139329115, +1873836001, +414664567, +2282248934, +4279200368, +1711684554, +285281116, +2405801727, +4167216745, +1634467795, +376229701, +2685067896, +3608007406, +1308918612, +956543938, +2808555105, +3495958263, +1231636301, +1047427035, +2932959818, +3654703836, +1088359270, +936918000, +2847714899, +3736837829, +1202900863, +817233897, +3183342108, +3401237130, +1404277552, +615818150, +3134207493, +3453421203, +1423857449, +601450431, +3009837614, +3294710456, +1567103746, +711928724, +3020668471, +3272380065, +1510334235, +755167117}; + +uint32 GenerateCRCRecipe(uint32 initial, void* buf, uint32 len) +{ + uint32 c = 0xFFFFFFFF; + sint8* u = static_cast(buf); + for (size_t i = 0; i < len; ++i) + { + c = CRCArray[(c ^ u[i]) & 0xFF] ^ (c >> 8); + } + return c; +} \ No newline at end of file diff --git a/old/common/packet_functions.h b/old/common/packet_functions.h new file mode 100644 index 0000000..318ff2d --- /dev/null +++ b/old/common/packet_functions.h @@ -0,0 +1,45 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef PACKET_FUNCTIONS_H +#define PACKET_FUNCTIONS_H +#include "types.h" +#include "EQPacket.h" + +int32 roll(int32 in, int8 bits); +int64 roll(int64 in, int8 bits); +int32 rorl(int32 in, int8 bits); +int64 rorl(int64 in, int8 bits); + +void EncryptProfilePacket(EQApplicationPacket* app); +void EncryptProfilePacket(uchar* pBuffer, int32 size); + +#define EncryptSpawnPacket EncryptZoneSpawnPacket +//void EncryptSpawnPacket(EQApplicationPacket* app); +//void EncryptSpawnPacket(uchar* pBuffer, int32 size); + +void EncryptZoneSpawnPacket(EQApplicationPacket* app); +void EncryptZoneSpawnPacket(uchar* pBuffer, int32 size); + +int DeflatePacket(unsigned char* in_data, int in_length, unsigned char* out_data, int max_out_length); +uint32 InflatePacket(uchar* indata, uint32 indatalen, uchar* outdata, uint32 outdatalen, bool iQuiet = false); +uint32 GenerateCRC(int32 b, int32 bufsize, uchar *buf); +uint32 GenerateCRCRecipe(uint32 b, void* buf, uint32 bufsize); + +#endif diff --git a/old/common/queue.h b/old/common/queue.h new file mode 100644 index 0000000..e29ea05 --- /dev/null +++ b/old/common/queue.h @@ -0,0 +1,128 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef QUEUE_H +#define QUEUE_H + +template +class MyQueue; + +template +class MyQueueNode +{ +public: + MyQueueNode(T* data) + { + next = 0; + this->data = data; + } + + friend class MyQueue; + +private: + T* data; + MyQueueNode* next; +}; + +template +class MyQueue +{ +public: + MyQueue() + { + head = tail = 0; + } + ~MyQueue() { + clear(); + } + + void push(T* data) + { + if (head == 0) + { + tail = head = new MyQueueNode(data); + } + else + { + tail->next = new MyQueueNode(data); + tail = tail->next; + } + } + + T* pop() + { + if (head == 0) + { + return 0; + } + + T* data = head->data; + MyQueueNode* next_node = head->next; + delete head; + head = next_node; + + return data; + } + + T* top() + { + if (head == 0) + { + return 0; + } + + return head->data; + } + + bool empty() + { + if (head == 0) + { + return true; + } + + return false; + } + + void clear() + { + T* d = 0; + while((d = pop())) { + delete d; + } + return; + } + + int count() + { + int count = 0; + MyQueueNode* d = head; + while(d != 0) { + count++; + d = d->next; + } + return(count); + } + +private: + MyQueueNode* head; + MyQueueNode* tail; +}; + +#endif diff --git a/old/common/queue.hpp b/old/common/queue.hpp deleted file mode 100644 index 88258aa..0000000 --- a/old/common/queue.hpp +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3+ License - -#pragma once - -template -class MyQueue; - -template -class MyQueueNode -{ -public: - // Constructor - initializes node with given data pointer - MyQueueNode(T* data) - { - next = nullptr; - this->data = data; - } - - friend class MyQueue; - -private: - T* data; // Pointer to the data stored in this node - MyQueueNode* next; // Pointer to the next node in the queue -}; - -template -class MyQueue -{ -public: - // Constructor - initializes empty queue - MyQueue() - { - head = tail = nullptr; - } - - // Destructor - cleans up all nodes and their data - ~MyQueue() - { - clear(); - } - - // Adds a new element to the back of the queue - void push(T* data) - { - if (head == nullptr) { - tail = head = new MyQueueNode(data); - } else { - tail->next = new MyQueueNode(data); - tail = tail->next; - } - } - - // Removes and returns the front element from the queue - T* pop() - { - if (head == nullptr) { - return nullptr; - } - - T* data = head->data; - MyQueueNode* next_node = head->next; - delete head; - head = next_node; - - return data; - } - - // Returns the front element without removing it from the queue - T* top() - { - if (head == nullptr) { - return nullptr; - } - - return head->data; - } - - // Returns true if the queue is empty - bool empty() - { - return head == nullptr; - } - - // Removes all elements from the queue and deletes their data - void clear() - { - T* d = nullptr; - while ((d = pop())) { - delete d; - } - } - - // Returns the number of elements in the queue - int count() - { - int count = 0; - MyQueueNode* d = head; - while (d != nullptr) { - count++; - d = d->next; - } - return count; - } - -private: - MyQueueNode* head; // Pointer to the first node in the queue - MyQueueNode* tail; // Pointer to the last node in the queue -}; \ No newline at end of file diff --git a/old/common/rc4.hpp b/old/common/rc4.hpp deleted file mode 100644 index 5160941..0000000 --- a/old/common/rc4.hpp +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) - GPL v3 License - -#pragma once - -#include -#include -#include - -// RC4 stream cipher implementation for cryptographic operations -class RC4 -{ -public: - // Initialize RC4 cipher with 64-bit key value - RC4(std::int64_t key) - { - if (!init_state_initialized) { - for (std::int16_t i = 0; i < 256; i++) - init_state[i] = static_cast(i); - init_state_initialized = true; - } - Init(key); - } - - // Destructor - cleanup cipher state - ~RC4() = default; - - // Initialize or reinitialize cipher with new key - void Init(std::int64_t key) - { - // Copy initial state array to working state - std::memcpy(state.data(), init_state.data(), 256); - x = 0; - y = 0; - - // Key scheduling algorithm (KSA) - std::uint32_t key_index = 0; - std::uint32_t state_index = 0; - auto* key_bytes = reinterpret_cast(&key); - - for (std::int16_t i = 0; i < 256; i++) { - std::uint32_t temp = state[i]; - state_index += key_bytes[key_index] + temp; - state_index &= 0xFF; - state[i] = state[state_index]; - state[state_index] = static_cast(temp); - key_index++; - key_index &= 7; // Wrap around 8-byte key - } - } - - // Encrypt/decrypt data buffer using RC4 stream cipher - // RC4 is symmetric - same operation for encrypt and decrypt - void Cypher(std::uint8_t* buffer, std::int32_t length) - { - std::int32_t offset = 0; - std::uint8_t key1 = x; - std::uint8_t key2 = y; - - if (length > 0) { - do { - // Pseudo-random generation algorithm (PRGA) - key1++; - std::uint8_t key_val1 = state[key1]; - - key2 += key_val1; - std::uint8_t key_val2 = state[key2]; - - // Swap state values - state[key1] = key_val2; - state[key2] = key_val1; - - // XOR buffer byte with keystream byte - buffer[offset++] ^= state[(key_val1 + key_val2) & 0xFF]; - } while (offset < length); - } - - // Update cipher state - x = key1; - y = key2; - } - -private: - std::array state; // RC4 internal state array - std::uint8_t x; // First state index - std::uint8_t y; // Second state index - - // Static initialization state shared across all instances - static inline bool init_state_initialized = false; - static inline std::array init_state; -}; \ No newline at end of file diff --git a/old/common/separator.hpp b/old/common/separator.hpp deleted file mode 100644 index 70ccc1f..0000000 --- a/old/common/separator.hpp +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License - -#pragma once - -#include -#include -#include -#include - -/** - * String separator class that intelligently splits strings at delimiter characters. - * Supports multiple delimiters, quote handling, and empty argument skipping. - * Written by Quagmire, modernized for C++20. - */ -class Separator -{ -public: - /** - * Constructs a separator that splits the input message at specified delimiters. - * @param message The string to split - * @param div Primary delimiter character (default: space) - * @param maxArgNum Maximum number of arguments to parse (default: 10) - * @param argLen Maximum length per argument (default: 100) - * @param obeyQuotes Whether to respect quoted strings (default: false) - * @param div2 Secondary delimiter character (default: tab) - * @param div3 Tertiary delimiter character (default: none) - * @param skipEmpty Whether to skip empty arguments (default: true) - */ - Separator(const char* message, char div = ' ', int16_t maxArgNum = 10, - int16_t argLen = 100, bool obeyQuotes = false, char div2 = '\t', - char div3 = 0, bool skipEmpty = true) - : maxArgNum_(maxArgNum), argNum_(0), originalMessage_(message ? message : "") - { - parseMessage(message, div, argLen, obeyQuotes, div2, div3, skipEmpty); - } - - /** - * Default destructor - automatic cleanup with STL containers. - */ - ~Separator() = default; - - /** - * Copy constructor for proper resource management. - */ - Separator(const Separator& other) = default; - - /** - * Move constructor for efficient transfers. - */ - Separator(Separator&& other) noexcept = default; - - /** - * Copy assignment operator. - */ - Separator& operator=(const Separator& other) = default; - - /** - * Move assignment operator. - */ - Separator& operator=(Separator&& other) noexcept = default; - - /** - * Checks if the argument at the specified index is set (non-empty). - * @param num Index of the argument to check - * @return True if argument exists and is non-empty - */ - bool IsSet(int num) const - { - return num >= 0 && num < static_cast(arguments_.size()) && - IsSet(arguments_[num].c_str()); - } - - /** - * Checks if the argument at the specified index is a valid number. - * @param num Index of the argument to check - * @return True if argument is a valid numeric value - */ - bool IsNumber(int num) const - { - return num >= 0 && num < static_cast(arguments_.size()) && - IsNumber(arguments_[num].c_str()); - } - - /** - * Checks if the argument at the specified index is a valid hexadecimal number. - * @param num Index of the argument to check - * @return True if argument is a valid hex number (0x... format) - */ - bool IsHexNumber(int num) const - { - return num >= 0 && num < static_cast(arguments_.size()) && - IsHexNumber(arguments_[num].c_str()); - } - - /** - * Static utility to check if a string is set (non-empty). - * @param check The string to check - * @return True if string is non-null and non-empty - */ - static bool IsSet(const char* check) - { - return check && check[0] != '\0'; - } - - /** - * Static utility to validate if a string represents a number. - * Supports integers, floats, and signed values. - * @param check The string to validate - * @return True if string represents a valid number - */ - static bool IsNumber(const char* check) - { - if (!check || !check[0]) - return false; - - bool seenDecimal = false; - int len = std::strlen(check); - - for (int i = 0; i < len; i++) { - char c = check[i]; - - if (c < '0' || c > '9') { - if (c == '.' && !seenDecimal) { - seenDecimal = true; - } - else if (i == 0 && (c == '-' || c == '+') && len > 1) { - // Valid sign prefix - } - else { - return false; - } - } - } - return true; - } - - /** - * Static utility to validate hexadecimal number format. - * Must start with 0x or 0X followed by valid hex digits. - * @param check The string to validate - * @return True if string is valid hexadecimal format - */ - static bool IsHexNumber(const char* check) - { - if (!check) - return false; - - int len = std::strlen(check); - if (len < 3) - return false; - - if (check[0] != '0' || (check[1] != 'x' && check[1] != 'X')) - return false; - - for (int i = 2; i < len; i++) { - char c = check[i]; - if (!std::isxdigit(c)) - return false; - } - return true; - } - - /** - * Gets the maximum number of arguments this separator can handle. - * @return Maximum argument count - */ - int16_t GetMaxArgNum() const { return maxArgNum_; } - - /** - * Gets the actual number of arguments parsed from the input. - * @return Current argument count - */ - int16_t GetArgNumber() const { return argNum_; } - - /** - * Gets the argument at the specified index as a string. - * @param index Index of the argument to retrieve - * @return Argument string, or empty string if index is invalid - */ - std::string GetArg(int index) const - { - if (index >= 0 && index < static_cast(arguments_.size())) - return arguments_[index]; - return ""; - } - - /** - * Gets a pointer to the original position in the message for the argument. - * This points to the original string without copying. - * @param index Index of the argument - * @return Pointer to argument position in original string - */ - const char* GetArgPlus(int index) const - { - if (index >= 0 && index < static_cast(argumentPointers_.size())) - return argumentPointers_[index]; - return ""; - } - - /** - * Gets the original message that was parsed. - * @return Copy of the original input message - */ - const std::string& GetOriginalMessage() const { return originalMessage_; } - -private: - /** - * Core parsing logic that splits the message according to specified rules. - * Handles quote recognition, multiple delimiters, and empty argument skipping. - */ - void parseMessage(const char* message, char div, int16_t argLen, - bool obeyQuotes, char div2, char div3, bool skipEmpty) - { - if (!message || !message[0]) - return; - - int len = std::strlen(message); - int start = 0; - bool inArg = (!skipEmpty || !isDelimiter(message[0], div, div2, div3)); - bool inQuote = (obeyQuotes && (message[0] == '\"' || message[0] == '\'')); - - // Reserve space for efficiency - arguments_.reserve(maxArgNum_ + 1); - argumentPointers_.reserve(maxArgNum_ + 1); - - if (inArg) - argumentPointers_.push_back(&message[0]); - - for (int i = 0; i < len && argNum_ <= maxArgNum_; i++) { - char currentChar = message[i]; - - if (inArg) { - bool shouldEndArg = false; - - if (!inQuote && isDelimiter(currentChar, div, div2, div3)) { - shouldEndArg = true; - } - else if (inQuote && (currentChar == '\'' || currentChar == '\"')) { - bool nextIsDelimOrEnd = (i + 1 >= len || - isDelimiter(message[i + 1], div, div2, div3)); - if (nextIsDelimOrEnd) { - inQuote = false; - shouldEndArg = true; - } - } - - if (shouldEndArg) { - // Extract argument text - int argStart = argumentPointers_[argNum_] - message; - int argLength = i - argStart; - - if (argLength >= argLen) - argLength = argLen - 1; - - std::string arg; - if (argLength > 0) { - // Handle quoted strings by removing quotes - if (argLength > 1 && - (argumentPointers_[argNum_][0] == '\'' || - argumentPointers_[argNum_][0] == '\"')) { - arg = std::string(argumentPointers_[argNum_] + 1, argLength - 1); - } - else { - arg = std::string(argumentPointers_[argNum_], argLength); - } - } - - arguments_.push_back(arg); - argNum_++; - - if (skipEmpty) { - inArg = false; - } - else { - start = i + 1; - if (start < len && argNum_ <= maxArgNum_) - argumentPointers_.push_back(&message[start]); - } - } - } - else { - if (obeyQuotes && (currentChar == '\"' || currentChar == '\'')) { - inQuote = true; - start = i; - argumentPointers_.push_back(&message[start]); - inArg = true; - } - else if (!isDelimiter(currentChar, div, div2, div3)) { - start = i; - argumentPointers_.push_back(&message[start]); - inArg = true; - } - } - } - - // Handle final argument if we ended while parsing one - if (inArg && argNum_ <= maxArgNum_ && !argumentPointers_.empty()) { - int argStart = argumentPointers_[argNum_] - message; - int argLength = len - argStart; - - if (argLength >= argLen) - argLength = argLen - 1; - - if (argLength > 0) { - std::string arg(argumentPointers_[argNum_], argLength); - arguments_.push_back(arg); - } - } - } - - /** - * Helper function to check if a character is one of the specified delimiters. - * @param c Character to check - * @param div Primary delimiter - * @param div2 Secondary delimiter - * @param div3 Tertiary delimiter - * @return True if character matches any delimiter - */ - static bool isDelimiter(char c, char div, char div2, char div3) - { - return c == div || c == div2 || (div3 != 0 && c == div3); - } - - int16_t maxArgNum_; // Maximum number of arguments to parse - int16_t argNum_; // Actual number of arguments parsed - std::string originalMessage_; // Copy of original input message - std::vector arguments_; // Parsed arguments as strings - std::vector argumentPointers_; // Pointers to original message positions -}; \ No newline at end of file diff --git a/old/common/seperator.h b/old/common/seperator.h new file mode 100644 index 0000000..b033aa8 --- /dev/null +++ b/old/common/seperator.h @@ -0,0 +1,165 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +// This class will split up a string smartly at the div character (default is space and tab) +// Seperator.arg[i] is a copy of the string chopped at the divs +// Seperator.argplus[i] is a pointer to the original string so it doesnt end at the div + +// Written by Quagmire +#ifndef SEPERATOR_H +#define SEPERATOR_H + +#include +#include + +class Seperator +{ +public: + Seperator(const char* message, char div = ' ', int16 in_maxargnum = 10, int16 arglen = 100, bool iObeyQuotes = false, char div2 = '\t', char div3 = 0, bool iSkipEmpty = true) { + int i; + argnum = 0; + msg = strdup(message); + this->maxargnum = in_maxargnum; + argplus = new const char *[maxargnum+1]; + arg = new char *[maxargnum+1]; + for (i=0; i<=maxargnum; i++) { + argplus[i]=arg[i] = new char[arglen+1]; + memset(arg[i], 0, arglen+1); + } + + int len = strlen(message); + int s = 0, l = 0; + bool inarg = (!iSkipEmpty || !(message[0] == div || message[0] == div2 || message[0] == div3)); + bool inquote = (iObeyQuotes && (message[0] == '\"' || message[0] == '\'')); + argplus[0] = message; + if (len == 0) + return; + + for (i=0; i= arglen) + l = arglen; + if (l){ + if(l > 1 && (argplus[argnum][0] == '\'' || argplus[argnum][0] == '\"')){ + l--; + memcpy(arg[argnum], argplus[argnum]+1, l); + } + else + memcpy(arg[argnum], argplus[argnum], l); + } + arg[argnum][l] = 0; + argnum++; + if (iSkipEmpty) + inarg = false; + else { + s=i+1; + argplus[argnum] = &message[s]; + } + } + } + else if (iObeyQuotes && (message[i] == '\"' || message[i] == '\'')) { + inquote = true; + } + else { + s = i; + argplus[argnum] = &message[s]; + if (!(message[i] == div || message[i] == div2 || message[i] == div3)) { + inarg = true; + } + } + if (argnum > maxargnum) + break; + } + if (inarg && argnum <= maxargnum) { + l = i-s; + if (l >= arglen) + l = arglen; + if (l) + memcpy(arg[argnum], argplus[argnum], l); + } + } + ~Seperator() { + for (int i=0; i<=maxargnum; i++) + safe_delete_array(arg[i]); + safe_delete_array(arg); + safe_delete_array(argplus); + if (msg) + free(msg); + } + int16 argnum; + char** arg; + const char** argplus; + char * msg; + bool IsSet(int num) const { + return IsSet(arg[num]); + } + bool IsNumber(int num) const { + return IsNumber(arg[num]); + } + bool IsHexNumber(int num) const { + return IsHexNumber(arg[num]); + } + static bool IsSet(const char *check) { + return check[0] != '\0'; + } + static bool IsNumber(const char* check) { + bool SeenDec = false; + int len = strlen(check); + if (len == 0) { + return false; + } + int i; + for (i = 0; i < len; i++) { + if (check[i] < '0' || check[i] > '9') { + if (check[i] == '.' && !SeenDec) { + SeenDec = true; + } + else if (i == 0 && (check[i] == '-' || check[i] == '+') && !check[i+1] == 0) { + // this is ok, do nothin + } + else { + return false; + } + } + } + return true; + } + static bool IsHexNumber(char* check) { + int len = strlen(check); + if (len < 3) + return false; + if (check[0] != '0' || (check[1] != 'x' && check[1] != 'X')) + return false; + for (int i=2; i '9') && (check[i] < 'A' || check[i] > 'F') && (check[i] < 'a' || check[i] > 'f')) + return false; + } + return true; + } + inline int16 GetMaxArgNum() const { return maxargnum; } + inline int16 GetArgNumber() const { return argnum; } +private: + int16 maxargnum; +}; + +#endif diff --git a/old/common/servertalk.h b/old/common/servertalk.h new file mode 100644 index 0000000..fa2c1c1 --- /dev/null +++ b/old/common/servertalk.h @@ -0,0 +1,754 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef EQ_SOPCODES_H +#define EQ_SOPCODES_H + +#define EQEMU_PROTOCOL_VERSION "0.5.0" + +#include "types.h" +#include "packet_functions.h" +#include + +#define SERVER_TIMEOUT 45000 // how often keepalive gets sent +#define INTERSERVER_TIMER 10000 +#define LoginServer_StatusUpdateInterval 15000 +#define LoginServer_AuthStale 60000 +#define AUTHCHANGE_TIMEOUT 900 // in seconds + +#define ServerOP_KeepAlive 0x0001 // packet to test if port is still open +#define ServerOP_ChannelMessage 0x0002 // broadcast/guildsay +#define ServerOP_SetZone 0x0003 // client -> server zoneinfo +#define ServerOP_ShutdownAll 0x0004 // exit(0); +#define ServerOP_ZoneShutdown 0x0005 // unload all data, goto sleep mode +#define ServerOP_ZoneBootup 0x0006 // come out of sleep mode and load zone specified +#define ServerOP_ZoneStatus 0x0007 // Shows status of all zones +#define ServerOP_SetConnectInfo 0x0008 // Tells server address and port # +#define ServerOP_EmoteMessage 0x0009 // Worldfarts +#define ServerOP_ClientList 0x000A // Update worldserver's client list, for #whos +#define ServerOP_Who 0x000B // #who +#define ServerOP_ZonePlayer 0x000C // #zone, or #summon +#define ServerOP_KickPlayer 0x000D // #kick +#define ServerOP_RefreshGuild 0x000E // Notice to all zoneservers to refresh their guild cache for ID# in packet +#define ServerOP_GuildKickAll 0x000F // Remove all clients from this guild +#define ServerOP_GuildInvite 0x0010 +#define ServerOP_GuildRemove 0x0011 +#define ServerOP_GuildPromote 0x0012 +#define ServerOP_GuildDemote 0x0013 +#define ServerOP_GuildLeader 0x0014 +#define ServerOP_GuildGMSet 0x0015 +#define ServerOP_GuildGMSetRank 0x0016 +#define ServerOP_FlagUpdate 0x0018 // GM Flag updated for character, refresh the memory cache +#define ServerOP_GMGoto 0x0019 +#define ServerOP_MultiLineMsg 0x001A +#define ServerOP_Lock 0x001B // For #lock/#unlock inside server +#define ServerOP_Motd 0x001C // For changing MoTD inside server. +#define ServerOP_Uptime 0x001D +#define ServerOP_Petition 0x001E +#define ServerOP_KillPlayer 0x001F +#define ServerOP_UpdateGM 0x0020 +#define ServerOP_RezzPlayer 0x0021 +#define ServerOP_ZoneReboot 0x0022 +#define ServerOP_ZoneToZoneRequest 0x0023 +#define ServerOP_AcceptWorldEntrance 0x0024 +#define ServerOP_ZAAuth 0x0025 +#define ServerOP_ZAAuthFailed 0x0026 +#define ServerOP_ZoneIncClient 0x0027 // Incomming client +#define ServerOP_ClientListKA 0x0028 +#define ServerOP_ChangeWID 0x0029 +#define ServerOP_IPLookup 0x002A +#define ServerOP_LockZone 0x002B +#define ServerOP_ItemStatus 0x002C +#define ServerOP_OOCMute 0x002D +#define ServerOP_Revoke 0x002E +#define ServerOP_GuildJoin 0x002F +#define ServerOP_GroupIDReq 0x0030 +#define ServerOP_GroupIDReply 0x0031 +#define ServerOP_GroupLeave 0x0032 // for disbanding out of zone folks +#define ServerOP_RezzPlayerAccept 0x0033 +#define ServerOP_SpawnCondition 0x0034 +#define ServerOP_SpawnEvent 0x0035 + +#define UpdateServerOP_Verified 0x5090 +#define UpdateServerOP_DisplayMsg 0x5091 +#define UpdateServerOP_Completed 0x5092 + +#define ServerOP_LSInfo 0x1000 +#define ServerOP_LSStatus 0x1001 +#define ServerOP_LSClientAuth 0x1002 +#define ServerOP_LSFatalError 0x1003 +#define ServerOP_SystemwideMessage 0x1005 +#define ServerOP_ListWorlds 0x1006 +#define ServerOP_PeerConnect 0x1007 + +#define ServerOP_LSZoneInfo 0x3001 +#define ServerOP_LSZoneStart 0x3002 +#define ServerOP_LSZoneBoot 0x3003 +#define ServerOP_LSZoneShutdown 0x3004 +#define ServerOP_LSZoneSleep 0x3005 +#define ServerOP_LSPlayerLeftWorld 0x3006 +#define ServerOP_LSPlayerJoinWorld 0x3007 +#define ServerOP_LSPlayerZoneChange 0x3008 + +#define ServerOP_UsertoWorldReq 0xAB00 +#define ServerOP_UsertoWorldResp 0xAB01 + +#define ServerOP_EncapPacket 0x2007 // Packet within a packet +#define ServerOP_WorldListUpdate 0x2008 +#define ServerOP_WorldListRemove 0x2009 +#define ServerOP_TriggerWorldListRefresh 0x200A + +#define ServerOP_WhoAll 0x0210 + +#define ServerOP_SetWorldTime 0x200B +#define ServerOP_GetWorldTime 0x200C +#define ServerOP_SyncWorldTime 0x200E + +//EQ2 Opcodes +#define ServerOP_CharTimeStamp 0x200F + +#define ServerOP_NameFilterCheck 0x2011 +#define ServerOP_BasicCharUpdate 0x2012 +#define ServerOP_CharacterCreate 0x2013 +#define ServerOP_NameCharUpdate 0x2014 +#define ServerOP_GetLatestTables 0x2015 +#define ServerOP_GetTableQuery 0x2016 +#define ServerOP_GetTableData 0x2017 +#define ServerOP_RaceUpdate 0x2018 +#define ServerOP_ZoneUpdate 0x2019 +#define ServerOP_BugReport 0x201A +#define ServerOP_ResetDatabase 0x201B +#define ServerOP_ZoneUpdates 0x201C +#define ServerOP_LoginEquipment 0x201D // updates charater select screen item appearances (gear appear) +#define ServerOP_CharacterPicture 0x201E + + +/************ PACKET RELATED STRUCT ************/ +class ServerPacket +{ +public: + ~ServerPacket() { safe_delete_array(pBuffer); } + ServerPacket(int16 in_opcode = 0, int32 in_size = 0) { + this->compressed = false; + size = in_size; + opcode = in_opcode; + if (size == 0) { + pBuffer = 0; + } + else { + pBuffer = new uchar[size]; + memset(pBuffer, 0, size); + } + destination = 0; + InflatedSize = 0; + } + ServerPacket* Copy() { + if (this == 0) { + return 0; + } + ServerPacket* ret = new ServerPacket(this->opcode, this->size); + if (this->size) + memcpy(ret->pBuffer, this->pBuffer, this->size); + ret->compressed = this->compressed; + ret->InflatedSize = this->InflatedSize; + return ret; + } + bool Deflate() { + if (compressed) + return false; + if ((!this->pBuffer) || (!this->size)) + return false; + uchar* tmp = new uchar[this->size + 128]; + int32 tmpsize = DeflatePacket(this->pBuffer, this->size, tmp, this->size + 128); + if (!tmpsize) { + safe_delete_array(tmp); + return false; + } + this->compressed = true; + this->InflatedSize = this->size; + this->size = tmpsize; + uchar* new_buffer = new uchar[this->size]; + memcpy(new_buffer, tmp, this->size); + safe_delete_array(tmp); + uchar* tmpdel = this->pBuffer; + this->pBuffer = new_buffer; + safe_delete_array(tmpdel); + return true; + } + bool Inflate() { + if (!compressed) + return false; + if ((!this->pBuffer) || (!this->size)) + return false; + uchar* tmp = new uchar[InflatedSize]; + int32 tmpsize = InflatePacket(this->pBuffer, this->size, tmp, InflatedSize); + if (!tmpsize) { + safe_delete_array(tmp); + return false; + } + compressed = false; + this->size = tmpsize; + uchar* tmpdel = this->pBuffer; + this->pBuffer = tmp; + safe_delete_array(tmpdel); + return true; + } + int32 size; + int16 opcode; + uchar* pBuffer; + bool compressed; + int32 InflatedSize; + int32 destination; +}; + +#pragma pack(1) + +struct GetLatestTables_Struct{ + float table_version; + float data_version; +}; + +struct ServerLSInfo_Struct { + char name[201]; // name the worldserver wants + char address[250]; // DNS address of the server + char account[31]; // account name for the worldserver + char password[256]; // password for the name + char protocolversion[25]; // Major protocol version number + char serverversion[64]; // minor server software version number + int8 servertype; // 0=world, 1=chat, 2=login, 3=MeshLogin, 4=World Debug + int32 dbversion; // database major+minor version from version.h (for PatchServer) +}; + +struct ServerLSStatus_Struct { + sint32 status; + sint32 num_players; + sint32 num_zones; + int8 world_max_level; +}; + +struct ServerSystemwideMessage { + int32 lsaccount_id; + char key[30]; // sessionID key for verification + int32 type; + char message[0]; +}; + +struct ServerSyncWorldList_Struct { + int32 RemoteID; + int32 ip; + sint32 status; + char name[201]; + char address[250]; + char account[31]; + int32 accountid; + int8 authlevel; + int8 servertype; // 0=world, 1=chat, 2=login + int32 adminid; + int8 showdown; + sint32 num_players; + sint32 num_zones; + bool placeholder; +}; + +struct UsertoWorldRequest_Struct { + int32 lsaccountid; + int32 char_id; + int32 worldid; + int32 FromID; + int32 ToID; + char ip_address[21]; +}; + +struct UsertoWorldResponse_Struct { + int32 lsaccountid; + int32 char_id; + int32 worldid; + int32 access_key; + int8 response; + char ip_address[80]; + int32 port; + int32 FromID; + int32 ToID; +}; + +struct ServerEncapPacket_Struct { + int32 ToID; // ID number of the LWorld on the other server + int16 opcode; + int16 size; + uchar data[0]; +}; + +struct ServerEmoteMessage_Struct { + char to[64]; + int32 guilddbid; + sint16 minstatus; + int32 type; + char message[0]; +}; + +/*struct TableVersion{ + char name[64]; + int32 version; + int32 max_table_version; + int32 max_data_version; + sint32 data_version; + int8 last; + char column_names[1000]; +};*/ + +typedef struct { + char name[256]; + unsigned int name_len; + unsigned int version; + unsigned int data_version; +} TableVersion; + +template void AddPtrData(string* buffer, Type& data){ + buffer->append((char*)&data, sizeof(Type)); +} +template void AddPtrData(string* buffer, Type* data, int16 size){ + buffer->append(data, size); +} +class LatestTableVersions { +public: + LatestTableVersions(){ + tables = 0; + current_index = 0; + total_tables = 0; + data_version = 0; + } + ~LatestTableVersions(){ + safe_delete_array(tables); + } + void SetTableSize(int16 size){ + total_tables = size; + tables = new TableVersion[total_tables]; + } + void AddTable(char* name, int32 version, int32 data_version){ + strcpy(tables[current_index].name, name); + tables[current_index].version = version; + tables[current_index].data_version = data_version; + current_index++; + } + int16 GetTotalSize(){ + return total_tables * sizeof(TableVersion) + sizeof(int16); + } + int16 GetTotalTables(){ + return total_tables; + } + TableVersion* GetTables(){ + return tables; + } + TableVersion GetTable(int16 index){ + return tables[index]; + } + string Serialize(){ + AddPtrData(&buffer, total_tables); + for(int16 i=0;i tmp_queries; +}; +class TableDataQuery{ +public: + TableDataQuery(char* table_name){ + if( strlen(table_name) >= sizeof(tablename) ) + return; + strcpy(tablename, table_name); + num_queries = 0; + columns_size = 0; + columns = 0; + version = 0; + table_size = 0; + } + TableDataQuery(){ + num_queries = 0; + columns_size = 0; + columns = 0; + version = 0; + table_size = 0; + } + ~TableDataQuery(){ + safe_delete_array(columns); + for(int32 i=0;iquery); + safe_delete(queries[i]); + } + } + int32 GetTotalQueries(){ + return num_queries; + } + string* Serialize(){ + buffer = ""; + num_queries = queries.size(); + if(GetTotalQueries() == 0) + return 0; + table_size = strlen(tablename); + AddPtrData(&buffer, table_size); + AddPtrData(&buffer, tablename, table_size + 1); + AddPtrData(&buffer, version); + if(num_queries > 200){ + int32 max_queries = 200; + AddPtrData(&buffer, max_queries); + } + else + AddPtrData(&buffer, num_queries); + AddPtrData(&buffer, columns_size); + AddPtrData(&buffer, columns, columns_size); + vector::iterator query_iterator; + int16 count = 0; + for(int i=GetTotalQueries() - 1;i >=0 && count < 200;i--){ + AddPtrData(&buffer, queries[i]->size); + AddPtrData(&buffer, queries[i]->query, queries[i]->size); + safe_delete_array(queries[i]->query); + safe_delete(queries[i]); + queries.pop_back(); + count++; + } + return &buffer; + } + void DeSerialize(uchar* data){ + uchar* ptr = data; + + memcpy(&table_size, ptr, sizeof(table_size)); + ptr+= sizeof(table_size); + memcpy(&tablename, ptr, table_size + 1); + ptr+= table_size + 1; + + memcpy(&version, ptr, sizeof(version)); + ptr+= sizeof(version); + + memcpy(&num_queries, ptr, sizeof(num_queries)); + ptr+= sizeof(num_queries); + + memcpy(&columns_size, ptr, sizeof(columns_size)); + ptr+= sizeof(columns_size); + columns = new char[columns_size + 1]; + memcpy(columns, ptr, columns_size + 1); + ptr+= columns_size; + + for(int32 i=0;isize, ptr, sizeof(new_query->size)); + ptr+= sizeof(new_query->size); + new_query->query = new char[new_query->size + 1]; + memcpy(new_query->query, ptr, new_query->size); + ptr+= new_query->size; + queries.push_back(new_query); + } + catch( bad_alloc &ba ) + { + cout << ba.what() << endl; + if( NULL != new_query ) + delete new_query; + } + } + } + string buffer; + int32 num_queries; + int32 version; + int16 table_size; + char tablename[64]; + int16 columns_size; + char* columns; + vector queries; +}; + +// Max number of equipment updates to send at once +struct EquipmentUpdateRequest_Struct +{ + int16 max_per_batch; +}; + +// Login's structure of equipment data +struct LoginEquipmentUpdate +{ + int32 world_char_id; + int16 equip_type; + int8 red; + int8 green; + int8 blue; + int8 highlight_red; + int8 highlight_green; + int8 highlight_blue; + int32 slot; +}; + +// World's structure of equipment data +struct EquipmentUpdate_Struct +{ + int32 id; // unique record identifier per world + int32 world_char_id; + int16 equip_type; + int8 red; + int8 green; + int8 blue; + int8 highlight_red; + int8 highlight_green; + int8 highlight_blue; + int32 slot; +}; + +// How many equipmment updates are there to send? +struct EquipmentUpdateList_Struct +{ + sint16 total_updates; +}; + +struct ZoneUpdateRequest_Struct{ + int16 max_per_batch; +}; + +struct LoginZoneUpdate{ + string name; + string description; +}; + +struct ZoneUpdate_Struct{ + int32 zone_id; + int8 zone_name_length; + int8 zone_desc_length; + char data[0]; +}; + +struct ZoneUpdateList_Struct{ + uint16 total_updates; + char data[0]; +}; + +//EQ2 Specific Structures Login -> World (Image) +struct CharacterTimeStamp_Struct { +int32 char_id; +int32 account_id; +int32 unix_timestamp; +}; + +//EQ2 Specific Structures World -> Login (Image) + +/**UPDATE_FIELD TYPES** +These will be stored beside the timestamp on the world server to determine what has changed on between the timestamp, when the update is sent, it will remove the flag. + +8 bits in a byte: +Example: 01001100 +0 Level Flag +1 Race Flag +0 Class Flag +0 Gender Flag +1 Zone Flag +1 Armor Flag +0 Name Flag +0 Delete Flag +**/ +#define LEVEL_UPDATE_FLAG 1 +#define RACE_UPDATE_FLAG 2 +#define CLASS_UPDATE_FLAG 4 +#define GENDER_UPDATE_FLAG 8 +#define ZONE_UPDATE_FLAG 16 +#define ARMOR_UPDATE_FLAG 32 +#define NAME_UPDATE_FLAG 64 +#define DELETE_UPDATE_FLAG 128 +//This structure used for basic changes such as level,class,gender, and deletes that are not able to be backed up +struct CharDataUpdate_Struct { +int32 account_id; +int32 char_id; +int8 update_field; +int32 update_data; +}; +struct BugReport{ +char category[64]; +char subcategory[64]; +char causes_crash[64]; +char reproducible[64]; +char summary[128]; +char description[2000]; +char version[32]; +char player[64]; +int32 account_id; +char spawn_name[64]; +int32 spawn_id; +int32 zone_id; +}; + +struct RaceUpdate_Struct { +int32 account_id; +int32 char_id; +int16 model_type; +int8 race; +}; + +//If this structure comes in with more than 74 bytes, should probably discard (leaves 65 bytes for new_name) +#define CHARNAMEUPDATESTRUCT_MAXSIZE 74 +struct CharNameUpdate_Struct { +int32 account_id; +int32 char_id; +int8 name_length; // If its longer than 64, something is wrong :-/ +char new_name[0]; +}; + +//If this structure comes in with more than 78 bytes, should probably discard (leaves 65 bytes for new_zone) +#define CHARZONESTRUCT_MAXSIZE 78 +struct CharZoneUpdate_Struct { +int32 account_id; +int32 char_id; +int32 zone_id; +int8 zone_length; // If its longer than 64, something is wrong :-/ +char new_zone[0]; +}; + +struct WorldCharCreate_Struct { +int32 account_id; +int32 char_id; +int16 model_type; +int16 char_size; +uchar character[0]; +}; + +struct WorldCharNameFilter_Struct { +int32 account_id; +int16 name_length; +uchar name[0]; +}; + +struct WorldCharNameFilterResponse_Struct { +int32 account_id; +int32 char_id; +int8 response; +}; + +#define CHARPICSTRUCT_MINSIZE 10 +// Should only be used for the headshot picture +struct CharPictureUpdate_Struct { + int32 account_id; + int32 char_id; + int16 pic_size; + char pic[0]; +}; + +#pragma pack() + +#endif diff --git a/old/common/servertalk.hpp b/old/common/servertalk.hpp deleted file mode 100644 index dcb5972..0000000 --- a/old/common/servertalk.hpp +++ /dev/null @@ -1,1047 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GNU GPL v3 - -#pragma once - -#define EQEMU_PROTOCOL_VERSION "0.5.0" - -#include -#include -#include -#include -#include - -#include "types.hpp" -#include "packet/packet_functions.hpp" - -// Server timing constants -#define SERVER_TIMEOUT 45000 -#define INTERSERVER_TIMER 10000 -#define LoginServer_StatusUpdateInterval 15000 -#define LoginServer_AuthStale 60000 -#define AUTHCHANGE_TIMEOUT 900 - -// Server operation codes -#define ServerOP_KeepAlive 0x0001 -#define ServerOP_ChannelMessage 0x0002 -#define ServerOP_SetZone 0x0003 -#define ServerOP_ShutdownAll 0x0004 -#define ServerOP_ZoneShutdown 0x0005 -#define ServerOP_ZoneBootup 0x0006 -#define ServerOP_ZoneStatus 0x0007 -#define ServerOP_SetConnectInfo 0x0008 -#define ServerOP_EmoteMessage 0x0009 -#define ServerOP_ClientList 0x000A -#define ServerOP_Who 0x000B -#define ServerOP_ZonePlayer 0x000C -#define ServerOP_KickPlayer 0x000D -#define ServerOP_RefreshGuild 0x000E -#define ServerOP_GuildKickAll 0x000F -#define ServerOP_GuildInvite 0x0010 -#define ServerOP_GuildRemove 0x0011 -#define ServerOP_GuildPromote 0x0012 -#define ServerOP_GuildDemote 0x0013 -#define ServerOP_GuildLeader 0x0014 -#define ServerOP_GuildGMSet 0x0015 -#define ServerOP_GuildGMSetRank 0x0016 -#define ServerOP_FlagUpdate 0x0018 -#define ServerOP_GMGoto 0x0019 -#define ServerOP_MultiLineMsg 0x001A -#define ServerOP_Lock 0x001B -#define ServerOP_Motd 0x001C -#define ServerOP_Uptime 0x001D -#define ServerOP_Petition 0x001E -#define ServerOP_KillPlayer 0x001F -#define ServerOP_UpdateGM 0x0020 -#define ServerOP_RezzPlayer 0x0021 -#define ServerOP_ZoneReboot 0x0022 -#define ServerOP_ZoneToZoneRequest 0x0023 -#define ServerOP_AcceptWorldEntrance 0x0024 -#define ServerOP_ZAAuth 0x0025 -#define ServerOP_ZAAuthFailed 0x0026 -#define ServerOP_ZoneIncClient 0x0027 -#define ServerOP_ClientListKA 0x0028 -#define ServerOP_ChangeWID 0x0029 -#define ServerOP_IPLookup 0x002A -#define ServerOP_LockZone 0x002B -#define ServerOP_ItemStatus 0x002C -#define ServerOP_OOCMute 0x002D -#define ServerOP_Revoke 0x002E -#define ServerOP_GuildJoin 0x002F -#define ServerOP_GroupIDReq 0x0030 -#define ServerOP_GroupIDReply 0x0031 -#define ServerOP_GroupLeave 0x0032 -#define ServerOP_RezzPlayerAccept 0x0033 -#define ServerOP_SpawnCondition 0x0034 -#define ServerOP_SpawnEvent 0x0035 - -// Update server operation codes -#define UpdateServerOP_Verified 0x5090 -#define UpdateServerOP_DisplayMsg 0x5091 -#define UpdateServerOP_Completed 0x5092 - -// Login server operation codes -#define ServerOP_LSInfo 0x1000 -#define ServerOP_LSStatus 0x1001 -#define ServerOP_LSClientAuth 0x1002 -#define ServerOP_LSFatalError 0x1003 -#define ServerOP_SystemwideMessage 0x1005 -#define ServerOP_ListWorlds 0x1006 -#define ServerOP_PeerConnect 0x1007 - -// Login server zone operation codes -#define ServerOP_LSZoneInfo 0x3001 -#define ServerOP_LSZoneStart 0x3002 -#define ServerOP_LSZoneBoot 0x3003 -#define ServerOP_LSZoneShutdown 0x3004 -#define ServerOP_LSZoneSleep 0x3005 -#define ServerOP_LSPlayerLeftWorld 0x3006 -#define ServerOP_LSPlayerJoinWorld 0x3007 -#define ServerOP_LSPlayerZoneChange 0x3008 - -// User to world operation codes -#define ServerOP_UsertoWorldReq 0xAB00 -#define ServerOP_UsertoWorldResp 0xAB01 - -// Packet operation codes -#define ServerOP_EncapPacket 0x2007 -#define ServerOP_WorldListUpdate 0x2008 -#define ServerOP_WorldListRemove 0x2009 -#define ServerOP_TriggerWorldListRefresh 0x200A -#define ServerOP_WhoAll 0x0210 - -// Time operation codes -#define ServerOP_SetWorldTime 0x200B -#define ServerOP_GetWorldTime 0x200C -#define ServerOP_SyncWorldTime 0x200E - -// EQ2 specific operation codes -#define ServerOP_CharTimeStamp 0x200F -#define ServerOP_NameFilterCheck 0x2011 -#define ServerOP_BasicCharUpdate 0x2012 -#define ServerOP_CharacterCreate 0x2013 -#define ServerOP_NameCharUpdate 0x2014 -#define ServerOP_GetLatestTables 0x2015 -#define ServerOP_GetTableQuery 0x2016 -#define ServerOP_GetTableData 0x2017 -#define ServerOP_RaceUpdate 0x2018 -#define ServerOP_ZoneUpdate 0x2019 -#define ServerOP_BugReport 0x201A -#define ServerOP_ResetDatabase 0x201B -#define ServerOP_ZoneUpdates 0x201C -#define ServerOP_LoginEquipment 0x201D -#define ServerOP_CharacterPicture 0x201E - -// Character update flags -#define LEVEL_UPDATE_FLAG 1 -#define RACE_UPDATE_FLAG 2 -#define CLASS_UPDATE_FLAG 4 -#define GENDER_UPDATE_FLAG 8 -#define ZONE_UPDATE_FLAG 16 -#define ARMOR_UPDATE_FLAG 32 -#define NAME_UPDATE_FLAG 64 -#define DELETE_UPDATE_FLAG 128 - -// Structure size limits -#define CHARNAMEUPDATESTRUCT_MAXSIZE 74 -#define CHARZONESTRUCT_MAXSIZE 78 -#define CHARPICSTRUCT_MINSIZE 10 - -/** - * Core server packet class for inter-server communication - * Handles packet creation, compression, and memory management - */ -class ServerPacket -{ -public: - /** - * Constructor - Creates a new server packet with specified opcode and size - * @param in_opcode - The operation code for this packet - * @param in_size - The size of the packet data buffer - */ - ServerPacket(int16 in_opcode = 0, int32 in_size = 0) - { - compressed = false; - size = in_size; - opcode = in_opcode; - if (size == 0) { - pBuffer = nullptr; - } else { - pBuffer = new uchar[size]; - std::memset(pBuffer, 0, size); - } - destination = 0; - InflatedSize = 0; - } - - /** - * Destructor - Cleans up packet buffer memory - */ - ~ServerPacket() - { - delete[] pBuffer; - } - - /** - * Creates a deep copy of this packet - * @return New ServerPacket instance with copied data, or nullptr if this packet is null - */ - ServerPacket* Copy() - { - if (this == nullptr) { - return nullptr; - } - ServerPacket* ret = new ServerPacket(this->opcode, this->size); - if (this->size) { - std::memcpy(ret->pBuffer, this->pBuffer, this->size); - } - ret->compressed = this->compressed; - ret->InflatedSize = this->InflatedSize; - return ret; - } - - /** - * Compresses the packet data using deflate algorithm - * @return true if compression successful, false if already compressed or invalid data - */ - bool Deflate() - { - if (compressed) { - return false; - } - if (!pBuffer || !size) { - return false; - } - - uchar* tmp = new uchar[size + 128]; - int32 tmpsize = DeflatePacket(pBuffer, size, tmp, size + 128); - if (!tmpsize) { - delete[] tmp; - return false; - } - - compressed = true; - InflatedSize = size; - size = tmpsize; - uchar* new_buffer = new uchar[size]; - std::memcpy(new_buffer, tmp, size); - delete[] tmp; - delete[] pBuffer; - pBuffer = new_buffer; - return true; - } - - /** - * Decompresses the packet data using inflate algorithm - * @return true if decompression successful, false if not compressed or invalid data - */ - bool Inflate() - { - if (!compressed) { - return false; - } - if (!pBuffer || !size) { - return false; - } - - uchar* tmp = new uchar[InflatedSize]; - int32 tmpsize = InflatePacket(pBuffer, size, tmp, InflatedSize); - if (!tmpsize) { - delete[] tmp; - return false; - } - - compressed = false; - size = tmpsize; - delete[] pBuffer; - pBuffer = tmp; - return true; - } - - int32 size; // Current size of packet buffer - int16 opcode; // Operation code identifying packet type - uchar* pBuffer; // Raw packet data buffer - bool compressed; // Flag indicating if packet is compressed - int32 InflatedSize; // Original size before compression - int32 destination; // Target server ID for routing -}; - -#pragma pack(1) - -/** - * Structure for requesting latest table versions - */ -struct GetLatestTables_Struct -{ - float table_version; // Current table version - float data_version; // Current data version -}; - -/** - * Login server information structure - */ -struct ServerLSInfo_Struct -{ - char name[201]; // Server name identifier - char address[250]; // DNS address of the server - char account[31]; // Account name for authentication - char password[256]; // Password for authentication - char protocolversion[25]; // Protocol version string - char serverversion[64]; // Server software version - int8 servertype; // Server type: 0=world, 1=chat, 2=login, 3=MeshLogin, 4=World Debug - int32 dbversion; // Database version for compatibility -}; - -/** - * Login server status information - */ -struct ServerLSStatus_Struct -{ - sint32 status; // Current server status - sint32 num_players; // Number of connected players - sint32 num_zones; // Number of active zones - int8 world_max_level; // Maximum character level allowed -}; - -/** - * System-wide message structure - */ -struct ServerSystemwideMessage -{ - int32 lsaccount_id; // Login server account ID - char key[30]; // Session ID key for verification - int32 type; // Message type identifier - char message[0]; // Variable length message data -}; - -/** - * World list synchronization structure - */ -struct ServerSyncWorldList_Struct -{ - int32 RemoteID; // Remote server identifier - int32 ip; // Server IP address - sint32 status; // Server status code - char name[201]; // Server name - char address[250]; // Server address - char account[31]; // Account name - int32 accountid; // Account ID - int8 authlevel; // Authorization level - int8 servertype; // Server type classification - int32 adminid; // Administrator ID - int8 showdown; // Shutdown flag - sint32 num_players; // Current player count - sint32 num_zones; // Current zone count - bool placeholder; // Placeholder server flag -}; - -/** - * User to world request structure - */ -struct UsertoWorldRequest_Struct -{ - int32 lsaccountid; // Login server account ID - int32 char_id; // Character ID - int32 worldid; // Target world ID - int32 FromID; // Source server ID - int32 ToID; // Destination server ID - char ip_address[21]; // Client IP address -}; - -/** - * User to world response structure - */ -struct UsertoWorldResponse_Struct -{ - int32 lsaccountid; // Login server account ID - int32 char_id; // Character ID - int32 worldid; // World ID - int32 access_key; // Access key for authentication - int8 response; // Response code - char ip_address[80]; // Server IP address - int32 port; // Server port - int32 FromID; // Source server ID - int32 ToID; // Destination server ID -}; - -/** - * Encapsulated packet structure for packet forwarding - */ -struct ServerEncapPacket_Struct -{ - int32 ToID; // Target server ID - int16 opcode; // Encapsulated packet opcode - int16 size; // Encapsulated packet size - uchar data[0]; // Variable length packet data -}; - -/** - * Emote message structure - */ -struct ServerEmoteMessage_Struct -{ - char to[64]; // Target recipient - int32 guilddbid; // Guild database ID - sint16 minstatus; // Minimum status required - int32 type; // Emote type - char message[0]; // Variable length message -}; - -/** - * Table version information structure - */ -typedef struct -{ - char name[256]; // Table name - unsigned int name_len; // Length of table name - unsigned int version; // Table version number - unsigned int data_version; // Data version number -} TableVersion; - -/** - * Helper template function to append data to string buffer - */ -template -void AddPtrData(std::string* buffer, Type& data) -{ - buffer->append(reinterpret_cast(&data), sizeof(Type)); -} - -/** - * Helper template function to append array data to string buffer - */ -template -void AddPtrData(std::string* buffer, Type* data, int16 size) -{ - buffer->append(reinterpret_cast(data), size); -} - -/** - * Class for managing latest table versions across servers - * Handles serialization and deserialization of table version data - */ -class LatestTableVersions -{ -public: - /** - * Constructor - Initializes empty table version list - */ - LatestTableVersions() - { - tables = nullptr; - current_index = 0; - total_tables = 0; - data_version = 0; - } - - /** - * Destructor - Cleans up allocated table array - */ - ~LatestTableVersions() - { - delete[] tables; - } - - /** - * Allocates memory for specified number of tables - * @param size - Number of tables to allocate space for - */ - void SetTableSize(int16 size) - { - total_tables = size; - tables = new TableVersion[total_tables]; - } - - /** - * Adds a table version entry to the collection - * @param name - Table name - * @param version - Table version number - * @param data_version - Data version number - */ - void AddTable(char* name, int32 version, int32 data_version) - { - std::strcpy(tables[current_index].name, name); - tables[current_index].version = version; - tables[current_index].data_version = data_version; - current_index++; - } - - /** - * Calculates total serialized size of all table data - * @return Total size in bytes - */ - int16 GetTotalSize() - { - return total_tables * sizeof(TableVersion) + sizeof(int16); - } - - /** - * Gets total number of tables in collection - * @return Number of tables - */ - int16 GetTotalTables() - { - return total_tables; - } - - /** - * Gets pointer to table array - * @return Pointer to TableVersion array - */ - TableVersion* GetTables() - { - return tables; - } - - /** - * Gets specific table by index - * @param index - Index of table to retrieve - * @return TableVersion structure - */ - TableVersion GetTable(int16 index) - { - return tables[index]; - } - - /** - * Serializes all table data into string buffer - * @return Serialized data string - */ - std::string Serialize() - { - AddPtrData(&buffer, total_tables); - for (int16 i = 0; i < total_tables; i++) { - AddPtrData(&buffer, tables[i].name, sizeof(tables[i].name)); - AddPtrData(&buffer, tables[i].version); - AddPtrData(&buffer, tables[i].data_version); - } - return buffer; - } - - /** - * Deserializes table data from byte array - * @param data - Raw byte data to deserialize - */ - void DeSerialize(uchar* data) - { - uchar* ptr = data; - std::memcpy(&total_tables, ptr, sizeof(total_tables)); - ptr += sizeof(total_tables); - tables = new TableVersion[total_tables]; - for (int16 i = 0; i < total_tables; i++) { - std::memcpy(&tables[i].name, ptr, sizeof(tables[i].name)); - ptr += sizeof(tables[i].name); - std::memcpy(&tables[i].version, ptr, sizeof(tables[i].version)); - ptr += sizeof(tables[i].version); - std::memcpy(&tables[i].data_version, ptr, sizeof(tables[i].data_version)); - ptr += sizeof(tables[i].data_version); - } - } - - int32 data_version; // Global data version - -private: - int16 current_index; // Current insertion index - int16 total_tables; // Total number of tables allocated - TableVersion* tables; // Array of table versions - std::string buffer; // Serialization buffer -}; - -/** - * Structure for table data queries - */ -struct TableData -{ - int16 size; // Size of query string - char* query; // SQL query string -}; - -/** - * Class for managing table query operations - * Handles multiple SQL queries with version tracking - */ -class TableQuery -{ -public: - /** - * Constructor - Initializes empty query collection - */ - TableQuery() - { - try_delete = true; - num_queries = 0; - data_version = 0; - current_index = 0; - latest_version = 0; - your_version = 0; - total_size = sizeof(num_queries) + sizeof(latest_version) + sizeof(your_version) + sizeof(tablename); - } - - /** - * Destructor - Cleans up query strings if owned - */ - ~TableQuery() - { - if (try_delete) { - for (size_t i = 0; i < tmp_queries.size(); i++) { - delete[] tmp_queries[i]; - } - } - } - - /** - * Concatenates all queries into single string - * @return Combined query string with newline separators - */ - std::string GetQueriesString() - { - std::string query_string; - for (size_t i = 0; i < tmp_queries.size(); i++) { - query_string.append(tmp_queries[i]).append("\n"); - } - return query_string; - } - - /** - * Adds a query string to the collection - * @param query - SQL query string to add - */ - void AddQuery(char* query) - { - num_queries++; - total_size += std::strlen(query) + 1; - tmp_queries.push_back(query); - } - - /** - * Gets total serialized size of query collection - * @return Total size in bytes - */ - int16 GetTotalSize() - { - return total_size; - } - - /** - * Gets number of queries in collection - * @return Query count - */ - int16 GetTotalQueries() - { - return num_queries; - } - - /** - * Gets specific query by index - * @param index - Index of query to retrieve - * @return Query string pointer - */ - char* GetQuery(int16 index) - { - return tmp_queries[index]; - } - - /** - * Serializes query collection to string buffer - * @return Serialized data string - */ - std::string Serialize() - { - num_queries = tmp_queries.size(); - AddPtrData(&buffer, num_queries); - AddPtrData(&buffer, latest_version); - AddPtrData(&buffer, your_version); - AddPtrData(&buffer, data_version); - AddPtrData(&buffer, tablename, sizeof(tablename)); - for (int16 i = 0; i < GetTotalQueries(); i++) { - AddPtrData(&buffer, tmp_queries[i], std::strlen(tmp_queries[i]) + 1); - } - return buffer; - } - - /** - * Deserializes query data from byte array - * @param data - Raw byte data to deserialize - */ - void DeSerialize(uchar* data) - { - try_delete = false; - uchar* ptr = data; - std::memcpy(&num_queries, ptr, sizeof(num_queries)); - ptr += sizeof(num_queries); - std::memcpy(&latest_version, ptr, sizeof(latest_version)); - ptr += sizeof(latest_version); - std::memcpy(&your_version, ptr, sizeof(your_version)); - ptr += sizeof(your_version); - std::memcpy(&data_version, ptr, sizeof(data_version)); - ptr += sizeof(data_version); - std::memcpy(&tablename, ptr, sizeof(tablename)); - ptr += sizeof(tablename); - for (int16 i = 0; i < GetTotalQueries(); i++) { - tmp_queries.push_back(reinterpret_cast(ptr)); - ptr += std::strlen(reinterpret_cast(ptr)) + 1; - } - } - - int16 current_index; // Current processing index - int16 num_queries; // Number of queries in collection - int32 latest_version; // Latest available version - int32 your_version; // Client's current version - int32 data_version; // Data version number - bool try_delete; // Flag to control memory cleanup - char tablename[64]; // Associated table name - int32 total_size; // Total serialized size - std::string buffer; // Serialization buffer - -private: - std::vector tmp_queries; // Collection of query strings -}; - -/** - * Class for managing table data queries with column information - * Handles database query results with metadata - */ -class TableDataQuery -{ -public: - /** - * Constructor - Initializes with table name - * @param table_name - Name of the associated table - */ - TableDataQuery(char* table_name) - { - if (std::strlen(table_name) >= sizeof(tablename)) { - return; - } - std::strcpy(tablename, table_name); - num_queries = 0; - columns_size = 0; - columns = nullptr; - version = 0; - table_size = 0; - } - - /** - * Default constructor - */ - TableDataQuery() - { - num_queries = 0; - columns_size = 0; - columns = nullptr; - version = 0; - table_size = 0; - } - - /** - * Destructor - Cleans up column data and queries - */ - ~TableDataQuery() - { - delete[] columns; - for (int32 i = 0; i < num_queries; i++) { - delete[] queries[i]->query; - delete queries[i]; - } - } - - /** - * Gets total number of queries - * @return Query count - */ - int32 GetTotalQueries() - { - return num_queries; - } - - /** - * Serializes query data to string buffer with query limit - * @return Pointer to serialized data string, or nullptr if no queries - */ - std::string* Serialize() - { - buffer = ""; - num_queries = queries.size(); - if (GetTotalQueries() == 0) { - return nullptr; - } - table_size = std::strlen(tablename); - AddPtrData(&buffer, table_size); - AddPtrData(&buffer, tablename, table_size + 1); - AddPtrData(&buffer, version); - if (num_queries > 200) { - int32 max_queries = 200; - AddPtrData(&buffer, max_queries); - } else { - AddPtrData(&buffer, num_queries); - } - AddPtrData(&buffer, columns_size); - AddPtrData(&buffer, columns, columns_size); - - // Process queries in reverse order with limit of 200 - int16 count = 0; - for (int i = GetTotalQueries() - 1; i >= 0 && count < 200; i--) { - AddPtrData(&buffer, queries[i]->size); - AddPtrData(&buffer, queries[i]->query, queries[i]->size); - delete[] queries[i]->query; - delete queries[i]; - queries.pop_back(); - count++; - } - return &buffer; - } - - /** - * Deserializes query data from byte array - * @param data - Raw byte data to deserialize - */ - void DeSerialize(uchar* data) - { - uchar* ptr = data; - - std::memcpy(&table_size, ptr, sizeof(table_size)); - ptr += sizeof(table_size); - std::memcpy(&tablename, ptr, table_size + 1); - ptr += table_size + 1; - - std::memcpy(&version, ptr, sizeof(version)); - ptr += sizeof(version); - - std::memcpy(&num_queries, ptr, sizeof(num_queries)); - ptr += sizeof(num_queries); - - std::memcpy(&columns_size, ptr, sizeof(columns_size)); - ptr += sizeof(columns_size); - columns = new char[columns_size + 1]; - std::memcpy(columns, ptr, columns_size + 1); - ptr += columns_size; - - for (int32 i = 0; i < GetTotalQueries(); i++) { - TableData* new_query = new TableData; - try { - std::memcpy(&new_query->size, ptr, sizeof(new_query->size)); - ptr += sizeof(new_query->size); - new_query->query = new char[new_query->size + 1]; - std::memcpy(new_query->query, ptr, new_query->size); - ptr += new_query->size; - queries.push_back(new_query); - } catch (const std::bad_alloc& ba) { - std::cout << ba.what() << std::endl; - delete new_query; - } - } - } - - std::string buffer; // Serialization buffer - int32 num_queries; // Number of queries in collection - int32 version; // Table version number - int16 table_size; // Size of table name string - char tablename[64]; // Associated table name - int16 columns_size; // Size of column definition string - char* columns; // Column definitions - std::vector queries; // Collection of table data queries -}; - -/** - * Equipment update request structure - */ -struct EquipmentUpdateRequest_Struct -{ - int16 max_per_batch; // Maximum updates per batch -}; - -/** - * Login server equipment update structure - */ -struct LoginEquipmentUpdate -{ - int32 world_char_id; // Character ID - int16 equip_type; // Equipment type identifier - int8 red; // Red color component - int8 green; // Green color component - int8 blue; // Blue color component - int8 highlight_red; // Highlight red component - int8 highlight_green; // Highlight green component - int8 highlight_blue; // Highlight blue component - int32 slot; // Equipment slot identifier -}; - -/** - * World server equipment update structure - */ -struct EquipmentUpdate_Struct -{ - int32 id; // Unique record identifier - int32 world_char_id; // Character ID - int16 equip_type; // Equipment type identifier - int8 red; // Red color component - int8 green; // Green color component - int8 blue; // Blue color component - int8 highlight_red; // Highlight red component - int8 highlight_green; // Highlight green component - int8 highlight_blue; // Highlight blue component - int32 slot; // Equipment slot identifier -}; - -/** - * Equipment update list structure - */ -struct EquipmentUpdateList_Struct -{ - sint16 total_updates; // Total number of updates available -}; - -/** - * Zone update request structure - */ -struct ZoneUpdateRequest_Struct -{ - int16 max_per_batch; // Maximum updates per batch -}; - -/** - * Login server zone update structure - */ -struct LoginZoneUpdate -{ - std::string name; // Zone name - std::string description; // Zone description -}; - -/** - * Zone update structure with variable data - */ -struct ZoneUpdate_Struct -{ - int32 zone_id; // Zone identifier - int8 zone_name_length; // Length of zone name - int8 zone_desc_length; // Length of zone description - char data[0]; // Variable length name and description data -}; - -/** - * Zone update list structure - */ -struct ZoneUpdateList_Struct -{ - uint16 total_updates; // Total number of zone updates - char data[0]; // Variable length update data -}; - -/** - * Character timestamp structure for EQ2 - */ -struct CharacterTimeStamp_Struct -{ - int32 char_id; // Character identifier - int32 account_id; // Account identifier - int32 unix_timestamp; // Unix timestamp of last update -}; - -/** - * Character data update structure for basic changes - */ -struct CharDataUpdate_Struct -{ - int32 account_id; // Account identifier - int32 char_id; // Character identifier - int8 update_field; // Bitfield of updated fields - int32 update_data; // Updated data value -}; - -/** - * Bug report structure - */ -struct BugReport -{ - char category[64]; // Bug category - char subcategory[64]; // Bug subcategory - char causes_crash[64]; // Crash information - char reproducible[64]; // Reproducibility info - char summary[128]; // Bug summary - char description[2000]; // Detailed description - char version[32]; // Game version - char player[64]; // Player name - int32 account_id; // Account identifier - char spawn_name[64]; // Associated spawn name - int32 spawn_id; // Spawn identifier - int32 zone_id; // Zone identifier -}; - -/** - * Race update structure - */ -struct RaceUpdate_Struct -{ - int32 account_id; // Account identifier - int32 char_id; // Character identifier - int16 model_type; // Character model type - int8 race; // Race identifier -}; - -/** - * Character name update structure - */ -struct CharNameUpdate_Struct -{ - int32 account_id; // Account identifier - int32 char_id; // Character identifier - int8 name_length; // Length of new name - char new_name[0]; // Variable length new name -}; - -/** - * Character zone update structure - */ -struct CharZoneUpdate_Struct -{ - int32 account_id; // Account identifier - int32 char_id; // Character identifier - int32 zone_id; // Zone identifier - int8 zone_length; // Length of zone name - char new_zone[0]; // Variable length zone name -}; - -/** - * World character creation structure - */ -struct WorldCharCreate_Struct -{ - int32 account_id; // Account identifier - int32 char_id; // Character identifier - int16 model_type; // Character model type - int16 char_size; // Size of character data - uchar character[0]; // Variable length character data -}; - -/** - * Character name filter request structure - */ -struct WorldCharNameFilter_Struct -{ - int32 account_id; // Account identifier - int16 name_length; // Length of name to filter - uchar name[0]; // Variable length name data -}; - -/** - * Character name filter response structure - */ -struct WorldCharNameFilterResponse_Struct -{ - int32 account_id; // Account identifier - int32 char_id; // Character identifier - int8 response; // Filter result code -}; - -/** - * Character picture update structure - */ -struct CharPictureUpdate_Struct -{ - int32 account_id; // Account identifier - int32 char_id; // Character identifier - int16 pic_size; // Size of picture data - char pic[0]; // Variable length picture data -}; - -#pragma pack() \ No newline at end of file diff --git a/old/common/sha512.cpp b/old/common/sha512.cpp new file mode 100644 index 0000000..10b9592 --- /dev/null +++ b/old/common/sha512.cpp @@ -0,0 +1,155 @@ +#include +#include +#include "sha512.h" + +const unsigned long long SHA512::sha512_k[80] = //ULL = uint64 + {0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, + 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, + 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, + 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, + 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, + 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, + 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, + 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, + 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, + 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, + 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL}; + +void SHA512::transform(const unsigned char *message, unsigned int block_nb) +{ + uint64 w[80]; + uint64 wv[8]; + uint64 t1, t2; + const unsigned char *sub_block; + int i, j; + for (i = 0; i < (int) block_nb; i++) { + sub_block = message + (i << 7); + for (j = 0; j < 16; j++) { + SHA2_PACK64(&sub_block[j << 3], &w[j]); + } + for (j = 16; j < 80; j++) { + w[j] = SHA512_F4(w[j - 2]) + w[j - 7] + SHA512_F3(w[j - 15]) + w[j - 16]; + } + for (j = 0; j < 8; j++) { + wv[j] = m_h[j]; + } + for (j = 0; j < 80; j++) { + t1 = wv[7] + SHA512_F2(wv[4]) + SHA2_CH(wv[4], wv[5], wv[6]) + + sha512_k[j] + w[j]; + t2 = SHA512_F1(wv[0]) + SHA2_MAJ(wv[0], wv[1], wv[2]); + wv[7] = wv[6]; + wv[6] = wv[5]; + wv[5] = wv[4]; + wv[4] = wv[3] + t1; + wv[3] = wv[2]; + wv[2] = wv[1]; + wv[1] = wv[0]; + wv[0] = t1 + t2; + } + for (j = 0; j < 8; j++) { + m_h[j] += wv[j]; + } + + } +} + +void SHA512::init() +{ + m_h[0] = 0x6a09e667f3bcc908ULL; + m_h[1] = 0xbb67ae8584caa73bULL; + m_h[2] = 0x3c6ef372fe94f82bULL; + m_h[3] = 0xa54ff53a5f1d36f1ULL; + m_h[4] = 0x510e527fade682d1ULL; + m_h[5] = 0x9b05688c2b3e6c1fULL; + m_h[6] = 0x1f83d9abfb41bd6bULL; + m_h[7] = 0x5be0cd19137e2179ULL; + m_len = 0; + m_tot_len = 0; +} + +void SHA512::update(const unsigned char *message, unsigned int len) +{ + unsigned int block_nb; + unsigned int new_len, rem_len, tmp_len; + const unsigned char *shifted_message; + tmp_len = SHA384_512_BLOCK_SIZE - m_len; + rem_len = len < tmp_len ? len : tmp_len; + memcpy(&m_block[m_len], message, rem_len); + if (m_len + len < SHA384_512_BLOCK_SIZE) { + m_len += len; + return; + } + new_len = len - rem_len; + block_nb = new_len / SHA384_512_BLOCK_SIZE; + shifted_message = message + rem_len; + transform(m_block, 1); + transform(shifted_message, block_nb); + rem_len = new_len % SHA384_512_BLOCK_SIZE; + memcpy(m_block, &shifted_message[block_nb << 7], rem_len); + m_len = rem_len; + m_tot_len += (block_nb + 1) << 7; +} + +void SHA512::final(unsigned char *digest) +{ + unsigned int block_nb; + unsigned int pm_len; + unsigned int len_b; + int i; + block_nb = 1 + ((SHA384_512_BLOCK_SIZE - 17) + < (m_len % SHA384_512_BLOCK_SIZE)); + len_b = (m_tot_len + m_len) << 3; + pm_len = block_nb << 7; + memset(m_block + m_len, 0, pm_len - m_len); + m_block[m_len] = 0x80; + SHA2_UNPACK32(len_b, m_block + pm_len - 4); + transform(m_block, block_nb); + for (i = 0 ; i < 8; i++) { + SHA2_UNPACK64(m_h[i], &digest[i << 3]); + } +} + +std::string sha512(std::string input) +{ + unsigned char digest[SHA512::DIGEST_SIZE]; + memset(digest,0,SHA512::DIGEST_SIZE); + SHA512 ctx = SHA512(); + ctx.init(); + ctx.update((unsigned char*)input.c_str(), input.length()); + ctx.final(digest); + + char buf[2*SHA512::DIGEST_SIZE+1]; + buf[2*SHA512::DIGEST_SIZE] = 0; + for (int i = 0; i < SHA512::DIGEST_SIZE; i++) + sprintf(buf+i*2, "%02x", digest[i]); + return std::string(buf); +} \ No newline at end of file diff --git a/old/common/sha512.h b/old/common/sha512.h new file mode 100644 index 0000000..72ce5a8 --- /dev/null +++ b/old/common/sha512.h @@ -0,0 +1,71 @@ +#ifndef SHA512_H +#define SHA512_H +#include + +class SHA512 +{ +protected: + typedef unsigned char uint8; + typedef unsigned int uint32; + typedef unsigned long long uint64; + + const static uint64 sha512_k[]; + static const unsigned int SHA384_512_BLOCK_SIZE = (1024/8); + +public: + void init(); + void update(const unsigned char *message, unsigned int len); + void final(unsigned char *digest); + static const unsigned int DIGEST_SIZE = ( 512 / 8); + +protected: + void transform(const unsigned char *message, unsigned int block_nb); + unsigned int m_tot_len; + unsigned int m_len; + unsigned char m_block[2 * SHA384_512_BLOCK_SIZE]; + uint64 m_h[8]; +}; + + +std::string sha512(std::string input); + +#define SHA2_SHFR(x, n) (x >> n) +#define SHA2_ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) +#define SHA2_ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n))) +#define SHA2_CH(x, y, z) ((x & y) ^ (~x & z)) +#define SHA2_MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) +#define SHA512_F1(x) (SHA2_ROTR(x, 28) ^ SHA2_ROTR(x, 34) ^ SHA2_ROTR(x, 39)) +#define SHA512_F2(x) (SHA2_ROTR(x, 14) ^ SHA2_ROTR(x, 18) ^ SHA2_ROTR(x, 41)) +#define SHA512_F3(x) (SHA2_ROTR(x, 1) ^ SHA2_ROTR(x, 8) ^ SHA2_SHFR(x, 7)) +#define SHA512_F4(x) (SHA2_ROTR(x, 19) ^ SHA2_ROTR(x, 61) ^ SHA2_SHFR(x, 6)) +#define SHA2_UNPACK32(x, str) \ +{ \ + *((str) + 3) = (uint8) ((x) ); \ + *((str) + 2) = (uint8) ((x) >> 8); \ + *((str) + 1) = (uint8) ((x) >> 16); \ + *((str) + 0) = (uint8) ((x) >> 24); \ +} +#define SHA2_UNPACK64(x, str) \ +{ \ + *((str) + 7) = (uint8) ((x) ); \ + *((str) + 6) = (uint8) ((x) >> 8); \ + *((str) + 5) = (uint8) ((x) >> 16); \ + *((str) + 4) = (uint8) ((x) >> 24); \ + *((str) + 3) = (uint8) ((x) >> 32); \ + *((str) + 2) = (uint8) ((x) >> 40); \ + *((str) + 1) = (uint8) ((x) >> 48); \ + *((str) + 0) = (uint8) ((x) >> 56); \ +} +#define SHA2_PACK64(str, x) \ +{ \ + *(x) = ((uint64) *((str) + 7) ) \ + | ((uint64) *((str) + 6) << 8) \ + | ((uint64) *((str) + 5) << 16) \ + | ((uint64) *((str) + 4) << 24) \ + | ((uint64) *((str) + 3) << 32) \ + | ((uint64) *((str) + 2) << 40) \ + | ((uint64) *((str) + 1) << 48) \ + | ((uint64) *((str) + 0) << 56); \ +} + +#endif \ No newline at end of file diff --git a/old/common/sha512.hpp b/old/common/sha512.hpp deleted file mode 100644 index 2df1cc6..0000000 --- a/old/common/sha512.hpp +++ /dev/null @@ -1,283 +0,0 @@ -// Copyright (c) SHA512 Implementation - MIT License - -#pragma once - -#include -#include -#include - -class SHA512 -{ -protected: - typedef unsigned char uint8; - typedef unsigned int uint32; - typedef unsigned long long uint64; - - // SHA512 round constants (80 64-bit words) - const static uint64 sha512_k[]; - static const unsigned int SHA384_512_BLOCK_SIZE = (1024/8); - -public: - static const unsigned int DIGEST_SIZE = (512 / 8); - - // Initialize SHA512 context with standard initial hash values - void init(); - - // Process input message data in chunks, handling partial blocks - void update(const unsigned char *message, unsigned int len); - - // Finalize hash computation and output 64-byte digest - void final(unsigned char *digest); - -protected: - // Core SHA512 transformation function - processes message blocks - void transform(const unsigned char *message, unsigned int block_nb); - - unsigned int m_tot_len; // Total length of processed data - unsigned int m_len; // Current length in buffer - unsigned char m_block[2 * SHA384_512_BLOCK_SIZE]; // Input buffer - uint64 m_h[8]; // Hash state (8 64-bit words) -}; - -// Convenience function - compute SHA512 hash of string input -std::string sha512(std::string input); - -// SHA2 bit manipulation macros -#define SHA2_SHFR(x, n) (x >> n) -#define SHA2_ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) -#define SHA2_ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n))) -#define SHA2_CH(x, y, z) ((x & y) ^ (~x & z)) -#define SHA2_MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) -#define SHA512_F1(x) (SHA2_ROTR(x, 28) ^ SHA2_ROTR(x, 34) ^ SHA2_ROTR(x, 39)) -#define SHA512_F2(x) (SHA2_ROTR(x, 14) ^ SHA2_ROTR(x, 18) ^ SHA2_ROTR(x, 41)) -#define SHA512_F3(x) (SHA2_ROTR(x, 1) ^ SHA2_ROTR(x, 8) ^ SHA2_SHFR(x, 7)) -#define SHA512_F4(x) (SHA2_ROTR(x, 19) ^ SHA2_ROTR(x, 61) ^ SHA2_SHFR(x, 6)) - -// Pack/unpack 32-bit and 64-bit values to/from byte arrays (big-endian) -#define SHA2_UNPACK32(x, str) \ -{ \ - *((str) + 3) = (uint8) ((x) ); \ - *((str) + 2) = (uint8) ((x) >> 8); \ - *((str) + 1) = (uint8) ((x) >> 16); \ - *((str) + 0) = (uint8) ((x) >> 24); \ -} - -#define SHA2_UNPACK64(x, str) \ -{ \ - *((str) + 7) = (uint8) ((x) ); \ - *((str) + 6) = (uint8) ((x) >> 8); \ - *((str) + 5) = (uint8) ((x) >> 16); \ - *((str) + 4) = (uint8) ((x) >> 24); \ - *((str) + 3) = (uint8) ((x) >> 32); \ - *((str) + 2) = (uint8) ((x) >> 40); \ - *((str) + 1) = (uint8) ((x) >> 48); \ - *((str) + 0) = (uint8) ((x) >> 56); \ -} - -#define SHA2_PACK64(str, x) \ -{ \ - *(x) = ((uint64) *((str) + 7) ) \ - | ((uint64) *((str) + 6) << 8) \ - | ((uint64) *((str) + 5) << 16) \ - | ((uint64) *((str) + 4) << 24) \ - | ((uint64) *((str) + 3) << 32) \ - | ((uint64) *((str) + 2) << 40) \ - | ((uint64) *((str) + 1) << 48) \ - | ((uint64) *((str) + 0) << 56); \ -} - -// SHA512 round constants - precomputed fractional parts of cube roots of first 80 primes -const unsigned long long SHA512::sha512_k[80] = { - 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, - 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, - 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, - 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, - 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, - 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, - 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, - 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, - 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, - 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, - 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, - 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, - 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, - 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, - 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, - 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, - 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, - 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, - 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, - 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, - 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, - 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, - 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, - 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, - 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, - 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, - 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, - 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, - 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, - 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, - 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, - 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, - 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, - 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, - 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, - 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, - 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, - 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, - 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, - 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL -}; - -// Core SHA512 transformation - processes message blocks through compression function -void SHA512::transform(const unsigned char *message, unsigned int block_nb) -{ - uint64 w[80]; // Message schedule array - uint64 wv[8]; // Working variables - uint64 t1, t2; // Temporary values - const unsigned char *sub_block; - int i, j; - - // Process each 1024-bit block - for (i = 0; i < (int) block_nb; i++) { - sub_block = message + (i << 7); // Point to current 128-byte block - - // Prepare first 16 words of message schedule from input - for (j = 0; j < 16; j++) { - SHA2_PACK64(&sub_block[j << 3], &w[j]); - } - - // Extend to 80 words using SHA512 message schedule - for (j = 16; j < 80; j++) { - w[j] = SHA512_F4(w[j - 2]) + w[j - 7] + SHA512_F3(w[j - 15]) + w[j - 16]; - } - - // Initialize working variables with current hash values - for (j = 0; j < 8; j++) { - wv[j] = m_h[j]; - } - - // Main compression loop - 80 rounds - for (j = 0; j < 80; j++) { - t1 = wv[7] + SHA512_F2(wv[4]) + SHA2_CH(wv[4], wv[5], wv[6]) + sha512_k[j] + w[j]; - t2 = SHA512_F1(wv[0]) + SHA2_MAJ(wv[0], wv[1], wv[2]); - wv[7] = wv[6]; - wv[6] = wv[5]; - wv[5] = wv[4]; - wv[4] = wv[3] + t1; - wv[3] = wv[2]; - wv[2] = wv[1]; - wv[1] = wv[0]; - wv[0] = t1 + t2; - } - - // Add compressed chunk to current hash values - for (j = 0; j < 8; j++) { - m_h[j] += wv[j]; - } - } -} - -// Initialize SHA512 context with standard IV values from FIPS 180-4 -void SHA512::init() -{ - m_h[0] = 0x6a09e667f3bcc908ULL; - m_h[1] = 0xbb67ae8584caa73bULL; - m_h[2] = 0x3c6ef372fe94f82bULL; - m_h[3] = 0xa54ff53a5f1d36f1ULL; - m_h[4] = 0x510e527fade682d1ULL; - m_h[5] = 0x9b05688c2b3e6c1fULL; - m_h[6] = 0x1f83d9abfb41bd6bULL; - m_h[7] = 0x5be0cd19137e2179ULL; - m_len = 0; - m_tot_len = 0; -} - -// Process input data, handling buffering and block alignment -void SHA512::update(const unsigned char *message, unsigned int len) -{ - unsigned int block_nb; - unsigned int new_len, rem_len, tmp_len; - const unsigned char *shifted_message; - - // Calculate space remaining in current block - tmp_len = SHA384_512_BLOCK_SIZE - m_len; - rem_len = len < tmp_len ? len : tmp_len; - - // Fill current block buffer - memcpy(&m_block[m_len], message, rem_len); - - // If block not complete, just update length and return - if (m_len + len < SHA384_512_BLOCK_SIZE) { - m_len += len; - return; - } - - // Process complete block(s) - new_len = len - rem_len; - block_nb = new_len / SHA384_512_BLOCK_SIZE; - shifted_message = message + rem_len; - - // Transform current buffered block - transform(m_block, 1); - - // Transform any additional complete blocks - transform(shifted_message, block_nb); - - // Store remainder in buffer for next update - rem_len = new_len % SHA384_512_BLOCK_SIZE; - memcpy(m_block, &shifted_message[block_nb << 7], rem_len); - m_len = rem_len; - m_tot_len += (block_nb + 1) << 7; -} - -// Finalize hash computation with padding and length encoding -void SHA512::final(unsigned char *digest) -{ - unsigned int block_nb; - unsigned int pm_len; - unsigned int len_b; - int i; - - // Calculate number of blocks needed for padding - block_nb = 1 + ((SHA384_512_BLOCK_SIZE - 17) < (m_len % SHA384_512_BLOCK_SIZE)); - - // Total bit length for final block - len_b = (m_tot_len + m_len) << 3; - pm_len = block_nb << 7; - - // Pad with zeros, add required '1' bit, append length - memset(m_block + m_len, 0, pm_len - m_len); - m_block[m_len] = 0x80; - SHA2_UNPACK32(len_b, m_block + pm_len - 4); - - // Process final block(s) - transform(m_block, block_nb); - - // Output final hash values as big-endian bytes - for (i = 0; i < 8; i++) { - SHA2_UNPACK64(m_h[i], &digest[i << 3]); - } -} - -// Convenience function - compute SHA512 hash of string and return as hex -std::string sha512(std::string input) -{ - unsigned char digest[SHA512::DIGEST_SIZE]; - memset(digest, 0, SHA512::DIGEST_SIZE); - - // Initialize context and process input - SHA512 ctx = SHA512(); - ctx.init(); - ctx.update((unsigned char*)input.c_str(), input.length()); - ctx.final(digest); - - // Convert to hex string - char buf[2 * SHA512::DIGEST_SIZE + 1]; - buf[2 * SHA512::DIGEST_SIZE] = 0; - for (int i = 0; i < SHA512::DIGEST_SIZE; i++) { - sprintf(buf + i * 2, "%02x", digest[i]); - } - return std::string(buf); -} \ No newline at end of file diff --git a/old/common/stream/eq_stream.hpp b/old/common/stream/eq_stream.hpp deleted file mode 100644 index 1b1eb5e..0000000 --- a/old/common/stream/eq_stream.hpp +++ /dev/null @@ -1,2009 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPLv3 License - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "eq_stream_factory.hpp" -#include "../log.hpp" -#include "../misc.hpp" -#include "../timer.hpp" -#include "../crc16.hpp" -#include "../debug.hpp" -#include "../crypto.hpp" -#include "../eq_common_structs.hpp" -#include "../packet/eq_packet.hpp" -#include "../packet/packet_dump.hpp" -#include "../opcodes/opcodes.hpp" - -#ifdef LOGIN - #include "../LoginServer/login_structs.hpp" -#endif - -#ifdef WRITE_PACKETS - #include -#endif - -using namespace std; - -typedef enum { - ESTABLISHED, - WAIT_CLOSE, - CLOSING, - DISCONNECTING, - CLOSED -} EQStreamState; - -#define FLAG_COMPRESSED 0x01 -#define FLAG_ENCODED 0x04 -#define RATEBASE 1048576 // 1 MB -#define DECAYBASE 78642 // RATEBASE/10 - -#ifndef RETRANSMIT_TIMEOUT_MULT -#define RETRANSMIT_TIMEOUT_MULT 3.0 -#endif - -#ifndef RETRANSMIT_TIMEOUT_MAX -#define RETRANSMIT_TIMEOUT_MAX 5000 -#endif - -#ifndef AVERAGE_DELTA_MAX -#define AVERAGE_DELTA_MAX 2500 -#endif - -#pragma pack(1) -struct SessionRequest -{ - uint32 UnknownA; - uint32 Session; - uint32 MaxLength; -}; - -struct SessionResponse -{ - uint32 Session; - uint32 Key; - uint8 UnknownA; - uint8 Format; - uint8 UnknownB; - uint32 MaxLength; - uint32 UnknownD; -}; - -struct ClientSessionStats -{ -/*000*/ uint16 RequestID; -/*002*/ uint32 last_local_delta; -/*006*/ uint32 average_delta; -/*010*/ uint32 low_delta; -/*014*/ uint32 high_delta; -/*018*/ uint32 last_remote_delta; -/*022*/ uint64 packets_sent; -/*030*/ uint64 packets_recieved; -/*038*/ -}; - -struct ServerSessionStats -{ - uint16 RequestID; - uint32 current_time; - uint32 unknown1; - uint32 received_packets; - uint32 unknown2; - uint32 sent_packets; - uint32 unknown3; - uint32 sent_packets2; - uint32 unknown4; - uint32 received_packets2; -}; -#pragma pack() - -class OpcodeManager; -extern OpcodeManager *EQNetworkOpcodeManager; - -class EQStreamFactory; - -typedef enum { - UnknownStream=0, - LoginStream, - WorldStream, - ZoneStream, - ChatOrMailStream, - ChatStream, - MailStream, - EQ2Stream, -} EQStreamType; - -class EQStream -{ - protected: - typedef enum { - SeqPast, - SeqInOrder, - SeqFuture - } SeqOrder; - - uint32 received_packets; // Number of packets received - uint32 sent_packets; // Number of packets sent - uint32 remote_ip; // Remote client IP address - uint16 remote_port; // Remote client port - uint8 buffer[8192]; // Main I/O buffer - unsigned char *oversize_buffer; // Buffer for oversized packets - uint32 oversize_offset, oversize_length; // Oversize buffer management - unsigned char *rogue_buffer; // Buffer for handling rogue packets - uint32 roguebuf_offset, roguebuf_size; // Rogue buffer management - uint8 app_opcode_size; // Size of application opcodes - EQStreamType StreamType; // Type of stream (login, world, zone, etc.) - bool compressed, encoded; // Stream compression and encoding flags - - unsigned char write_buffer[2048]; // Temporary write buffer - - uint32 retransmittimer; // Timer for retransmission - uint32 retransmittimeout; // Timeout value for retransmission - uint16 sessionAttempts; // Number of session establishment attempts - uint16 reconnectAttempt; // Number of reconnection attempts - bool streamactive; // Whether stream is actively processing packets - - uint32 Session, Key; // Session ID and encryption key - uint16 NextInSeq; // Next expected incoming sequence number - uint16 NextOutSeq; // Next outgoing sequence number - uint16 SequencedBase; // Base sequence number for sequenced queue - uint32 MaxLen; // Maximum packet length - uint16 MaxSends; // Maximum sends per cycle - int8 timeout_delays; // Number of timeout delays encountered - - uint8 active_users; // Number of active users of this stream - Mutex MInUse; // Mutex for active user count - -#ifdef WRITE_PACKETS - FILE* write_packets = NULL; // File handle for packet logging - Mutex MWritePackets; // Mutex for packet writing -#endif - - EQStreamState State; // Current stream state - Mutex MState; // Mutex for state changes - - uint32 LastPacket; // Timestamp of last packet - Mutex MVarlock; // General variable mutex - - EQApplicationPacket* CombinedAppPacket; // Combined application packet - Mutex MCombinedAppPacket; // Mutex for combined packet - - long LastSeqSent; // Last sequence number sent - Mutex MLastSeqSent; // Mutex for last sequence sent - - // Acknowledgment sequence tracking - long MaxAckReceived, NextAckToSend, LastAckSent; - Mutex MAcks; // Mutex for acknowledgment tracking - - // Outbound packet queues - queue NonSequencedQueue; // Non-sequenced packets - deque SequencedQueue; // Sequenced packets - map OutOfOrderpackets; // Out-of-order packets - Mutex MOutboundQueue; // Mutex for outbound queues - - // Inbound packet queue - deque InboundQueue; // Incoming application packets - Mutex MInboundQueue; // Mutex for inbound queue - - static uint16 MaxWindowSize; // Maximum window size for sequencing - - sint32 BytesWritten; // Bytes written in current cycle - Mutex MRate; // Mutex for rate limiting - sint32 RateThreshold; // Rate limiting threshold - sint32 DecayRate; // Rate decay value - uint32 AverageDelta; // Average delta time - - EQStreamFactory *Factory; // Factory that created this stream - - public: - Mutex MCombineQueueLock; // Mutex for combine queue - deque combine_queue; // Queue for packet combining - Timer* combine_timer; // Timer for packet combining - Crypto* crypto; // Cryptography handler - z_stream stream; // Zlib compression stream - uchar* stream_buffer; // Compression buffer - int32 stream_buffer_size; // Size of compression buffer - bool eq2_compressed; // EQ2 compression flag - int8 compressed_offset; // Compression offset - int16 client_version; // Client version - - Mutex MResendQue; // Mutex for resend queue - Mutex MCompressData; // Mutex for compression data - deque resend_que; // Resend queue - Timer* resend_que_timer; // Resend queue timer - - // Default constructor - initializes basic stream - EQStream() - { - init(); - remote_ip = 0; - remote_port = 0; - State = CLOSED; - StreamType = UnknownStream; - compressed = true; - encoded = false; - app_opcode_size = 2; - } - - // Constructor with socket address - creates stream for specific client - EQStream(sockaddr_in addr); - - // Destructor - cleans up all resources - virtual ~EQStream() - { - MOutboundQueue.lock(); - SetState(CLOSED); - MOutboundQueue.unlock(); - RemoveData(); - safe_delete(crypto); - safe_delete(combine_timer); - safe_delete(resend_que_timer); - safe_delete_array(oversize_buffer); - safe_delete_array(rogue_buffer); - deque::iterator cmb; - MCombineQueueLock.lock(); - for (cmb = combine_queue.begin(); cmb != combine_queue.end(); cmb++){ - safe_delete(*cmb); - } - MCombineQueueLock.unlock(); - deflateEnd(&stream); - map::iterator oop; - for (oop = OutOfOrderpackets.begin(); oop != OutOfOrderpackets.end(); oop++){ - safe_delete(oop->second); - } -#ifdef WRITE_PACKETS - if (write_packets) - fclose(write_packets); -#endif - } - - // Factory management - inline void SetFactory(EQStreamFactory *f) { Factory=f; } - - // Initialize or reset stream state - void init(bool resetSession = true); - - // Packet length management - void SetMaxLen(uint32 length) { MaxLen = length; } - - // Timeout management - int8 getTimeoutDelays() { return timeout_delays; } - void addTimeoutDelay() { timeout_delays++; } - - // EQ2 specific packet operations - void EQ2QueuePacket(EQ2Packet* app, bool attempted_combine = false); - void PreparePacket(EQ2Packet* app, int8 offset = 0); - void UnPreparePacket(EQ2Packet* app); - void EncryptPacket(EQ2Packet* app, int8 compress_offset, int8 offset); - int8 EQ2_Compress(EQ2Packet* app, int8 offset = 3); - - // Generic packet operations - void SendPacket(EQApplicationPacket *p); - void SendPacket(EQProtocolPacket *p); - void NonSequencedPush(EQProtocolPacket *p); - void SequencedPush(EQProtocolPacket *p); - - // Network I/O - void Write(int eq_fd); - void WritePacket(int fd, EQProtocolPacket *p); - static EQProtocolPacket *Read(int eq_fd, sockaddr_in *from); - - // Packet processing - void Process(const unsigned char *data, const uint32 length); - void ProcessPacket(EQProtocolPacket *p, EQProtocolPacket* lastp = NULL); - bool ProcessEmbeddedPacket(uchar* pBuffer, uint16 length, int8 opcode = OP_Packet); - bool HandleEmbeddedPacket(EQProtocolPacket *p, int16 offset = 2, int16 length = 0); - EQProtocolPacket* ProcessEncryptedPacket(EQProtocolPacket *p); - EQProtocolPacket* ProcessEncryptedData(uchar* data, int32 size, int16 opcode); - - // Acknowledgment handling - void AckPackets(uint16 seq); - void SendAck(uint16 seq); - void SendOutOfOrderAck(uint16 seq); - - // Session management - void SendSessionResponse(); - void SendSessionRequest(); - void SendDisconnect(bool setstate = true); - void SendKeyRequest(); - int16 processRSAKey(EQProtocolPacket *p, uint16 subpacket_length = 0); - - // Queue management - void InboundQueuePush(EQApplicationPacket *p); - EQApplicationPacket *PopPacket(); - void InboundQueueClear(); - void OutboundQueueClear(); - bool HasOutgoingData(); - bool CheckCombineQueue(); - void CheckResend(int eq_fd); - - // State and properties - inline EQStreamState GetState() { return State; } - inline void SetState(EQStreamState state) { MState.lock(); State = state; MState.unlock(); } - inline uint32 GetRemoteIP() { return remote_ip; } - inline uint32 GetrIP() { return remote_ip; } - inline uint16 GetRemotePort() { return remote_port; } - inline uint16 GetrPort() { return remote_port; } - inline const EQStreamType GetStreamType() const { return StreamType; } - - // Client version management - int16 GetClientVersion() { return client_version; } - void SetClientVersion(int16 version) { client_version = version; } - - // Connection management - void SetActive(bool val) { streamactive = val; } - void ResetSessionAttempts() { reconnectAttempt = 0; } - bool HasSessionAttempts() { return reconnectAttempt > 0; } - bool CheckActive() { return (GetState() == ESTABLISHED); } - bool CheckClosed() { return GetState() == CLOSED; } - bool CheckTimeout(uint32 now, uint32 timeout = 30) { return (LastPacket && (now - LastPacket) > timeout); } - bool Stale(uint32 now, uint32 timeout = 30) { return (LastPacket && (now - LastPacket) > timeout); } - void Close() { SendDisconnect(); } - - // Resource management - inline bool IsInUse() { bool flag; MInUse.lock(); flag = (active_users > 0); MInUse.unlock(); return flag; } - inline void PutInUse() { MInUse.lock(); active_users++; MInUse.unlock(); } - inline void ReleaseFromUse() { MInUse.lock(); if(active_users > 0) active_users--; MInUse.unlock(); } - void RemoveData() { InboundQueueClear(); OutboundQueueClear(); if (CombinedAppPacket) delete CombinedAppPacket; } - - // Configuration - void SetOpcodeSize(uint8 s) { app_opcode_size = s; } - void SetStreamType(EQStreamType t); - uint32 GetKey() { return Key; } - void SetKey(uint32 k) { Key = k; } - void SetSession(uint32 s) { Session = s; } - void SetLastPacketTime(uint32 t) { LastPacket = t; } - - // Sequence management - static SeqOrder CompareSequence(uint16 expected_seq, uint16 seq); - void ProcessQueue(); - EQProtocolPacket* RemoveQueue(uint16 seq); - - // Rate limiting and performance - void Decay(); - void AdjustRates(uint32 average_delta); - - // Utility functions - virtual void DispatchPacket(EQApplicationPacket *p) { p->DumpRaw(); } - void EncryptPacket(uchar* data, int16 size); - -#ifdef WRITE_PACKETS - char GetChar(uchar in); - void WriteToFile(char* pFormat, ...); - void WritePackets(const char* opcodeName, uchar* data, int32 size, bool outgoing); - void WritePackets(EQ2Packet* app, bool outgoing); -#endif - - private: - // Acknowledgment tracking methods - long GetMaxAckReceived(); - long GetNextAckToSend(); - long GetLastAckSent(); - void SetMaxAckReceived(uint32 seq); - void SetNextAckToSend(uint32); - void SetLastAckSent(uint32); - void SetLastSeqSent(uint32); -}; - -// Implementation - -uint16 EQStream::MaxWindowSize = 2048; - -// Initialize stream state and variables -void EQStream::init(bool resetSession) -{ - if (resetSession) { - streamactive = false; - sessionAttempts = 0; - } - - timeout_delays = 0; - - MInUse.lock(); - active_users = 0; - MInUse.unlock(); - - Session = 0; - Key = 0; - MaxLen = 0; - NextInSeq = 0; - NextOutSeq = 0; - CombinedAppPacket = NULL; - - MAcks.lock(); - MaxAckReceived = -1; - NextAckToSend = -1; - LastAckSent = -1; - MAcks.unlock(); - - LastSeqSent = -1; - MaxSends = 5; - LastPacket = Timer::GetCurrentTime2(); - oversize_buffer = NULL; - oversize_length = 0; - oversize_offset = 0; - Factory = NULL; - - rogue_buffer = NULL; - roguebuf_offset = 0; - roguebuf_size = 0; - - MRate.lock(); - RateThreshold = RATEBASE / 250; - DecayRate = DECAYBASE / 250; - MRate.unlock(); - - BytesWritten = 0; - SequencedBase = 0; - AverageDelta = 500; - - crypto->setRC4Key(0); - - retransmittimer = Timer::GetCurrentTime2(); - retransmittimeout = 500 * RETRANSMIT_TIMEOUT_MULT; - - reconnectAttempt = 0; - if (uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { - LogWrite(PACKET__DEBUG, 9, "Packet", "init Invalid Sequenced queue: BS %u + SQ %u != NOS %u", SequencedBase, SequencedQueue.size(), NextOutSeq); - } -} - -// Constructor with socket address -EQStream::EQStream(sockaddr_in addr) -{ - crypto = new Crypto(); - resend_que_timer = new Timer(1000); - combine_timer = new Timer(250); - combine_timer->Start(); - resend_que_timer->Start(); - init(); - remote_ip = addr.sin_addr.s_addr; - remote_port = addr.sin_port; - State = CLOSED; - StreamType = UnknownStream; - compressed = true; - encoded = false; - app_opcode_size = 2; - - memset(&stream, 0, sizeof(z_stream)); - stream.zalloc = (alloc_func)0; - stream.zfree = (free_func)0; - stream.opaque = (voidpf)0; - deflateInit2(&stream, 9, Z_DEFLATED, 13, 9, Z_DEFAULT_STRATEGY); - compressed_offset = 0; - client_version = 0; - received_packets = 0; - sent_packets = 0; - -#ifdef WRITE_PACKETS - write_packets = 0; - char write_packets_filename[64]; - snprintf(write_packets_filename, sizeof(write_packets_filename), "PacketLog%i.log", Timer::GetCurrentTime2()); - write_packets = fopen(write_packets_filename, "w+"); -#endif -} - -// Process encrypted data and return protocol packet -EQProtocolPacket* EQStream::ProcessEncryptedData(uchar* data, int32 size, int16 opcode) -{ - crypto->RC4Decrypt(data, size); - int8 offset = 0; - if (data[0] == 0xFF && size > 2) { - offset = 3; - memcpy(&opcode, data + sizeof(int8), sizeof(int16)); - } - else { - offset = 1; - memcpy(&opcode, data, sizeof(int8)); - } - return new EQProtocolPacket(opcode, data + offset, size - offset); -} - -// Process encrypted protocol packet -EQProtocolPacket* EQStream::ProcessEncryptedPacket(EQProtocolPacket *p) -{ - EQProtocolPacket* ret = NULL; - if (p->opcode == OP_Packet && p->size > 2) - ret = ProcessEncryptedData(p->pBuffer + 2, p->size - 2, p->opcode); - else - ret = ProcessEncryptedData(p->pBuffer, p->size, p->opcode); - return ret; -} - -// Process embedded packet within another packet -bool EQStream::ProcessEmbeddedPacket(uchar* pBuffer, int16 length, int8 opcode) -{ - if (!pBuffer || !crypto->isEncrypted()) - return false; - - MCombineQueueLock.lock(); - EQProtocolPacket* newpacket = ProcessEncryptedData(pBuffer, length, opcode); - MCombineQueueLock.unlock(); - - if (newpacket) { - EQApplicationPacket* ap = newpacket->MakeApplicationPacket(2); - if (ap->version == 0) - ap->version = client_version; - InboundQueuePush(ap); -#ifdef WRITE_PACKETS - WritePackets(ap->GetOpcodeName(), pBuffer, length, false); -#endif - safe_delete(newpacket); - return true; - } - - return false; -} - -// Handle embedded packet with offset and length validation -bool EQStream::HandleEmbeddedPacket(EQProtocolPacket *p, int16 offset, int16 length) -{ - if (!p) - return false; - - if (p->size >= ((uint32)(offset + 2))) { - if (p->pBuffer[offset] == 0 && p->pBuffer[offset + 1] == 0x19) { - uint32 data_length = 0; - if (length == 0) { - if (p->size < offset + 2) { - return false; - } - data_length = p->size - offset - 2; - } else { - if (length < 2) { - return false; - } - data_length = length - 2; - } - - if (offset + 2 + data_length > p->size) { - return false; - } - EQProtocolPacket *subp = new EQProtocolPacket(OP_AppCombined, p->pBuffer + offset + 2, data_length); - subp->copyInfo(p); - ProcessPacket(subp, p); - safe_delete(subp); - return true; - } - else if (p->pBuffer[offset] == 0 && p->pBuffer[offset + 1] == 0) { - if (length == 0) - length = p->size - 1 - offset; - else - length--; - - uchar* buffer = (p->pBuffer + 1 + offset); - bool valid = ProcessEmbeddedPacket(buffer, length); - - if (valid) - return true; - } - else if (offset + 4 < p->size && ntohl(*(uint32 *)(p->pBuffer + offset)) != 0xffffffff) { - if (length == 0) - length = p->size - offset; - - uchar* buffer = (p->pBuffer + offset); - bool valid = ProcessEmbeddedPacket(buffer, length); - - if (valid) - return true; - } - else if (p->pBuffer[offset] != 0xff && p->pBuffer[offset + 1] == 0xff && p->size >= offset + 3) { - uint16 total_length = p->pBuffer[offset]; - if (total_length + offset + 2 == p->size && total_length >= 2) { - uint32 data_length = total_length - 2; - EQProtocolPacket *subp = new EQProtocolPacket(p->pBuffer + offset + 2, data_length, OP_Packet); - subp->copyInfo(p); - ProcessPacket(subp, p); - delete subp; - return true; - } - } - } - return false; -} - -// Main packet processing function - handles all protocol packet types -void EQStream::ProcessPacket(EQProtocolPacket *p, EQProtocolPacket* lastp) -{ - uint32 processed = 0, subpacket_length = 0; - - if (p) { - if (p->opcode != OP_SessionRequest && p->opcode != OP_SessionResponse && !Session) { -#ifdef EQN_DEBUG - LogWrite(PACKET__ERROR, 0, "Packet", "*** Session not initialized, packet ignored "); -#endif - return; - } - - switch (p->opcode) { - case OP_Combined: { - processed = 0; - int8 offset = 0; - int count = 0; - - while (processed < p->size) { - if ((subpacket_length = (unsigned char)*(p->pBuffer + processed)) == 0xff) { - subpacket_length = ntohs(*(uint16*)(p->pBuffer + processed + 1)); - offset = 3; - } - else { - offset = 1; - } - - count++; - bool isSubPacket = EQProtocolPacket::IsProtocolPacket(p->pBuffer + processed + offset, subpacket_length, false); - if (isSubPacket) { - EQProtocolPacket* subp = new EQProtocolPacket(p->pBuffer + processed + offset, subpacket_length); - subp->copyInfo(p); - ProcessPacket(subp, p); - delete subp; - } - else { - offset = 1; - if (ntohs(*reinterpret_cast(p->pBuffer + processed + offset)) <= 0x1e) { - subpacket_length = (unsigned char)*(p->pBuffer + processed); - LogWrite(PACKET__ERROR, 0, "Packet", "!!!!!!!!!Garbage Packet Unknown Process as OP_Packet!!!!!!!!!!!!!\n"); - DumpPacket(p->pBuffer + processed + offset, subpacket_length); - uchar* newbuf = p->pBuffer; - newbuf += processed + offset; - EQProtocolPacket *subp = new EQProtocolPacket(newbuf, subpacket_length); - subp->copyInfo(p); - ProcessPacket(subp, p); - delete subp; - } - else { - crypto->RC4Decrypt(p->pBuffer + processed + offset, subpacket_length); - LogWrite(PACKET__ERROR, 0, "Packet", "!!!!!!!!!Garbage Packet!!!!!!!!!!!!! processed: %u, offset: %u, count: %i, subpacket_length: %u, offset_pos_1: %u, oversized_buffer_present: %u, offset size: %u, offset length: %u\n", - processed, offset, count, subpacket_length, p->pBuffer[processed + offset], oversize_buffer ? 1 : 0, oversize_offset, oversize_length); - if (p->pBuffer[processed + offset] == 0xff) { - uchar* newbuf = p->pBuffer; - newbuf += processed + offset + 1; - - DumpPacket(p->pBuffer + processed + offset, subpacket_length); - EQProtocolPacket *subp = new EQProtocolPacket(newbuf, subpacket_length, OP_Packet); - subp->copyInfo(p); - ProcessPacket(subp, p); - delete subp; - } - else - break; - } - } - processed += subpacket_length + offset; - } - break; - } - case OP_AppCombined: { - processed = 0; - EQProtocolPacket* newpacket = 0; - int8 offset = 0; - int count = 0; - - while (processed < p->size) { - count++; - if ((subpacket_length = (unsigned char)*(p->pBuffer + processed)) == 0xff) { - subpacket_length = ntohs(*(uint16 *)(p->pBuffer + processed + 1)); - offset = 3; - } else - offset = 1; - - if (crypto->getRC4Key() == 0 && p && subpacket_length > 8 + offset) { - p->pBuffer += offset; - processRSAKey(p, subpacket_length); - p->pBuffer -= offset; - } - else if (crypto->isEncrypted()) { - if (!HandleEmbeddedPacket(p, processed + offset, subpacket_length)) { - uchar* buffer = (p->pBuffer + processed + offset); - if (!ProcessEmbeddedPacket(buffer, subpacket_length, OP_AppCombined)) { - LogWrite(PACKET__ERROR, 0, "Packet", "*** This is bad, ProcessEmbeddedPacket failed, report to Image!"); - } - } - } - processed += subpacket_length + offset; - } - } - break; - case OP_Packet: { - if (!p->pBuffer || (p->Size() < 4)) { - break; - } - - uint16 seq = ntohs(*(uint16 *)(p->pBuffer)); - sint8 check = CompareSequence(NextInSeq, seq); - if (check == SeqFuture) { -#ifdef EQN_DEBUG - LogWrite(PACKET__DEBUG, 1, "Packet", "*** Future packet: Expecting Seq=%i, but got Seq=%i", NextInSeq, seq); - LogWrite(PACKET__DEBUG, 1, "Packet", "[Start]"); - p->DumpRawHeader(seq); - LogWrite(PACKET__DEBUG, 1, "Packet", "[End]"); -#endif - OutOfOrderpackets[seq] = p->Copy(); - } else if (check == SeqPast) { -#ifdef EQN_DEBUG - LogWrite(PACKET__DEBUG, 1, "Packet", "*** Duplicate packet: Expecting Seq=%i, but got Seq=%i", NextInSeq, seq); - LogWrite(PACKET__DEBUG, 1, "Packet", "[Start]"); - p->DumpRawHeader(seq); - LogWrite(PACKET__DEBUG, 1, "Packet", "[End]"); -#endif - SendOutOfOrderAck(seq); - } else { - EQProtocolPacket* qp = RemoveQueue(seq); - if (qp) { - LogWrite(PACKET__DEBUG, 1, "Packet", "OP_Fragment: Removing older queued packet with sequence %i", seq); - delete qp; - } - - SetNextAckToSend(seq); - NextInSeq++; - - if (HandleEmbeddedPacket(p)) - break; - if (crypto->getRC4Key() == 0 && p && p->size >= 69) { - processRSAKey(p); - } - else if (crypto->isEncrypted() && p) { - MCombineQueueLock.lock(); - EQProtocolPacket* newpacket = ProcessEncryptedPacket(p); - MCombineQueueLock.unlock(); - if (newpacket) { - EQApplicationPacket* ap = newpacket->MakeApplicationPacket(2); - if (ap->version == 0) - ap->version = client_version; -#ifdef WRITE_PACKETS - WritePackets(ap->GetOpcodeName(), p->pBuffer, p->size, false); -#endif - InboundQueuePush(ap); - safe_delete(newpacket); - } - } - } - } - break; - case OP_Fragment: { - if (!p->pBuffer || (p->Size() < 4)) { - break; - } - - uint16 seq = ntohs(*(uint16 *)(p->pBuffer)); - sint8 check = CompareSequence(NextInSeq, seq); - if (check == SeqFuture) { -#ifdef EQN_DEBUG - LogWrite(PACKET__DEBUG, 1, "Packet", "*** Future packet2: Expecting Seq=%i, but got Seq=%i", NextInSeq, seq); - LogWrite(PACKET__DEBUG, 1, "Packet", "[Start]"); - LogWrite(PACKET__DEBUG, 1, "Packet", "[End]"); -#endif - OutOfOrderpackets[seq] = p->Copy(); - } else if (check == SeqPast) { -#ifdef EQN_DEBUG - LogWrite(PACKET__DEBUG, 1, "Packet", "*** Duplicate packet2: Expecting Seq=%i, but got Seq=%i", NextInSeq, seq); - LogWrite(PACKET__DEBUG, 1, "Packet", "[Start]"); - LogWrite(PACKET__DEBUG, 1, "Packet", "[End]"); -#endif - SendOutOfOrderAck(seq); - } else { - EQProtocolPacket* qp = RemoveQueue(seq); - if (qp) { - LogWrite(PACKET__DEBUG, 1, "Packet", "OP_Fragment: Removing older queued packet with sequence %i", seq); - delete qp; - } - - SetNextAckToSend(seq); - NextInSeq++; - if (oversize_buffer) { - memcpy(oversize_buffer + oversize_offset, p->pBuffer + 2, p->size - 2); - oversize_offset += p->size - 2; - if (oversize_offset == oversize_length) { - if (*(p->pBuffer + 2) == 0x00 && *(p->pBuffer + 3) == 0x19) { - EQProtocolPacket *subp = new EQProtocolPacket(oversize_buffer, oversize_offset); - subp->copyInfo(p); - ProcessPacket(subp, p); - delete subp; - } else { - if (crypto->isEncrypted() && p && p->size > 2) { - MCombineQueueLock.lock(); - EQProtocolPacket* p2 = ProcessEncryptedData(oversize_buffer, oversize_offset, p->opcode); - MCombineQueueLock.unlock(); - EQApplicationPacket* ap = p2->MakeApplicationPacket(2); - ap->copyInfo(p); - if (ap->version == 0) - ap->version = client_version; -#ifdef WRITE_PACKETS - WritePackets(ap->GetOpcodeName(), oversize_buffer, oversize_offset, false); -#endif - ap->copyInfo(p); - InboundQueuePush(ap); - safe_delete(p2); - } - } - delete[] oversize_buffer; - oversize_buffer = NULL; - oversize_offset = 0; - } - } else if (!oversize_buffer) { - oversize_length = ntohl(*(uint32 *)(p->pBuffer + 2)); - oversize_buffer = new unsigned char[oversize_length]; - memcpy(oversize_buffer, p->pBuffer + 6, p->size - 6); - oversize_offset = p->size - 6; - } - } - } - break; - case OP_KeepAlive: { -#ifndef COLLECTOR - NonSequencedPush(new EQProtocolPacket(p->opcode, p->pBuffer, p->size)); -#endif - } - break; - case OP_Ack: { - if (!p->pBuffer || (p->Size() < 4)) { - LogWrite(PACKET__DEBUG, 9, "Packet", "Received OP_Ack that was of malformed size"); - break; - } - uint16 seq = ntohs(*(uint16*)(p->pBuffer)); - AckPackets(seq); - retransmittimer = Timer::GetCurrentTime2(); - } - break; - case OP_SessionRequest: { - if (p->Size() < sizeof(SessionRequest)) { - break; - } - - if (GetState() == ESTABLISHED) { - if (streamactive || (sessionAttempts > 30)) { - SendDisconnect(false); - SetState(CLOSED); - break; - } - } - - sessionAttempts++; - if (GetState() == WAIT_CLOSE) { - printf("WAIT_CLOSE Reconnect with streamactive %u, sessionAttempts %u\n", streamactive, sessionAttempts); - reconnectAttempt++; - } - init(GetState() != ESTABLISHED); - OutboundQueueClear(); - SessionRequest *Request = (SessionRequest *)p->pBuffer; - Session = ntohl(Request->Session); - SetMaxLen(ntohl(Request->MaxLength)); -#ifndef COLLECTOR - NextInSeq = 0; - Key = 0x33624702; - SendSessionResponse(); -#endif - SetState(ESTABLISHED); - } - break; - case OP_SessionResponse: { - if (p->Size() < sizeof(SessionResponse)) { - break; - } - init(); - OutboundQueueClear(); - SetActive(true); - SessionResponse *Response = (SessionResponse *)p->pBuffer; - SetMaxLen(ntohl(Response->MaxLength)); - Key = ntohl(Response->Key); - NextInSeq = 0; - SetState(ESTABLISHED); - if (!Session) - Session = ntohl(Response->Session); - compressed = (Response->Format & FLAG_COMPRESSED); - encoded = (Response->Format & FLAG_ENCODED); - - if (compressed) { - if (remote_port == 9000 || (remote_port == 0 && p->src_port == 9000)) - SetStreamType(WorldStream); - else - SetStreamType(ZoneStream); - } else if (encoded) - SetStreamType(ChatOrMailStream); - else - SetStreamType(LoginStream); - } - break; - case OP_SessionDisconnect: { - SendDisconnect(); - } - break; - case OP_OutOfOrderAck: { - if (!p->pBuffer || (p->Size() < 4)) { - LogWrite(PACKET__DEBUG, 9, "Packet", "Received OP_OutOfOrderAck that was of malformed size"); - break; - } - uint16 seq = ntohs(*(uint16*)(p->pBuffer)); - MOutboundQueue.lock(); - - if (uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { - LogWrite(PACKET__DEBUG, 9, "Packet", "Pre-OOA Invalid Sequenced queue: BS %u + SQ %u != NOS %u", SequencedBase, SequencedQueue.size(), NextOutSeq); - } - - if (CompareSequence(SequencedBase, seq) != SeqPast && CompareSequence(NextOutSeq, seq) == SeqPast) { - uint16 sqsize = SequencedQueue.size(); - uint16 index = seq - SequencedBase; - LogWrite(PACKET__DEBUG, 9, "Packet", "OP_OutOfOrderAck marking packet acked in queue (queue index = %u, queue size = %u)", index, sqsize); - if (index < sqsize) { - SequencedQueue[index]->acked = true; - uint16 count = 0; - uint32 timeout = AverageDelta * 2 + 100; - for (auto sitr = SequencedQueue.begin(); sitr != SequencedQueue.end() && count < index; ++sitr, ++count) { - if (!(*sitr)->acked && (*sitr)->sent_time > 0 && (((*sitr)->sent_time + timeout) < Timer::GetCurrentTime2())) { - (*sitr)->sent_time = 0; - LogWrite(PACKET__DEBUG, 9, "Packet", "OP_OutOfOrderAck Flagging packet %u for retransmission", SequencedBase + count); - } - } - } - - if (RETRANSMIT_TIMEOUT_MULT) { - retransmittimer = Timer::GetCurrentTime2(); - } - } - else { - LogWrite(PACKET__DEBUG, 9, "Packet", "Received OP_OutOfOrderAck for out-of-window %u. Window (%u->%u)", seq, SequencedBase, NextOutSeq); - } - - if (uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { - LogWrite(PACKET__DEBUG, 9, "Packet", "Post-OOA Invalid Sequenced queue: BS %u + SQ %u != NOS %u", SequencedBase, SequencedQueue.size(), NextOutSeq); - } - - MOutboundQueue.unlock(); - } - break; - case OP_ServerKeyRequest: { - if (p->Size() < sizeof(ClientSessionStats)) { - break; - } - - ClientSessionStats* Stats = (ClientSessionStats*)p->pBuffer; - int16 request_id = Stats->RequestID; - AdjustRates(ntohl(Stats->average_delta)); - ServerSessionStats* stats = (ServerSessionStats*)p->pBuffer; - memset(stats, 0, sizeof(ServerSessionStats)); - stats->RequestID = request_id; - stats->current_time = ntohl(Timer::GetCurrentTime2()); - stats->sent_packets = ntohl(sent_packets); - stats->sent_packets2 = ntohl(sent_packets); - stats->received_packets = ntohl(received_packets); - stats->received_packets2 = ntohl(received_packets); - NonSequencedPush(new EQProtocolPacket(OP_SessionStatResponse, p->pBuffer, p->size)); - if (!crypto->isEncrypted()) - SendKeyRequest(); - else - SendSessionResponse(); - } - break; - case OP_SessionStatResponse: { - LogWrite(PACKET__INFO, 0, "Packet", "OP_SessionStatResponse"); - } - break; - case OP_OutOfSession: { - LogWrite(PACKET__INFO, 0, "Packet", "OP_OutOfSession"); - SendDisconnect(); - SetState(CLOSED); - } - break; - default: - cout << "Orig Packet: " << p->opcode << endl; - DumpPacket(p->pBuffer, p->size); - if (p && p->size >= 69) { - processRSAKey(p); - } - MCombineQueueLock.lock(); - EQProtocolPacket* p2 = ProcessEncryptedData(p->pBuffer, p->size, OP_Packet); - MCombineQueueLock.unlock(); - cout << "Decrypted Packet: " << p2->opcode << endl; - DumpPacket(p2->pBuffer, p2->size); - - safe_delete(p2); - LogWrite(PACKET__INFO, 0, "Packet", "Received unknown packet type, not adding to inbound queue"); - break; - } - } -} - -// Compress EQ2 packet data using zlib compression -int8 EQStream::EQ2_Compress(EQ2Packet* app, int8 offset) -{ - uchar* pDataPtr = app->pBuffer + offset; - int xpandSize = app->size * 2; - uchar* deflate_buff = new uchar[xpandSize]; - MCompressData.lock(); - stream.next_in = pDataPtr; - stream.avail_in = app->size - offset; - stream.next_out = deflate_buff; - stream.avail_out = xpandSize; - - int ret = deflate(&stream, Z_SYNC_FLUSH); - - if (ret != Z_OK) { - printf("ZLIB COMPRESSION RETFAIL: %i, %i (Ret: %i)\n", app->size, stream.avail_out, ret); - MCompressData.unlock(); - safe_delete_array(deflate_buff); - return 0; - } - - int32 newsize = xpandSize - stream.avail_out; - safe_delete_array(app->pBuffer); - app->size = newsize + offset; - app->pBuffer = new uchar[app->size]; - app->pBuffer[(offset - 1)] = 1; - memcpy(app->pBuffer + offset, deflate_buff, newsize); - MCompressData.unlock(); - safe_delete_array(deflate_buff); - - return offset - 1; -} - -// Process RSA key from packet for encryption setup -int16 EQStream::processRSAKey(EQProtocolPacket *p, uint16 subpacket_length) -{ - if (subpacket_length) - crypto->setRC4Key(Crypto::RSADecrypt(p->pBuffer + subpacket_length - 8, 8)); - else - crypto->setRC4Key(Crypto::RSADecrypt(p->pBuffer + p->size - 8, 8)); - - return 0; -} - -// Send encryption key request to client -void EQStream::SendKeyRequest() -{ - int32 crypto_key_size = 60; - int16 size = sizeof(KeyGen_Struct) + sizeof(KeyGen_End_Struct) + crypto_key_size; - EQ2Packet *outapp = new EQ2Packet(OP_WSLoginRequestMsg, NULL, size); - memcpy(&outapp->pBuffer[0], &crypto_key_size, sizeof(int32)); - memset(&outapp->pBuffer[4], 0xFF, crypto_key_size); - memset(&outapp->pBuffer[size - 5], 1, 1); - memset(&outapp->pBuffer[size - 1], 1, 1); - EQ2QueuePacket(outapp, true); -} - -// Encrypt packet data using RC4 encryption -void EQStream::EncryptPacket(EQ2Packet* app, int8 compress_offset, int8 offset) -{ - if (app->size > 2 && crypto->isEncrypted()) { - app->packet_encrypted = true; - uchar* crypt_buff = app->pBuffer; - if (app->eq2_compressed) - crypto->RC4Encrypt(crypt_buff + compress_offset, app->size - compress_offset); - else - crypto->RC4Encrypt(crypt_buff + 2 + offset, app->size - 2 - offset); - } -} - -// Queue EQ2 packet for transmission with optional combining -void EQStream::EQ2QueuePacket(EQ2Packet* app, bool attempted_combine) -{ - if (CheckActive()) { - if (!attempted_combine) { - MCombineQueueLock.lock(); - combine_queue.push_back(app); - MCombineQueueLock.unlock(); - } - else { - MCombineQueueLock.lock(); - PreparePacket(app); - MCombineQueueLock.unlock(); - SendPacket(app); - } - } -} - -// Reverse packet preparation for debugging purposes -void EQStream::UnPreparePacket(EQ2Packet* app) -{ - if (app->pBuffer[2] == 0 && app->pBuffer[3] == 19) { - uchar* new_buffer = new uchar[app->size - 3]; - memcpy(new_buffer + 2, app->pBuffer + 5, app->size - 3); - delete[] app->pBuffer; - app->size -= 3; - app->pBuffer = new_buffer; - } -} - -#ifdef WRITE_PACKETS -// Convert unprintable characters to dots for packet logging -char EQStream::GetChar(uchar in) -{ - if (in < ' ' || in > '~') - return '.'; - return (char)in; -} - -// Write formatted output to packet log file -void EQStream::WriteToFile(char* pFormat, ...) -{ - va_list args; - va_start(args, pFormat); - vfprintf(write_packets, pFormat, args); - va_end(args); -} - -// Log packet data in hex dump format -void EQStream::WritePackets(const char* opcodeName, uchar* data, int32 size, bool outgoing) -{ - MWritePackets.lock(); - struct in_addr ip_addr; - ip_addr.s_addr = remote_ip; - char timebuffer[80]; - time_t rawtime; - struct tm* timeinfo; - time(&rawtime); - timeinfo = localtime(&rawtime); - strftime(timebuffer, 80, "%m/%d/%Y %H:%M:%S", timeinfo); - if (outgoing) - WriteToFile("-- %s --\n%s\nSERVER -> %s\n", opcodeName, timebuffer, inet_ntoa(ip_addr)); - else - WriteToFile("-- %s --\n%s\n%s -> SERVER\n", opcodeName, timebuffer, inet_ntoa(ip_addr)); - int i; - int nLines = size / 16; - int nExtra = size % 16; - uchar* pPtr = data; - for (i = 0; i < nLines; i++) { - WriteToFile("%4.4X:\t%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n", i * 16, pPtr[0], pPtr[1], pPtr[2], pPtr[3], pPtr[4], pPtr[5], pPtr[6], pPtr[7], pPtr[8], pPtr[9], pPtr[10], pPtr[11], pPtr[12], pPtr[13], pPtr[14], pPtr[15], GetChar(pPtr[0]), GetChar(pPtr[1]), GetChar(pPtr[2]), GetChar(pPtr[3]), GetChar(pPtr[4]), GetChar(pPtr[5]), GetChar(pPtr[6]), GetChar(pPtr[7]), GetChar(pPtr[8]), GetChar(pPtr[9]), GetChar(pPtr[10]), GetChar(pPtr[11]), GetChar(pPtr[12]), GetChar(pPtr[13]), GetChar(pPtr[14]), GetChar(pPtr[15])); - pPtr += 16; - } - if (nExtra) { - WriteToFile("%4.4X\t", nLines * 16); - for (i = 0; i < nExtra; i++) { - WriteToFile("%2.2X ", pPtr[i]); - } - for (i; i < 16; i++) - WriteToFile(" "); - for (i = 0; i < nExtra; i++) { - WriteToFile("%c", GetChar(pPtr[i])); - } - WriteToFile("\n"); - } - WriteToFile("\n\n"); - fflush(write_packets); - MWritePackets.unlock(); -} - -// Log EQ2 packet with opcode name resolution -void EQStream::WritePackets(EQ2Packet* app, bool outgoing) -{ - if (app->version == 0) - app->version = client_version; - WritePackets(app->GetOpcodeName(), app->pBuffer, app->size, outgoing); -} -#endif - -// Prepare EQ2 packet for transmission (compression, encryption) -void EQStream::PreparePacket(EQ2Packet* app, int8 offset) -{ - app->setVersion(client_version); - compressed_offset = 0; - - if (!app->packet_prepared) { - if (app->PreparePacket(MaxLen) == 255) - return; - } - -#ifdef WRITE_PACKETS - if (!app->eq2_compressed && !app->packet_encrypted) - WritePackets(app, true); -#endif - - if (!app->eq2_compressed && app->size > 128) { - compressed_offset = EQ2_Compress(app); - if (compressed_offset) - app->eq2_compressed = true; - } - if (!app->packet_encrypted) { - EncryptPacket(app, compressed_offset, offset); - if (app->size > 2 && app->pBuffer[2] == 0) { - uchar* new_buffer = new uchar[app->size + 1]; - new_buffer[2] = 0; - memcpy(new_buffer + 3, app->pBuffer + 2, app->size - 2); - delete[] app->pBuffer; - app->pBuffer = new_buffer; - app->size++; - } - } -} - -// Send protocol packet, fragmenting if necessary -void EQStream::SendPacket(EQProtocolPacket *p) -{ - uint32 chunksize, used; - uint32 length; - - if (p->size > (MaxLen - 8)) { - uchar* tmpbuff = p->pBuffer; - length = p->size - 2; - - EQProtocolPacket *out = new EQProtocolPacket(OP_Fragment, NULL, MaxLen - 4); - *(uint32 *)(out->pBuffer + 2) = htonl(length); - used = MaxLen - 10; - memcpy(out->pBuffer + 6, tmpbuff + 2, used); - - SequencedPush(out); - - while (used < length) { - chunksize = min(length - used, MaxLen - 6); - out = new EQProtocolPacket(OP_Fragment, NULL, chunksize + 2); - memcpy(out->pBuffer + 2, tmpbuff + used + 2, chunksize); - SequencedPush(out); - used += chunksize; - } - delete p; - } else { - SequencedPush(p); - } -} - -// Send application packet, converting to protocol packet first -void EQStream::SendPacket(EQApplicationPacket *p) -{ - uint32 chunksize, used; - uint32 length; - - if (p->size > (MaxLen - 8)) { - unsigned char *tmpbuff = new unsigned char[p->size + 2]; - length = p->serialize(tmpbuff); - - EQProtocolPacket *out = new EQProtocolPacket(OP_Fragment, NULL, MaxLen - 4); - *(uint32 *)(out->pBuffer + 2) = htonl(p->Size()); - memcpy(out->pBuffer + 6, tmpbuff, MaxLen - 10); - used = MaxLen - 10; - SequencedPush(out); - - while (used < length) { - out = new EQProtocolPacket(OP_Fragment, NULL, MaxLen - 4); - chunksize = min(length - used, MaxLen - 6); - memcpy(out->pBuffer + 2, tmpbuff + used, chunksize); - out->size = chunksize + 2; - SequencedPush(out); - used += chunksize; - } - delete p; - delete[] tmpbuff; - } else { - EQProtocolPacket *out = new EQProtocolPacket(OP_Packet, NULL, p->Size() + 2); - p->serialize(out->pBuffer + 2); - SequencedPush(out); - delete p; - } -} - -// Add packet to sequenced queue with sequence number -void EQStream::SequencedPush(EQProtocolPacket *p) -{ - p->setVersion(client_version); - MOutboundQueue.lock(); - *(uint16 *)(p->pBuffer) = htons(NextOutSeq); - SequencedQueue.push_back(p); - p->sequence = NextOutSeq; - NextOutSeq++; - MOutboundQueue.unlock(); -} - -// Add packet to non-sequenced queue -void EQStream::NonSequencedPush(EQProtocolPacket *p) -{ - p->setVersion(client_version); - MOutboundQueue.lock(); - NonSequencedQueue.push(p); - MOutboundQueue.unlock(); -} - -// Send acknowledgment for received packet -void EQStream::SendAck(uint16 seq) -{ - uint16 Seq = htons(seq); - SetLastAckSent(seq); - NonSequencedPush(new EQProtocolPacket(OP_Ack, (unsigned char *)&Seq, sizeof(uint16))); -} - -// Send out-of-order acknowledgment -void EQStream::SendOutOfOrderAck(uint16 seq) -{ - uint16 Seq = htons(seq); - NonSequencedPush(new EQProtocolPacket(OP_OutOfOrderAck, (unsigned char *)&Seq, sizeof(uint16))); -} - -// Check and process combine queue for packet combining -bool EQStream::CheckCombineQueue() -{ - bool ret = true; - MCombineQueueLock.lock(); - if (combine_queue.size() > 0) { - EQ2Packet* first = combine_queue.front(); - combine_queue.pop_front(); - if (combine_queue.size() == 0) { - EQ2QueuePacket(first, true); - } - else { - PreparePacket(first); - EQ2Packet* second = 0; - bool combine_worked = false; - int16 count = 0; - while (combine_queue.size()) { - count++; - second = combine_queue.front(); - combine_queue.pop_front(); - PreparePacket(second); - - if (!first->AppCombine(second)) { - first->SetProtocolOpcode(OP_Packet); - if (combine_worked) { - SequencedPush(first); - } - else { - EQ2QueuePacket(first, true); - } - first = second; - combine_worked = false; - } - else { - combine_worked = true; - } - if (count >= 60 || first->size > 4000) { - ret = false; - break; - } - } - if (first) { - first->SetProtocolOpcode(OP_Packet); - if (combine_worked) { - SequencedPush(first); - } - else { - EQ2QueuePacket(first, true); - } - } - } - } - MCombineQueueLock.unlock(); - return ret; -} - -// Check resend queue and retransmit packets as needed -void EQStream::CheckResend(int eq_fd) -{ - int32 curr = Timer::GetCurrentTime2(); - EQProtocolPacket* packet = 0; - deque::iterator itr; - MResendQue.lock(); - for (itr = resend_que.begin(); itr != resend_que.end(); itr++) { - packet = *itr; - if (packet->attempt_count >= 5) { - safe_delete(packet); - itr = resend_que.erase(itr); - if (itr == resend_que.end()) - break; - } - else { - if ((curr - packet->sent_time) < 1000) - continue; - packet->sent_time -= 1000; - packet->attempt_count++; - WritePacket(eq_fd, packet); - } - } - MResendQue.unlock(); -} - -// Compare sequence numbers accounting for wrap-around -EQStream::SeqOrder EQStream::CompareSequence(uint16 expected_seq, uint16 seq) -{ - if (expected_seq == seq) { - return SeqInOrder; - } - else if ((seq > expected_seq && (uint32)seq < ((uint32)expected_seq + EQStream::MaxWindowSize)) || seq < (expected_seq - EQStream::MaxWindowSize)) { - return SeqFuture; - } - else { - return SeqPast; - } -} - -// Process acknowledgments and remove acked packets from queue -void EQStream::AckPackets(uint16 seq) -{ - std::deque::iterator itr, tmp; - - MOutboundQueue.lock(); - - SeqOrder ord = CompareSequence(SequencedBase, seq); - if (ord == SeqInOrder) { - LogWrite(PACKET__DEBUG, 9, "Packet", "Received an ack with no window advancement (seq %u)", seq); - } - else if (ord == SeqPast) { - LogWrite(PACKET__DEBUG, 9, "Packet", "Received an ack with backward window advancement (they gave %u, our window starts at %u). This is bad", seq, SequencedBase); - } - else { - LogWrite(PACKET__DEBUG, 9, "Packet", "Received an ack up through sequence %u. Our base is %u", seq, SequencedBase); - - seq++; - while (SequencedBase != seq) { - if (SequencedQueue.empty()) { - LogWrite(PACKET__DEBUG, 9, "Packet", "OUT OF PACKETS acked packet with sequence %u. Next send is %u before this", (unsigned long)SequencedBase, SequencedQueue.size()); - SequencedBase = NextOutSeq; - break; - } - LogWrite(PACKET__DEBUG, 9, "Packet", "Removing acked packet with sequence %u", (unsigned long)SequencedBase); - delete SequencedQueue.front(); - SequencedQueue.pop_front(); - SequencedBase++; - } - if (uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { - LogWrite(PACKET__DEBUG, 9, "Packet", "Post-Ack on %u Invalid Sequenced queue: BS %u + SQ %u != NOS %u", seq, SequencedBase, SequencedQueue.size(), NextOutSeq); - } - } - - MOutboundQueue.unlock(); -} - -// Main write function - sends packets to network -void EQStream::Write(int eq_fd) -{ - queue ReadyToSend; - long maxack; - - MRate.lock(); - sint32 threshold = RateThreshold; - MRate.unlock(); - if (BytesWritten > threshold) { - return; - } - - MCombinedAppPacket.lock(); - EQApplicationPacket *CombPack = CombinedAppPacket; - CombinedAppPacket = NULL; - MCombinedAppPacket.unlock(); - - if (CombPack) { - SendPacket(CombPack); - } - - MAcks.lock(); - maxack = MaxAckReceived; - if (NextAckToSend > LastAckSent || LastAckSent == 0x0000ffff) - SendAck(NextAckToSend); - MAcks.unlock(); - - MOutboundQueue.lock(); - - EQProtocolPacket *p = NULL; - std::deque::iterator sitr; - - sitr = SequencedQueue.begin(); - uint16 count = 0; - - while (sitr != SequencedQueue.end() && (*sitr)->sent_time > 0) { - ++sitr; - ++count; - } - - bool SeqEmpty = false, NonSeqEmpty = false; - - while (!SeqEmpty || !NonSeqEmpty) { - if (!NonSequencedQueue.empty()) { - if (!p) { - p = NonSequencedQueue.front(); - LogWrite(PACKET__DEBUG, 9, "Packet", "Starting combined packet with non-seq packet of len %u", p->size); - NonSequencedQueue.pop(); - } - else if (!p->combine(NonSequencedQueue.front())) { - LogWrite(PACKET__DEBUG, 9, "Packet", "Combined packet full at len %u, next non-seq packet is len %u", p->size, (NonSequencedQueue.front())->size); - ReadyToSend.push(p); - BytesWritten += p->size; - p = nullptr; - - if (BytesWritten > threshold) { - LogWrite(PACKET__DEBUG, 9, "Packet", "Exceeded write threshold in nonseq (%u > %u)", BytesWritten, threshold); - break; - } - } - else { - LogWrite(PACKET__DEBUG, 9, "Packet", "Combined non-seq packet of len %u, yeilding %u combined", (NonSequencedQueue.front())->size, p->size); - delete NonSequencedQueue.front(); - NonSequencedQueue.pop(); - } - } - else { - NonSeqEmpty = true; - } - - if (sitr != SequencedQueue.end()) { - uint16 seq_send = SequencedBase + count; - - if (SequencedQueue.empty()) { - LogWrite(PACKET__DEBUG, 9, "Packet", "Tried to write a packet with an empty queue (%u is past next out %u)", seq_send, NextOutSeq); - SeqEmpty = true; - continue; - } - - if ((*sitr)->acked || (*sitr)->sent_time != 0) { - ++sitr; - ++count; - if (p) { - LogWrite(PACKET__DEBUG, 9, "Packet", "Final combined packet not full, len %u", p->size); - ReadyToSend.push(p); - BytesWritten += p->size; - p = nullptr; - } - LogWrite(PACKET__DEBUG, 9, "Packet", "Not retransmitting seq packet %u because already marked as acked", seq_send); - } - else if (!p) { - p = (*sitr)->Copy(); - LogWrite(PACKET__DEBUG, 9, "Packet", "Starting combined packet with seq packet %u of len %u", seq_send, p->size); - (*sitr)->sent_time = Timer::GetCurrentTime2(); - ++sitr; - ++count; - } - else if (!p->combine(*sitr)) { - LogWrite(PACKET__DEBUG, 9, "Packet", "Combined packet full at len %u, next seq packet %u is len %u", p->size, seq_send + 1, (*sitr)->size); - ReadyToSend.push(p); - BytesWritten += p->size; - p = nullptr; - if ((*sitr)->opcode != OP_Fragment && BytesWritten > threshold) { - LogWrite(PACKET__DEBUG, 9, "Packet", "Exceeded write threshold in seq (%u > %u)", BytesWritten, threshold); - break; - } - } - else { - LogWrite(PACKET__DEBUG, 9, "Packet", "Combined seq packet %u of len %u, yeilding %u combined", seq_send, (*sitr)->size, p->size); - (*sitr)->sent_time = Timer::GetCurrentTime2(); - ++sitr; - ++count; - } - - if (uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { - LogWrite(PACKET__DEBUG, 9, "Packet", "Post send Invalid Sequenced queue: BS %u + SQ %u != NOS %u", SequencedBase, SequencedQueue.size(), NextOutSeq); - } - } - else { - SeqEmpty = true; - } - } - MOutboundQueue.unlock(); - - if (p) { - LogWrite(PACKET__DEBUG, 9, "Packet", "Final combined packet not full, len %u", p->size); - ReadyToSend.push(p); - BytesWritten += p->size; - } - - while (!ReadyToSend.empty()) { - p = ReadyToSend.front(); - WritePacket(eq_fd, p); - delete p; - ReadyToSend.pop(); - } - - if (SeqEmpty && NonSeqEmpty) { - if (GetState() == CLOSING) { - MOutboundQueue.lock(); - if (SequencedQueue.size() > 0) { - // retransmission attempts - } - else { - LogWrite(PACKET__DEBUG, 9, "Packet", "All outgoing data flushed, disconnecting client."); - SendDisconnect(); - } - MOutboundQueue.unlock(); - } - } -} - -// Write packet to socket -void EQStream::WritePacket(int eq_fd, EQProtocolPacket *p) -{ - uint32 length = 0; - sockaddr_in address; - address.sin_family = AF_INET; - address.sin_addr.s_addr = remote_ip; - address.sin_port = remote_port; - - length = p->serialize(buffer); - if (p->opcode != OP_SessionRequest && p->opcode != OP_SessionResponse) { - if (compressed) { - BytesWritten -= p->size; - uint32 newlen = EQProtocolPacket::Compress(buffer, length, write_buffer, 2048); - memcpy(buffer, write_buffer, newlen); - length = newlen; - BytesWritten += newlen; - } - if (encoded) { - EQProtocolPacket::ChatEncode(buffer, length, Key); - } - *(uint16 *)(buffer + length) = htons(CRC16(buffer, length, Key)); - length += 2; - } - sent_packets++; - sendto(eq_fd, (char *)buffer, length, 0, (sockaddr *)&address, sizeof(address)); -} - -// Read packet from socket -EQProtocolPacket *EQStream::Read(int eq_fd, sockaddr_in *from) -{ - int socklen; - int length = 0; - unsigned char buffer[2048]; - EQProtocolPacket *p = NULL; - char temp[15]; - - socklen = sizeof(sockaddr); - length = recvfrom(eq_fd, buffer, 2048, 0, (struct sockaddr*)from, (socklen_t *)&socklen); - - if (length >= 2) { - DumpPacket(buffer, length); - p = new EQProtocolPacket(buffer[1], &buffer[2], length - 2); - uint32 ip = from->sin_addr.s_addr; - sprintf(temp, "%d.%d.%d.%d:%d", - *(unsigned char *)&ip, - *((unsigned char *)&ip + 1), - *((unsigned char *)&ip + 2), - *((unsigned char *)&ip + 3), - ntohs(from->sin_port)); - } - return p; -} - -// Send session response to client -void EQStream::SendSessionResponse() -{ - EQProtocolPacket *out = new EQProtocolPacket(OP_SessionResponse, NULL, sizeof(SessionResponse)); - SessionResponse *Response = (SessionResponse *)out->pBuffer; - Response->Session = htonl(Session); - Response->MaxLength = htonl(MaxLen); - Response->UnknownA = 2; - Response->Format = 0; - if (compressed) - Response->Format |= FLAG_COMPRESSED; - if (encoded) - Response->Format |= FLAG_ENCODED; - Response->Key = htonl(Key); - - out->size = sizeof(SessionResponse); - NonSequencedPush(out); -} - -// Send session request to server -void EQStream::SendSessionRequest() -{ - EQProtocolPacket *out = new EQProtocolPacket(OP_SessionRequest, NULL, sizeof(SessionRequest)); - SessionRequest *Request = (SessionRequest *)out->pBuffer; - memset(Request, 0, sizeof(SessionRequest)); - Request->Session = htonl(time(NULL)); - Request->MaxLength = htonl(512); - - NonSequencedPush(out); -} - -// Send disconnect message to peer -void EQStream::SendDisconnect(bool setstate) -{ - try { - if (GetState() != ESTABLISHED && GetState() != WAIT_CLOSE) - return; - - EQProtocolPacket *out = new EQProtocolPacket(OP_SessionDisconnect, NULL, sizeof(uint32) + sizeof(int16)); - *(uint32 *)out->pBuffer = htonl(Session); - out->pBuffer[4] = 0; - out->pBuffer[5] = 6; - NonSequencedPush(out); - if (setstate) - SetState(CLOSING); - } - catch (...) {} -} - -// Add packet to inbound queue -void EQStream::InboundQueuePush(EQApplicationPacket *p) -{ - MInboundQueue.lock(); - InboundQueue.push_back(p); - MInboundQueue.unlock(); -} - -// Remove and return next packet from inbound queue -EQApplicationPacket *EQStream::PopPacket() -{ - EQApplicationPacket *p = NULL; - - MInboundQueue.lock(); - if (InboundQueue.size()) { - p = InboundQueue.front(); - InboundQueue.pop_front(); - } - MInboundQueue.unlock(); - if (p) - p->setVersion(client_version); - return p; -} - -// Clear all packets from inbound queue -void EQStream::InboundQueueClear() -{ - MInboundQueue.lock(); - while (InboundQueue.size()) { - delete InboundQueue.front(); - InboundQueue.pop_front(); - } - MInboundQueue.unlock(); -} - -// Encrypt packet data (placeholder implementation) -void EQStream::EncryptPacket(uchar* data, int16 size) -{ - if (size > 6) { - // Encryption implementation would go here - } -} - -// Check if stream has outgoing data ready to send -bool EQStream::HasOutgoingData() -{ - bool flag; - - if (CheckClosed()) - return false; - - MOutboundQueue.lock(); - flag = (!NonSequencedQueue.empty()); - if (!flag) { - flag = (!SequencedQueue.empty()); - } - MOutboundQueue.unlock(); - - if (!flag) { - MAcks.lock(); - flag = (NextAckToSend > LastAckSent); - MAcks.unlock(); - } - - if (!flag) { - MCombinedAppPacket.lock(); - flag = (CombinedAppPacket != NULL); - MCombinedAppPacket.unlock(); - } - - return flag; -} - -// Clear all outbound queues -void EQStream::OutboundQueueClear() -{ - MOutboundQueue.lock(); - while (NonSequencedQueue.size()) { - delete NonSequencedQueue.front(); - NonSequencedQueue.pop(); - } - while (SequencedQueue.size()) { - delete SequencedQueue.front(); - SequencedQueue.pop_front(); - } - MOutboundQueue.unlock(); -} - -// Process incoming data buffer -void EQStream::Process(const unsigned char *buffer, const uint32 length) -{ - received_packets++; - static unsigned char newbuffer[2048]; - uint32 newlength = 0; - - if (EQProtocolPacket::ValidateCRC(buffer, length, Key)) { - if (compressed) { - newlength = EQProtocolPacket::Decompress(buffer, length, newbuffer, 2048); - } else { - memcpy(newbuffer, buffer, length); - newlength = length; - if (encoded) - EQProtocolPacket::ChatDecode(newbuffer, newlength - 2, Key); - } - - uint16 opcode = ntohs(*(const uint16 *)newbuffer); - if (opcode > 0 && opcode <= OP_OutOfSession) { - if (buffer[1] != 0x01 && buffer[1] != 0x02 && buffer[1] != 0x1d) - newlength -= 2; - - EQProtocolPacket p(newbuffer, newlength); - ProcessPacket(&p); - } - else { - cout << "2Orig Packet: " << opcode << endl; - DumpPacket(newbuffer, newlength); - ProcessEmbeddedPacket(newbuffer, newlength, OP_Fragment); - } - ProcessQueue(); - } else { - cout << "Incoming packet failed checksum:" << endl; - dump_message_column(const_cast(buffer), length, "CRC failed: "); - } -} - -// Acknowledgment tracking getters -long EQStream::GetMaxAckReceived() -{ - MAcks.lock(); - long l = MaxAckReceived; - MAcks.unlock(); - return l; -} - -long EQStream::GetNextAckToSend() -{ - MAcks.lock(); - long l = NextAckToSend; - MAcks.unlock(); - return l; -} - -long EQStream::GetLastAckSent() -{ - MAcks.lock(); - long l = LastAckSent; - MAcks.unlock(); - return l; -} - -// Acknowledgment tracking setters -void EQStream::SetMaxAckReceived(uint32 seq) -{ - deque::iterator itr; - - MAcks.lock(); - MaxAckReceived = seq; - MAcks.unlock(); - MOutboundQueue.lock(); - if (long(seq) > LastSeqSent) - LastSeqSent = seq; - MResendQue.lock(); - EQProtocolPacket* packet = 0; - for (itr = resend_que.begin(); itr != resend_que.end(); itr++) { - packet = *itr; - if (packet && packet->sequence <= seq) { - safe_delete(packet); - itr = resend_que.erase(itr); - if (itr == resend_que.end()) - break; - } - } - MResendQue.unlock(); - MOutboundQueue.unlock(); -} - -void EQStream::SetNextAckToSend(uint32 seq) -{ - MAcks.lock(); - NextAckToSend = seq; - MAcks.unlock(); -} - -void EQStream::SetLastAckSent(uint32 seq) -{ - MAcks.lock(); - LastAckSent = seq; - MAcks.unlock(); -} - -void EQStream::SetLastSeqSent(uint32 seq) -{ - MOutboundQueue.lock(); - LastSeqSent = seq; - MOutboundQueue.unlock(); -} - -// Set stream type and configure related settings -void EQStream::SetStreamType(EQStreamType type) -{ - StreamType = type; - switch (StreamType) { - case LoginStream: - app_opcode_size = 1; - compressed = false; - encoded = false; - break; - case EQ2Stream: - app_opcode_size = 2; - compressed = false; - encoded = false; - break; - case ChatOrMailStream: - case ChatStream: - case MailStream: - app_opcode_size = 1; - compressed = false; - encoded = true; - break; - case ZoneStream: - case WorldStream: - default: - app_opcode_size = 2; - compressed = true; - encoded = false; - break; - } -} - -// Process out-of-order packet queue -void EQStream::ProcessQueue() -{ - if (OutOfOrderpackets.empty()) { - return; - } - - EQProtocolPacket* qp = NULL; - while ((qp = RemoveQueue(NextInSeq)) != NULL) { - ProcessPacket(qp); - delete qp; - } -} - -// Remove specific sequence from out-of-order queue -EQProtocolPacket* EQStream::RemoveQueue(uint16 seq) -{ - map::iterator itr; - EQProtocolPacket* qp = NULL; - if ((itr = OutOfOrderpackets.find(seq)) != OutOfOrderpackets.end()) { - qp = itr->second; - OutOfOrderpackets.erase(itr); - } - return qp; -} - -// Decay byte counters and check for packet timeouts -void EQStream::Decay() -{ - MRate.lock(); - uint32 rate = DecayRate; - MRate.unlock(); - if (BytesWritten > 0) { - BytesWritten -= rate; - if (BytesWritten < 0) - BytesWritten = 0; - } - - int count = 0; - MOutboundQueue.lock(); - for (auto sitr = SequencedQueue.begin(); sitr != SequencedQueue.end(); ++sitr, count++) { - if (!(*sitr)->acked && (*sitr)->sent_time > 0 && ((*sitr)->sent_time + retransmittimeout) < Timer::GetCurrentTime2()) { - (*sitr)->sent_time = 0; - LogWrite(PACKET__DEBUG, 9, "Packet", "Timeout exceeded for seq %u. Flagging packet for retransmission", SequencedBase + count); - } - } - MOutboundQueue.unlock(); -} - -// Adjust transmission rates based on network conditions -void EQStream::AdjustRates(uint32 average_delta) -{ - if (average_delta && (average_delta <= AVERAGE_DELTA_MAX)) { - MRate.lock(); - AverageDelta = average_delta; - RateThreshold = RATEBASE / average_delta; - DecayRate = DECAYBASE / average_delta; - if (BytesWritten > RateThreshold) - BytesWritten = RateThreshold + DecayRate; - MRate.unlock(); - } - else { - AverageDelta = AVERAGE_DELTA_MAX; - } -} \ No newline at end of file diff --git a/old/common/stream/eq_stream_factory.hpp b/old/common/stream/eq_stream_factory.hpp deleted file mode 100644 index 89c1e00..0000000 --- a/old/common/stream/eq_stream_factory.hpp +++ /dev/null @@ -1,500 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "log.hpp" -#include "timer.hpp" -#include "eq_stream.hpp" -#include "../packet/packet_dump.hpp" -#include "../opcodes/opcodes.hpp" -#include "../opcodes/opcode_manager.hpp" - -#ifdef WORLD - #include "../../WorldServer/client.h" - extern ClientList client_list; -#endif - -#define STREAM_TIMEOUT 45000 - -class EQStreamFactory -{ -public: - char* listen_ip_address; - - // Default constructor - initializes factory with stream type only - EQStreamFactory(EQStreamType type) - { - ReaderRunning = false; - WriterRunning = false; - CombinePacketRunning = false; - StreamType = type; - Port = 0; - sock = -1; - listen_ip_address = nullptr; - } - - // Constructor with port - initializes factory with stream type and port - EQStreamFactory(EQStreamType type, int port) - { - StreamType = type; - Port = port; - listen_ip_address = nullptr; - sock = -1; - ReaderRunning = false; - WriterRunning = false; - CombinePacketRunning = false; - } - - // Destructor - cleans up allocated resources - ~EQStreamFactory() - { - if (listen_ip_address) { - delete[] listen_ip_address; - } - } - - // Retrieves the next available stream from the new streams queue - EQStream* Pop() - { - if (!NewStreams.size()) - return nullptr; - - EQStream* s = nullptr; - std::lock_guard lock(MNewStreams); - if (NewStreams.size()) { - s = NewStreams.front(); - NewStreams.pop(); - s->PutInUse(); - } - return s; - } - - // Adds a new stream to the new streams queue - void Push(EQStream* s) - { - std::lock_guard lock(MNewStreams); - NewStreams.push(s); - } - - // Opens the socket and starts the reader/writer/combine packet threads - bool Open() - { - struct sockaddr_in address; - - // Setup internet address information for bind() call - memset(&address, 0, sizeof(address)); - address.sin_family = AF_INET; - address.sin_port = htons(Port); - -#if defined(LOGIN) || defined(MINILOGIN) - if (listen_ip_address) - address.sin_addr.s_addr = inet_addr(listen_ip_address); - else - address.sin_addr.s_addr = htonl(INADDR_ANY); -#else - address.sin_addr.s_addr = htonl(INADDR_ANY); -#endif - - // Setup UDP socket for new clients - sock = socket(AF_INET, SOCK_DGRAM, 0); - if (sock < 0) { - return false; - } - - if (::bind(sock, (struct sockaddr*)&address, sizeof(address)) < 0) { - sock = -1; - return false; - } - - // Set socket to non-blocking mode - fcntl(sock, F_SETFL, O_NONBLOCK); - -#ifdef LOGIN - LogWrite(LOGIN__DEBUG, 0, "Login", "Starting factory Reader"); - LogWrite(LOGIN__DEBUG, 0, "Login", "Starting factory Writer"); -#elif WORLD - LogWrite(WORLD__DEBUG, 0, "World", "Starting factory Reader"); - LogWrite(WORLD__DEBUG, 0, "World", "Starting factory Writer"); -#endif - - // Start threads for packet processing - std::thread(&EQStreamFactory::ReaderLoop, this).detach(); - std::thread(&EQStreamFactory::WriterLoop, this).detach(); - std::thread(&EQStreamFactory::CombinePacketLoop, this).detach(); - - return true; - } - - // Opens with specified port override - bool Open(unsigned long port) - { - Port = port; - return Open(); - } - - // Closes the factory and cleans up all connections - void Close() - { - CheckTimeout(true); - Stop(); - if (sock != -1) { - close(sock); - sock = -1; - } - } - - // Main reader loop - processes incoming packets from clients - void ReaderLoop() - { - fd_set readset; - std::map::iterator stream_itr; - int num; - int length; - unsigned char buffer[2048]; - sockaddr_in from; - socklen_t socklen = sizeof(sockaddr_in); - timeval sleep_time; - - ReaderRunning = true; - while (sock != -1) { - { - std::lock_guard lock(MReaderRunning); - if (!ReaderRunning) - break; - } - - FD_ZERO(&readset); - FD_SET(sock, &readset); - - sleep_time.tv_sec = 30; - sleep_time.tv_usec = 0; - - if ((num = select(sock + 1, &readset, nullptr, nullptr, &sleep_time)) < 0) { - // Socket error occurred - } - else if (num == 0) { - continue; // Timeout, no data available - } - - if (FD_ISSET(sock, &readset)) { - if ((length = recvfrom(sock, buffer, 2048, 0, (struct sockaddr*)&from, &socklen)) < 2) { - // Invalid packet received - } - else { - char temp[25]; - sprintf(temp, "%u.%d", ntohl(from.sin_addr.s_addr), ntohs(from.sin_port)); - - std::lock_guard streams_lock(MStreams); - if ((stream_itr = Streams.find(temp)) == Streams.end() || buffer[1] == OP_SessionRequest) { - // New session request or existing session requesting new connection - if (buffer[1] == OP_SessionRequest) { - if (stream_itr != Streams.end() && stream_itr->second) - stream_itr->second->SetState(CLOSED); - - EQStream* s = new EQStream(from); - s->SetFactory(this); - s->SetStreamType(StreamType); - Streams[temp] = s; - WriterWork.notify_one(); - Push(s); - s->Process(buffer, length); - s->SetLastPacketTime(Timer::GetCurrentTime2()); - } - } - else { - EQStream* curstream = stream_itr->second; - // Don't process packets for closed connections - if (curstream->CheckClosed()) { - curstream = nullptr; - } - else { - curstream->PutInUse(); - } - - if (curstream) { - curstream->Process(buffer, length); - curstream->SetLastPacketTime(Timer::GetCurrentTime2()); - curstream->ReleaseFromUse(); - } - } - } - } - } - } - - // Checks for timed out connections and removes them - void CheckTimeout(bool remove_all = false) - { - // Lock streams for the entire timeout check operation - std::lock_guard lock(MStreams); - - unsigned long now = Timer::GetCurrentTime2(); - std::map::iterator stream_itr; - - for (stream_itr = Streams.begin(); stream_itr != Streams.end();) { - EQStream* s = stream_itr->second; - EQStreamState state = s->GetState(); - - if (state == CLOSING && !s->HasOutgoingData()) { - stream_itr->second->SetState(CLOSED); - state = CLOSED; - } - else if (s->CheckTimeout(now, STREAM_TIMEOUT)) { - const char* stateString; - switch (state) { - case ESTABLISHED: - stateString = "Established"; - break; - case CLOSING: - stateString = "Closing"; - break; - case CLOSED: - stateString = "Closed"; - break; - case WAIT_CLOSE: - stateString = "Wait-Close"; - break; - default: - stateString = "Unknown"; - break; - } - LogWrite(WORLD__DEBUG, 0, "World", "Timeout up!, state=%s (%u)", stateString, state); - - if (state == ESTABLISHED) { - s->Close(); - } - else if (state == WAIT_CLOSE) { - s->SetState(CLOSING); - state = CLOSING; - } - else if (state == CLOSING) { - // Timeout in closing state, force close - s->SetState(CLOSED); - state = CLOSED; - } - } - - // Check for closed connections to remove - if (remove_all || state == CLOSED) { - if (!remove_all && s->getTimeoutDelays() < 2) { - s->addTimeoutDelay(); - // Give time for other threads to finish with stream - } - else { - // Safe to delete the stream now -#ifdef LOGIN - LogWrite(LOGIN__DEBUG, 0, "Login", "Removing connection..."); -#else - LogWrite(WORLD__DEBUG, 0, "World", "Removing connection..."); -#endif - std::map::iterator temp = stream_itr; - stream_itr++; - -#ifdef WORLD - client_list.RemoveConnection(temp->second); -#endif - EQStream* stream = temp->second; - Streams.erase(temp); - delete stream; - continue; - } - } - stream_itr++; - } - } - - // Processes packet combining for streams that need it - void CombinePacketLoop() - { - std::deque combine_que; - CombinePacketRunning = true; - bool packets_waiting = false; - - while (sock != -1) { - if (!CombinePacketRunning) - break; - - { - std::lock_guard lock(MStreams); - std::map::iterator stream_itr; - for (stream_itr = Streams.begin(); stream_itr != Streams.end(); stream_itr++) { - if (!stream_itr->second) { - continue; - } - if (stream_itr->second->combine_timer && stream_itr->second->combine_timer->Check()) - combine_que.push_back(stream_itr->second); - } - } - - EQStream* stream = nullptr; - packets_waiting = false; - while (combine_que.size()) { - stream = combine_que.front(); - if (stream->CheckActive()) { - if (!stream->CheckCombineQueue()) - packets_waiting = true; - } - combine_que.pop_front(); - } - - if (!packets_waiting) - std::this_thread::sleep_for(std::chrono::milliseconds(25)); - - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - } - - // Main writer loop - sends outgoing packets to clients - void WriterLoop() - { - std::map::iterator stream_itr; - std::vector wants_write; - std::vector::iterator cur, end; - std::deque resend_que; - bool decay = false; - uint32_t stream_count; - - Timer DecayTimer(20); - - WriterRunning = true; - DecayTimer.Enable(); - - while (sock != -1) { - Timer::SetCurrentTime(); - - { - std::lock_guard lock(MWriterRunning); - if (!WriterRunning) - break; - } - - wants_write.clear(); - decay = DecayTimer.Check(); - - // Copy streams into separate list to avoid keeping MStreams locked during writes - { - std::lock_guard lock(MStreams); - for (stream_itr = Streams.begin(); stream_itr != Streams.end(); stream_itr++) { - if (!stream_itr->second) { - Streams.erase(stream_itr); - break; - } - - // Decay bytes sent if timer expired - if (decay) - stream_itr->second->Decay(); - - if (stream_itr->second->HasOutgoingData()) { - stream_itr->second->PutInUse(); - wants_write.push_back(stream_itr->second); - } - - if (stream_itr->second->resend_que_timer->Check()) - resend_que.push_back(stream_itr->second); - } - } - - // Perform actual packet writes - cur = wants_write.begin(); - end = wants_write.end(); - for (; cur != end; cur++) { - (*cur)->Write(sock); - (*cur)->ReleaseFromUse(); - } - - // Process resend queue - while (resend_que.size()) { - resend_que.front()->CheckResend(sock); - resend_que.pop_front(); - } - - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - - { - std::lock_guard lock(MStreams); - stream_count = Streams.size(); - } - - if (!stream_count) { - // No streams available, wait for work signal - std::unique_lock writer_lock(WriterWorkMutex); - WriterWork.wait(writer_lock); - } - } - } - - // Stops all factory operations - void Stop() - { - StopReader(); - StopWriter(); - StopCombinePacket(); - } - - // Stops the reader thread - void StopReader() - { - std::lock_guard lock(MReaderRunning); - ReaderRunning = false; - } - - // Stops the writer thread - void StopWriter() - { - { - std::lock_guard lock(MWriterRunning); - WriterRunning = false; - } - WriterWork.notify_one(); - } - - // Stops the combine packet thread - void StopCombinePacket() - { - std::lock_guard lock(MCombinePacketRunning); - CombinePacketRunning = false; - } - - // Signals the writer thread that work is available - void SignalWriter() - { - WriterWork.notify_one(); - } - -private: - int sock; // Socket file descriptor - int Port; // Port number to listen on - - bool ReaderRunning; // Reader thread running flag - std::mutex MReaderRunning; // Mutex for reader thread state - bool WriterRunning; // Writer thread running flag - std::mutex MWriterRunning; // Mutex for writer thread state - bool CombinePacketRunning; // Combine packet thread running flag - std::mutex MCombinePacketRunning; // Mutex for combine packet thread state - - std::condition_variable WriterWork; // Condition variable for writer thread work signal - std::mutex WriterWorkMutex; // Mutex for writer work condition variable - - EQStreamType StreamType; // Type of streams this factory creates - - std::queue NewStreams; // Queue of newly created streams - std::mutex MNewStreams; // Mutex for new streams queue - - std::map Streams; // Map of active streams by IP:Port - std::mutex MStreams; // Mutex for streams map -}; \ No newline at end of file diff --git a/old/common/string_util.hpp b/old/common/string_util.cpp similarity index 51% rename from old/common/string_util.hpp rename to old/common/string_util.cpp index 1a13f64..df3790d 100644 --- a/old/common/string_util.hpp +++ b/old/common/string_util.cpp @@ -1,48 +1,70 @@ -// Copyright 2013 Facebook, Inc. Licensed under Apache License 2.0 - -#pragma once +/* + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "string_util.h" #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -// String formatting with variable arguments - creates formatted string from format specifier and arguments +#ifdef _WINDOWS + #include + + #define snprintf _snprintf + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + +#else + #include + #include +#include + +#endif + +#ifndef va_copy + #define va_copy(d,s) ((d) = (s)) +#endif + +// original source: +// https://github.com/facebook/folly/blob/master/folly/String.cpp +// const std::string vStringFormat(const char* format, va_list args) { std::string output; va_list tmpargs; - va_copy(tmpargs, args); + va_copy(tmpargs,args); int characters_used = vsnprintf(nullptr, 0, format, tmpargs); va_end(tmpargs); - // Looks like we have a valid format string + // Looks like we have a valid format string. if (characters_used > 0) { output.resize(characters_used + 1); - va_copy(tmpargs, args); + va_copy(tmpargs,args); characters_used = vsnprintf(&output[0], output.capacity(), format, tmpargs); va_end(tmpargs); output.resize(characters_used); - // Format error handling - return empty string if error occurred + // We shouldn't have a format error by this point, but I can't imagine what error we + // could have by this point. Still, return empty string; if (characters_used < 0) output.clear(); } return output; } -// Convert string to lowercase - returns new lowercase string const std::string str_tolower(std::string s) { std::transform( @@ -52,11 +74,10 @@ const std::string str_tolower(std::string s) return s; } -// Split string by delimiter - returns vector of substrings std::vector split(std::string str_to_split, char delimiter) { - std::stringstream ss(str_to_split); - std::string item; + std::stringstream ss(str_to_split); + std::string item; std::vector exploded_values; while (std::getline(ss, item, delimiter)) { exploded_values.push_back(item); @@ -65,7 +86,6 @@ std::vector split(std::string str_to_split, char delimiter) return exploded_values; } -// Convert string to uppercase - returns new uppercase string const std::string str_toupper(std::string s) { std::transform( @@ -75,7 +95,6 @@ const std::string str_toupper(std::string s) return s; } -// Capitalize first character - returns string with first character uppercase const std::string ucfirst(std::string s) { std::string output = s; @@ -85,7 +104,6 @@ const std::string ucfirst(std::string s) return output; } -// Format string with printf-style arguments - creates formatted string const std::string StringFormat(const char *format, ...) { va_list args; @@ -95,28 +113,25 @@ const std::string StringFormat(const char *format, ...) return output; } -// Split string by delimiter - alternative implementation returning vector of substrings -std::vector SplitString(const std::string &str, char delim) -{ +std::vector SplitString(const std::string &str, char delim) { std::vector ret; std::stringstream ss(str); - std::string item; + std::string item; - while(std::getline(ss, item, delim)) { - ret.push_back(item); - } + while(std::getline(ss, item, delim)) { + ret.push_back(item); + } return ret; } -// Join vector of strings with glue string - concatenates strings with separator std::string implode(std::string glue, std::vector src) { if (src.empty()) { return {}; } - std::ostringstream output; + std::ostringstream output; std::vector::iterator src_iter; for (src_iter = src.begin(); src_iter != src.end(); src_iter++) { @@ -124,14 +139,12 @@ std::string implode(std::string glue, std::vector src) } std::string final_output = output.str(); - final_output.resize(output.str().size() - glue.size()); + final_output.resize (output.str().size () - glue.size()); return final_output; } -// Escape special characters in string - adds backslash escapes for special characters -std::string EscapeString(const std::string &s) -{ +std::string EscapeString(const std::string &s) { std::string ret; size_t sz = s.length(); @@ -168,9 +181,7 @@ std::string EscapeString(const std::string &s) return ret; } -// Escape special characters in C string - adds backslash escapes for special characters -std::string EscapeString(const char *src, size_t sz) -{ +std::string EscapeString(const char *src, size_t sz) { std::string ret; for(size_t i = 0; i < sz; ++i) { @@ -206,9 +217,7 @@ std::string EscapeString(const char *src, size_t sz) return ret; } -// Check if string represents a number - returns true if string can be converted to double -bool StringIsNumber(const std::string &s) -{ +bool StringIsNumber(const std::string &s) { try { auto r = stod(s); return true; @@ -218,21 +227,15 @@ bool StringIsNumber(const std::string &s) } } -// Convert string to lowercase in-place - modifies original string -void ToLowerString(std::string &s) -{ +void ToLowerString(std::string &s) { std::transform(s.begin(), s.end(), s.begin(), ::tolower); } -// Convert string to uppercase in-place - modifies original string -void ToUpperString(std::string &s) -{ +void ToUpperString(std::string &s) { std::transform(s.begin(), s.end(), s.begin(), ::toupper); } -// Join vector of strings with delimiter - concatenates strings with separator -std::string JoinString(const std::vector& ar, const std::string &delim) -{ +std::string JoinString(const std::vector& ar, const std::string &delim) { std::string ret; for (size_t i = 0; i < ar.size(); ++i) { if (i != 0) { @@ -245,7 +248,6 @@ std::string JoinString(const std::vector& ar, const std::string &de return ret; } -// Find and replace all occurrences in string - replaces search string with replacement string void find_replace(std::string &string_subject, const std::string &search_string, const std::string &replace_string) { if (string_subject.find(search_string) == std::string::npos) { @@ -257,9 +259,9 @@ void find_replace(std::string &string_subject, const std::string &search_string, string_subject.replace(start_pos, search_string.length(), replace_string); start_pos += replace_string.length(); } + } -// Parse account string format - extracts account and login server from colon-separated string void ParseAccountString(const std::string &s, std::string &account, std::string &loginserver) { auto split = SplitString(s, ':'); @@ -272,9 +274,11 @@ void ParseAccountString(const std::string &s, std::string &account, std::string } } -// String copy with null termination - copies string ensuring null termination -char* strn0cpy(char* dest, const char* source, std::uint32_t size) -{ +//Const char based + +// normal strncpy doesnt put a null term on copied strings, this one does +// ref: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecrt/htm/_wcecrt_strncpy_wcsncpy.asp +char* strn0cpy(char* dest, const char* source, uint32 size) { if (!dest) return 0; if (size == 0 || source == 0) { @@ -286,9 +290,9 @@ char* strn0cpy(char* dest, const char* source, std::uint32_t size) return dest; } -// String copy with truncation check - copies string and returns whether it was truncated -bool strn0cpyt(char* dest, const char* source, std::uint32_t size) -{ +// String N w/null Copy Truncated? +// return value =true if entire string(source) fit, false if it was truncated +bool strn0cpyt(char* dest, const char* source, uint32 size) { if (!dest) return 0; if (size == 0 || source == 0) { @@ -300,9 +304,7 @@ bool strn0cpyt(char* dest, const char* source, std::uint32_t size) return (bool)(source[strlen(dest)] == 0); } -// Convert string to lowercase using static buffer - returns pointer to lowercase string -const char *MakeLowerString(const char *source) -{ +const char *MakeLowerString(const char *source) { static char str[128]; if (!source) return nullptr; @@ -310,29 +312,26 @@ const char *MakeLowerString(const char *source) return str; } -// Convert string to lowercase into target buffer - writes lowercase string to target -void MakeLowerString(const char *source, char *target) -{ +void MakeLowerString(const char *source, char *target) { if (!source || !target) { *target = 0; return; } - while (*source) { + while (*source) + { *target = tolower(*source); target++; source++; } *target = 0; } -// Allocate and format string of any length - dynamically allocates buffer for formatted string -int MakeAnyLenString(char** ret, const char* format, ...) -{ +int MakeAnyLenString(char** ret, const char* format, ...) { int buf_len = 128; int chars = -1; va_list argptr, tmpargptr; va_start(argptr, format); while (chars == -1 || chars >= buf_len) { - delete[] *ret; + safe_delete_array(*ret); if (chars == -1) buf_len *= 2; else @@ -345,9 +344,7 @@ int MakeAnyLenString(char** ret, const char* format, ...) return chars; } -// Append formatted string to existing buffer - extends buffer and appends formatted text -std::uint32_t AppendAnyLenString(char** ret, std::uint32_t* bufsize, std::uint32_t* strlen, const char* format, ...) -{ +uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...) { if (*bufsize == 0) *bufsize = 256; if (*ret == 0) @@ -356,7 +353,7 @@ std::uint32_t AppendAnyLenString(char** ret, std::uint32_t* bufsize, std::uint32 char* oldret = 0; va_list argptr, tmpargptr; va_start(argptr, format); - while (chars == -1 || chars >= (int32_t)(*bufsize - *strlen)) { + while (chars == -1 || chars >= (int32)(*bufsize - *strlen)) { if (chars == -1) *bufsize += 256; else @@ -366,7 +363,7 @@ std::uint32_t AppendAnyLenString(char** ret, std::uint32_t* bufsize, std::uint32 if (oldret) { if (*strlen) memcpy(*ret, oldret, *strlen); - delete[] oldret; + safe_delete_array(oldret); } va_copy(tmpargptr, argptr); chars = vsnprintf(&(*ret)[*strlen], (*bufsize - *strlen), format, tmpargptr); @@ -376,9 +373,7 @@ std::uint32_t AppendAnyLenString(char** ret, std::uint32_t* bufsize, std::uint32 return *strlen; } -// Convert hexadecimal string to integer - parses hex string to 32-bit unsigned integer -std::uint32_t hextoi(const char* num) -{ +uint32 hextoi(const char* num) { if (num == nullptr) return 0; @@ -389,7 +384,7 @@ std::uint32_t hextoi(const char* num) if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) return 0; - std::uint32_t ret = 0; + uint32 ret = 0; int mul = 1; for (int i = len - 1; i >= 2; i--) { if (num[i] >= 'A' && num[i] <= 'F') @@ -405,9 +400,7 @@ std::uint32_t hextoi(const char* num) return ret; } -// Convert hexadecimal string to 64-bit integer - parses hex string to 64-bit unsigned integer -std::uint64_t hextoi64(const char* num) -{ +uint64 hextoi64(const char* num) { if (num == nullptr) return 0; @@ -418,7 +411,7 @@ std::uint64_t hextoi64(const char* num) if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) return 0; - std::uint64_t ret = 0; + uint64 ret = 0; int mul = 1; for (int i = len - 1; i >= 2; i--) { if (num[i] >= 'A' && num[i] <= 'F') @@ -434,9 +427,8 @@ std::uint64_t hextoi64(const char* num) return ret; } -// Convert string to boolean - parses various boolean representations to bool -bool atobool(const char* iBool) -{ +bool atobool(const char* iBool) { + if (iBool == nullptr) return false; if (!strcasecmp(iBool, "true")) @@ -468,27 +460,30 @@ bool atobool(const char* iBool) return false; } -// Clean mob name for display - removes non-alphabetic characters and converts underscores to spaces +// removes the crap and turns the underscores into spaces. char *CleanMobName(const char *in, char *out) { unsigned i, j; - for (i = j = 0; i < strlen(in); i++) { - // Convert _ to space - other conversions may be added here - if (in[i] == '_') { + for (i = j = 0; i < strlen(in); i++) + { + // convert _ to space.. any other conversions like this? I *think* this + // is the only non alpha char that's not stripped but converted. + if (in[i] == '_') + { out[j++] = ' '; } - else { - // Only keep alphabetic characters and backticks - skip numbers and other characters - if (isalpha(in[i]) || (in[i] == '`')) + else + { + if (isalpha(in[i]) || (in[i] == '`')) // numbers, #, or any other crap just gets skipped out[j++] = in[i]; } } - out[j] = 0; // Terminate the string before returning + out[j] = 0; // terimnate the string before returning it return out; } -// Remove apostrophes from string in-place - replaces apostrophes with underscores + void RemoveApostrophes(std::string &s) { for (unsigned int i = 0; i < s.length(); ++i) @@ -496,7 +491,6 @@ void RemoveApostrophes(std::string &s) s[i] = '_'; } -// Remove apostrophes from C string - creates new string with apostrophes replaced by underscores char *RemoveApostrophes(const char *s) { auto NewString = new char[strlen(s) + 1]; @@ -510,24 +504,21 @@ char *RemoveApostrophes(const char *s) return NewString; } -// Convert integer to string - formats integer as string in provided buffer const char *ConvertArray(int input, char *returnchar) { sprintf(returnchar, "%i", input); return returnchar; } -// Convert float to string - formats float as string with 2 decimal places in provided buffer const char *ConvertArrayF(float input, char *returnchar) { sprintf(returnchar, "%0.2f", input); return returnchar; } -// Check if string contains only alphanumeric characters - returns true if all characters are letters or digits bool isAlphaNumeric(const char *text) { - for (unsigned int charIndex = 0; charIndex < strlen(text); charIndex++) { + for (unsigned int charIndex = 0; charIndex 'z') && (text[charIndex] < 'A' || text[charIndex] > 'Z') && (text[charIndex] < '0' || text[charIndex] > '9')) @@ -536,109 +527,3 @@ bool isAlphaNumeric(const char *text) return true; } - -// Inline utility functions for string trimming - -// Trim whitespace from left side of string - removes leading whitespace characters -inline std::string <rim(std::string &str, const std::string &chars = "\t\n\v\f\r ") -{ - str.erase(0, str.find_first_not_of(chars)); - return str; -} - -// Trim whitespace from right side of string - removes trailing whitespace characters -inline std::string &rtrim(std::string &str, const std::string &chars = "\t\n\v\f\r ") -{ - str.erase(str.find_last_not_of(chars) + 1); - return str; -} - -// Trim whitespace from both sides of string - removes leading and trailing whitespace -inline std::string &trim(std::string &str, const std::string &chars = "\t\n\v\f\r ") -{ - return ltrim(rtrim(str, chars), chars); -} - -// Template function to join vector elements with encapsulation - joins elements with glue and wraps each in encapsulation characters -template -std::string implode(const std::string &glue, const std::pair &encapsulation, const std::vector &src) -{ - if (src.empty()) { - return {}; - } - - std::ostringstream oss; - - for (const T &src_iter : src) { - oss << encapsulation.first << src_iter << encapsulation.second << glue; - } - - std::string output(oss.str()); - output.resize(output.size() - glue.size()); - - return output; -} - -// Template function to join vector of pairs - formats pairs with encapsulation and glue -template -std::vector join_pair(const std::string &glue, const std::pair &encapsulation, const std::vector> &src) -{ - if (src.empty()) { - return {}; - } - - std::vector output; - - for (const std::pair &src_iter : src) { - output.push_back( - std::format( - "{}{}{}{}{}{}{}", - encapsulation.first, - src_iter.first, - encapsulation.second, - glue, - encapsulation.first, - src_iter.second, - encapsulation.second - ) - ); - } - - return output; -} - -// Template function to join vector of tuples - formats 4-tuples with encapsulation and glue -template -std::vector join_tuple(const std::string &glue, const std::pair &encapsulation, const std::vector> &src) -{ - if (src.empty()) { - return {}; - } - - std::vector output; - - for (const std::tuple &src_iter : src) { - output.push_back( - std::format( - "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", - encapsulation.first, - std::get<0>(src_iter), - encapsulation.second, - glue, - encapsulation.first, - std::get<1>(src_iter), - encapsulation.second, - glue, - encapsulation.first, - std::get<2>(src_iter), - encapsulation.second, - glue, - encapsulation.first, - std::get<3>(src_iter), - encapsulation.second - ) - ); - } - - return output; -} \ No newline at end of file diff --git a/old/common/string_util.h b/old/common/string_util.h new file mode 100644 index 0000000..037d6a2 --- /dev/null +++ b/old/common/string_util.h @@ -0,0 +1,193 @@ +/* + * Copyright 2013 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef _STRINGUTIL_H_ +#define _STRINGUTIL_H_ + +#include +#include +#include +#include +#include + +#ifndef _WIN32 +// this doesn't appear to affect linux-based systems..need feedback for _WIN64 +#include +#endif + +#ifdef _WINDOWS +#include +#include +#include +#endif + +#include "types.h" + +//std::string based +const std::string str_tolower(std::string s); +const std::string str_toupper(std::string s); +const std::string ucfirst(std::string s); +std::vector split(std::string str_to_split, char delimiter); +const std::string StringFormat(const char* format, ...); +const std::string vStringFormat(const char* format, va_list args); +std::string implode(std::string glue, std::vector src); + +/** + * @param str + * @param chars + * @return + */ +inline std::string <rim(std::string &str, const std::string &chars = "\t\n\v\f\r ") +{ + str.erase(0, str.find_first_not_of(chars)); + return str; +} + +/** + * @param str + * @param chars + * @return + */ +inline std::string &rtrim(std::string &str, const std::string &chars = "\t\n\v\f\r ") +{ + str.erase(str.find_last_not_of(chars) + 1); + return str; +} + +/** + * @param str + * @param chars + * @return + */ +inline std::string &trim(std::string &str, const std::string &chars = "\t\n\v\f\r ") +{ + return ltrim(rtrim(str, chars), chars); +} + +template +std::string implode(const std::string &glue, const std::pair &encapsulation, const std::vector &src) +{ + if (src.empty()) { + return {}; + } + + std::ostringstream oss; + + for (const T &src_iter : src) { + oss << encapsulation.first << src_iter << encapsulation.second << glue; + } + + std::string output(oss.str()); + output.resize(output.size() - glue.size()); + + return output; +} + +// _WIN32 builds require that #include be included in whatever code file the invocation is made from (no header files) +template +std::vector join_pair(const std::string &glue, const std::pair &encapsulation, const std::vector> &src) +{ + if (src.empty()) { + return {}; + } + + std::vector output; + + for (const std::pair &src_iter : src) { + output.push_back( + + fmt::format( + "{}{}{}{}{}{}{}", + encapsulation.first, + src_iter.first, + encapsulation.second, + glue, + encapsulation.first, + src_iter.second, + encapsulation.second + ) + ); + } + + return output; +} + +// _WIN32 builds require that #include be included in whatever code file the invocation is made from (no header files) +template +std::vector join_tuple(const std::string &glue, const std::pair &encapsulation, const std::vector> &src) +{ + if (src.empty()) { + return {}; + } + + std::vector output; + + for (const std::tuple &src_iter : src) { + + output.push_back( + + fmt::format( + "{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + encapsulation.first, + std::get<0>(src_iter), + encapsulation.second, + glue, + encapsulation.first, + std::get<1>(src_iter), + encapsulation.second, + glue, + encapsulation.first, + std::get<2>(src_iter), + encapsulation.second, + glue, + encapsulation.first, + std::get<3>(src_iter), + encapsulation.second + ) + ); + } + + return output; +} + +std::vector SplitString(const std::string &s, char delim); +std::string EscapeString(const char *src, size_t sz); +std::string EscapeString(const std::string &s); +bool StringIsNumber(const std::string &s); +void ToLowerString(std::string &s); +void ToUpperString(std::string &s); +std::string JoinString(const std::vector& ar, const std::string &delim); +void find_replace(std::string& string_subject, const std::string& search_string, const std::string& replace_string); +void ParseAccountString(const std::string &s, std::string &account, std::string &loginserver); + +//const char based + +bool atobool(const char* iBool); +bool isAlphaNumeric(const char *text); +bool strn0cpyt(char* dest, const char* source, uint32 size); +char *CleanMobName(const char *in, char *out); +char *RemoveApostrophes(const char *s); +char* strn0cpy(char* dest, const char* source, uint32 size); +const char *ConvertArray(int input, char *returnchar); +const char *ConvertArrayF(float input, char *returnchar); +const char *MakeLowerString(const char *source); +int MakeAnyLenString(char** ret, const char* format, ...); +uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...); +uint32 hextoi(const char* num); +uint64 hextoi64(const char* num); +void MakeLowerString(const char *source, char *target); +void RemoveApostrophes(std::string &s); + +#endif diff --git a/old/common/tcp.hpp b/old/common/tcp.hpp deleted file mode 100644 index 75bd9b8..0000000 --- a/old/common/tcp.hpp +++ /dev/null @@ -1,1631 +0,0 @@ -// EQ2Emulator: Everquest II Server Emulator - Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "queue.hpp" -#include "timer.hpp" -#include "types.hpp" -#include "servertalk.hpp" -#include "linked_list.hpp" -#include "misc_functions.hpp" - -class TCPServer; - -#define TCPConnection_ErrorBufferSize 1024 -#define MaxTCPReceiveBufferSize 524288 -#define LOOP_GRANULARITY 3 -#define SERVER_LOOP_GRANULARITY 3 - -#define TCPS_Ready 0 -#define TCPS_Connecting 1 -#define TCPS_Connected 100 -#define TCPS_Disconnecting 200 -#define TCPS_Disconnected 201 -#define TCPS_Closing 250 -#define TCPS_Error 255 - -#ifndef DEF_eConnectionType -#define DEF_eConnectionType -enum eConnectionType { Incomming, Outgoing }; -#endif - -enum eTCPMode { modeConsole, modeTransition, modePacket }; - -// Forward declarations for thread functions -void* TCPServerLoop(void* tmp); -void* TCPConnectionLoop(void* tmp); - -// Primary TCP connection class for handling client connections -class TCPConnection -{ -public: -#pragma pack(1) - struct TCPNetPacket_Struct - { - int32 size; - struct { - int8 - compressed : 1, - destination : 1, - flag3 : 1, - flag4 : 1, - flag5 : 1, - flag6 : 1, - flag7 : 1, - flag8 : 1; - } flags; - int16 opcode; - uchar buffer[0]; - }; -#pragma pack() - - // Creates a network packet structure from ServerPacket - static TCPNetPacket_Struct* MakePacket(ServerPacket* pack, int32 iDestination = 0); - - // Constructor for incoming connections from server socket - TCPConnection(TCPServer* iServer, int iSock, int32 irIP, int16 irPort, bool iOldFormat = false); - // Constructor for outgoing connections - TCPConnection(bool iOldFormat = false, TCPServer* iRelayServer = 0, eTCPMode iMode = modePacket); - // Constructor for relay connections - TCPConnection(TCPServer* iServer, TCPConnection* iRelayLink, int32 iRemoteID, int32 irIP, int16 irPort); - virtual ~TCPConnection(); - - // Establishes connection to remote host by hostname - bool Connect(char* irAddress, int16 irPort, char* errbuf = 0); - // Establishes connection to remote host by IP address - bool Connect(int32 irIP, int16 irPort, char* errbuf = 0); - // Initiates asynchronous connection by hostname - void AsyncConnect(char* irAddress, int16 irPort); - // Initiates asynchronous connection by IP address - void AsyncConnect(int32 irIP, int16 irPort); - // Disconnects the TCP connection - virtual void Disconnect(bool iSendRelayDisconnect = true); - - // Sends a ServerPacket over the connection - virtual bool SendPacket(ServerPacket* pack, int32 iDestination = 0); - // Sends a pre-formatted network packet - virtual bool SendPacket(TCPNetPacket_Struct* tnps); - // Sends raw data over the connection - bool Send(const uchar* data, sint32 size); - - // Retrieves next line from console mode queue - char* PopLine(); - // Retrieves next packet from incoming queue - ServerPacket* PopPacket(); - - // Accessors for connection information - inline int32 GetrIP() { return rIP; } - inline int16 GetrPort() { return rPort; } - virtual int8 GetState(); - eTCPMode GetMode() { return TCPMode; } - inline bool Connected() { return (GetState() == TCPS_Connected); } - inline bool ConnectReady() { return (bool)(GetState() == TCPS_Ready && ConnectionType == Outgoing); } - inline int32 GetID() { return id; } - inline bool IsRelayServer() { return RelayServer; } - inline int32 GetRemoteID() { return RemoteID; } - inline TCPConnection* GetRelayLink() { return RelayLink; } - - // Marks connection as ready for cleanup - void Free(); - // Gets echo mode status for console connections - bool GetEcho(); - // Sets echo mode for console connections - void SetEcho(bool iValue); - -protected: - friend class TCPServer; - friend void* TCPConnectionLoop(void* tmp); - - // Main processing loop for handling network I/O - virtual bool Process(); - // Sets the connection state with thread safety - void SetState(int8 iState); - // Checks if connection object is marked for deletion - inline bool IsFree() { return pFree; } - // Verifies if connection is active for network operations - bool CheckNetActive(); - - int sock; // Socket file descriptor - // Main connection processing loop control - bool RunLoop(); - - std::mutex MLoopRunning; // Guards loop execution state - std::mutex MAsyncConnect; // Guards async connection state - bool GetAsyncConnect(); - bool SetAsyncConnect(bool iValue); - char* charAsyncConnect; // Hostname for async connection - - // Adds packet to outgoing queue - void OutQueuePush(ServerPacket* pack); - // Removes relay connection from this server - void RemoveRelay(TCPConnection* relay, bool iSendRelayDisconnect); - -private: - // Handles special network layer control packets - void ProcessNetworkLayerPacket(ServerPacket* pack); - // Sends error packet to remote connection - void SendNetErrorPacket(const char* reason = 0); - - TCPServer* Server; // Parent server instance - TCPConnection* RelayLink; // Link to relay connection - int32 RemoteID; // Remote connection identifier - sint32 RelayCount; // Number of relay connections - - bool pOldFormat; // Legacy packet format flag - - // Core network I/O functions - bool SendData(char* errbuf = 0); - bool RecvData(char* errbuf = 0); - bool ProcessReceivedData(char* errbuf = 0); - bool ProcessReceivedDataAsPackets(char* errbuf = 0); - bool ProcessReceivedDataAsOldPackets(char* errbuf = 0); - // Clears all internal buffers and queues - void ClearBuffers(); - - bool pAsyncConnect; // Async connection in progress flag - - eConnectionType ConnectionType; // Incoming or outgoing connection - eTCPMode TCPMode; // Current connection mode - bool RelayServer; // Server acting as relay - std::mutex MRunLoop; // Guards main loop execution - bool pRunLoop; // Main loop control flag - - int connection_socket; // Active connection socket - int32 id; // Unique connection identifier - int32 rIP; // Remote IP address - int16 rPort; // Remote port (host byte order) - bool pFree; // Marked for deletion flag - - std::mutex MState; // Guards connection state - int8 pState; // Current connection state - - // Console mode line processing - void LineOutQueuePush(char* line); - MyQueue LineOutQueue; // Outgoing console lines - MyQueue OutQueue; // Outgoing packets - std::mutex MOutQueueLock; // Guards output queues - - Timer* keepalive_timer; // Connection keepalive timer - Timer* timeout_timer; // Connection timeout timer - - // Network receive buffer management - uchar* recvbuf; // Receive buffer - sint32 recvbuf_size; // Total buffer size - sint32 recvbuf_used; // Used buffer space - sint32 recvbuf_echo; // Echo position for console mode - bool pEcho; // Echo mode enabled flag - std::mutex MEcho; // Guards echo state - - // Packet mode queue for state transitions - void InModeQueuePush(TCPNetPacket_Struct* tnps); - MyQueue InModeQueue; // Transition mode packet queue - - // Send buffer management - std::mutex MSendQueue; // Guards send operations - uchar* sendbuf; // Send buffer - sint32 sendbuf_size; // Total send buffer size - sint32 sendbuf_used; // Used send buffer space - - // Send queue manipulation functions - bool ServerSendQueuePop(uchar** data, sint32* size); - void ServerSendQueuePushEnd(const uchar* data, sint32 size); - void ServerSendQueuePushEnd(uchar** data, sint32 size); - void ServerSendQueuePushFront(uchar* data, sint32 size); -}; - -// TCP server class for accepting and managing client connections -class TCPServer -{ -public: - // Creates TCP server instance on specified port - TCPServer(int16 iPort = 0, bool iOldFormat = false); - virtual ~TCPServer(); - - // Opens server socket for listening - bool Open(int16 iPort = 0, char* errbuf = 0); - // Closes server socket - void Close(); - // Checks if server socket is open - bool IsOpen(); - // Gets configured server port - inline int16 GetPort() { return pPort; } - - // Retrieves new connection from queue - TCPConnection* NewQueuePop(); - // Broadcasts packet to all connected clients - void SendPacket(ServerPacket* pack); - // Broadcasts pre-formatted packet to all clients - void SendPacket(TCPConnection::TCPNetPacket_Struct** tnps); - -protected: - friend void* TCPServerLoop(void* tmp); - friend class TCPConnection; - - // Main server processing loop - void Process(); - // Controls main server loop execution - bool RunLoop(); - std::mutex MLoopRunning; // Guards server loop state - - // Generates unique connection ID - inline int32 GetNextID() { return NextID++; } - // Adds new connection to managed list - void AddConnection(TCPConnection* con); - // Finds connection by ID - TCPConnection* GetConnection(int32 iID); - -private: - // Accepts new incoming connections - void ListenNewConnections(); - - int32 NextID; // Next connection ID to assign - bool pOldFormat; // Legacy packet format support - - std::mutex MRunLoop; // Guards main loop control - bool pRunLoop; // Main loop execution flag - - std::mutex MSock; // Guards socket operations - int sock; // Server listening socket - int16 pPort; // Server listening port - - // New connection queue management - std::mutex MNewQueue; // Guards new connection queue - MyQueue NewQueue; // Queue of new connections - - // Broadcast packet queue management - void CheckInQueue(); - std::mutex MInQueue; // Guards broadcast queue - TCPConnection::TCPNetPacket_Struct* InQueuePop(); - MyQueue InQueue; // Broadcast packet queue - - LinkedList* list; // List of active connections -}; - -//============================================================================= -// TCPConnection Implementation -//============================================================================= - -// Creates network packet from ServerPacket with optional destination routing -TCPConnection::TCPNetPacket_Struct* TCPConnection::MakePacket(ServerPacket* pack, int32 iDestination) -{ - sint32 size = sizeof(TCPNetPacket_Struct) + pack->size; - if (pack->compressed) { - size += 4; - } - if (iDestination) { - size += 4; - } - TCPNetPacket_Struct* tnps = (TCPNetPacket_Struct*)new uchar[size]; - tnps->size = size; - tnps->opcode = pack->opcode; - *((int8*)&tnps->flags) = 0; - uchar* buffer = tnps->buffer; - if (pack->compressed) { - tnps->flags.compressed = 1; - *((sint32*)buffer) = pack->InflatedSize; - buffer += 4; - } - if (iDestination) { - tnps->flags.destination = 1; - *((sint32*)buffer) = iDestination; - buffer += 4; - } - memcpy(buffer, pack->pBuffer, pack->size); - return tnps; -} - -// Constructor for outgoing connections with optional relay server support -TCPConnection::TCPConnection(bool iOldFormat, TCPServer* iRelayServer, eTCPMode iMode) -{ - id = 0; - Server = iRelayServer; - if (Server) - RelayServer = true; - else - RelayServer = false; - RelayLink = 0; - RelayCount = 0; - RemoteID = 0; - pOldFormat = iOldFormat; - ConnectionType = Outgoing; - TCPMode = iMode; - pState = TCPS_Ready; - pFree = false; - pEcho = false; - sock = 0; - rIP = 0; - rPort = 0; - keepalive_timer = new Timer(SERVER_TIMEOUT); - timeout_timer = new Timer(SERVER_TIMEOUT * 2); - recvbuf = 0; - sendbuf = 0; - pRunLoop = false; - charAsyncConnect = 0; - pAsyncConnect = false; - connection_socket = 0; - recvbuf_size = 0; - recvbuf_used = 0; - recvbuf_echo = 0; - sendbuf_size = 0; - sendbuf_used = 0; -} - -// Constructor for incoming connections from server socket -TCPConnection::TCPConnection(TCPServer* iServer, int in_socket, int32 irIP, int16 irPort, bool iOldFormat) -{ - Server = iServer; - RelayLink = 0; - RelayServer = false; - RelayCount = 0; - RemoteID = 0; - id = Server->GetNextID(); - ConnectionType = Incomming; - pOldFormat = iOldFormat; - TCPMode = modePacket; - pState = TCPS_Connected; - pFree = false; - pEcho = false; - sock = 0; - connection_socket = in_socket; - rIP = irIP; - rPort = irPort; - keepalive_timer = new Timer(SERVER_TIMEOUT); - timeout_timer = new Timer(SERVER_TIMEOUT * 2); - recvbuf = 0; - sendbuf = 0; - pRunLoop = false; - charAsyncConnect = 0; - pAsyncConnect = false; - recvbuf_size = 0; - recvbuf_used = 0; - recvbuf_echo = 0; - sendbuf_size = 0; - sendbuf_used = 0; -} - -// Constructor for relay connections that forward data through another connection -TCPConnection::TCPConnection(TCPServer* iServer, TCPConnection* iRelayLink, int32 iRemoteID, int32 irIP, int16 irPort) -{ - Server = iServer; - RelayLink = iRelayLink; - RelayServer = true; - id = Server->GetNextID(); - RelayCount = 0; - RemoteID = iRemoteID; - if (!RemoteID) - ThrowError("Error: TCPConnection: RemoteID == 0 on RelayLink constructor"); - pOldFormat = false; - ConnectionType = Incomming; - TCPMode = modePacket; - pState = TCPS_Connected; - pFree = false; - pEcho = false; - sock = 0; - connection_socket = 0; - rIP = irIP; - rPort = irPort; - keepalive_timer = 0; - timeout_timer = 0; - recvbuf = 0; - sendbuf = 0; - pRunLoop = false; - charAsyncConnect = 0; - pAsyncConnect = false; - recvbuf_size = 0; - recvbuf_used = 0; - recvbuf_echo = 0; - sendbuf_size = 0; - sendbuf_used = 0; -} - -// Destructor that cleans up all resources and stops processing threads -TCPConnection::~TCPConnection() -{ - Disconnect(); - ClearBuffers(); - if (ConnectionType == Outgoing) { - std::lock_guard lock1(MRunLoop); - pRunLoop = false; - } - delete keepalive_timer; - delete timeout_timer; - delete[] recvbuf; - delete[] sendbuf; - delete[] charAsyncConnect; -} - -// Thread-safe state setter with mutex protection -void TCPConnection::SetState(int8 in_state) -{ - std::lock_guard lock(MState); - pState = in_state; -} - -// Thread-safe state getter with mutex protection -int8 TCPConnection::GetState() -{ - std::lock_guard lock(MState); - return pState; -} - -// Marks incoming connection as ready for cleanup by server -void TCPConnection::Free() -{ - if (ConnectionType == Outgoing) { - ThrowError("TCPConnection::Free() called on an Outgoing connection"); - } - Disconnect(); - pFree = true; -} - -// Sends ServerPacket over connection with optional destination routing -bool TCPConnection::SendPacket(ServerPacket* pack, int32 iDestination) -{ - std::lock_guard lock(MState); - if (!Connected()) - return false; - eTCPMode tmp = GetMode(); - if (tmp != modePacket && tmp != modeTransition) - return false; - if (RemoteID) - return RelayLink->SendPacket(pack, RemoteID); - else { - TCPNetPacket_Struct* tnps = MakePacket(pack, iDestination); - if (tmp == modeTransition) { - InModeQueuePush(tnps); - } - else { - ServerSendQueuePushEnd((uchar**)&tnps, tnps->size); - } - } - return true; -} - -// Sends pre-formatted network packet structure -bool TCPConnection::SendPacket(TCPNetPacket_Struct* tnps) -{ - std::lock_guard lock(MState); - if (RemoteID) - return false; - if (!Connected()) - return false; - eTCPMode tmp = GetMode(); - if (tmp == modeTransition) { - TCPNetPacket_Struct* tnps2 = (TCPNetPacket_Struct*)new uchar[tnps->size]; - memcpy(tnps2, tnps, tnps->size); - InModeQueuePush(tnps2); - return true; - } - if (GetMode() != modePacket) - return false; - ServerSendQueuePushEnd((const uchar*)tnps, tnps->size); - return true; -} - -// Sends raw data for console mode connections -bool TCPConnection::Send(const uchar* data, sint32 size) -{ - if (!Connected()) - return false; - if (GetMode() != modeConsole) - return false; - if (!size) - return true; - ServerSendQueuePushEnd(data, size); - return true; -} - -// Adds packet to mode transition queue during state changes -void TCPConnection::InModeQueuePush(TCPNetPacket_Struct* tnps) -{ - std::lock_guard lock(MSendQueue); - InModeQueue.push(tnps); -} - -// Appends data to send buffer with automatic resizing -void TCPConnection::ServerSendQueuePushEnd(const uchar* data, sint32 size) -{ - std::lock_guard lock(MSendQueue); - if (sendbuf == 0) { - sendbuf = new uchar[size]; - sendbuf_size = size; - sendbuf_used = 0; - } - else if (size > (sendbuf_size - sendbuf_used)) { - sendbuf_size += size + 1024; - uchar* tmp = new uchar[sendbuf_size]; - memcpy(tmp, sendbuf, sendbuf_used); - delete[] sendbuf; - sendbuf = tmp; - } - memcpy(&sendbuf[sendbuf_used], data, size); - sendbuf_used += size; -} - -// Transfers ownership of data buffer to send queue for efficient packet handling -void TCPConnection::ServerSendQueuePushEnd(uchar** data, sint32 size) -{ - std::lock_guard lock(MSendQueue); - if (sendbuf == 0) { - sendbuf = *data; - sendbuf_size = size; - sendbuf_used = size; - *data = 0; - return; - } - if (size > (sendbuf_size - sendbuf_used)) { - sendbuf_size += size; - uchar* tmp = new uchar[sendbuf_size]; - memcpy(tmp, sendbuf, sendbuf_used); - delete[] sendbuf; - sendbuf = tmp; - } - memcpy(&sendbuf[sendbuf_used], *data, size); - sendbuf_used += size; - delete[] (TCPNetPacket_Struct*)*data; -} - -// Prepends data to front of send buffer for priority transmission -void TCPConnection::ServerSendQueuePushFront(uchar* data, sint32 size) -{ - std::lock_guard lock(MSendQueue); - if (sendbuf == 0) { - sendbuf = new uchar[size]; - sendbuf_size = size; - sendbuf_used = 0; - } - else if (size > (sendbuf_size - sendbuf_used)) { - sendbuf_size += size; - uchar* tmp = new uchar[sendbuf_size]; - memcpy(&tmp[size], sendbuf, sendbuf_used); - delete[] sendbuf; - sendbuf = tmp; - } - memcpy(sendbuf, data, size); - sendbuf_used += size; -} - -// Non-blocking retrieval of send buffer contents -bool TCPConnection::ServerSendQueuePop(uchar** data, sint32* size) -{ - if (!MSendQueue.try_lock()) - return false; - std::lock_guard lock(MSendQueue, std::adopt_lock); - - if (sendbuf) { - *data = sendbuf; - *size = sendbuf_used; - sendbuf = 0; - return true; - } - return false; -} - -// Retrieves next packet from incoming queue for processing -ServerPacket* TCPConnection::PopPacket() -{ - if (!MOutQueueLock.try_lock()) - return 0; - std::lock_guard lock(MOutQueueLock, std::adopt_lock); - return OutQueue.pop(); -} - -// Retrieves next console line from incoming queue -char* TCPConnection::PopLine() -{ - if (!MOutQueueLock.try_lock()) - return 0; - std::lock_guard lock(MOutQueueLock, std::adopt_lock); - return (char*)LineOutQueue.pop(); -} - -// Adds incoming packet to processing queue -void TCPConnection::OutQueuePush(ServerPacket* pack) -{ - std::lock_guard lock(MOutQueueLock); - OutQueue.push(pack); -} - -// Processes console line input with special command handling -void TCPConnection::LineOutQueuePush(char* line) -{ - if (strcmp(line, "**PACKETMODE**") == 0) { - std::lock_guard lock(MSendQueue); - delete[] sendbuf; - sendbuf = 0; - if (TCPMode == modeConsole) - Send((const uchar*)"\0**PACKETMODE**\r", 16); - TCPMode = modePacket; - TCPNetPacket_Struct* tnps = 0; - while ((tnps = InModeQueue.pop())) { - SendPacket(tnps); - delete[] tnps; - } - delete[] line; - return; - } - std::lock_guard lock(MOutQueueLock); - LineOutQueue.push(line); -} - -// Cleanly disconnects TCP connection and cleans up resources -void TCPConnection::Disconnect(bool iSendRelayDisconnect) -{ - if (connection_socket != -1 && connection_socket != 0) { - std::lock_guard lock(MState); - if (pState == TCPS_Connected || pState == TCPS_Disconnecting || pState == TCPS_Disconnected) - SendData(); - pState = TCPS_Closing; - shutdown(connection_socket, SHUT_WR); - shutdown(connection_socket, SHUT_RD); - close(connection_socket); - connection_socket = 0; - rIP = 0; - rPort = 0; - ClearBuffers(); - } - SetState(TCPS_Ready); - if (RelayLink) { - RelayLink->RemoveRelay(this, iSendRelayDisconnect); - RelayLink = 0; - } -} - -// Thread-safe accessor for async connection state -bool TCPConnection::GetAsyncConnect() -{ - std::lock_guard lock(MAsyncConnect); - return pAsyncConnect; -} - -// Thread-safe setter for async connection state -bool TCPConnection::SetAsyncConnect(bool iValue) -{ - std::lock_guard lock(MAsyncConnect); - bool ret = pAsyncConnect; - pAsyncConnect = iValue; - return ret; -} - -// Initiates asynchronous connection to remote host by hostname -void TCPConnection::AsyncConnect(char* irAddress, int16 irPort) -{ - if (ConnectionType != Outgoing) { - ThrowError("TCPConnection::AsyncConnect() call on a Incomming connection object!"); - return; - } - if (GetState() != TCPS_Ready) - return; - std::lock_guard lock(MAsyncConnect); - if (pAsyncConnect) { - return; - } - pAsyncConnect = true; - delete[] charAsyncConnect; - charAsyncConnect = new char[strlen(irAddress) + 1]; - strcpy(charAsyncConnect, irAddress); - rPort = irPort; - - if (!pRunLoop) { - pRunLoop = true; - std::thread thread(TCPConnectionLoop, this); - thread.detach(); - } -} - -// Initiates asynchronous connection to remote host by IP address -void TCPConnection::AsyncConnect(int32 irIP, int16 irPort) -{ - if (ConnectionType != Outgoing) { - ThrowError("TCPConnection::AsyncConnect() call on a Incomming connection object!"); - return; - } - if (GetState() != TCPS_Ready) - return; - std::lock_guard lock(MAsyncConnect); - if (pAsyncConnect) { - return; - } - pAsyncConnect = true; - delete charAsyncConnect; - charAsyncConnect = 0; - rIP = irIP; - rPort = irPort; - - if (!pRunLoop) { - pRunLoop = true; - std::thread thread(TCPConnectionLoop, this); - thread.detach(); - } -} - -// Establishes synchronous connection to remote host by hostname -bool TCPConnection::Connect(char* irAddress, int16 irPort, char* errbuf) -{ - if (errbuf) - errbuf[0] = 0; - int32 tmpIP = ResolveIP(irAddress); - if (!tmpIP) { - if (errbuf) { - snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Couldn't resolve hostname. Error #%i: %s", errno, strerror(errno)); - } - return false; - } - return Connect(tmpIP, irPort, errbuf); -} - -// Establishes synchronous connection to remote host by IP address -bool TCPConnection::Connect(int32 in_ip, int16 in_port, char* errbuf) -{ - if (errbuf) - errbuf[0] = 0; - if (ConnectionType != Outgoing) { - ThrowError("TCPConnection::Connect() call on a Incomming connection object!"); - return false; - } - std::lock_guard lock(MState); - if (pState == TCPS_Ready) { - pState = TCPS_Connecting; - } - else { - SetAsyncConnect(false); - return false; - } - - if (!pRunLoop) { - pRunLoop = true; - std::thread thread(TCPConnectionLoop, this); - thread.detach(); - } - - connection_socket = -1; - struct sockaddr_in server_sin; - - if ((connection_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1 || connection_socket == 0) { - if (errbuf) - snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Allocating socket failed. Error: %s", strerror(errno)); - SetState(TCPS_Ready); - SetAsyncConnect(false); - return false; - } - server_sin.sin_family = AF_INET; - server_sin.sin_addr.s_addr = in_ip; - server_sin.sin_port = htons(in_port); - - if (connect(connection_socket, (struct sockaddr*)&server_sin, sizeof(server_sin)) == -1) { - if (errbuf) - snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): connect() failed. Error: %s", strerror(errno)); - close(connection_socket); - connection_socket = 0; - SetState(TCPS_Ready); - SetAsyncConnect(false); - return false; - } - - int bufsize = 64 * 1024; - setsockopt(connection_socket, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, sizeof(bufsize)); - fcntl(connection_socket, F_SETFL, O_NONBLOCK); - - SetEcho(false); - std::lock_guard sendLock(MSendQueue); - ClearBuffers(); - TCPMode = modePacket; - - rIP = in_ip; - rPort = in_port; - SetState(TCPS_Connected); - SetAsyncConnect(false); - return true; -} - -// Clears all internal buffers and resets timers -void TCPConnection::ClearBuffers() -{ - std::lock_guard lock1(MSendQueue); - std::lock_guard lock2(MOutQueueLock); - std::lock_guard lock3(MRunLoop); - std::lock_guard lock4(MState); - - delete[] recvbuf; - recvbuf = 0; - delete[] sendbuf; - sendbuf = 0; - - ServerPacket* pack = 0; - while ((pack = PopPacket())) - delete pack; - TCPNetPacket_Struct* tnps = 0; - while ((tnps = InModeQueue.pop())) - delete tnps; - char* line = 0; - while ((line = LineOutQueue.pop())) - delete[] line; - - if (keepalive_timer) - keepalive_timer->Start(); - if (timeout_timer) - timeout_timer->Start(); -} - -// Checks if connection is in active network state -bool TCPConnection::CheckNetActive() -{ - std::lock_guard lock(MState); - if (pState == TCPS_Connected || pState == TCPS_Disconnecting) { - return true; - } - return false; -} - -// Main processing function handling network I/O and state management -bool TCPConnection::Process() -{ - char errbuf[TCPConnection_ErrorBufferSize]; - if (!CheckNetActive()) { - if (ConnectionType == Outgoing) { - if (GetAsyncConnect()) { - if (charAsyncConnect) - rIP = ResolveIP(charAsyncConnect); - Connect(rIP, rPort); - } - } - if (GetState() == TCPS_Disconnected) { - Disconnect(); - return false; - } - else if (GetState() == TCPS_Connecting) - return true; - else - return false; - } - if (!SendData(errbuf)) { - struct in_addr in; - in.s_addr = GetrIP(); - std::cout << inet_ntoa(in) << ":" << GetrPort() << ": " << errbuf << std::endl; - return false; - } - if (!Connected()) - return false; - if (!RecvData(errbuf)) { - struct in_addr in; - in.s_addr = GetrIP(); - std::cout << inet_ntoa(in) << ":" << GetrPort() << ": " << errbuf << std::endl; - return false; - } - return true; -} - -// Receives data from socket and processes it based on current mode -bool TCPConnection::RecvData(char* errbuf) -{ - if (errbuf) - errbuf[0] = 0; - if (!Connected()) { - return false; - } - - int status = 0; - if (recvbuf == 0) { - recvbuf = new uchar[5120]; - recvbuf_size = 5120; - recvbuf_used = 0; - recvbuf_echo = 0; - } - else if ((recvbuf_size - recvbuf_used) < 2048) { - uchar* tmpbuf = new uchar[recvbuf_size + 5120]; - memcpy(tmpbuf, recvbuf, recvbuf_used); - recvbuf_size += 5120; - delete[] recvbuf; - recvbuf = tmpbuf; - if (recvbuf_size >= MaxTCPReceiveBufferSize) { - if (errbuf) - snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): recvbuf_size >= MaxTCPReceiveBufferSize"); - return false; - } - } - - status = recv(connection_socket, (char*)&recvbuf[recvbuf_used], (recvbuf_size - recvbuf_used), 0); - if (status >= 1) { - recvbuf_used += status; - if (timeout_timer) - timeout_timer->Start(); - if (!ProcessReceivedData(errbuf)) - return false; - } - else if (status == -1) { - if (!(errno == EWOULDBLOCK)) { - if (errbuf) - snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): Error: %s", strerror(errno)); - return false; - } - } - if ((TCPMode == modePacket || TCPMode == modeTransition) && timeout_timer && timeout_timer->Check()) { - if (errbuf) - snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): Connection timeout"); - return false; - } - - return true; -} - -// Thread-safe accessor for echo mode state -bool TCPConnection::GetEcho() -{ - std::lock_guard lock(MEcho); - return pEcho; -} - -// Thread-safe setter for echo mode state -void TCPConnection::SetEcho(bool iValue) -{ - std::lock_guard lock(MEcho); - pEcho = iValue; -} - -// Processes received data based on current connection mode -bool TCPConnection::ProcessReceivedData(char* errbuf) -{ - if (errbuf) - errbuf[0] = 0; - if (!recvbuf) - return true; - if (TCPMode == modePacket) { - return ProcessReceivedDataAsPackets(errbuf); - } - else { - // Console mode processing with line parsing and echo support - for (int i = 0; i < recvbuf_used; i++) { - if (GetEcho() && i >= recvbuf_echo) { - Send(&recvbuf[i], 1); - recvbuf_echo = i + 1; - } - switch (recvbuf[i]) { - case 0: { // Clear buffer command - if (i == 0) { - recvbuf_used--; - recvbuf_echo--; - memcpy(recvbuf, &recvbuf[1], recvbuf_used); - i = -1; - } else { - if (i == recvbuf_used) { - delete[] recvbuf; - recvbuf = 0; - i = -1; - } - else { - uchar* tmpdel = recvbuf; - recvbuf = new uchar[recvbuf_size]; - memcpy(recvbuf, &tmpdel[i + 1], recvbuf_used - i); - recvbuf_used -= i + 1; - recvbuf_echo -= i + 1; - delete tmpdel; - i = -1; - } - } - break; - } - case 10: - case 13: { // Newline processing - if (i == 0) { - recvbuf_used--; - recvbuf_echo--; - memcpy(recvbuf, &recvbuf[1], recvbuf_used); - i = -1; - } else { - char* line = new char[i + 1]; - memset(line, 0, i + 1); - memcpy(line, recvbuf, i); - uchar* tmpdel = recvbuf; - recvbuf = new uchar[recvbuf_size]; - recvbuf_used -= i + 1; - recvbuf_echo -= i + 1; - memcpy(recvbuf, &tmpdel[i + 1], recvbuf_used); - delete tmpdel; - if (strlen(line) > 0) - LineOutQueuePush(line); - else - delete[] line; - if (TCPMode == modePacket) { - return ProcessReceivedDataAsPackets(errbuf); - } - i = -1; - } - break; - } - case 8: { // Backspace processing - if (i == 0) { - recvbuf_used--; - recvbuf_echo--; - memcpy(recvbuf, &recvbuf[1], recvbuf_used); - i = -1; - } else { - uchar* tmpdel = recvbuf; - recvbuf = new uchar[recvbuf_size]; - memcpy(recvbuf, tmpdel, i - 1); - memcpy(&recvbuf[i - 1], &tmpdel[i + 1], recvbuf_used - i); - recvbuf_used -= 2; - recvbuf_echo -= 2; - delete tmpdel; - i -= 2; - } - break; - } - } - } - if (recvbuf_used < 0) { - delete[] recvbuf; - recvbuf = 0; - } - } - return true; -} - -// Processes received data as structured network packets -bool TCPConnection::ProcessReceivedDataAsPackets(char* errbuf) -{ - if (errbuf) - errbuf[0] = 0; - sint32 base = 0; - sint32 size = 0; - uchar* buffer; - sint32 sizeReq = sizeof(TCPNetPacket_Struct); - ServerPacket* pack = 0; - - while ((recvbuf_used - base) >= size) { - TCPNetPacket_Struct* tnps = (TCPNetPacket_Struct*)&recvbuf[base]; - buffer = tnps->buffer; - size = tnps->size; - - if (size < sizeReq || recvbuf_used < sizeReq || size >= MaxTCPReceiveBufferSize) { - if (errbuf) - snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::ProcessReceivedDataAsPackets(): size provided %i, recvbuf_used %i, checks failed: struct_size < %i || recvbuf_used < sizeReq || size >= MaxTCPReceiveBufferSize", size, recvbuf_used, sizeReq); - return false; - } - if ((recvbuf_used - base) >= size) { - delete pack; - pack = new ServerPacket; - pack->size = size - sizeof(TCPNetPacket_Struct); - pack->opcode = tnps->opcode; - - // Process compression flag - if (tnps->flags.compressed) { - sizeReq += 4; - if (size < sizeReq || recvbuf_used < sizeReq) { - if (errbuf) - snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::ProcessReceivedDataAsPackets(Flags.Compressed): size provided %i, recvbuf_used %i, checks failed: struct_size < %i || recvbuf_used < sizeReq", size, recvbuf_used, sizeReq); - delete pack; - return false; - } - pack->compressed = true; - pack->InflatedSize = *((sint32*)buffer); - pack->size -= 4; - buffer += 4; - } - - // Process destination flag for routing - if (tnps->flags.destination) { - sizeReq += 4; - if (size < sizeReq || recvbuf_used < sizeReq) { - if (errbuf) - snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::ProcessReceivedDataAsPackets(Flags.Destination): size provided %i, recvbuf_used %i, checks failed: struct_size < %i || recvbuf_used < sizeReq", size, recvbuf_used, sizeReq); - delete pack; - return false; - } - pack->destination = *((sint32*)buffer); - pack->size -= 4; - buffer += 4; - } - - // Copy packet data - if (pack->size > 0) { - if (tnps->flags.compressed) { - pack->compressed = false; - if (pack->InflatedSize < MaxTCPReceiveBufferSize) { - pack->pBuffer = new uchar[pack->InflatedSize]; - pack->size = InflatePacket(buffer, pack->size, pack->pBuffer, pack->InflatedSize); - if (!pack->size) { - if (errbuf) - snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::ProcessReceivedDataAsPackets(InflatePacket): size provided %i, recvbuf_used: %i, sizeReq: %i, could not inflate packet", size, recvbuf_used, sizeReq); - delete pack; - return false; - } - } - else { - std::cout << "Invalid inflated packet." << std::endl; - delete pack; - return false; - } - } - else { - pack->pBuffer = new uchar[pack->size]; - memcpy(pack->pBuffer, buffer, pack->size); - } - } - - // Handle different packet types - if (pack->opcode == 0) { - if (pack->size) { - ProcessNetworkLayerPacket(pack); - } - } - else { - if (RelayServer && Server && pack->destination) { - TCPConnection* con = Server->GetConnection(pack->destination); - if (!con) { - // Connection not found for relay - } - else { - con->OutQueuePush(pack); - pack = 0; - } - } - else { - OutQueuePush(pack); - pack = 0; - } - } - base += size; - size = 7; - } - } - delete pack; - - // Clean up processed data from buffer - if (base != 0) { - if (base >= recvbuf_used) { - delete[] recvbuf; - recvbuf = 0; - } - else { - uchar* tmpbuf = new uchar[recvbuf_size - base]; - memcpy(tmpbuf, &recvbuf[base], recvbuf_used - base); - delete[] recvbuf; - recvbuf = tmpbuf; - recvbuf_used -= base; - recvbuf_size -= base; - } - } - return true; -} - -// Processes old format packets for backwards compatibility -bool TCPConnection::ProcessReceivedDataAsOldPackets(char* errbuf) -{ - sint32 base = 0; - sint32 size = 4; - uchar* buffer; - ServerPacket* pack = 0; - - while ((recvbuf_used - base) >= size) { - buffer = &recvbuf[base]; - memcpy(&size, &buffer[2], 2); - if (size >= MaxTCPReceiveBufferSize) { - if (errbuf) - snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::ProcessReceivedDataAsPackets(): size >= MaxTCPReceiveBufferSize"); - return false; - } - if ((recvbuf_used - base) >= size) { - pack = new ServerPacket; - memcpy(&pack->opcode, &buffer[0], 2); - pack->size = size - 4; - - if (pack->size > 0) { - pack->pBuffer = new uchar[pack->size]; - memcpy(pack->pBuffer, &buffer[4], pack->size); - } - if (pack->opcode == 0) { - delete pack; - } - else { - OutQueuePush(pack); - } - base += size; - size = 4; - } - } - - // Clean up processed data - if (base != 0) { - if (base >= recvbuf_used) { - delete[] recvbuf; - recvbuf = 0; - } - else { - uchar* tmpbuf = new uchar[recvbuf_size - base]; - memcpy(tmpbuf, &recvbuf[base], recvbuf_used - base); - delete[] recvbuf; - recvbuf = tmpbuf; - recvbuf_used -= base; - recvbuf_size -= base; - } - } - return true; -} - -// Handles network layer control packets for connection management -void TCPConnection::ProcessNetworkLayerPacket(ServerPacket* pack) -{ - int8 opcode = pack->pBuffer[0]; - - // Disable relay capabilities for security - only allow keepalive - if (opcode > 0) { - Disconnect(); - return; - } - - int8* data = &pack->pBuffer[1]; - switch (opcode) { - case 0: { // Keepalive packet - break; - } - } -} - -// Sends error packet to remote connection for debugging -void TCPConnection::SendNetErrorPacket(const char* reason) -{ - ServerPacket* pack = new ServerPacket(0); - pack->size = 1; - if (reason) - pack->size += strlen(reason) + 1; - pack->pBuffer = new uchar[pack->size]; - memset(pack->pBuffer, 0, pack->size); - pack->pBuffer[0] = 255; - strcpy((char*)&pack->pBuffer[1], reason); - SendPacket(pack); - delete pack; -} - -// Removes relay connection and optionally notifies remote endpoint -void TCPConnection::RemoveRelay(TCPConnection* relay, bool iSendRelayDisconnect) -{ - if (iSendRelayDisconnect) { - ServerPacket* pack = new ServerPacket(0, 5); - pack->pBuffer[0] = 3; - *((int32*)&pack->pBuffer[1]) = relay->GetRemoteID(); - SendPacket(pack); - delete pack; - } - RelayCount--; -} - -// Sends queued data over socket with congestion handling -bool TCPConnection::SendData(char* errbuf) -{ - if (errbuf) - errbuf[0] = 0; - - uchar* data = 0; - sint32 size = 0; - int status = 0; - - if (ServerSendQueuePop(&data, &size)) { - status = send(connection_socket, data, size, MSG_NOSIGNAL); - if (errno == EPIPE) status = -1; - - if (status >= 1) { - if (keepalive_timer) - keepalive_timer->Start(); - if (status < (signed)size) { - // Network congestion - push remaining data back to queue - ServerSendQueuePushFront(&data[status], size - status); - } - else if (status > (signed)size) { - ThrowError("TCPConnection::SendData(): WTF! status > size"); - return false; - } - } - else { - ServerSendQueuePushFront(data, size); - } - - delete[] data; - if (status == -1) { - if (errno != EWOULDBLOCK) { - if (errbuf) { - snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::SendData(): send(): Errorcode: %s", strerror(errno)); - } - return false; - } - } - } - - // Send keepalive packet if timer expired - if (TCPMode == modePacket && keepalive_timer && keepalive_timer->Check()) { - ServerPacket* pack = new ServerPacket(0, 0); - SendPacket(pack); - delete pack; - } - return true; -} - -// Main connection processing loop for outgoing connections -void* TCPConnectionLoop(void* tmp) -{ - if (tmp == 0) { - ThrowError("TCPConnectionLoop(): tmp = 0!"); - return nullptr; - } - TCPConnection* tcpc = (TCPConnection*)tmp; - std::lock_guard lock(tcpc->MLoopRunning); - - while (tcpc->RunLoop()) { - std::this_thread::sleep_for(std::chrono::milliseconds(LOOP_GRANULARITY)); - if (tcpc->GetState() != TCPS_Ready) { - if (!tcpc->Process()) { - tcpc->Disconnect(); - } - } - else if (tcpc->GetAsyncConnect()) { - if (tcpc->charAsyncConnect) - tcpc->Connect(tcpc->charAsyncConnect, tcpc->GetrPort()); - else - tcpc->Connect(tcpc->GetrIP(), tcpc->GetrPort()); - tcpc->SetAsyncConnect(false); - } - else - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - return nullptr; -} - -// Thread-safe accessor for main loop control -bool TCPConnection::RunLoop() -{ - std::lock_guard lock(MRunLoop); - return pRunLoop; -} - -//============================================================================= -// TCPServer Implementation -//============================================================================= - -// Constructor that initializes server and starts listening thread -TCPServer::TCPServer(int16 in_port, bool iOldFormat) -{ - NextID = 1; - pPort = in_port; - sock = 0; - pOldFormat = iOldFormat; - list = new LinkedList; - pRunLoop = true; - std::thread thread(TCPServerLoop, this); - thread.detach(); -} - -// Destructor that stops server loop and cleans up connections -TCPServer::~TCPServer() -{ - std::lock_guard lock1(MRunLoop); - pRunLoop = false; - - while (NewQueue.pop()); // Clear queue without deleting objects - delete list; -} - -// Thread-safe accessor for server loop control -bool TCPServer::RunLoop() -{ - std::lock_guard lock(MRunLoop); - return pRunLoop; -} - -// Main server processing loop that handles connections and I/O -void* TCPServerLoop(void* tmp) -{ - if (tmp == 0) { - ThrowError("TCPServerLoop(): tmp = 0!"); - return nullptr; - } - TCPServer* tcps = (TCPServer*)tmp; - std::lock_guard lock(tcps->MLoopRunning); - - while (tcps->RunLoop()) { - std::this_thread::sleep_for(std::chrono::milliseconds(SERVER_LOOP_GRANULARITY)); - tcps->Process(); - } - return nullptr; -} - -// Processes server operations including new connections and client I/O -void TCPServer::Process() -{ - CheckInQueue(); - ListenNewConnections(); - LinkedListIterator iterator(*list); - - iterator.Reset(); - while (iterator.MoreElements()) { - if (iterator.GetData()->IsFree() && (!iterator.GetData()->CheckNetActive())) { - iterator.RemoveCurrent(); - } - else { - if (!iterator.GetData()->Process()) - iterator.GetData()->Disconnect(); - iterator.Advance(); - } - } -} - -// Accepts new incoming connections and creates TCPConnection objects -void TCPServer::ListenNewConnections() -{ - int tmpsock; - struct sockaddr_in from; - struct in_addr in; - unsigned int fromlen; - TCPConnection* con; - - from.sin_family = AF_INET; - fromlen = sizeof(from); - std::lock_guard lock(MSock); - if (!sock) - return; - - // Accept pending connections - while ((tmpsock = accept(sock, (struct sockaddr*)&from, &fromlen)) != -1) { - fcntl(tmpsock, F_SETFL, O_NONBLOCK); - int bufsize = 64 * 1024; - setsockopt(tmpsock, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, sizeof(bufsize)); - in.s_addr = from.sin_addr.s_addr; - - con = new TCPConnection(this, tmpsock, in.s_addr, ntohs(from.sin_port), pOldFormat); - AddConnection(con); - } -} - -// Opens server socket for listening on specified port -bool TCPServer::Open(int16 in_port, char* errbuf) -{ - if (errbuf) - errbuf[0] = 0; - std::lock_guard lock(MSock); - if (sock != 0) { - if (errbuf) - snprintf(errbuf, TCPConnection_ErrorBufferSize, "Listening socket already open"); - return false; - } - if (in_port != 0) { - pPort = in_port; - } - - struct sockaddr_in address; - int reuse_addr = 1; - - memset((char*)&address, 0, sizeof(address)); - address.sin_family = AF_INET; - address.sin_port = htons(pPort); - address.sin_addr.s_addr = htonl(INADDR_ANY); - - sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock == -1) { - if (errbuf) - snprintf(errbuf, TCPConnection_ErrorBufferSize, "socket(): INVALID_SOCKET"); - return false; - } - - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse_addr, sizeof(reuse_addr)); - - if (::bind(sock, (struct sockaddr*)&address, sizeof(address)) < 0) { - close(sock); - sock = 0; - if (errbuf) - sprintf(errbuf, "bind(): <0"); - return false; - } - - int bufsize = 64 * 1024; - setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, sizeof(bufsize)); - fcntl(sock, F_SETFL, O_NONBLOCK); - - if (listen(sock, SOMAXCONN) == -1) { - close(sock); - if (errbuf) - snprintf(errbuf, TCPConnection_ErrorBufferSize, "listen() failed, Error: %s", strerror(errno)); - sock = 0; - return false; - } - - return true; -} - -// Closes server listening socket -void TCPServer::Close() -{ - std::lock_guard lock(MSock); - if (sock) { - close(sock); - } - sock = 0; -} - -// Checks if server socket is currently open -bool TCPServer::IsOpen() -{ - std::lock_guard lock(MSock); - return (bool)(sock != 0); -} - -// Retrieves new connection from queue for processing -TCPConnection* TCPServer::NewQueuePop() -{ - std::lock_guard lock(MNewQueue); - return NewQueue.pop(); -} - -// Adds new connection to server's managed connection list -void TCPServer::AddConnection(TCPConnection* con) -{ - list->Append(con); - std::lock_guard lock(MNewQueue); - NewQueue.push(con); -} - -// Finds connection by unique identifier -TCPConnection* TCPServer::GetConnection(int32 iID) -{ - LinkedListIterator iterator(*list); - - iterator.Reset(); - while (iterator.MoreElements()) { - if (iterator.GetData()->GetID() == iID) - return iterator.GetData(); - iterator.Advance(); - } - return 0; -} - -// Broadcasts ServerPacket to all connected clients -void TCPServer::SendPacket(ServerPacket* pack) -{ - TCPConnection::TCPNetPacket_Struct* tnps = TCPConnection::MakePacket(pack); - SendPacket(&tnps); -} - -// Broadcasts pre-formatted packet to all connected clients -void TCPServer::SendPacket(TCPConnection::TCPNetPacket_Struct** tnps) -{ - std::lock_guard lock(MInQueue); - InQueue.push(*tnps); - *tnps = 0; -} - -// Processes broadcast packet queue and sends to all clients -void TCPServer::CheckInQueue() -{ - LinkedListIterator iterator(*list); - TCPConnection::TCPNetPacket_Struct* tnps = 0; - - while ((tnps = InQueuePop())) { - iterator.Reset(); - while (iterator.MoreElements()) { - if (iterator.GetData()->GetMode() != modeConsole && iterator.GetData()->GetRemoteID() == 0) - iterator.GetData()->SendPacket(tnps); - iterator.Advance(); - } - delete tnps; - } -} - -// Retrieves packet from broadcast queue -TCPConnection::TCPNetPacket_Struct* TCPServer::InQueuePop() -{ - std::lock_guard lock(MInQueue); - return InQueue.pop(); -} \ No newline at end of file diff --git a/old/common/timer.cpp b/old/common/timer.cpp new file mode 100644 index 0000000..e9c08ef --- /dev/null +++ b/old/common/timer.cpp @@ -0,0 +1,207 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include "../common/debug.h" +// Disgrace: for windows compile +#ifndef WIN32 + #include +#else + #include +#endif + +#include +using namespace std; + +#include "timer.h" + +int32 started_unix_timestamp = 0; +int32 current_time = 0; +int32 last_time = 0; + +Timer::Timer(){ + timer_time = 30000; //default to 30 seconds + start_time = current_time; + set_at_trigger = timer_time; + pUseAcurateTiming = false; + enabled = false; +} +Timer::Timer(int32 in_timer_time, bool iUseAcurateTiming) { + timer_time = in_timer_time; + start_time = current_time; + set_at_trigger = timer_time; + pUseAcurateTiming = iUseAcurateTiming; + if (timer_time == 0) { + enabled = false; + } + else { + enabled = true; + } +} + +Timer::Timer(int32 start, int32 timer, bool iUseAcurateTiming = false) { + timer_time = timer; + start_time = start; + set_at_trigger = timer_time; + pUseAcurateTiming = iUseAcurateTiming; + if (timer_time == 0) { + enabled = false; + } + else { + enabled = true; + } +} + +/* Reimplemented for MSVC - Bounce */ +#ifdef WIN32 +int gettimeofday (timeval *tp, ...) +{ + timeb tb; + + ftime (&tb); + + tp->tv_sec = tb.time; + tp->tv_usec = tb.millitm * 1000; + + return 0; +} +#endif + +/* This function checks if the timer triggered */ +bool Timer::Check(bool iReset) +{ + if (enabled && current_time-start_time > timer_time) { + if (iReset) { + if (pUseAcurateTiming) + start_time += timer_time; + else + start_time = current_time; // Reset timer + timer_time = set_at_trigger; + } + return true; + } + + return false; +} + +/* This function disables the timer */ +void Timer::Disable() { + enabled = false; +} + +void Timer::Enable() { + enabled = true; +} + +/* This function set the timer and restart it */ +void Timer::Start(int32 set_timer_time, bool ChangeResetTimer) { + start_time = current_time; + enabled = true; + if (set_timer_time != 0) + { + timer_time = set_timer_time; + if (ChangeResetTimer) + set_at_trigger = set_timer_time; + } +} + +/* This timer updates the timer without restarting it */ +void Timer::SetTimer(int32 set_timer_time) { + /* If we were disabled before => restart the timer */ + if (!enabled) { + start_time = current_time; + enabled = true; + } + if (set_timer_time != 0) { + timer_time = set_timer_time; + set_at_trigger = set_timer_time; + } +} + +int32 Timer::GetElapsedTime(){ + if (enabled) { + return current_time - start_time; + } + else { + return 0xFFFFFFFF; + } +} + +int32 Timer::GetRemainingTime() { + if (enabled) { + if (current_time-start_time > timer_time) + return 0; + else + return (start_time + timer_time) - current_time; + } + else { + return 0xFFFFFFFF; + } +} + +void Timer::SetAtTrigger(int32 in_set_at_trigger, bool iEnableIfDisabled) { + set_at_trigger = in_set_at_trigger; + if (!Enabled() && iEnableIfDisabled) { + Enable(); + } +} + +void Timer::Trigger() +{ + enabled = true; + + timer_time = set_at_trigger; + start_time = current_time-timer_time-1; +} + +const int32& Timer::GetCurrentTime2() +{ + return current_time; +} + +const int32& Timer::SetCurrentTime() +{ + struct timeval read_time; + int32 this_time; + + gettimeofday(&read_time,0); + if(started_unix_timestamp == 0) + started_unix_timestamp = read_time.tv_sec; + + this_time = (read_time.tv_sec - started_unix_timestamp) * 1000 + read_time.tv_usec / 1000; + + if (last_time == 0) + { + current_time = 0; + } + else + { + current_time += this_time - last_time; + } + + last_time = this_time; + +// cerr << "Current time:" << current_time << endl; + return current_time; +} + +int32 Timer::GetUnixTimeStamp(){ + struct timeval read_time; + gettimeofday(&read_time,0); + return read_time.tv_sec; +} diff --git a/old/common/timer.h b/old/common/timer.h new file mode 100644 index 0000000..a70a2d2 --- /dev/null +++ b/old/common/timer.h @@ -0,0 +1,88 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef TIMER_H +#define TIMER_H + +#include "types.h" +#include + +// Disgrace: for windows compile +#ifdef WIN32 + #include + #include + int gettimeofday (timeval *tp, ...); +#endif + +class Timer +{ +public: + Timer(); + Timer(int32 timer_time, bool iUseAcurateTiming = false); + Timer(int32 start, int32 timer, bool iUseAcurateTiming); + ~Timer() { } + + bool Check(bool iReset = true); + void Enable(); + void Disable(); + void Start(int32 set_timer_time=0, bool ChangeResetTimer = true); + void SetTimer(int32 set_timer_time=0); + int32 GetRemainingTime(); + int32 GetElapsedTime(); + inline const int32& GetTimerTime() { return timer_time; } + inline const int32& GetSetAtTrigger() { return set_at_trigger; } + void Trigger(); + void SetAtTrigger(int32 set_at_trigger, bool iEnableIfDisabled = false); + + inline bool Enabled() { return enabled; } + inline int32 GetStartTime() { return(start_time); } + inline int32 GetDuration() { return(timer_time); } + + static const int32& SetCurrentTime(); + static const int32& GetCurrentTime2(); + static int32 GetUnixTimeStamp(); + +private: + int32 start_time; + int32 timer_time; + bool enabled; + int32 set_at_trigger; + + // Tells the timer to be more acurate about happening every X ms. + // Instead of Check() setting the start_time = now, + // it it sets it to start_time += timer_time + bool pUseAcurateTiming; + +// static int32 current_time; +// static int32 last_time; +}; + +struct BenchTimer +{ + typedef std::chrono::high_resolution_clock clock; + + BenchTimer() : start_time(clock::now()) {} + void reset() { start_time = clock::now(); } + // this is seconds + double elapsed() { return std::chrono::duration(clock::now() - start_time).count(); } +private: + std::chrono::time_point start_time; +}; + +#endif diff --git a/old/common/timer.hpp b/old/common/timer.hpp deleted file mode 100644 index a0fff56..0000000 --- a/old/common/timer.hpp +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3+ - http://www.eq2emulator.net - -#pragma once - -#include - -#include "types.hpp" - -class Timer -{ -public: - // Default constructor - creates a 30-second timer that is disabled by default - Timer() - { - timer_time = 30000; - start_time = GetCurrentTime(); - set_at_trigger = timer_time; - use_accurate_timing = false; - enabled = false; - } - - // Constructor with timer duration and optional accurate timing - Timer(int32 timer_time, bool use_accurate_timing = false) - { - this->timer_time = timer_time; - start_time = GetCurrentTime(); - set_at_trigger = timer_time; - this->use_accurate_timing = use_accurate_timing; - enabled = (timer_time != 0); - } - - // Constructor with custom start time, timer duration, and optional accurate timing - Timer(int32 start, int32 timer, bool use_accurate_timing = false) - { - timer_time = timer; - start_time = start; - set_at_trigger = timer_time; - this->use_accurate_timing = use_accurate_timing; - enabled = (timer_time != 0); - } - - ~Timer() { } - - // Checks if the timer has triggered and optionally resets it - bool Check(bool reset = true) - { - if (enabled && GetCurrentTime() - start_time > timer_time) { - if (reset) { - if (use_accurate_timing) - start_time += timer_time; - else - start_time = GetCurrentTime(); - timer_time = set_at_trigger; - } - return true; - } - return false; - } - - // Enables the timer - void Enable() - { - enabled = true; - } - - // Disables the timer - void Disable() - { - enabled = false; - } - - // Starts the timer with optional new duration and reset behavior - void Start(int32 set_timer_time = 0, bool change_reset_timer = true) - { - start_time = GetCurrentTime(); - enabled = true; - if (set_timer_time != 0) { - timer_time = set_timer_time; - if (change_reset_timer) - set_at_trigger = set_timer_time; - } - } - - // Sets the timer duration without restarting it - void SetTimer(int32 set_timer_time = 0) - { - if (!enabled) { - start_time = GetCurrentTime(); - enabled = true; - } - if (set_timer_time != 0) { - timer_time = set_timer_time; - set_at_trigger = set_timer_time; - } - } - - // Returns the elapsed time since timer started, or 0xFFFFFFFF if disabled - int32 GetElapsedTime() - { - if (enabled) { - return GetCurrentTime() - start_time; - } - return 0xFFFFFFFF; - } - - // Returns the remaining time until timer triggers, or 0xFFFFFFFF if disabled - int32 GetRemainingTime() - { - if (enabled) { - if (GetCurrentTime() - start_time > timer_time) - return 0; - else - return (start_time + timer_time) - GetCurrentTime(); - } - return 0xFFFFFFFF; - } - - // Sets the trigger value and optionally enables the timer - void SetAtTrigger(int32 trigger_value, bool enable_if_disabled = false) - { - set_at_trigger = trigger_value; - if (!Enabled() && enable_if_disabled) { - Enable(); - } - } - - // Forces the timer to trigger immediately - void Trigger() - { - enabled = true; - timer_time = set_at_trigger; - start_time = GetCurrentTime() - timer_time - 1; - } - - // Returns the current timer duration - inline const int32& GetTimerTime() { return timer_time; } - - // Returns the trigger value - inline const int32& GetSetAtTrigger() { return set_at_trigger; } - - // Returns whether the timer is enabled - inline bool Enabled() { return enabled; } - - // Returns the start time of the timer - inline int32 GetStartTime() { return start_time; } - - // Returns the duration of the timer - inline int32 GetDuration() { return timer_time; } - - // Returns the current time in milliseconds since program start - static int32 GetCurrentTime() - { - static auto program_start = std::chrono::steady_clock::now(); - auto now = std::chrono::steady_clock::now(); - auto duration = std::chrono::duration_cast(now - program_start); - return static_cast(duration.count()); - } - - // Legacy method name for current time - static const int32 GetCurrentTime2() - { - static int32 current = GetCurrentTime(); - current = GetCurrentTime(); - return current; - } - - // Legacy method name for setting current time - static const int32 SetCurrentTime() - { - return GetCurrentTime(); - } - - // Returns the current Unix timestamp in seconds - static int32 GetUnixTimeStamp() - { - auto now = std::chrono::system_clock::now(); - auto time_t = std::chrono::system_clock::to_time_t(now); - return static_cast(time_t); - } - -private: - int32 start_time; // Time when timer was started - int32 timer_time; // Duration of the timer in milliseconds - bool enabled; // Whether the timer is active - int32 set_at_trigger; // Value to reset timer_time to when triggered - - // Uses accurate timing - adds timer_time to start_time instead of setting to current time - bool use_accurate_timing; -}; - -// High-resolution timer for benchmarking purposes -struct BenchTimer -{ - typedef std::chrono::high_resolution_clock clock; - - // Constructor - starts timing immediately - BenchTimer() : start_time(clock::now()) {} - - // Resets the timer to current time - void reset() { start_time = clock::now(); } - - // Returns elapsed time in seconds as a double - double elapsed() { return std::chrono::duration(clock::now() - start_time).count(); } - -private: - std::chrono::time_point start_time; // Timer start point -}; \ No newline at end of file diff --git a/old/common/types.h b/old/common/types.h new file mode 100644 index 0000000..c65ccf1 --- /dev/null +++ b/old/common/types.h @@ -0,0 +1,191 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef TYPES_H +#define TYPES_H + +#include + +using namespace std; + +//atoi is not int32 or uint32 safe!!!! +#define atoul(str) strtoul(str, NULL, 10) +#ifdef WIN32 + #define atoi64(str) _atoi64(str) +#else + #define atoi64(str) strtoll(str, NULL, 10) +#endif +typedef unsigned char int8; +typedef unsigned short int16; +typedef unsigned int int32; + +typedef unsigned char uint8; +typedef signed char sint8; +typedef unsigned short uint16; +typedef signed short sint16; +typedef unsigned int uint32; +typedef signed int sint32; + +#ifdef WIN32 + #if defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 64 + typedef unsigned __int64 int64; + typedef unsigned __int64 uint64; + typedef signed __int64 sint64; + #else + #error __int64 not supported + #endif +#else +typedef unsigned long long int64; +typedef unsigned long long uint64; +typedef signed long long sint64; +//typedef __u64 int64; +//typedef __u64 uint64; +//typedef __s64 sint64; +#endif + +typedef unsigned long ulong; +typedef unsigned short ushort; +typedef unsigned char uchar; + +#ifdef WIN32 + #define snprintf _snprintf + #define vsnprintf _vsnprintf + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + typedef void ThreadReturnType; +// #define THREAD_RETURN(x) return; + #define THREAD_RETURN(x) _endthread(); return; +#else + typedef void* ThreadReturnType; + typedef int SOCKET; + #define THREAD_RETURN(x) return(x); +#endif + +#define safe_delete(d) if(d) { delete d; d=0; } +#define safe_delete_array(d) if(d) { delete[] d; d=0; } +#define L32(i) ((int32) i) +#define H32(i) ((int32) (i >> 32)) +#define L16(i) ((int16) i) + +#ifndef WIN32 +// More WIN32 compatability + typedef unsigned long DWORD; + typedef unsigned char BYTE; + typedef char CHAR; + typedef unsigned short WORD; + typedef float FLOAT; + typedef FLOAT *PFLOAT; + typedef BYTE *PBYTE,*LPBYTE; + typedef int *PINT,*LPINT; + typedef WORD *PWORD,*LPWORD; + typedef long *LPLONG, LONG; + typedef DWORD *PDWORD,*LPDWORD; + typedef int INT; + typedef unsigned int UINT,*PUINT,*LPUINT; +#endif + + +#ifdef WIN32 +#define DLLFUNC extern "C" __declspec(dllexport) +#else +#define DLLFUNC extern "C" +#endif + + +#pragma pack(1) +struct uint16_breakdown { + union { + uint16 all; + struct { + uint8 b1; + uint8 b2; + } bytes; + }; + inline uint16& operator=(const uint16& val) { return (all=val); } + inline uint16* operator&() { return &all; } + inline operator uint16&() { return all; } + inline uint8& b1() { return bytes.b1; } + inline uint8& b2() { return bytes.b2; } +}; + +struct uint32_breakdown { + union { + uint32 all; + struct { + uint16 w1; + uint16 w2; + } words; + struct { + uint8 b1; + union { + struct { + uint8 b2; + uint8 b3; + } middle; + uint16 w2_3; // word bytes 2 to 3 + }; + uint8 b4; + } bytes; + }; + inline uint32& operator=(const uint32& val) { return (all=val); } + inline uint32* operator&() { return &all; } + inline operator uint32&() { return all; } + + inline uint16& w1() { return words.w1; } + inline uint16& w2() { return words.w2; } + inline uint16& w2_3() { return bytes.w2_3; } + inline uint8& b1() { return bytes.b1; } + inline uint8& b2() { return bytes.middle.b2; } + inline uint8& b3() { return bytes.middle.b3; } + inline uint8& b4() { return bytes.b4; } +}; + +struct EQ2_32BitString{ + int32 size; + string data; +}; +struct EQ2_16BitString{ + int16 size; + string data; +}; +struct EQ2_8BitString{ + int8 size; + string data; +}; + +struct EQ2_Color{ + int8 red; + int8 green; + int8 blue; +}; + +struct WorldTime{ + int16 year; + int month; + int day; + int hour; + int minute; +}; + +typedef enum QUERY_TYPE{ Q_SELECT, Q_UPDATE, Q_REPLACE, Q_INSERT, Q_DELETE, Q_DBMS} QUERY_TYPE; + +#pragma pack() + + +#endif diff --git a/old/common/types.hpp b/old/common/types.hpp deleted file mode 100644 index fe7922d..0000000 --- a/old/common/types.hpp +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3+ License - -#pragma once - -#include -#include - -using namespace std; - -// Safe string to unsigned long conversion -#define atoul(str) strtoul(str, NULL, 10) -// Safe string to 64-bit integer conversion -#define atoi64(str) strtoll(str, NULL, 10) - -// Legacy type definitions for backward compatibility -typedef unsigned char int8; -typedef unsigned short int16; -typedef unsigned int int32; -typedef unsigned char uint8; -typedef signed char sint8; -typedef unsigned short uint16; -typedef signed short sint16; -typedef unsigned int uint32; -typedef signed int sint32; -typedef unsigned long long int64; -typedef unsigned long long uint64; -typedef signed long long sint64; - -typedef unsigned long ulong; -typedef unsigned short ushort; -typedef unsigned char uchar; - -// Thread return type for Linux systems -typedef void* ThreadReturnType; -typedef int SOCKET; -#define THREAD_RETURN(x) return(x); - -// Safe deletion macros -#define safe_delete(d) if(d) { delete d; d=0; } -#define safe_delete_array(d) if(d) { delete[] d; d=0; } - -// Bit manipulation macros -#define L32(i) ((int32) i) -#define H32(i) ((int32) (i >> 32)) -#define L16(i) ((int16) i) - -// Windows compatibility types for Linux -typedef unsigned long DWORD; -typedef unsigned char BYTE; -typedef char CHAR; -typedef unsigned short WORD; -typedef float FLOAT; -typedef FLOAT *PFLOAT; -typedef BYTE *PBYTE,*LPBYTE; -typedef int *PINT,*LPINT; -typedef WORD *PWORD,*LPWORD; -typedef long *LPLONG, LONG; -typedef DWORD *PDWORD,*LPDWORD; -typedef int INT; -typedef unsigned int UINT,*PUINT,*LPUINT; - -// DLL function export macro for Linux -#define DLLFUNC extern "C" - -#pragma pack(1) - -/** - * Provides byte-level access to a 16-bit unsigned integer - * Allows manipulation of individual bytes within the value - */ -struct uint16_breakdown -{ - union { - uint16 all; - struct { - uint8 b1; - uint8 b2; - } bytes; - }; - - // Assignment operator - sets the entire 16-bit value - inline uint16& operator=(const uint16& val) { return (all=val); } - // Address operator - returns pointer to the 16-bit value - inline uint16* operator&() { return &all; } - // Conversion operator - allows implicit conversion to uint16 - inline operator uint16&() { return all; } - // Access to first byte of the 16-bit value - inline uint8& b1() { return bytes.b1; } - // Access to second byte of the 16-bit value - inline uint8& b2() { return bytes.b2; } -}; - -/** - * Provides word and byte-level access to a 32-bit unsigned integer - * Allows manipulation of individual words and bytes within the value - */ -struct uint32_breakdown -{ - union { - uint32 all; - struct { - uint16 w1; - uint16 w2; - } words; - struct { - uint8 b1; - union { - struct { - uint8 b2; - uint8 b3; - } middle; - uint16 w2_3; // word bytes 2 to 3 - }; - uint8 b4; - } bytes; - }; - - // Assignment operator - sets the entire 32-bit value - inline uint32& operator=(const uint32& val) { return (all=val); } - // Address operator - returns pointer to the 32-bit value - inline uint32* operator&() { return &all; } - // Conversion operator - allows implicit conversion to uint32 - inline operator uint32&() { return all; } - - // Access to first word (bits 0-15) of the 32-bit value - inline uint16& w1() { return words.w1; } - // Access to second word (bits 16-31) of the 32-bit value - inline uint16& w2() { return words.w2; } - // Access to combined bytes 2-3 as a word - inline uint16& w2_3() { return bytes.w2_3; } - // Access to first byte of the 32-bit value - inline uint8& b1() { return bytes.b1; } - // Access to second byte of the 32-bit value - inline uint8& b2() { return bytes.middle.b2; } - // Access to third byte of the 32-bit value - inline uint8& b3() { return bytes.middle.b3; } - // Access to fourth byte of the 32-bit value - inline uint8& b4() { return bytes.b4; } -}; - -/** - * String structure with 32-bit size prefix - * Used for network protocol serialization - */ -struct EQ2_32BitString -{ - int32 size; // Length of the string data - string data; // The actual string content -}; - -/** - * String structure with 16-bit size prefix - * Used for network protocol serialization with smaller strings - */ -struct EQ2_16BitString -{ - int16 size; // Length of the string data - string data; // The actual string content -}; - -/** - * String structure with 8-bit size prefix - * Used for network protocol serialization with very small strings - */ -struct EQ2_8BitString -{ - int8 size; // Length of the string data - string data; // The actual string content -}; - -/** - * RGB color representation - * Each component ranges from 0-255 - */ -struct EQ2_Color -{ - int8 red; // Red component (0-255) - int8 green; // Green component (0-255) - int8 blue; // Blue component (0-255) -}; - -/** - * Game world time representation - * Stores the current in-game date and time - */ -struct WorldTime -{ - int16 year; // Game year - int month; // Month (1-12) - int day; // Day of month - int hour; // Hour (0-23) - int minute; // Minute (0-59) -}; - -/** - * Database query type enumeration - * Defines the different types of SQL operations - */ -typedef enum QUERY_TYPE -{ - Q_SELECT, // SELECT query for data retrieval - Q_UPDATE, // UPDATE query for modifying existing records - Q_REPLACE, // REPLACE query for insert-or-update operations - Q_INSERT, // INSERT query for adding new records - Q_DELETE, // DELETE query for removing records - Q_DBMS // Database management system query -} QUERY_TYPE; - -#pragma pack() \ No newline at end of file diff --git a/old/common/unix.cpp b/old/common/unix.cpp new file mode 100644 index 0000000..6476c3a --- /dev/null +++ b/old/common/unix.cpp @@ -0,0 +1,45 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#include "unix.h" +#include +#include + +void Sleep(unsigned int x) { + if (x > 0) + usleep(x*1000); +} + +char* strupr(char* tmp) { + int l = strlen(tmp); + for (int x = 0; x < l; x++) { + tmp[x] = toupper(tmp[x]); + } + return tmp; +} + +char* strlwr(char* tmp) { + int l = strlen(tmp); + for (int x = 0; x < l; x++) { + tmp[x] = tolower(tmp[x]); + } + return tmp; +} + + diff --git a/old/common/unix.h b/old/common/unix.h new file mode 100644 index 0000000..82560a1 --- /dev/null +++ b/old/common/unix.h @@ -0,0 +1,34 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ +#ifndef WIN32 +#ifndef __UNIX_H__ +#define __UNIX_H__ + #ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP + #define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP {0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, __LOCK_INITIALIZER} + #endif +#include + +typedef int SOCKET; + +void Sleep(unsigned int x); +char* strupr(char* tmp); +char* strlwr(char* tmp); +#endif +#endif diff --git a/old/common/unix.hpp b/old/common/unix.hpp deleted file mode 100644 index 6fccdc0..0000000 --- a/old/common/unix.hpp +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GNU GPL v3+ License - -#pragma once - -#include -#include -#include -#include -#include -#include - -#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP - #define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP {0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, __LOCK_INITIALIZER} -#endif - -typedef int SOCKET; - -// Cross-platform sleep function - pauses execution for specified milliseconds -void Sleep(unsigned int x) -{ - if (x > 0) { - std::this_thread::sleep_for(std::chrono::milliseconds(x)); - } -} - -// Converts string to uppercase in-place - modifies original string and returns pointer -char* strupr(char* tmp) -{ - if (!tmp) return tmp; - - int l = strlen(tmp); - for (int x = 0; x < l; x++) { - tmp[x] = toupper(tmp[x]); - } - return tmp; -} - -// Converts string to lowercase in-place - modifies original string and returns pointer -char* strlwr(char* tmp) -{ - if (!tmp) return tmp; - - int l = strlen(tmp); - for (int x = 0; x < l; x++) { - tmp[x] = tolower(tmp[x]); - } - return tmp; -} \ No newline at end of file diff --git a/old/common/version.h b/old/common/version.h new file mode 100644 index 0000000..0d6beb4 --- /dev/null +++ b/old/common/version.h @@ -0,0 +1,56 @@ +/* + EQ2Emulator: Everquest II Server Emulator + Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net) + + This file is part of EQ2Emulator. + + EQ2Emulator 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. + + EQ2Emulator 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 EQ2Emulator. If not, see . +*/ + +#include "LogTypes.h" + +#ifndef VERSION_H +#define VERSION_H + +#define CURRENT_DATABASE_MINORVERSION 43 +#define CURRENT_DATABASE_MAJORVERSION 730 +#if defined(LOGIN) + #define EQ2EMU_MODULE "EQ2EMu LoginServer" +#elif defined(PATCHER) + #define EQ2EMU_MODULE "EQ2EMu PatchServer" +#elif defined(CHAT) + #define EQ2EMU_MODULE "EQ2EMu ChatServer" +#elif defined(ZONE) + #define EQ2EMU_MODULE "EQ2EMu ZoneServer" +#else + #define EQ2EMU_MODULE "EQ2EMu WorldServer" +#endif + +#if defined(LOGIN) +#define CURRENT_VERSION "0.9.9-Nebula" +#elif defined(WORLD) +#define CURRENT_VERSION "0.9.9-Nebula" +#else +#define CURRENT_VERSION "0.9.9-Nebula" +#endif + +#define COMPILE_DATE __DATE__ +#define COMPILE_TIME __TIME__ +#ifndef WIN32 + #define LAST_MODIFIED __TIME__ +#else + #define LAST_MODIFIED __TIMESTAMP__ +#endif + +#endif \ No newline at end of file diff --git a/old/common/version.hpp b/old/common/version.hpp deleted file mode 100644 index 0139476..0000000 --- a/old/common/version.hpp +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2007 EQ2EMulator Development Team - GNU GPL v3 - -#pragma once - -#include "log_types.hpp" - -#define CURRENT_DATABASE_MINORVERSION 43 -#define CURRENT_DATABASE_MAJORVERSION 730 - -#if defined(LOGIN) - #define EQ2EMU_MODULE "EQ2EMu LoginServer" -#elif defined(PATCHER) - #define EQ2EMU_MODULE "EQ2EMu PatchServer" -#elif defined(CHAT) - #define EQ2EMU_MODULE "EQ2EMu ChatServer" -#elif defined(ZONE) - #define EQ2EMU_MODULE "EQ2EMu ZoneServer" -#else - #define EQ2EMU_MODULE "EQ2EMu WorldServer" -#endif - -#if defined(LOGIN) - #define CURRENT_VERSION "0.9.8-thetascorpii-DR1" -#elif defined(WORLD) - #define CURRENT_VERSION "0.9.8-thetascorpii-DR1" -#else - #define CURRENT_VERSION "0.9.8-thetascorpii-DR1" -#endif - -#define COMPILE_DATE __DATE__ -#define COMPILE_TIME __TIME__ -#define LAST_MODIFIED __TIME__ \ No newline at end of file diff --git a/old/common/web_server.hpp b/old/common/web_server.hpp deleted file mode 100644 index d5ecc1e..0000000 --- a/old/common/web_server.hpp +++ /dev/null @@ -1,447 +0,0 @@ -// Copyright (C) EQ2EMU Team, GPL v3 License - -#ifndef __WEBSERVER_HPP__ -#define __WEBSERVER_HPP__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "types.hpp" -#include "version.hpp" - -#ifdef WORLD - #include "../WorldServer/WorldDatabase.h" - extern WorldDatabase database; -#endif -#ifdef LOGIN - #include "../LoginServer/login_database.hpp" - extern LoginDatabase database; -#endif - -namespace beast = boost::beast; -namespace http = beast::http; -namespace boost_net = boost::asio; -namespace ssl = boost::asio::ssl; -using tcp = boost_net::ip::tcp; - -// Forward declaration for thread function -ThreadReturnType RunWebServer(void* tmp); - -// Global variable for SSL key password -static std::string keypasswd; - -// Constants for session management -constexpr size_t SESSION_ID_LENGTH = 32; -constexpr std::string_view HEX_CHARS = "0123456789abcdef"; -constexpr std::string_view BASIC_AUTH_PREFIX = "Basic "; -constexpr std::string_view SESSION_COOKIE_NAME = "session_id"; - -// Handles version endpoint requests - returns application version information in JSON format -inline void web_handle_version(const http::request& req, http::response& res) -{ - res.set(http::field::content_type, "application/json"); - boost::property_tree::ptree pt; - - // Add key-value pairs to the property tree - pt.put("eq2emu_process", EQ2EMU_MODULE); - pt.put("version", CURRENT_VERSION); - pt.put("compile_date", COMPILE_DATE); - pt.put("compile_time", COMPILE_TIME); - - std::ostringstream oss; - boost::property_tree::write_json(oss, pt); - res.body() = oss.str(); - res.prepare_payload(); -} - -// Handles root endpoint requests - returns simple greeting message -inline void web_handle_root(const http::request& req, http::response& res) -{ - res.set(http::field::content_type, "text/html"); - res.body() = "Hello!"; - res.prepare_payload(); -} - -class WebServer -{ -public: - // Constructor - initializes web server with SSL support and authentication - WebServer(std::string_view address, unsigned short port, std::string_view cert_file, std::string_view key_file, std::string_view key_password, std::string_view hardcode_user, std::string_view hardcode_password); - - // Destructor - stops IO context - ~WebServer(); - - // Starts the web server in a separate thread - void run(); - - // Starts the web server in current thread - void start(); - - // Registers a new route with optional authentication requirement - void register_route(std::string_view uri, std::function&, http::response&)> handler, bool auth_required = true); - -private: - bool is_ssl = false; // Flag indicating if SSL is enabled - - // SSL password callback function for encrypted private keys - static std::string my_password_callback(std::size_t max_length, ssl::context::password_purpose purpose); - - // Accepts incoming connections asynchronously - void do_accept(); - - // Handles accepted connections and determines SSL vs non-SSL session - void on_accept(beast::error_code ec, tcp::socket socket); - - // Handles SSL encrypted sessions - void do_session_ssl(tcp::socket socket); - - // Handles non-SSL sessions - void do_session(tcp::socket socket); - - // Main request handler template - processes HTTP requests and routes them appropriately - template - void handle_request(http::request>&& req, std::function&&)> send); - - // Authenticates user credentials and manages sessions - std::optional authenticate(const http::request& req, int32* user_status = nullptr); - - // Generates random session IDs for authenticated users - std::string generate_session_id(); - - // Parses session cookie from request - std::optional parse_session_cookie(std::string_view cookie_header); - - // Parses basic auth credentials from header - std::optional> parse_basic_auth(std::string_view auth_header); - - boost_net::io_context ioc_{1}; // IO context for async operations - ssl::context ssl_ctx_{ssl::context::tlsv13_server}; // SSL context for encrypted connections - tcp::acceptor acceptor_{ioc_}; // TCP acceptor for incoming connections - std::unordered_map sessions_; // Maps session_id to username - std::unordered_map sessions_status_; // Maps session_id to user status level - std::unordered_map credentials_; // Maps username to password for hardcoded users - std::unordered_map route_required_status_; // Maps route to required status level - std::unordered_map&, http::response&)>> routes_; // Authenticated routes - std::unordered_map&, http::response&)>> noauth_routes_; // Non-authenticated routes - std::mt19937 rng_{std::random_device{}()}; // Random number generator for session IDs - std::uniform_int_distribution<> hex_dist_{0, 15}; // Distribution for hex characters -}; - -// SSL password callback implementation - returns the stored key password -std::string WebServer::my_password_callback(std::size_t max_length, ssl::context::password_purpose purpose) -{ - return keypasswd; -} - -// WebServer constructor - sets up SSL context, acceptor, and default routes -WebServer::WebServer(std::string_view address, unsigned short port, std::string_view cert_file, std::string_view key_file, std::string_view key_password, std::string_view hardcode_user, std::string_view hardcode_password) - : acceptor_(ioc_, {boost_net::ip::make_address(address), port}) -{ - keypasswd = key_password; - - // Initialize SSL context if certificates provided - if (!cert_file.empty() && !key_file.empty()) { - ssl_ctx_.set_password_callback(my_password_callback); - ssl_ctx_.use_certificate_chain_file(std::string(cert_file)); - ssl_ctx_.use_private_key_file(std::string(key_file), ssl::context::file_format::pem); - is_ssl = true; - } - - keypasswd.clear(); // reset no longer needed - - // Initialize hardcoded credentials if provided - if (!hardcode_user.empty() && !hardcode_password.empty()) { - credentials_[std::string(hardcode_user)] = hardcode_password; - } - - register_route("/", web_handle_root); - register_route("/version", web_handle_version); -} - -// WebServer destructor - stops the IO context -WebServer::~WebServer() -{ - ioc_.stop(); -} - -// Thread function for running web server in separate thread -ThreadReturnType RunWebServer(void* tmp) -{ - if (!tmp) THREAD_RETURN(NULL); - - auto* ws = static_cast(tmp); - ws->start(); - THREAD_RETURN(NULL); -} - -// Starts the web server by accepting connections and running IO context -void WebServer::start() -{ - do_accept(); - ioc_.run(); -} - -// Runs the web server in a detached thread -void WebServer::run() -{ - pthread_t thread; - pthread_create(&thread, nullptr, RunWebServer, this); - pthread_detach(thread); -} - -// Registers a route handler with optional authentication and database override -void WebServer::register_route(std::string_view uri, std::function&, http::response&)> handler, bool auth_req) -{ - const std::string uri_str{uri}; - const int32 status = database.NoAuthRoute(const_cast(uri_str.c_str())); // overrides the default hardcode settings via DB - - if (status == 0) auth_req = false; - - if (auth_req) { - routes_[uri_str] = std::move(handler); - } else { - noauth_routes_[uri_str] = std::move(handler); - } - route_required_status_[uri_str] = status; -} - -// Initiates asynchronous accept operation for incoming connections -void WebServer::do_accept() -{ - acceptor_.async_accept([this](beast::error_code ec, tcp::socket socket) { - this->on_accept(ec, std::move(socket)); - }); -} - -// Handles accepted connections and starts appropriate session type -void WebServer::on_accept(beast::error_code ec, tcp::socket socket) -{ - if (!ec) { - if (is_ssl) { - std::thread(&WebServer::do_session_ssl, this, std::move(socket)).detach(); - } else { - std::thread(&WebServer::do_session, this, std::move(socket)).detach(); - } - } - do_accept(); -} - -// Handles SSL encrypted sessions with handshake and request processing -void WebServer::do_session_ssl(tcp::socket socket) -{ - try { - ssl::stream stream(std::move(socket), ssl_ctx_); - stream.handshake(ssl::stream_base::server); - - beast::flat_buffer buffer; - bool close = false; - - while (!close) { - http::request req; - http::read(stream, buffer, req); - - handle_request(std::move(req), [&](auto&& response) { - if (response.need_eof()) close = true; - http::write(stream, response); - }); - } - - beast::error_code ec; - socket.shutdown(tcp::socket::shutdown_send, ec); - } - catch (const std::exception&) { - // Connection errors are expected and logged elsewhere if needed - } -} - -// Handles non-SSL sessions with request processing loop -void WebServer::do_session(tcp::socket socket) -{ - try { - beast::flat_buffer buffer; - bool close = false; - - while (!close) { - http::request req; - http::read(socket, buffer, req); - - handle_request(std::move(req), [&](auto&& response) { - if (response.need_eof()) close = true; - http::write(socket, response); - }); - } - - beast::error_code ec; - socket.shutdown(tcp::socket::shutdown_send, ec); - } - catch (const std::exception&) { - // Connection errors are expected and logged elsewhere if needed - } -} - -// Main request handler - routes requests based on authentication and authorization -template -void WebServer::handle_request(http::request>&& req, std::function&&)> send) -{ - const std::string target = req.target().to_string(); - - // Check for non-authenticated routes first - if (const auto it = noauth_routes_.find(target); it != noauth_routes_.end()) { - http::response res{http::status::ok, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - it->second(req, res); - return send(std::move(res)); - } - - // Authenticate user - int32 user_status = 0; - const auto session_id = authenticate(req, &user_status); - if (!session_id) { - http::response res{http::status::unauthorized, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.set(http::field::www_authenticate, "Basic realm=\"example\""); - res.body() = "Unauthorized"; - res.prepare_payload(); - return send(std::move(res)); - } - - // Check authorization level - if (const auto status_it = route_required_status_.find(target); status_it != route_required_status_.end()) { - const auto required_status = status_it->second; - if (required_status > 0 && required_status != 0xFFFFFFFF && required_status > user_status) { - http::response res{http::status::unauthorized, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - res.body() = "Unauthorized status"; - res.prepare_payload(); - return send(std::move(res)); - } - } - - // Handle authenticated routes - if (const auto it = routes_.find(target); it != routes_.end()) { - http::response res{http::status::ok, req.version()}; - res.set(http::field::set_cookie, std::string(SESSION_COOKIE_NAME) + "=" + *session_id); - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); - it->second(req, res); - return send(std::move(res)); - } - - return send(http::response{http::status::bad_request, req.version()}); -} - -// Parses session cookie from cookie header -std::optional WebServer::parse_session_cookie(std::string_view cookie_header) -{ - const auto pos = cookie_header.find(SESSION_COOKIE_NAME); - if (pos == std::string_view::npos) return std::nullopt; - - const auto value_start = pos + SESSION_COOKIE_NAME.length() + 1; // +1 for '=' - if (value_start >= cookie_header.length()) return std::nullopt; - - const auto value_end = cookie_header.find(';', value_start); - const auto session_id = cookie_header.substr(value_start, - value_end == std::string_view::npos ? std::string_view::npos : value_end - value_start); - - return std::string(session_id); -} - -// Parses basic auth credentials from authorization header -std::optional> WebServer::parse_basic_auth(std::string_view auth_header) -{ - if (!auth_header.starts_with(BASIC_AUTH_PREFIX)) return std::nullopt; - - const auto encoded_credentials = auth_header.substr(BASIC_AUTH_PREFIX.length()); - std::string decoded_credentials; - decoded_credentials.resize(boost::beast::detail::base64::decoded_size(encoded_credentials.size())); - - const auto result = boost::beast::detail::base64::decode( - decoded_credentials.data(), - encoded_credentials.data(), - encoded_credentials.size() - ); - decoded_credentials.resize(result.first); - - const auto colon_pos = decoded_credentials.find(':'); - if (colon_pos == std::string::npos) return std::nullopt; - - return std::make_pair( - decoded_credentials.substr(0, colon_pos), - decoded_credentials.substr(colon_pos + 1) - ); -} - -// Authenticates users via session cookies or Basic Auth and returns session ID -std::optional WebServer::authenticate(const http::request& req, int32* user_status) -{ - // Try session cookie first - if (const auto cookie_it = req.find(http::field::cookie); cookie_it != req.end()) { - if (const auto session_id = parse_session_cookie(cookie_it->value().to_string())) { - if (const auto session_it = sessions_.find(*session_id); session_it != sessions_.end()) { - if (user_status) { - if (const auto status_it = sessions_status_.find(*session_id); status_it != sessions_status_.end()) { - *user_status = status_it->second; - } - } - return *session_id; - } - } - } - - // Try basic authentication - if (const auto auth_it = req.find(http::field::authorization); auth_it != req.end()) { - if (const auto credentials = parse_basic_auth(auth_it->value().to_string())) { - const auto& [username, password] = *credentials; - int32 out_status = 0; - - // Check hardcoded credentials or database - const bool auth_success = (credentials_.contains(username) && credentials_[username] == password) || - (database.AuthenticateWebUser(const_cast(username.c_str()), const_cast(password.c_str()), &out_status) > 0); - - if (auth_success) { - const auto session_id = generate_session_id(); - sessions_[session_id] = username; - sessions_status_[session_id] = out_status; - if (user_status) *user_status = out_status; - return session_id; - } - } - } - - return std::nullopt; -} - -// Generates a random 32-character hexadecimal session ID -std::string WebServer::generate_session_id() -{ - std::string session_id; - session_id.reserve(SESSION_ID_LENGTH); - - for (size_t i = 0; i < SESSION_ID_LENGTH; ++i) { - session_id += HEX_CHARS[hex_dist_(rng_)]; - } - - return session_id; -} - -#endif // __WEBSERVER_HPP__ \ No newline at end of file diff --git a/old/common/xml_parser.cpp b/old/common/xmlParser.cpp similarity index 99% rename from old/common/xml_parser.cpp rename to old/common/xmlParser.cpp index da0399c..e0f7f33 100644 --- a/old/common/xml_parser.cpp +++ b/old/common/xmlParser.cpp @@ -51,7 +51,7 @@ #ifndef _CRT_SECURE_NO_DEPRECATE #define _CRT_SECURE_NO_DEPRECATE #endif -#include "xml_parser.hpp" +#include "xmlParser.h" #ifdef _XMLWINDOWS //#ifdef _DEBUG //#define _CRTDBG_MAP_ALLOC diff --git a/old/common/xml_parser.hpp b/old/common/xmlParser.h similarity index 100% rename from old/common/xml_parser.hpp rename to old/common/xmlParser.h