diff --git a/source/LoginServer/Character.cpp b/source/LoginServer/Character.cpp
new file mode 100644
index 0000000..67423d8
--- /dev/null
+++ b/source/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/source/LoginServer/Character.h b/source/LoginServer/Character.h
new file mode 100644
index 0000000..a0e89a8
--- /dev/null
+++ b/source/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/source/LoginServer/EQ2 Login.sln b/source/LoginServer/EQ2 Login.sln
new file mode 100644
index 0000000..48169c7
--- /dev/null
+++ b/source/LoginServer/EQ2 Login.sln
@@ -0,0 +1,25 @@
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EQ2 Login", "Login.vcxproj", "{BE2C1914-FCCC-4F65-A7DD-105142B36104}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ EQ2Login|Win32 = EQ2Login|Win32
+ MiniLogin Release|Win32 = MiniLogin Release|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {BE2C1914-FCCC-4F65-A7DD-105142B36104}.Debug|Win32.ActiveCfg = EQ2Login|Win32
+ {BE2C1914-FCCC-4F65-A7DD-105142B36104}.Debug|Win32.Build.0 = EQ2Login|Win32
+ {BE2C1914-FCCC-4F65-A7DD-105142B36104}.EQ2Login|Win32.ActiveCfg = EQ2Login|Win32
+ {BE2C1914-FCCC-4F65-A7DD-105142B36104}.EQ2Login|Win32.Build.0 = EQ2Login|Win32
+ {BE2C1914-FCCC-4F65-A7DD-105142B36104}.MiniLogin Release|Win32.ActiveCfg = EQ2Login|Win32
+ {BE2C1914-FCCC-4F65-A7DD-105142B36104}.MiniLogin Release|Win32.Build.0 = EQ2Login|Win32
+ {BE2C1914-FCCC-4F65-A7DD-105142B36104}.Release|Win32.ActiveCfg = EQ2Login|Win32
+ {BE2C1914-FCCC-4F65-A7DD-105142B36104}.Release|Win32.Build.0 = EQ2Login|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/source/LoginServer/EQ2 Login.suo b/source/LoginServer/EQ2 Login.suo
new file mode 100644
index 0000000..d090a9e
Binary files /dev/null and b/source/LoginServer/EQ2 Login.suo differ
diff --git a/source/LoginServer/LWorld.cpp b/source/LoginServer/LWorld.cpp
new file mode 100644
index 0000000..aa80ad4
--- /dev/null
+++ b/source/LoginServer/LWorld.cpp
@@ -0,0 +1,1546 @@
+/*
+ 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
+
+#ifdef WIN32
+#define WIN32_LEAN_AND_MEAN
+#include
+#include
+#include
+#else
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "../common/unix.h"
+
+#define SOCKET_ERROR -1
+#define INVALID_SOCKET -1
+
+extern int errno;
+#endif
+
+#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"
+
+#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 LoginDatabase database;
+extern ConfigReader configReader;
+extern volatile bool RunLoops;
+
+#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;
+ in.s_addr = in_con->GetrIP();
+ char* ipadd = inet_ntoa(in);
+ if(ipadd)
+ strncpy(IPAddr,ipadd,64);
+
+ if (iPort)
+ port = iPort;
+ else
+ port = in_con->GetrPort();
+ ID = 0;
+ pClientPort = 0;
+ memset(account, 0, sizeof(account));
+ memset(address, 0, sizeof(address));
+ memset(worldname, 0, sizeof(worldname));
+ status = 0;
+ accountid = 0;
+ admin_id = 0;
+ IsInit = false;
+ kicked = false;
+ pNeverKick = iNeverKick;
+ pPlaceholder = false;
+ pshowdown = false;
+ pConnected = in_con->Connected();
+ pReconnectTimer = 0;
+ pStatsTimer = NULL;
+ isAuthenticated = false;
+
+ if (in_OutgoingLoginUplink) {
+ pClientPort = port;
+ ptype = Login;
+ OutgoingUplink = true;
+ if (net.GetLoginMode() == Mesh) {
+ pReconnectTimer = new Timer(INTERSERVER_TIMER);
+ pReconnectTimer->Trigger();
+ }
+ }
+ else {
+ ptype = UnknownW;
+ OutgoingUplink = false;
+ }
+
+ in.s_addr = GetIP();
+ strcpy(address, inet_ntoa(in));
+ isaddressip = true;
+
+ num_players = 0;
+ num_zones = 0;
+}
+
+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,"");
+ pClientPort = 0;
+ memset(account, 0, sizeof(account));
+ memset(address, 0, sizeof(address));
+ memset(worldname, 0, sizeof(worldname));
+ status = 0;
+ accountid = in_accountid;
+ admin_id = in_admin_id;
+ IsInit = false;
+ kicked = false;
+ pNeverKick = false;
+ pshowdown = true;
+ RemoteID = 0;
+ LinkWorldID = 0;
+ 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;
+}
+
+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)
+ 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);
+
+ port = 0;
+ ID = 0;
+ pClientPort = 0;
+ memset(account, 0, sizeof(account));
+ memset(address, 0, sizeof(address));
+ memset(worldname, 0, sizeof(worldname));
+ status = in_status;
+ accountid = in_accountid;
+ admin_id = in_adminid;
+ IsInit = true;
+ kicked = false;
+ pNeverKick = false;
+ pPlaceholder = in_placeholder;
+ pshowdown = in_showdown;
+ OutgoingUplink = false;
+ 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;
+}
+
+LWorld::~LWorld() {
+
+ safe_delete ( pStatsTimer );
+ num_zones = 0;
+ num_players = 0;
+ database.UpdateWorldServerStats(this, -4);
+
+ if (ptype == World && RemoteID == 0) {
+ if (net.GetLoginMode() != Mesh || (!pPlaceholder && IsInit)) {
+ ServerPacket* pack = new ServerPacket(ServerOP_WorldListRemove, sizeof(int32));
+ *((int32*) pack->pBuffer) = GetID();
+ world_list.SendPacketLogin(pack);
+ delete pack;
+ }
+ }
+
+ if (Link != 0 && RemoteID == 0) {
+ world_list.RemoveByLink(Link, 0, this);
+ if (OutgoingUplink)
+ delete Link;
+ else
+ Link->Free();
+ }
+ Link = 0;
+ safe_delete(pReconnectTimer);
+
+ world_list.RemoveByID ( this->GetID ( ) );
+}
+
+bool LWorld::Process() {
+ bool ret = true;
+ if (Link == 0)
+ return true;
+ if (Link->Connected()) {
+ if (!pConnected) {
+ pConnected = true;
+ }
+ }
+ else {
+ pConnected = false;
+ if (pReconnectTimer) {
+ if (pReconnectTimer->Check() && Link->ConnectReady()) {
+ pReconnectTimer->Start(pReconnectTimer->GetTimerTime() + (rand()%15000), false);
+ }
+ return true;
+ }
+ return false;
+ }
+ if (RemoteID != 0)
+ return true;
+
+ 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())) {
+
+ // 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) {
+ case 0:
+ break;
+ case ServerOP_KeepAlive: {
+ // ignore this
+ break;
+ }
+ 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){
+ client_list.FindByCreateRequest();
+ }
+ break;
+ }
+ 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) {
+ 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) {
+ break;
+ }
+ if (world) {
+ seps->ToID = world->GetRemoteID();
+ world->SendPacket(pack);
+ }
+ }
+ else {
+ Client* client = 0;
+ client = client_list.FindByLSID(seps->lsaccountid);
+ if(client == 0)
+ break;
+ 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: { // 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());
+
+ //Todo: Synch with the other login servers
+
+ break;
+ }
+ case ServerOP_GetTableData:{
+ Kick("This is not an update server.");
+ break;
+ }
+ 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))
+ break;
+
+ RaceUpdate_Struct* ru = (RaceUpdate_Struct*) pack->pBuffer;
+ 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))
+ break;
+
+ CharDataUpdate_Struct* cdu = (CharDataUpdate_Struct*) pack->pBuffer;
+
+ switch(cdu->update_field)
+ {
+ case LEVEL_UPDATE_FLAG:
+ {
+ 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;
+ }
+ case GENDER_UPDATE_FLAG:
+ {
+ 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());
+ break;
+ }
+ }
+ break;
+ }
+ case ServerOP_LSInfo: {
+ if (pack->size != sizeof(ServerLSInfo_Struct)) {
+ this->Kick(ERROR_BADVERSION);
+ ret = false;
+ //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;
+ //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;
+ //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) {
+ 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) {
+ // 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) {
+ break;
+ }
+ world_list.SendWorldStatus(this, (char*) pack->pBuffer);
+ break;
+ }
+ case ServerOP_WorldListUpdate: {
+ break;
+ }
+ case ServerOP_WorldListRemove: {
+ 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));
+ if (world && world->GetRemoteID() != 0) {
+ *((int32*) pack->pBuffer) = world->GetID();
+ if (net.GetLoginMode() != Mesh)
+ world_list.SendPacketLogin(pack, this);
+ 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:{
+ pack->Inflate();
+ ZoneUpdateList_Struct* updates = 0;
+ 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){
+ 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;
+ }
+ 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");
+ 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){
+ 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;
+ pos += sizeof(EquipmentUpdate_Struct);
+ num_updates++;
+ 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)
+ {
+ world_list.AddServerEquipmentUpdates(this, equip_updates);
+ }
+ 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
+ {
+ 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)){
+ 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) {
+ // 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 {
+ 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;
+ delete inpack;
+ }
+ }
+ 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);
+ //Kick("Possible Hacking Attempt");
+ break;
+ }
+ }
+ delete pack;
+ }
+ return ret;
+}
+
+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;
+ seps->ToID = RemoteID;
+ seps->opcode = pack->opcode;
+ seps->size = pack->size;
+ memcpy(seps->data, pack->pBuffer, pack->size);
+ Link->SendPacket(outpack);
+ delete outpack;
+ }
+ else {
+ Link->SendPacket(pack);
+ }
+}
+
+void LWorld::Message(const char* to, const char* message, ...) {
+ va_list argptr;
+ char buffer[256];
+
+ va_start(argptr, message);
+ vsnprintf(buffer, 256, message, argptr);
+ va_end(argptr);
+
+ ServerPacket* pack = new ServerPacket(ServerOP_EmoteMessage, sizeof(ServerEmoteMessage_Struct) + strlen(buffer) + 1);
+ ServerEmoteMessage_Struct* sem = (ServerEmoteMessage_Struct*) pack->pBuffer;
+ strcpy(sem->to, to);
+ strcpy(sem->message, buffer);
+ SendPacket(pack);
+ delete pack;
+}
+
+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();
+}
+bool LWorld::CheckServerName(const char* name) {
+ if (strlen(name) < 10)
+ return false;
+ 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;
+}
+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)
+ return false;
+ if(database.IsServerAccountDisabled(tmpAccount) || database.IsIPBanned(IPAddr) || (isaddressip && database.IsIPBanned(address)))
+ return false;
+
+ LWorld* world = world_list.FindByID(id);
+ if(world)
+ world->Kick("Ghost Kick!");
+
+ ID = id;
+ accountid = id;
+ strncpy(account,tmpAccount,30);
+ char* name = database.GetServerAccountName(id);
+ if(name)
+ snprintf(worldname, (sizeof(worldname)) - 1, "%s", name);
+ else{ //failed to get account
+ account[0] = 0;
+ IsInit = false;
+ this->Kick ( "Could not load server information." );
+ return false;
+ }
+ //world_list.KickGhostIP(GetIP(), this);
+ IsInit = true;
+ ptype = World;
+ world_list.SendWorldChanged(id, true);
+ }
+ else {
+ // name too short
+ account[0] = 0;
+ IsInit = false;
+ return false;
+ }
+
+ database.UpdateWorldVersion(GetAccountID(), in_version);
+ pStatsTimer = new Timer ( 60000 );
+ pStatsTimer->Start ( 60000 );
+
+ return true;
+}
+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())
+ world_changed->up = 0;
+ else
+ world_changed->up = 1;
+ if(sendtoallclients || sendto == 0)
+ client_list.SendPacketToAllClients(outapp);
+ else
+ sendto->QueuePacket(outapp);
+ world_list.SetUpdateServerList(true);
+}
+void LWorld::UpdateWorldList(LWorld* to) {
+ world_list.SetUpdateServerList( true );
+}
+
+void LWorld::ChangeToPlaceholder() {
+ ip = 0;
+ status = -1;
+ pPlaceholder = true;
+ if (Link != 0 && RemoteID == 0) {
+ Link->Disconnect();
+ }
+ UpdateWorldList();
+}
+
+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;
+ admin_id = in_adminid;
+ num_players = in_players;
+ num_zones = in_zones;
+}
+
+
+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;
+ #ifdef WIN32
+ _beginthread(ServerUpdateLoop, 0, this);
+ #else
+ pthread_t thread;
+ pthread_create(&thread, NULL, &ServerUpdateLoop, this);
+ #endif
+}
+
+LWorldList::~LWorldList() {
+ server_update_thread = false;
+ while(!server_update_thread){
+ Sleep(100);
+ }
+ safe_delete(tcplistener);
+ safe_delete(OutLink);
+}
+
+void LWorldList::Shutdown() {
+ LinkedListIterator iterator(list);
+
+ iterator.Reset();
+ while(iterator.MoreElements())
+ {
+ iterator.RemoveCurrent ( );
+ }
+
+ safe_delete(tcplistener);
+}
+
+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());
+}
+
+void LWorldList::AddInitiateWorld ( LWorld* world )
+{
+ list.Insert ( world );
+}
+
+void LWorldList::KickGhostIP(int32 ip, LWorld* NotMe, int16 iClientPort) {
+ if (ip == 0)
+ return;
+
+ map::iterator map_list;
+ MWorldMap.readlock();
+ 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;
+ in.s_addr = world->GetIP();
+ // 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;
+ }
+ world->Kick("Ghost IP kick");
+ }
+ }
+ }
+ }
+ MWorldMap.releasereadlock();
+}
+
+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++ ) {
+ LWorld* world = map_list->second;
+ 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");
+ }
+ }
+ MWorldMap.releasereadlock();
+}
+
+void LWorldList::UpdateWorldStats(){
+ map::iterator map_list;
+ MWorldMap.readlock();
+ for(map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) {
+ LWorld* world = map_list->second;
+ if(world && world->GetAccountID() > 0)
+ database.UpdateWorldServerStats(world, world->GetStatus());
+ }
+ MWorldMap.releasereadlock();
+}
+
+void LWorldList::Process() {
+ TCPConnection* newtcp = 0;
+ LWorld* newworld = 0;
+
+ 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 ( );
+ else
+ iterator.Advance();
+ }
+ }
+
+ while ((newtcp = tcplistener->NewQueuePop())) {
+ newworld = new LWorld(newtcp);
+ newworld->SetID(0);
+ AddInitiateWorld(newworld);
+ 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();
+ }
+ 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 );
+ net.numservers--;
+ net.UpdateWindowTitle();
+ safe_delete ( world );
+ continue;
+ }
+ 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 );
+
+ net.numservers--;
+ net.UpdateWindowTitle();
+ if(account_id > 0){
+ LWorld* world2 = FindByID(account_id);
+ if(world2)
+ world2->ShowDownActive(true);
+ }
+ SendWorldChanged(account_id, true);
+ safe_delete ( world );
+ continue;
+ }
+ else {
+ world->ChangeToPlaceholder();
+ }
+ }
+ 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) {
+ ServerPacket* outpack = new ServerPacket(ServerOP_EncapPacket, sizeof(ServerEncapPacket_Struct) + pack->size);
+ ServerEncapPacket_Struct* seps = (ServerEncapPacket_Struct*) outpack->pBuffer;
+ seps->ToID = 0xFFFFFFFF;
+ seps->opcode = pack->opcode;
+ seps->size = pack->size;
+ memcpy(seps->data, pack->pBuffer, pack->size);
+ world->SendPacket(outpack);
+ delete outpack;
+ }
+ else if (world->GetRemoteID() == 0) {
+ world->SendPacket(pack);
+ }
+ }
+ }
+}
+
+// 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) {
+ world->SendPacket(pack);
+ }
+ }
+}
+
+// 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) {
+ world->SendPacket(pack);
+ }
+ }
+}
+
+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);
+ }
+}
+
+LWorld* LWorldList::FindByID(int32 LWorldID) {
+ if(worldmap.count(LWorldID) > 0)
+ return worldmap[LWorldID];
+ return 0;
+}
+
+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++) {
+ world = map_list->second;
+ if (world->GetIP() == ip){
+ ret = world;
+ break;
+ }
+ }
+ MWorldMap.releasereadlock();
+ return ret;
+}
+
+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++) {
+ world = map_list->second;
+ if (strcasecmp(world->GetAddress(), address) == 0){
+ ret = world;
+ break;
+ }
+ }
+ MWorldMap.releasereadlock();
+ return ret;
+}
+
+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;
+ MWorldMap.readlock();
+ for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) {
+ world = map_list->second;
+ if (world->GetLink() == in_link && world->GetRemoteID() == in_id){
+ ret = world;
+ break;
+ }
+ }
+ MWorldMap.releasereadlock();
+ return ret;
+}
+
+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;
+ MWorldMap.readlock();
+ for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) {
+ world = map_list->second;
+ if (world->GetAccountID() == in_accountid && world->GetType() == in_type){
+ ret = world;
+ break;
+ }
+ }
+ MWorldMap.releasereadlock();
+ return ret;
+}
+
+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;
+ PacketStruct* packet = 0;
+ MWorldMap.readlock();
+ 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){
+ packet->setDataByName("server_id", world->GetID());
+ packet->setDataByName("up", 1);
+ if(world->IsLocked())
+ packet->setDataByName("locked", 1);
+ ret->push_back(packet);
+ }
+ }
+ }
+ MWorldMap.releasereadlock();
+ return ret;
+}
+
+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))
+ {
+ 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) {
+ tmpCount++;
+ }
+ }
+
+ PacketStruct* packet = configReader.getStruct("LS_WorldList", version);
+ packet->setArrayLengthByName("num_worlds", tmpCount);
+
+ 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) {
+ ServerNum++;
+ packet->setArrayDataByName("id", world->GetID(), ServerNum - 1);
+
+ if (version <= 283) {
+ packet->setArrayDataByName("name", world->GetName(), ServerNum - 1);
+ if (!world->ShowDown())
+ packet->setArrayDataByName("online", 1, ServerNum - 1);
+ if (world->IsLocked())
+ packet->setArrayDataByName("locked", 1, ServerNum - 1);
+ packet->setArrayDataByName("unknown2", 1, ServerNum - 1);
+ packet->setArrayDataByName("unknown3", 1, ServerNum - 1);
+ packet->setArrayDataByName("load", world->GetWorldStatus(), ServerNum - 1);
+ }
+ 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("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);
+ }
+ }
+ }
+
+ EQ2Packet* pack = packet->serialize();
+ #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);
+ ServerListData.erase(it);
+ }
+ ServerListData.insert(make_pair(version, pack));
+ MWorldMap.releasereadlock();
+
+ SetUpdateServerList(false);
+
+ return ServerListData[version];
+}
+
+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++) {
+ 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) {
+ 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()) {
+ chat->Message(adminname, " Address: %s", world->GetAddress());
+ }
+ count++;
+ }
+ }
+ chat->Message(adminname, "%i worlds listed.", count);
+}
+
+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++) {
+ LWorld* world = map_list->second;
+ 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 );
+ continue;
+ }
+ }
+}
+
+void LWorldList::RemoveByID(int32 in_id) {
+ if (in_id == 0)
+ return;
+
+ LWorld* existWorld = FindByID(in_id);
+ if ( existWorld != NULL )
+ {
+ MWorldMap.writelock();
+ worldmap.erase ( in_id );
+ MWorldMap.releasewritelock();
+ safe_delete ( existWorld );
+ }
+}
+
+bool LWorldList::Init() {
+
+ database.ResetWorldStats ( );
+
+ if (!tcplistener->IsOpen()) {
+ return tcplistener->Open(net.GetPort());
+ }
+
+ return false;
+}
+
+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++){
+ LWorld* world = FindByID(server_list[i]->GetAccountID());
+ if(!world){
+ server_list[i]->ShowDown(true);
+ server_list[i]->ShowDownActive(true);
+ server_list[i]->SetID ( server_list[i]->GetAccountID ( ) );
+ Add ( server_list[i] );
+ }
+ }
+}
+
+int32 LWorldList::GetCount(ConType type) {
+ int32 count = 0;
+ map::iterator map_list;
+ for( map_list = worldmap.begin(); map_list != worldmap.end(); map_list++) {
+ LWorld* world = map_list->second;
+ if (world->GetType() == type) {
+ count++;
+ }
+ }
+ return count;
+}
+
+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++) {
+ 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) {
+ cout << "ID: " << world->GetID() << ", Chat Server, IP: " << inet_ntoa(in) << ":" << world->GetPort() << ", AccID: " << world->GetAccountID() << endl;
+ }
+ 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 {
+ cout << "ID: " << world->GetID() << ", Unknown Type, Name: " << world->GetName() << ", IP: " << inet_ntoa(in) << ":" << world->GetPort() << ", AccID: " << world->GetAccountID() << endl;
+ }
+ }
+ cout << "============================" << endl;
+}
+
+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){
+ world->Kick("Hacking attempt.");
+ return;
+ }
+ zone_updates_already_used[server_id][itr->first] = true;
+ }
+ server_zone_updates.Put(server_id, updates);
+}
+//devn00b temp
+
+void LWorldList::AddServerEquipmentUpdates(LWorld* world, map updates){
+ int32 server_id = world->GetID();
+ map::iterator 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);
+}
+
+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);
+ delete pack;
+ zone_update_timeouts.Put(world->GetID(), Timer::GetCurrentTime2() + 30000);
+ }
+}
+
+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()){
+ 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++){
+ world = map_itr->second;
+ 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()){
+ awaiting_zone_update.erase(world->GetID());
+ RequestServerUpdates(world);
+ }
+ }
+ }
+ ProcessLSEquipUpdates();
+ MWorldMap.releasereadlock();
+
+
+}
+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; // 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);
+ }
+}
+void LWorldList::ProcessLSEquipUpdates()
+{
+ // 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); //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())
+ {
+ 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++)
+ {
+ world = map_itr->second;
+ 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())
+ {
+ LogWrite(LOGIN__DEBUG, 1, "Login", "Erase awaiting equip updates...");
+ awaiting_equip_update.erase(world->GetID());
+ RequestServerEquipUpdates(world);
+ }
+ }
+ }
+ MWorldMap.releasereadlock();
+}
+
+
+ThreadReturnType ServerUpdateLoop(void* tmp) {
+#ifdef WIN32
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
+#endif
+ if (tmp == 0) {
+ ThrowError("ServerUpdateLoop(): tmp = 0!");
+ THREAD_RETURN(NULL);
+ }
+ LWorldList* worldList = (LWorldList*) tmp;
+ while (worldList->ContinueServerUpdates()) {
+ Sleep(1000);
+ worldList->ProcessServerUpdates();
+ }
+ worldList->ResetServerUpdates();
+ THREAD_RETURN(NULL);
+}
diff --git a/source/LoginServer/LWorld.h b/source/LoginServer/LWorld.h
new file mode 100644
index 0000000..d9e3361
--- /dev/null
+++ b/source/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/source/LoginServer/Login.dsp b/source/LoginServer/Login.dsp
new file mode 100644
index 0000000..cf66e51
--- /dev/null
+++ b/source/LoginServer/Login.dsp
@@ -0,0 +1,447 @@
+# Microsoft Developer Studio Project File - Name="Login" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Console Application" 0x0103
+
+CFG=Login - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "Login.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "Login.mak" CFG="Login - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "Login - Win32 Release" (based on "Win32 (x86) Console Application")
+!MESSAGE "Login - Win32 Debug" (based on "Win32 (x86) Console Application")
+!MESSAGE "Login - Win32 MiniLogin" (based on "Win32 (x86) Console Application")
+!MESSAGE "Login - Win32 PublicLogin" (based on "Win32 (x86) Console Application")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "Login - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "../Build"
+# PROP Intermediate_Dir "../Build/Login"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
+# ADD CPP /nologo /MT /w /W0 /GX /Zi /O2 /Ob2 /D "LOGINCRYPTO" /D "INVERSEXY" /D _WIN32_WINNT=0x0400 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo /o"../Build/Login/Login.bsc"
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib zlib.lib mysqlclient.lib /nologo /subsystem:console /map:"../Build/Login.map" /debug /machine:I386
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF "$(CFG)" == "Login - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Login___Win32_Debug"
+# PROP BASE Intermediate_Dir "Login___Win32_Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "../build/login/Debug"
+# PROP Intermediate_Dir "../build/login/debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /Gm /GX /ZI /Od /D "LOGINCRYPTO" /D "INVERSEXY" /D _WIN32_WINNT=0x0400 /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /YX /FD /GZ /c
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib zlib.lib mysqlclient.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"LIBCMT" /out:"../build/login/Debug/LoginDebug.exe" /pdbtype:sept
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF "$(CFG)" == "Login - Win32 MiniLogin"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Login___Win32_MiniLogin"
+# PROP BASE Intermediate_Dir "Login___Win32_MiniLogin"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "../Build"
+# PROP Intermediate_Dir "../Build/MiniLogin"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /w /W0 /GX /O2 /Ob2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "BUILD_FOR_WINDOWS" /FR /YX /FD /c
+# ADD CPP /nologo /MT /w /W0 /GX /O2 /Ob2 /D _WIN32_WINNT=0x0400 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "MINILOGIN" /FR /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo /o"../Build/Login/Login.bsc"
+# ADD BSC32 /nologo /o"../Build/MiniLogin/Login.bsc"
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib zlib.lib mysqlclient.lib /nologo /subsystem:console /machine:I386
+# SUBTRACT BASE LINK32 /pdb:none
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib zlib.lib mysqlclient.lib /nologo /subsystem:console /machine:I386 /out:"../Build/MiniLogin.exe"
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF "$(CFG)" == "Login - Win32 PublicLogin"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Login___Win32_PublicLogin"
+# PROP BASE Intermediate_Dir "Login___Win32_PublicLogin"
+# PROP BASE Ignore_Export_Lib 0
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "../Build"
+# PROP Intermediate_Dir "../Build/PublicLogin"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /w /W0 /GX /O2 /Ob2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "BUILD_FOR_WINDOWS" /FR /YX /FD /c
+# ADD CPP /nologo /MT /w /W0 /GX /O2 /Ob2 /D _WIN32_WINNT=0x0400 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /D "PUBLICLOGIN" /FR /YX /FD /c
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo /o"../Build/Login/Login.bsc"
+# ADD BSC32 /nologo /o"../Build/Login/Login.bsc"
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib zlib.lib mysqlclient.lib /nologo /subsystem:console /machine:I386
+# SUBTRACT BASE LINK32 /pdb:none
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib zlib.lib mysqlclient.lib /nologo /subsystem:console /machine:I386 /out:"../Build/PublicLogin.exe"
+# SUBTRACT LINK32 /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "Login - Win32 Release"
+# Name "Login - Win32 Debug"
+# Name "Login - Win32 MiniLogin"
+# Name "Login - Win32 PublicLogin"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE=.\client.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\EQCrypto.cpp
+
+!IF "$(CFG)" == "Login - Win32 Release"
+
+!ELSEIF "$(CFG)" == "Login - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "Login - Win32 MiniLogin"
+
+# PROP Exclude_From_Build 1
+
+!ELSEIF "$(CFG)" == "Login - Win32 PublicLogin"
+
+# PROP Exclude_From_Build 1
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\logindatabase.cpp
+
+!IF "$(CFG)" == "Login - Win32 Release"
+
+!ELSEIF "$(CFG)" == "Login - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "Login - Win32 MiniLogin"
+
+# PROP Exclude_From_Build 1
+
+!ELSEIF "$(CFG)" == "Login - Win32 PublicLogin"
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\LWorld.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=.\net.cpp
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\client.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\EQCrypto.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\login_opcodes.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\login_structs.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\LWorld.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\net.h
+# End Source File
+# End Group
+# Begin Group "Common Source Files"
+
+# PROP Default_Filter ".cpp"
+# Begin Source File
+
+SOURCE=..\common\crc32.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\database.cpp
+
+!IF "$(CFG)" == "Login - Win32 Release"
+
+!ELSEIF "$(CFG)" == "Login - Win32 Debug"
+
+!ELSEIF "$(CFG)" == "Login - Win32 MiniLogin"
+
+# PROP Exclude_From_Build 1
+
+!ELSEIF "$(CFG)" == "Login - Win32 PublicLogin"
+
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\dbcore.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\DBMemLeak.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\debug.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\EQNetwork.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\md5.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\MiscFunctions.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\Mutex.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\packet_dump.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\packet_functions.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\TCPConnection.cpp
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\timer.cpp
+# End Source File
+# End Group
+# Begin Group "Common Header Files"
+
+# PROP Default_Filter ".h"
+# Begin Source File
+
+SOURCE=..\common\classes.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\crc32.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\database.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\DBMemLeak.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\debug.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\deity.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\eq_opcodes.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\eq_packet_structs.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\EQCheckTable.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\EQFragment.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\EQNetwork.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\EQOpcodes.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\EQPacket.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\EQPacketManager.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\errmsg.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\Guilds.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\linked_list.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\md5.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\MiscFunctions.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\moremath.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\Mutex.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\packet_dump.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\packet_dump_file.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\packet_functions.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\queue.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\queues.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\races.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\Seperator.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\servertalk.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\TCPConnection.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\timer.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\types.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\common\version.h
+# End Source File
+# End Group
+# Begin Group "Text Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Protocol.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\Tables.txt
+# End Source File
+# Begin Source File
+
+SOURCE=.\ThanksTo.txt
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/source/LoginServer/Login.dsw b/source/LoginServer/Login.dsw
new file mode 100644
index 0000000..4ed0adf
--- /dev/null
+++ b/source/LoginServer/Login.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Login"=.\Login.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/source/LoginServer/Login.vcproj b/source/LoginServer/Login.vcproj
new file mode 100644
index 0000000..7c1f7a6
--- /dev/null
+++ b/source/LoginServer/Login.vcproj
@@ -0,0 +1,542 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/LoginServer/Login.vcxproj b/source/LoginServer/Login.vcxproj
new file mode 100644
index 0000000..5622e75
--- /dev/null
+++ b/source/LoginServer/Login.vcxproj
@@ -0,0 +1,154 @@
+
+
+
+
+ EQ2Login
+ x64
+
+
+
+ EQ2Login
+ {BE2C1914-FCCC-4F65-A7DD-105142B36104}
+ EQ2 Login
+ 10.0
+
+
+
+ v142
+
+
+
+
+
+
+ <_ProjectFileVersion>10.0.30319.1
+
+
+ $(SolutionDir)..\source\depends\mariadb-10.1.19\include;$(SolutionDir)..\source\depends\zlib\include;$(SolutionDir)..\source\depends\recastnavigation\Detour\Include;$(SolutionDir)..\source\depends\boost_1_72_0\;$(SolutionDir)..\source\depends\glm\;$(VC_IncludePath);$(WindowsSDK_IncludePath);
+ $(SolutionDir)..\source\depends\recastnavigation\RecastDemo\Build\vs2019\lib\Debug;$(SolutionDir)..\source\depends\mariadb-10.1.19\lib\64-debug;$(SolutionDir)..\source\depends\zlib\lib;$(SolutionDir)..\source\depends\boost_1_72_0\lib64-msvc-14.2;$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64
+ false
+ $(SolutionDir)loginserver\
+ .\$(ProjectName)__Debug64\
+ $(ProjectName)__Debug64
+
+
+
+ Disabled
+ AnySuitable
+ _WIN32_WINNT=0x0400;WIN32;NDEBUG;_CONSOLE;LOGIN; EQ2; EQN_DEBUG;_CRT_SECURE_NO_DEPRECATE;_HAS_STD_BYTE=0
+;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebug
+ false
+ false
+
+
+ $(IntDir)
+
+
+ 4996;%(DisableSpecificWarnings)
+ stdcpp17
+
+
+ odbc32.lib;odbccp32.lib;ws2_32.lib;zlib.lib;mysqlclient.lib;DebugUtils.lib;Detour.lib;DetourCrowd.lib;DetourTileCache.lib;Recast.lib;%(AdditionalDependencies)
+ LIBCMT;LIBC;%(IgnoreSpecificDefaultLibraries)
+ true
+ $(IntDir)$(TargetName).pdb
+ true
+ true
+ Default
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/LoginServer/Login.vcxproj.filters b/source/LoginServer/Login.vcxproj.filters
new file mode 100644
index 0000000..fae3865
--- /dev/null
+++ b/source/LoginServer/Login.vcxproj.filters
@@ -0,0 +1,277 @@
+
+
+
+
+ {bfe8d6b0-594f-4b55-9f95-101bbcf4069c}
+ cpp;c;cxx;rc;def;r;odl;idl;hpj;bat
+
+
+ {d65b2760-468c-4206-a19a-48323a50ba5a}
+ h;hpp;hxx;hm;inl
+
+
+ {27b769a5-0972-4e9e-b78c-09ad3341579c}
+ .cpp
+
+
+ {11757e5a-691c-49c9-a627-df027ad58326}
+ .h
+
+
+ {99e7f9f9-abcd-4abf-8200-a4b5a467788c}
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+ Common Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ World Files
+
+
+ World Files
+
+
+ World Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+ Common Header Files
+
+
+
\ No newline at end of file
diff --git a/source/LoginServer/Login.vcxproj.user b/source/LoginServer/Login.vcxproj.user
new file mode 100644
index 0000000..ace9a86
--- /dev/null
+++ b/source/LoginServer/Login.vcxproj.user
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/source/LoginServer/LoginAccount.cpp b/source/LoginServer/LoginAccount.cpp
new file mode 100644
index 0000000..228f0b5
--- /dev/null
+++ b/source/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/source/LoginServer/LoginAccount.h b/source/LoginServer/LoginAccount.h
new file mode 100644
index 0000000..b5bb540
--- /dev/null
+++ b/source/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/source/LoginServer/LoginDatabase.cpp b/source/LoginServer/LoginDatabase.cpp
new file mode 100644
index 0000000..b750a62
--- /dev/null
+++ b/source/LoginServer/LoginDatabase.cpp
@@ -0,0 +1,1070 @@
+/*
+ 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;
+}
+
+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/source/LoginServer/LoginDatabase.h b/source/LoginServer/LoginDatabase.h
new file mode 100644
index 0000000..84f8d7b
--- /dev/null
+++ b/source/LoginServer/LoginDatabase.h
@@ -0,0 +1,95 @@
+/*
+ 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 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/source/LoginServer/PacketHeaders.cpp b/source/LoginServer/PacketHeaders.cpp
new file mode 100644
index 0000000..4511f6a
--- /dev/null
+++ b/source/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/source/LoginServer/PacketHeaders.h b/source/LoginServer/PacketHeaders.h
new file mode 100644
index 0000000..69a30c5
--- /dev/null
+++ b/source/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/source/LoginServer/Web/LoginWeb.cpp b/source/LoginServer/Web/LoginWeb.cpp
new file mode 100644
index 0000000..e1e26e3
--- /dev/null
+++ b/source/LoginServer/Web/LoginWeb.cpp
@@ -0,0 +1,64 @@
+#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("ip_addr", inet_ntoa(in));
+ maintree.add_child("WorldServer", pt);
+ }
+ }
+
+ boost::property_tree::write_json(oss, maintree);
+ std::string json = oss.str();
+ res.body() = json;
+ res.prepare_payload();
+}
+
diff --git a/source/LoginServer/Web/LoginWeb.o b/source/LoginServer/Web/LoginWeb.o
new file mode 100644
index 0000000..29c26ff
Binary files /dev/null and b/source/LoginServer/Web/LoginWeb.o differ
diff --git a/source/LoginServer/client.cpp b/source/LoginServer/client.cpp
new file mode 100644
index 0000000..d41728f
--- /dev/null
+++ b/source/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/source/LoginServer/client.h b/source/LoginServer/client.h
new file mode 100644
index 0000000..4d3936e
--- /dev/null
+++ b/source/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/source/LoginServer/login_opcodes.h b/source/LoginServer/login_opcodes.h
new file mode 100644
index 0000000..734ae2f
--- /dev/null
+++ b/source/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/source/LoginServer/login_structs.h b/source/LoginServer/login_structs.h
new file mode 100644
index 0000000..bd8e9b9
--- /dev/null
+++ b/source/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/source/LoginServer/makefile b/source/LoginServer/makefile
new file mode 100644
index 0000000..a4ce509
--- /dev/null
+++ b/source/LoginServer/makefile
@@ -0,0 +1,32 @@
+APP=login
+SF= ../common/Log.o ../common/timer.o ../common/packet_dump.o ../common/unix.o \
+ ../common/Mutex.o ../common/MiscFunctions.o LoginDatabase.o LoginAccount.o \
+ ../common/TCPConnection.o ../common/emu_opcodes.o \
+ client.o net.o PacketHeaders.o LWorld.o ../common/md5.o ../common/dbcore.o \
+ Web/LoginWeb.o \
+ ../common/EQEMuError.o ../common/misc.o ../common/Crypto.o ../common/RC4.o \
+ .obj/debug.o .obj/database.o .obj/EQStream.o ../common/xmlParser.o \
+ .obj/EQStreamFactory.o .obj/EQPacket.o ../common/CRC16.o ../common/packet_functions.o \
+ ../common/Condition.o ../common/opcodemgr.o ../common/PacketStruct.o ../common/ConfigReader.o \
+ ../common/DatabaseNew.o ../common/DatabaseResult.o ../common/Web/WebServer.o ../common/JsonParser.o
+
+CC=g++
+LINKER=gcc
+DFLAGS=-DEQ2 -DLOGIN
+WFLAGS=-Wall -Wuninitialized -Wwrite-strings -Wcast-qual -Wcomment -Wcast-align -Wno-deprecated
+COPTS=$(WFLAGS) -ggdb -march=native -pthread -pipe -DFX -D_GNU_SOURCE -DINVERSEXY $(DFLAGS) -I/usr/include/mariadb -I/usr/local/include/boost -std=c++17
+LINKOPTS=-rdynamic -L. -lstdc++ -lm -lz -L/usr/lib/x86_64-linux-gnu -lmariadb -lboost_system -lboost_thread -lboost_filesystem -lssl -lcrypto -lpthread -ldl
+all: $(APP)
+
+$(APP): $(SF)
+ $(LINKER) $(COPTS) $(OBJS) $^ $(LINKOPTS) -o $@
+
+clean:
+ rm -f $(SF) $(APP)
+
+%.o: %.cpp
+ $(CC) -c $(COPTS) $< -o $@
+
+.obj/%.o: ../common/%.cpp ../common/%.h
+ mkdir -p .obj
+ $(CC) $(COPTS) -c $< -o $@
diff --git a/source/LoginServer/net.cpp b/source/LoginServer/net.cpp
new file mode 100644
index 0000000..fe6b8cd
--- /dev/null
+++ b/source/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/source/LoginServer/net.h b/source/LoginServer/net.h
new file mode 100644
index 0000000..0f7b628
--- /dev/null
+++ b/source/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/source/WorldServer/Achievements/Achievements.cpp b/source/WorldServer/Achievements/Achievements.cpp
new file mode 100644
index 0000000..0d65164
--- /dev/null
+++ b/source/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/source/WorldServer/Achievements/Achievements.h b/source/WorldServer/Achievements/Achievements.h
new file mode 100644
index 0000000..7b1895c
--- /dev/null
+++ b/source/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