View Single Post
  #1  
Old 04-17-2010, 10:43 AM
Angelox
AX Classic Developer
 
Join Date: May 2006
Location: filler
Posts: 2,049
Default Dual Login Server Code

Secrets: I cant find your original Dual Login server code, so here's what I wanted to add on to it, as it's a continuation of what you did. This has been working fine and I've had it it on since I saw your post a while back.
In summary, here's what happens; first i wanted to maintain separate ID records of my users, so I added another table to table account and called it lsaccount_id2. This is what I have on my notes so has two alterations;
Code:
ALTER TABLE `account` ADD `lsaccount_id2` int(10) UNSIGNED DEFAULT '0'AFTER `lsaccount_id`;
ALTER TABLE `ax_classic`.`account` MODIFY COLUMN `lsaccount_id2` INT(10) UNSIGNED DEFAULT 0;
Since account already had all the users, and I needed one table to start at a higher increment, I converted the original account table;
Code:
UPDATE ax_classic.account SET lsaccount_id=lsaccount_id +5000000 WHERE lsaccount_id > 0;
And my accounts on my LS to match;
Code:
UPDATE new_login.login_accounts SET id=id +5000000;
so now I have my LS and accounts pointed to anything 5000000 or above, and anything below 5000000, must be from EqEmu- I don't think EqEmu will reach 5000000 during my lifetime, but if it did, then just up the increment to my LS accounts.
I have two sets of code, one that I have in use, but since it is sort of "hackish", i wanted one that was true in all areas, one that would monitor and single out/specify each port. But it is a lot of work that goes into a lot of code (and the work is triple for me, because I never know what I'm doing most the time), I got burnt out an put it on my "ToDo" list.
Here's the code I currently use, changes to LS are quoted with DUAL_SERVER, so you can easily pull them out and add them to your code, I posted the whole pages of script, cause diffs won't work with this code, it has other custom stuff, but you can see where the Dual LS code is placed (#ifdef DUAL_SERVER);

LoginServer.cpp
Code:
#include "../common/debug.h"
#include <iostream>
using namespace std;
#include <string.h>
#include <stdio.h>
#include <iomanip>
using namespace std;
#include <stdlib.h>
#include "../common/version.h"

#ifdef WIN32
	#include <process.h>
	#include <windows.h>
	#include <winsock.h>

	#define snprintf	_snprintf
#if (_MSC_VER < 1500)
	#define vsnprintf	_vsnprintf
#endif
	#define strncasecmp	_strnicmp
	#define strcasecmp	_stricmp
#else // Pyro: fix for linux
	#include <sys/socket.h>
#ifdef FREEBSD //Timothy Whitman - January 7, 2003
	#include <sys/types.h>
#endif
	#include <netinet/in.h>
	#include <arpa/inet.h>
	#include <pthread.h>
	#include <unistd.h>
	#include <errno.h>

	#include "../common/unix.h"

	#define SOCKET_ERROR -1
	#define INVALID_SOCKET -1
	extern int errno;
#endif

#define IGNORE_LS_FATAL_ERROR

#include "../common/servertalk.h"
#include "LoginServer.h"
#include "../common/eq_packet_structs.h"
#include "../common/packet_dump.h"
#include "zoneserver.h"
#include "worlddb.h"
#include "zonelist.h"
#include "clientlist.h"
#include "WorldConfig.h"

extern ZSList zoneserver_list;
extern LoginServer loginserver;
extern ClientList client_list;
extern uint32 numzones;
extern int32 numplayers;
extern volatile bool	RunLoops;
volatile bool LoginLoopRunning = false;

bool AttemptingConnect = false;

#ifdef DUAL_SERVER
LoginServer::LoginServer(const char* iAddress, const char* iAddress2, int16 iPort, int16 iPort2)
#else
LoginServer::LoginServer(const char* iAddress, int16 iPort)
#endif

: statusupdate_timer(LoginServer_StatusUpdateInterval)
{
	LoginServerIP = ResolveIP(iAddress);
	LoginServerPort = iPort;
	tcpc = new EmuTCPConnection(true);
	tcpc->SetPacketMode(EmuTCPConnection::packetModeLogin);

#ifdef DUAL_SERVER  //Angelox
	LoginServerIP = ResolveIP(iAddress2);
	LoginServerPort = iPort2;
	tcpc2 = new EmuTCPConnection(true);
	tcpc2->SetPacketMode(EmuTCPConnection::packetModeLogin);
#endif

}

LoginServer::~LoginServer() {
	delete tcpc;

#ifdef DUAL_SERVER
	delete tcpc2;
#endif

}

bool LoginServer::Process() {
	const WorldConfig *Config=WorldConfig::get();

	if (statusupdate_timer.Check()) {
		this->SendStatus();
	}
    
	/************ Get all packets from packet manager out queue and process them ************/
	ServerPacket *pack = 0;

#ifdef DUAL_SERVER
	ServerPacket *pack2 = 0;
#endif

	while((pack = tcpc->PopPacket()))
	{
		_log(WORLD__LS_TRACE,"Recevied ServerPacket from LS OpCode 0x04x",pack->opcode);
		_hex(WORLD__LS_TRACE,pack->pBuffer,pack->size);

		switch(pack->opcode) {
		case 0:
			break;
		case ServerOP_KeepAlive: {
			// ignore this
			break;
		}
		case ServerOP_UsertoWorldReq: {
			UsertoWorldRequest_Struct* utwr = (UsertoWorldRequest_Struct*) pack->pBuffer;	
			int32 id = database.GetAccountIDFromLSID(utwr->lsaccountid);
			sint16 status = database.CheckStatus(id);

			ServerPacket* outpack = new ServerPacket;
			outpack->opcode = ServerOP_UsertoWorldResp;
			outpack->size = sizeof(UsertoWorldResponse_Struct);
			outpack->pBuffer = new uchar[outpack->size];
			memset(outpack->pBuffer, 0, outpack->size);
			UsertoWorldResponse_Struct* utwrs = (UsertoWorldResponse_Struct*) outpack->pBuffer;
			utwrs->lsaccountid = utwr->lsaccountid;
			utwrs->ToID = utwr->FromID;

			if(Config->Locked == true)
			{
				if((status == 0 || status < 100) && (status != -2 || status != -1))
					utwrs->response = 0;
				if(status >= 100)
					utwrs->response = 1;
			}
			else {
				utwrs->response = 1;
			}

			sint32 x = Config->MaxClients;
			if( (sint32)numplayers >= x && x != -1 && x != 255 && status < 80)
				utwrs->response = -3;

			if(status == -1)
				utwrs->response = -1;
			if(status == -2)
				utwrs->response = -2;

			utwrs->worldid = utwr->worldid;
			SendPacket(outpack);
			delete outpack;
			break;
		}
		case ServerOP_LSClientAuth: {
			ServerLSClientAuth* slsca = (ServerLSClientAuth*) pack->pBuffer;

			if (RuleI(World, AccountSessionLimit) >= 0) {
				// Enforce the limit on the number of characters on the same account that can be 
				// online at the same time.
				client_list.EnforceSessionLimit(slsca->lsaccount_id); 
			}

			client_list.CLEAdd(slsca->lsaccount_id, slsca->name, slsca->key, slsca->worldadmin, slsca->ip, slsca->local);
			break;
		}
		case ServerOP_LSFatalError: {
#ifndef IGNORE_LS_FATAL_ERROR
			WorldConfig::DisableLoginserver();
			_log(WORLD__LS_ERR, "Login server responded with FatalError. Disabling reconnect.");
#else
		_log(WORLD__LS_ERR, "Login server responded with FatalError.");
#endif
			if (pack->size > 1) {
				_log(WORLD__LS_ERR, "     %s",pack->pBuffer);
			}
			break;
		}
		case ServerOP_SystemwideMessage: {
			ServerSystemwideMessage* swm = (ServerSystemwideMessage*) pack->pBuffer;
			zoneserver_list.SendEmoteMessageRaw(0, 0, 0, swm->type, swm->message);
			break;
		}
		case ServerOP_LSRemoteAddr: {
			if (!Config->WorldAddress.length()) {
				WorldConfig::SetWorldAddress((char *)pack->pBuffer);
				_log(WORLD__LS, "Loginserver provided %s as world address",pack->pBuffer);
			}
			break;
		}
		default:
		{
			_log(WORLD__LS_ERR, "Unknown LSOpCode: 0x%04x size=%d",(int)pack->opcode,pack->size);
DumpPacket(pack->pBuffer, pack->size);
			break;
		}
		}

		delete pack;
	}

#ifdef DUAL_SERVER
		while((pack2 = tcpc2->PopPacket()))
	{
		_log(WORLD__LS_TRACE,"Recevied ServerPacket from LS OpCode 0x04x",pack2->opcode);
		_hex(WORLD__LS_TRACE,pack2->pBuffer,pack2->size);

		switch(pack2->opcode) {
		case 0:
			break;
		case ServerOP_KeepAlive: {
			// ignore this
			break;
		}
		case ServerOP_UsertoWorldReq: {
			UsertoWorldRequest_Struct* utwr = (UsertoWorldRequest_Struct*) pack2->pBuffer;	
			int32 id = database.GetAccountIDFromLSID(utwr->lsaccountid);
			sint16 status = database.CheckStatus(id);

			ServerPacket* outpack = new ServerPacket;
			outpack->opcode = ServerOP_UsertoWorldResp;
			outpack->size = sizeof(UsertoWorldResponse_Struct);
			outpack->pBuffer = new uchar[outpack->size];
			memset(outpack->pBuffer, 0, outpack->size);
			UsertoWorldResponse_Struct* utwrs = (UsertoWorldResponse_Struct*) outpack->pBuffer;
			utwrs->lsaccountid = utwr->lsaccountid;
			utwrs->ToID = utwr->FromID;

			if(Config->Locked == true)
			{
				if((status == 0 || status < 100) && (status != -2 || status != -1))
					utwrs->response = 0;
				if(status >= 100)
					utwrs->response = 1;
			}
			else {
				utwrs->response = 1;
			}

			sint32 x = Config->MaxClients;
			if( (sint32)numplayers >= x && x != -1 && x != 255 && status < 80)
				utwrs->response = -3;

			if(status == -1)
				utwrs->response = -1;
			if(status == -2)
				utwrs->response = -2;

			utwrs->worldid = utwr->worldid;
			SendPacket2(outpack);
			delete outpack;
			break;
		}
		case ServerOP_LSClientAuth: {
			ServerLSClientAuth* slsca = (ServerLSClientAuth*) pack2->pBuffer;

			if (RuleI(World, AccountSessionLimit) >= 0) {
				// Enforce the limit on the number of characters on the same account that can be 
				// online at the same time.
				client_list.EnforceSessionLimit(slsca->lsaccount_id); 
			}

			client_list.CLEAdd(slsca->lsaccount_id, slsca->name, slsca->key, slsca->worldadmin, slsca->ip, slsca->local);
			break;
		}
		case ServerOP_LSFatalError: {
#ifndef IGNORE_LS_FATAL_ERROR
			WorldConfig::DisableLoginserver();
			_log(WORLD__LS_ERR, "Login server responded with FatalError. Disabling reconnect.");
#else
		_log(WORLD__LS_ERR, "Login server responded with FatalError.");
#endif
			if (pack2->size > 1) {
				_log(WORLD__LS_ERR, "     %s",pack2->pBuffer);
			}
			break;
		}
		case ServerOP_SystemwideMessage: {
			ServerSystemwideMessage* swm = (ServerSystemwideMessage*) pack2->pBuffer;
			zoneserver_list.SendEmoteMessageRaw(0, 0, 0, swm->type, swm->message);
			break;
		}
		case ServerOP_LSRemoteAddr: {
			if (!Config->WorldAddress.length()) {
				WorldConfig::SetWorldAddress((char *)pack2->pBuffer);
				_log(WORLD__LS, "Loginserver provided %s as world address",pack2->pBuffer);
			}
			break;
		}
		default:
		{
			_log(WORLD__LS_ERR, "Unknown LSOpCode: 0x%04x size=%d",(int)pack2->opcode,pack->size);
DumpPacket(pack2->pBuffer, pack2->size);
			break;
		}
		}

		delete pack2;
		}
#endif //END DUAL_SERVER

	return true;
}

// this should always be called in a new thread
#ifdef WIN32
	void AutoInitLoginServer(void *tmp) {
#else
	void *AutoInitLoginServer(void *tmp) {
#endif
	srand(time(NULL));

#ifdef DUAL_SERVER
	if (loginserver.ConnectReady() && loginserver.ConnectReady2()) {
#else
	if (loginserver.ConnectReady()) {
#endif

		InitLoginServer();
	}
#ifndef WIN32
	return 0;
#endif
}
//Start
bool InitLoginServer() {
#ifdef DUAL_SERVER
	_log(WORLD__LS, "Dual Connecting to the login servers...");
#else
	_log(WORLD__LS, "Connecting to login server...");
#endif
	const WorldConfig *Config=WorldConfig::get();

#ifdef DUAL_SERVER
	if ((!loginserver.ConnectReady()) || (!loginserver.ConnectReady2())) {
#else
	if (!loginserver.ConnectReady()) {
#endif

		_log(WORLD__LS_ERR,"InitLoginServer() while already attempting connect");
		return false;
	}
	if (!Config->LoginHost.length()) {
		_log(WORLD__LS_ERR,"Login server info not loaded");
		return false;
	}

	AttemptingConnect = true;

#ifdef DUAL_SERVER
	loginserver.Connect(Config->LoginHost.c_str(), Config->LoginPort, Config->LoginHost2.c_str(), Config->LoginPort2);
#else
	loginserver.Connect(Config->LoginHost.c_str(), Config->LoginPort);
#endif

	return true;
}

#ifdef DUAL_SERVER
bool LoginServer::Connect(const char* iAddress, int16 iPort, const char* iAddress2, int16 iPort2) {
#else
bool LoginServer::Connect(const char* iAddress, int16 iPort) {
#endif

	char tmp[25];
	if(database.GetVariable("loginType",tmp,sizeof(tmp)) && strcasecmp(tmp,"MinILogin") == 0){
		minilogin = true;
		_log(WORLD__LS, "Setting World to MiniLogin Server type");
	}
	else
		minilogin = false;

	if (minilogin && WorldConfig::get()->WorldAddress.length()==0) {
		_log(WORLD__LS_ERR, "**** For minilogin to work, you need to set the <address> element in the <world> section.");
		return false;
	}

	char errbuf[TCPConnection_ErrorBufferSize];

#ifdef DUAL_SERVER
	//char errbuf2[TCPConnection_ErrorBufferSize];
#endif

	if (iAddress == 0) {
		_log(WORLD__LS_ERR, "Null address given to LoginServer::Connect");
		return false;
	}
	else {
		if ((LoginServerIP = ResolveIP(iAddress, errbuf)) == 0)  {
			_log(WORLD__LS_ERR, "Unable to resolve '%s' to an IP.",iAddress);
			return false;
		}
	}
	if (iPort != 0)
		LoginServerPort = iPort;

	if (LoginServerIP == 0 || LoginServerPort == 0) {
		_log(WORLD__LS_ERR, "LoginServer::Connect: Connect info incomplete, cannot connect");
		return false;
	}

#ifdef DUAL_SERVER
	if (iAddress2 == 0) {
		_log(WORLD__LS_ERR, "Null address given to LoginServer::Connect");
		return false;
	}
	else {
		if ((LoginServerIP2 = ResolveIP(iAddress2, errbuf)) == 0) {
			_log(WORLD__LS_ERR, "Unable to resolve '%s' to an IP.",iAddress2);
			return false;
		}
	}
	if (iPort2 != 0)
		LoginServerPort2 = iPort2;

	if (LoginServerIP2 == 0 || LoginServerPort2 == 0) {
		_log(WORLD__LS_ERR, "LoginServer2::Connect: Connect info incomplete, cannot connect");
		return false;
	}


	if (tcpc->ConnectIP(LoginServerIP, LoginServerPort, errbuf) && tcpc2->ConnectIP(LoginServerIP2, LoginServerPort2, errbuf)) {
		_log(WORLD__LS, "Connected to EqEmu Loginserver: %s:%d",iAddress,LoginServerPort);
		_log(WORLD__LS, "Connected to AXClassic Loginserver: %s:%d",iAddress2,LoginServerPort2);
#else
	if (tcpc->ConnectIP(LoginServerIP, LoginServerPort, errbuf)) {
		_log(WORLD__LS, "Connected to Loginserver: %s:%d",iAddress,LoginServerPort);
#endif

		if (minilogin)
			SendInfo();
		else
			SendNewInfo();
		SendStatus();
		zoneserver_list.SendLSZones();
		return true;
	}
	else {

#ifdef DUAL_SERVER
		_log(WORLD__LS_ERR, "FATAL ERROR! - SERVERS NOT CONNECTED. One or more connections not esablished, so none are connected.",errbuf);
#else
		_log(WORLD__LS_ERR, "Could not connect to login server: %s",errbuf);
#endif

		return false;
	}
}
void LoginServer::SendInfo() {
	const WorldConfig *Config=WorldConfig::get();

	ServerPacket* pack = new ServerPacket;
	pack->opcode = ServerOP_LSInfo;
	pack->size = sizeof(ServerLSInfo_Struct);
	pack->pBuffer = new uchar[pack->size];
	memset(pack->pBuffer, 0, pack->size);
	ServerLSInfo_Struct* lsi = (ServerLSInfo_Struct*) pack->pBuffer;
	strcpy(lsi->protocolversion, EQEMU_PROTOCOL_VERSION);
	strcpy(lsi->serverversion, CURRENT_VERSION);
	strcpy(lsi->name, Config->LongName.c_str());
	strcpy(lsi->account, Config->LoginAccount.c_str());
	strcpy(lsi->password, Config->LoginPassword.c_str());
	strcpy(lsi->address, Config->WorldAddress.c_str());
	SendPacket(pack);

#ifdef DUAL_SERVER
	ServerPacket* pack2 = new ServerPacket;
	pack2->opcode = ServerOP_LSInfo;
	pack2->size = sizeof(ServerLSInfo_Struct);
	pack2->pBuffer = new uchar[pack2->size];
	memset(pack2->pBuffer, 0, pack2->size);
	ServerLSInfo_Struct* lsi2 = (ServerLSInfo_Struct*) pack2->pBuffer;
	strcpy(lsi2->protocolversion, EQEMU_PROTOCOL_VERSION);
	strcpy(lsi2->serverversion, CURRENT_VERSION);
	strcpy(lsi2->name, Config->LongName.c_str());
	strcpy(lsi2->account, Config->LoginAccount2.c_str());
	strcpy(lsi2->password, Config->LoginPassword2.c_str());
	strcpy(lsi2->address, Config->WorldAddress.c_str());
	SendPacket2(pack2);
#endif

	delete pack;

#ifdef DUAL_SERVER
	delete pack2;
#endif
}

void LoginServer::SendNewInfo() {
	uint16 port;
	const WorldConfig *Config=WorldConfig::get();

	ServerPacket* pack = new ServerPacket;
	pack->opcode = ServerOP_NewLSInfo;
	pack->size = sizeof(ServerNewLSInfo_Struct);
	pack->pBuffer = new uchar[pack->size];
	memset(pack->pBuffer, 0, pack->size);
	ServerNewLSInfo_Struct* lsi = (ServerNewLSInfo_Struct*) pack->pBuffer;
	strcpy(lsi->protocolversion, EQEMU_PROTOCOL_VERSION);
	strcpy(lsi->serverversion, CURRENT_VERSION);
	strcpy(lsi->name, Config->LongName.c_str());
	strcpy(lsi->shortname, Config->ShortName.c_str());
	strcpy(lsi->account, Config->LoginAccount.c_str());
	strcpy(lsi->password, Config->LoginPassword.c_str());
	if (Config->WorldAddress.length())
		strcpy(lsi->remote_address, Config->WorldAddress.c_str());
	if (Config->LocalAddress.length())
		strcpy(lsi->local_address, Config->LocalAddress.c_str());
	else {
		tcpc->GetSockName(lsi->local_address,&port);
		WorldConfig::SetLocalAddress(lsi->local_address);
	}
	SendPacket(pack);

#ifdef DUAL_SERVER
	ServerPacket* pack2 = new ServerPacket;
	pack2->opcode = ServerOP_NewLSInfo;
	pack2->size = sizeof(ServerNewLSInfo_Struct);
	pack2->pBuffer = new uchar[pack2->size];
	memset(pack2->pBuffer, 0, pack2->size);
	ServerNewLSInfo_Struct* lsi2 = (ServerNewLSInfo_Struct*) pack2->pBuffer;
	strcpy(lsi2->protocolversion, EQEMU_PROTOCOL_VERSION);
	strcpy(lsi2->serverversion, CURRENT_VERSION);
	strcpy(lsi2->name, Config->LongName.c_str());
	strcpy(lsi2->shortname, Config->ShortName.c_str());
	strcpy(lsi2->account, Config->LoginAccount.c_str());
	strcpy(lsi2->password, Config->LoginPassword.c_str());
	if (Config->WorldAddress.length())
		strcpy(lsi2->remote_address, Config->WorldAddress.c_str());
	if (Config->LocalAddress.length())
		strcpy(lsi2->local_address, Config->LocalAddress.c_str());
	else {
		tcpc2->GetSockName(lsi2->local_address,&port);
		WorldConfig::SetLocalAddress(lsi2->local_address);
	}
	//SendPacket(pack); Angelox
	SendPacket2(pack2);
#endif

	delete pack;

#ifdef DUAL_SERVER
	delete pack2;
#endif
}

void LoginServer::SendStatus() {
	statusupdate_timer.Start();
	ServerPacket* pack = new ServerPacket;
	pack->opcode = ServerOP_LSStatus;
	pack->size = sizeof(ServerLSStatus_Struct);
	pack->pBuffer = new uchar[pack->size];
	memset(pack->pBuffer, 0, pack->size);
	ServerLSStatus_Struct* lss = (ServerLSStatus_Struct*) pack->pBuffer;

	if (WorldConfig::get()->Locked)
		lss->status = -2;
	else if (numzones <= 0)
		lss->status = -2;
	else
		lss->status = numplayers;

	lss->num_zones = numzones;
	lss->num_players = numplayers;
	SendPacket(pack);

#ifdef DUAL_SERVER
	SendPacket2(pack);
#endif

	delete pack;
}
client.cpp
Code:
#include "../common/debug.h"
#include "../common/EQPacket.h"
#include "../common/EQStreamIntf.h"
#include "../common/misc.h"
#include <iostream>
using namespace std;
#include <iomanip>
using namespace std;
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <zlib.h>
#include <limits.h>
//FatherNitwit: uncomment to enable my IP based authentication hack
//#define IPBASED_AUTH_HACK

// Disgrace: for windows compile
#ifdef WIN32
	#include <windows.h>
	#include <winsock.h>
	#define snprintf	_snprintf
#if (_MSC_VER < 1500)
	#define vsnprintf	_vsnprintf
#endif
	#define strncasecmp	_strnicmp
	#define strcasecmp  _stricmp
#else
	#include <sys/socket.h>
#ifdef FREEBSD //Timothy Whitman - January 7, 2003
	#include <sys/types.h>
#endif
	#include <netinet/in.h>
	#include <arpa/inet.h>
	#include <unistd.h>
#endif

#include "client.h"
#include "../common/emu_opcodes.h"
#include "../common/eq_packet_structs.h"
#include "../common/packet_dump.h"
#include "../common/EQStreamIntf.h"
#include "worlddb.h"
#include "../common/Item.h"
#include "../common/races.h"
#include "../common/classes.h"
#include "../common/languages.h"
#include "../common/skills.h"
#include "../common/extprofile.h"
#include "WorldConfig.h"
#include "LoginServer.h"
#include "zoneserver.h"
#include "zonelist.h"
#include "clientlist.h"
#include "wguild_mgr.h"
#include "../common/rulesys.h"
#include "SoFCharCreateData.h"

extern ZSList zoneserver_list;
extern LoginServer loginserver;
extern ClientList client_list;
extern uint32 numclients;
extern volatile bool RunLoops;

Client::Client(EQStreamInterface* ieqs)
: autobootup_timeout(RuleI(World, ZoneAutobootTimeoutMS)),
  CLE_keepalive_timer(RuleI(World, ClientKeepaliveTimeoutMS)),
  connect(1000),
  eqs(ieqs)
{
	// Live does not send datarate as of 3/11/2005
	//eqs->SetDataRate(7);
	ip = eqs->GetRemoteIP();
	port = ntohs(eqs->GetRemotePort());

	autobootup_timeout.Disable();
	connect.Disable();
	seencharsel = false;
	cle = 0;
	zoneID = 0;
	char_name[0] = 0;
	charid = 0;
	pwaitingforbootup = 0;
	StartInTutorial = false;
	SoFClient = false;
	numclients++;
}

Client::~Client() {
	if (RunLoops && cle && zoneID == 0)
		cle->SetOnline(CLE_Status_Offline);

	numclients--;

	//let the stream factory know were done with this stream
	eqs->Close();
	eqs->ReleaseFromUse();
}

void Client::SendLogServer()
{
	EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogServer, sizeof(LogServer_Struct));
	LogServer_Struct *l=(LogServer_Struct *)outapp->pBuffer;
	const char *wsn=WorldConfig::get()->ShortName.c_str();
	memcpy(l->worldshortname,wsn,strlen(wsn));

	if(RuleB(Mail, EnableMailSystem))
		l->enablemail = 1;

	if(RuleB(Chat, EnableVoiceMacros))
		l->enablevoicemacros = 1;

	if(database.GetServerType() == 1)
		l->enable_pvp = 1;

	//enable when we are ready to implement this!
	//l->enable_petition_wnd = 1;

	QueuePacket(outapp);
	safe_delete(outapp);
}

void Client::SendEnterWorld(string name)
{
char char_name[32]= { 0 };
	if (pZoning && database.GetLiveChar(GetAccountID(), char_name)) {
		if(database.GetAccountIDByChar(char_name) != GetAccountID()) {
			eqs->Close();
			return;
		} else {
			clog(WORLD__CLIENT,"Telling client to continue session.");
		}
	}

	EQApplicationPacket *outapp = new EQApplicationPacket(OP_EnterWorld, strlen(char_name)+1);
	memcpy(outapp->pBuffer,char_name,strlen(char_name)+1);
	QueuePacket(outapp);
	safe_delete(outapp);
}

void Client::SendExpansionInfo() {
	EQApplicationPacket *outapp = new EQApplicationPacket(OP_ExpansionInfo, sizeof(ExpansionInfo_Struct));
	ExpansionInfo_Struct *eis = (ExpansionInfo_Struct*)outapp->pBuffer;
	char val[20] = {0};
	if (database.GetVariable("Expansions", val, 20)) {
		eis->Expansions = atoi(val);
	}
	else {
		eis->Expansions = 0x1FF;
	}
	QueuePacket(outapp);
	safe_delete(outapp);
}

void Client::SendCharInfo() {
	if (cle) {
		cle->SetOnline(CLE_Status_CharSelect);
	}

	seencharsel = true;


	// Send OP_SendCharInfo
	EQApplicationPacket *outapp = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct));
	CharacterSelect_Struct* cs = (CharacterSelect_Struct*)outapp->pBuffer;

	database.GetCharSelectInfo(GetAccountID(), cs);

	QueuePacket(outapp);
	safe_delete(outapp);
}

void Client::SendPostEnterWorld() {
	EQApplicationPacket *outapp = new EQApplicationPacket(OP_PostEnterWorld, 1);
	outapp->size=0;
	QueuePacket(outapp);
	safe_delete(outapp);
}

bool Client::HandlePacket(const EQApplicationPacket *app) {
	const WorldConfig *Config=WorldConfig::get();
	EmuOpcode opcode = app->GetOpcode();

	clog(WORLD__CLIENT_TRACE,"Recevied EQApplicationPacket");
	_pkt(WORLD__CLIENT_TRACE,app);

	bool ret = true;

	if (!eqs->CheckState(ESTABLISHED)) {
		clog(WORLD__CLIENT,"Client disconnected (net inactive on send)");
		return false;
	}

	// Voidd: Anti-GM Account hack, Checks source ip against valid GM Account IP Addresses
	if (RuleB(World, GMAccountIPList) && this->GetAdmin() >= (RuleI(World, MinGMAntiHackStatus))) {
		if(!database.CheckGMIPs(long2ip(this->GetIP()).c_str(), this->GetAccountID())) {
			clog(WORLD__CLIENT,"GM Account not permited from source address %s and accountid %i", long2ip(this->GetIP()).c_str(), this->GetAccountID());
			eqs->Close();
		}
	}

	if (GetAccountID() == 0 && opcode != OP_SendLoginInfo) {
		// Got a packet other than OP_SendLoginInfo when not logged in
		clog(WORLD__CLIENT_ERR,"Expecting OP_SendLoginInfo, got %s", OpcodeNames[opcode]);
		return false;
	}
	else if (opcode == OP_AckPacket) {
		return true;
	}

	switch(opcode)
	{
		case OP_CrashDump:
			break;
		case OP_SendLoginInfo:
		{
			if (app->size != sizeof(LoginInfo_Struct)) {
				ret = false;
				break;
			}

			string StreamDescription = eqs->Describe();

			if(StreamDescription == "Patch SoF")
				SoFClient = true;

			LoginInfo_Struct *li=(LoginInfo_Struct *)app->pBuffer;

			// Quagmire - max len for name is 18, pass 15
			char name[19] = {0};
			char password[16] = {0};
			strncpy(name, (char*)li->login_info,18);
			strncpy(password, (char*)&(li->login_info[strlen(name)+1]), 15);

			if (strlen(password) <= 1) {
				// TODO: Find out how to tell the client wrong username/password
				clog(WORLD__CLIENT_ERR,"Login without a password");
				ret = false;
				break;
			}

			pZoning=(li->zoning==1);

#ifdef IPBASED_AUTH_HACK
			struct in_addr tmpip;
			tmpip.s_addr = ip;
#endif
			int32 id=0;
			bool minilogin = loginserver.MiniLogin();
			if(minilogin){
				struct in_addr miniip;
				miniip.s_addr = ip;
				id = database.GetMiniLoginAccount(inet_ntoa(miniip));
			}
			else if(strncasecmp(name, "LS#", 3) == 0)
				id=atoi(&name[3]);
			else
				id=atoi(name);
#ifdef IPBASED_AUTH_HACK
			if ((cle = zoneserver_list.CheckAuth(inet_ntoa(tmpip), password)))
#else

#ifdef DUAL_SERVER
			if (loginserver.Connected() == false && !pZoning) {
				clog(WORLD__CLIENT_ERR,"Error: You're disconnected from one login server, you need to re-connect.");
			}
#else
			if (loginserver.Connected() == false && !pZoning) {
				clog(WORLD__CLIENT_ERR,"Error: Login server login while not connected to login server.");
				ret = false;
				break;
			}
#endif 
			if ((minilogin && (cle = client_list.CheckAuth(id,password,ip))) || (cle = client_list.CheckAuth(id, password)))
#endif
			{
				if (cle->AccountID() == 0 || (!minilogin && cle->LSID()==0)) {
					clog(WORLD__CLIENT_ERR,"ID is 0.  Is this server connected to minilogin?");
					if(!minilogin)
						clog(WORLD__CLIENT_ERR,"If so you forget the minilogin variable...");
					else
						clog(WORLD__CLIENT_ERR,"Could not find a minilogin account, verify ip address logging into minilogin is the same that is in your account table.");
					ret = false;
					break;
				}

				cle->SetOnline();

				clog(WORLD__CLIENT,"Logged in. Mode=%s",pZoning ? "(Zoning)" : "(CharSel)");

				if(minilogin){
					WorldConfig::DisableStats();
					clog(WORLD__CLIENT,"MiniLogin Account #%d",cle->AccountID());
				}
				else {
					clog(WORLD__CLIENT,"LS Account #%d",cle->LSID());
				}
				if(Config->UpdateStats){
					ServerPacket* pack = new ServerPacket;
					pack->opcode = ServerOP_LSPlayerJoinWorld;
					pack->size = sizeof(ServerLSPlayerJoinWorld_Struct);
					pack->pBuffer = new uchar[pack->size];
					memset(pack->pBuffer,0,pack->size);
					ServerLSPlayerJoinWorld_Struct* join =(ServerLSPlayerJoinWorld_Struct*)pack->pBuffer;
					strcpy(join->key,GetLSKey());
					join->lsaccount_id = GetLSID();
					loginserver.SendPacket(pack);
#ifdef DUAL_SERVER
					loginserver.SendPacket2(pack);
#endif 
					safe_delete(pack);
				}

				if (!pZoning)
					SendGuildList();
				SendLogServer();
				SendApproveWorld();
				SendEnterWorld(cle->name());
				SendPostEnterWorld();
				if (!pZoning) {
					SendExpansionInfo();
					SendCharInfo();
					database.LoginIP(cle->AccountID(), long2ip(GetIP()).c_str());
				}
			}
			else {
				// TODO: Find out how to tell the client wrong username/password
				clog(WORLD__CLIENT_ERR,"Bad/Expired session key '%s'",name);
				ret = false;
				break;
			}

			if (!cle)
				break;
			cle->SetIP(GetIP());
			break;
		}
		case OP_ApproveName: //Name approval
		{
			if (GetAccountID() == 0) {
				clog(WORLD__CLIENT_ERR,"Name approval request with no logged in account");
				ret = false;
				break;
			}
			snprintf(char_name, 64, "%s", (char*)app->pBuffer);
			uchar race = app->pBuffer[64];
			uchar clas = app->pBuffer[68];

			clog(WORLD__CLIENT,"Name approval request.  Name=%s, race=%s, class=%s",char_name,GetRaceName(race),GetEQClassName(clas));

			EQApplicationPacket *outapp;
			outapp = new EQApplicationPacket;
			outapp->SetOpcode(OP_ApproveName);
			outapp->pBuffer = new uchar[1];
			outapp->size = 1;
			bool valid;
			if(!database.CheckNameFilter(char_name)) {
				valid = false;
			}
			else if(char_name[0] < 'A' && char_name[0] > 'Z') {
				//name must begin with an upper-case letter.
				valid = false;
			}
			else if (database.ReserveName(GetAccountID(), char_name)) {
				valid = true;
			}
			else {
				valid = false;
			}
			outapp->pBuffer[0] = valid? 1 : 0;
			QueuePacket(outapp);
			safe_delete(outapp);
			break;
		}
		case OP_RandomNameGenerator:
		{
			// creates up to a 10 char name
			char vowels[18]="aeiouyaeiouaeioe";
			char cons[48]="bcdfghjklmnpqrstvwxzybcdgklmnprstvwbcdgkpstrkd";
			char rndname[17]="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
			char paircons[33]="ngrkndstshthphsktrdrbrgrfrclcr";
			int rndnum=rand()%76,n=1;
			bool dlc=false;
			bool vwl=false;
			bool dbl=false;
			if (rndnum>63)
			{	// rndnum is 0 - 75 where 64-75 is cons pair, 17-63 is cons, 0-16 is vowel
				rndnum=(rndnum-61)*2;	// name can't start with "ng" "nd" or "rk"
				rndname[0]=paircons[rndnum];
				rndname[1]=paircons[rndnum+1];
				n=2;
			}
			else if (rndnum>16)
			{
				rndnum-=17;
				rndname[0]=cons[rndnum];
			}
			else
			{
				rndname[0]=vowels[rndnum];
				vwl=true;
			}
			int namlen=(rand()%6)+5;
			for (int i=n;i<namlen;i++)
			{
				dlc=false;
				if (vwl)	//last char was a vowel
				{			// so pick a cons or cons pair
					rndnum=rand()%63;
					if (rndnum>46)
					{	// pick a cons pair
						if (i>namlen-3)	// last 2 chars in name?
						{	// name can only end in cons pair "rk" "st" "sh" "th" "ph" "sk" "nd" or "ng"
							rndnum=(rand()%8)*2;
						}
						else
						{	// pick any from the set
							rndnum=(rndnum-47)*2;
						}
						rndname[i]=paircons[rndnum];
						rndname[i+1]=paircons[rndnum+1];
						dlc=true;	// flag keeps second letter from being doubled below
						i+=1;
					}
					else
					{	// select a single cons
						rndname[i]=cons[rndnum];
					}
				}
				else
				{		// select a vowel
					rndname[i]=vowels[rand()%17];
				}
				vwl=!vwl;
				if (!dbl && !dlc)
				{	// one chance at double letters in name
					if (!(rand()%(i+10)))	// chances decrease towards end of name
					{
						rndname[i+1]=rndname[i];
						dbl=true;
						i+=1;
					}
				}
			}

			rndname[0]=toupper(rndname[0]);
			NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer;
			memset(ngs->name,0,64);
			strcpy(ngs->name,rndname);

//			char names[8][64] = { "How", "About", "You", "Think", "Of", "Your", "Own", "Name" };
//			//Could have parts of the random name in this struct and they compile together
//			NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer;
//			strncpy(ngs->name,"Notcreated",64);

			QueuePacket(app);
			break;

		}
		case OP_CharacterCreateRequest: {
			// New OpCode in SoF
			//
			SoFClient = true;
			EQApplicationPacket *outapp = new EQApplicationPacket(OP_CharacterCreateRequest, sizeof(SoFCharCreateInfo));
			memcpy(outapp->pBuffer, &SoFCharCreateInfo, sizeof(SoFCharCreateInfo));
			QueuePacket(outapp);
			safe_delete(outapp);
			break;
		}

		case OP_CharacterCreate: //Char create
		{
			if (GetAccountID() == 0)
			{
				clog(WORLD__CLIENT_ERR,"Account ID not set; unable to create character.");
				ret = false;
				break;
			}
			else if (app->size != sizeof(CharCreate_Struct))
			{
				clog(WORLD__CLIENT_ERR,"Wrong size on OP_CharacterCreate. Got: %d, Expected: %d",app->size,sizeof(CharCreate_Struct));
				DumpPacket(app);
				break;
			}

			CharCreate_Struct *cc = (CharCreate_Struct*)app->pBuffer;
			if(OPCharCreate(char_name,cc) == false)
			{
				database.DeleteCharacter(char_name);
				EQApplicationPacket *outapp = new EQApplicationPacket(OP_ApproveName, 1);
				outapp->pBuffer[0] = 0;
				QueuePacket(outapp);
				safe_delete(outapp);
			}
                  else
			StartInTutorial = true;
			SendCharInfo();

			break;
		}
		case OP_EnterWorld: // Enter world
		{
			if (GetAccountID() == 0) {
				clog(WORLD__CLIENT_ERR,"Enter world with no logged in account");
				eqs->Close();
				break;
			}
			if(GetAdmin() < 0)
			{
				clog(WORLD__CLIENT,"Account banned or suspended.");
				eqs->Close();
				break;
			}

			if (RuleI(World, MaxClientsPerIP) >= 0) {
	            client_list.GetCLEIP(this->GetIP());  //Lieka Edit Begin:  Check current CLE Entry IPs against incoming connection
            }

			EnterWorld_Struct *ew=(EnterWorld_Struct *)app->pBuffer;
			strncpy(char_name, ew->name, 64);

			EQApplicationPacket *outapp;
			int32 tmpaccid = 0;
			charid = database.GetCharacterInfo(char_name, &tmpaccid, &zoneID, &instanceID);
			if (charid == 0 || tmpaccid != GetAccountID()) {
				clog(WORLD__CLIENT_ERR,"Could not get CharInfo for '%s'",char_name);
				eqs->Close();
				break;
			}

			// Make sure this account owns this character
			if (tmpaccid != GetAccountID()) {
				clog(WORLD__CLIENT_ERR,"This account does not own the character named '%s'",char_name);
				eqs->Close();
				break;
			}

			if(!pZoning && ew->return_home)
			{
				CharacterSelect_Struct* cs = new CharacterSelect_Struct;
				memset(cs, 0, sizeof(CharacterSelect_Struct));
				database.GetCharSelectInfo(GetAccountID(), cs);
				bool home_enabled = false;

				for(int x = 0; x < 10; ++x)
				{
					if(strcasecmp(cs->name[x], char_name) == 0)
					{
						if(cs->gohome[x] == 1)
						{
							home_enabled = true;
							break;
						}
					}
				}
				safe_delete(cs);

				if(home_enabled)
				{
					zoneID = database.MoveCharacterToBind(charid,4);
				}
				else
				{
					clog(WORLD__CLIENT_ERR,"'%s' is trying to go home before they're able...",char_name);
					database.SetHackerFlag(GetAccountName(), char_name, "MQGoHome: player tried to enter the tutorial without having go home enabled for this character.");
					eqs->Close();
					break;
				}
			}
/*
			if(!pZoning && (RuleB(World, EnableTutorialButton) && (ew->tutorial || StartInTutorial))) {
				CharacterSelect_Struct* cs = new CharacterSelect_Struct;
				memset(cs, 0, sizeof(CharacterSelect_Struct));
				database.GetCharSelectInfo(GetAccountID(), cs);
				bool tutorial_enabled = false;

				for(int x = 0; x < 10; ++x)
				{
					if(strcasecmp(cs->name[x], char_name) == 0)
					{
						if(cs->tutorial[x] == 1)
						{
							tutorial_enabled = true;
							break;
						}
					}
				}
				safe_delete(cs);

				if(tutorial_enabled)
				{
					zoneID = RuleI(World, TutorialZoneID);
					database.MoveCharacterToZone(charid, database.GetZoneName(zoneID));
				}
				else
				{
					clog(WORLD__CLIENT_ERR,"'%s' is trying to go to tutorial but are not allowed...",char_name);
					database.SetHackerFlag(GetAccountName(), char_name, "MQTutorial: player tried to enter the tutorial without having tutorial enabled for this character.");
					eqs->Close();
					break;
				}

				// HACK: Entering the Tutorial directly from Character Creation (without going back to Char Select)
				// does not work correctly yet in SoF, so bounce them back to Character Select first.
				//
				if(SoFClient && StartInTutorial) {
					ZoneUnavail();
					StartInTutorial = false;
					break;
				}
			}
*/
			if (zoneID == 0 || !database.GetZoneName(zoneID)) {
				// This is to save people in an invalid zone, once it's removed from the DB
				database.MoveCharacterToZone(charid, "arena");
				clog(WORLD__CLIENT_ERR, "Zone not found in database zone_id=%i, moveing char to arena character:%s", zoneID, char_name);
			}

			if(instanceID > 0)
			{
				if(!database.VerifyInstanceAlive(instanceID, GetCharID()))
				{
					zoneID = database.MoveCharacterToBind(charid);
					instanceID = 0;
				}
				else
				{
					if(!database.VerifyZoneInstance(zoneID, instanceID))
					{
						zoneID = database.MoveCharacterToBind(charid);
						instanceID = 0;
					}
				}
			}

			if(!pZoning) {
				database.SetGroupID(char_name, 0, charid);
				database.SetLFP(charid, false);
				database.SetLFG(charid, false);
			}
			else{
				int32 groupid=database.GetGroupID(char_name);
				if(groupid>0){
					char* leader=0;
					char leaderbuf[64]={0};
					if((leader=database.GetGroupLeaderForLogin(char_name,leaderbuf)) && strlen(leader)>1){
						EQApplicationPacket* outapp3 = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct));
						GroupJoin_Struct* gj=(GroupJoin_Struct*)outapp3->pBuffer;
						gj->action=8;
						strcpy(gj->yourname,char_name);
						strcpy(gj->membername,leader);
						QueuePacket(outapp3);
						safe_delete(outapp3);
					}
				}
			}
//Angelox
			outapp = new EQApplicationPacket(OP_MOTD);

#ifdef DUAL_SERVER
			char tmp[500] = {0};
			if ((database.GetVariable("MOTD", tmp, 500)) && (GetLSID() > 4999999)) {
				outapp->size = strlen(tmp)+1;
				outapp->pBuffer = new uchar[outapp->size];
				memset(outapp->pBuffer,0,outapp->size);
				strcpy((char*)outapp->pBuffer, tmp);
			}
			else if ((database.GetVariable("MOTDEqEmu", tmp, 500)) && (GetLSID() < 5000000)) {
				outapp->size = strlen(tmp)+1;
				outapp->pBuffer = new uchar[outapp->size];
				memset(outapp->pBuffer,0,outapp->size);
				strcpy((char*)outapp->pBuffer, tmp);
			}
#else
			char tmp[500] = {0};
			if (database.GetVariable("MOTD", tmp, 500)) {
				outapp->size = strlen(tmp)+1;
				outapp->pBuffer = new uchar[outapp->size];
				memset(outapp->pBuffer,0,outapp->size);
				strcpy((char*)outapp->pBuffer, tmp);
			}
#endif

			else {
				// Null Message of the Day. :)
				outapp->size = 1;
				outapp->pBuffer = new uchar[outapp->size];
				outapp->pBuffer[0] = 0;
			}
			QueuePacket(outapp);
			safe_delete(outapp);

			int MailKey = MakeRandomInt(1, INT_MAX);

			database.SetMailKey(charid, GetIP(), MailKey);

			char ConnectionType = (SoFClient ? 'S' : 'C');

			EQApplicationPacket *outapp2 = new EQApplicationPacket(OP_SetChatServer);
			char buffer[112];
			sprintf(buffer,"%s,%i,%s.%s,%c%08X",
				Config->ChatHost.c_str(),
				Config->ChatPort,
				Config->ShortName.c_str(),
				this->GetCharName(), ConnectionType, MailKey
			);
			outapp2->size=strlen(buffer)+1;
			outapp2->pBuffer = new uchar[outapp2->size];
			memcpy(outapp2->pBuffer,buffer,outapp2->size);
			QueuePacket(outapp2);
			safe_delete(outapp2);

			outapp2 = new EQApplicationPacket(OP_SetChatServer2);

			if(!SoFClient)
				ConnectionType = 'M';

			sprintf(buffer,"%s,%i,%s.%s,%c%08X",
				Config->MailHost.c_str(),
				Config->MailPort,
				Config->ShortName.c_str(),
				this->GetCharName(), ConnectionType, MailKey
			);
			outapp2->size=strlen(buffer)+1;
			outapp2->pBuffer = new uchar[outapp2->size];
			memcpy(outapp2->pBuffer,buffer,outapp2->size);
			QueuePacket(outapp2);
			safe_delete(outapp2);

			EnterWorld();
			break;
		}
		case OP_LoginComplete:{
			break;
		}
		case OP_DeleteCharacter: {
			int32 char_acct_id = database.GetAccountIDByChar((char*)app->pBuffer);
			if(char_acct_id == GetAccountID())
			{
			clog(WORLD__CLIENT,"Delete character: %s",app->pBuffer);
			database.DeleteCharacter((char *)app->pBuffer);
			SendCharInfo();
			}
			break;
		}
		case OP_ApproveWorld:
		{
			break;
		}
		case OP_WorldClientReady:{
			break;
		}
		case OP_World_Client_CRC1:
		case OP_World_Client_CRC2: {
			// Derision: There is no obvious entry in the CC struct to indicate that the 'Start Tutorial button
			// is selected when a character is created. I have observed that in this case, OP_EnterWorld is sent
			// before OP_World_Client_CRC1. Therefore, if we receive OP_World_Client_CRC1 before OP_EnterWorld,
			// then 'Start Tutorial' was not chosen.
			StartInTutorial = false;
			break;
		}
		case OP_WearChange: { // User has selected a different character
			break;
		}
		case OP_WorldComplete: {
			eqs->Close();
			break;
		}
		case OP_LoginUnknown1:
		case OP_LoginUnknown2:
			break;

		default: {
			clog(WORLD__CLIENT_ERR,"Received unknown EQApplicationPacket");
			_pkt(WORLD__CLIENT_ERR,app);
			break;
		}
	}
	return ret;
}

bool Client::Process() {
	bool ret = true;
	//bool sendguilds = true;
    sockaddr_in to;

	memset((char *) &to, 0, sizeof(to));
    to.sin_family = AF_INET;
    to.sin_port = port;
    to.sin_addr.s_addr = ip;

	if (autobootup_timeout.Check()) {
		clog(WORLD__CLIENT_ERR, "Zone bootup timer expired, bootup failed or too slow.");
		ZoneUnavail();
	}
	if(connect.Check()){
		SendGuildList();// Send OPCode: OP_GuildsList
		SendApproveWorld();
		connect.Disable();
	}
	if (CLE_keepalive_timer.Check()) {
		if (cle)
			cle->KeepAlive();
	}

	/************ Get all packets from packet manager out queue and process them ************/
	EQApplicationPacket *app = 0;
	while(ret && (app = (EQApplicationPacket *)eqs->PopPacket())) {
		ret = HandlePacket(app);

		delete app;
	}

	if (!eqs->CheckState(ESTABLISHED)) {
		if(WorldConfig::get()->UpdateStats){
			ServerPacket* pack = new ServerPacket;
			pack->opcode = ServerOP_LSPlayerLeftWorld;
			pack->size = sizeof(ServerLSPlayerLeftWorld_Struct);
			pack->pBuffer = new uchar[pack->size];
			memset(pack->pBuffer,0,pack->size);
			ServerLSPlayerLeftWorld_Struct* logout =(ServerLSPlayerLeftWorld_Struct*)pack->pBuffer;
			strcpy(logout->key,GetLSKey());
			logout->lsaccount_id = GetLSID();
			loginserver.SendPacket(pack);
#ifdef DUAL_SERVER
			loginserver.SendPacket2(pack);
#endif
			safe_delete(pack);
		}
		clog(WORLD__CLIENT,"Client disconnected (not active in process)");
		return false;
	}

	return ret;
}

void Client::EnterWorld(bool TryBootup) {
	if (zoneID == 0)
		return;

	ZoneServer* zs = NULL;
	if(instanceID > 0)
	{
		if(database.VerifyInstanceAlive(instanceID, GetCharID()))
		{
			if(database.VerifyZoneInstance(zoneID, instanceID))
			{
				zs = zoneserver_list.FindByInstanceID(instanceID);
			}
			else
			{
				instanceID = 0;
				zs = NULL;
				database.MoveCharacterToBind(GetCharID());
				ZoneUnavail();
				return;
			}
		}
		else
		{
			instanceID = 0;
			zs = NULL;
			database.MoveCharacterToBind(GetCharID());
			ZoneUnavail();
			return;
		}
	}
	else
		zs = zoneserver_list.FindByZoneID(zoneID);


	const char *zone_name=database.GetZoneName(zoneID, true);
	if (zs) {
		// warn the world we're comming, so it knows not to shutdown
		zs->IncommingClient(this);
	}
	else {
		if (TryBootup) {
			clog(WORLD__CLIENT,"Attempting autobootup of %s (%d:%d)",zone_name,zoneID,instanceID);
			autobootup_timeout.Start();
			pwaitingforbootup = zoneserver_list.TriggerBootup(zoneID, instanceID);
			if (pwaitingforbootup == 0) {
				clog(WORLD__CLIENT_ERR,"No zoneserver available to boot up.");
				ZoneUnavail();
			}
			return;
		}
		else {
			clog(WORLD__CLIENT_ERR,"Requested zone %s is no running.",zone_name);
			ZoneUnavail();
			return;
		}
	}
	pwaitingforbootup = 0;

	cle->SetChar(charid, char_name);
	database.UpdateLiveChar(char_name, GetAccountID());
	clog(WORLD__CLIENT,"%s %s (%d:%d)",seencharsel ? "Entering zone" : "Zoning to",zone_name,zoneID,instanceID);
//	database.SetAuthentication(account_id, char_name, zone_name, ip);

	if (seencharsel) {
		if (GetAdmin() < 80 && zoneserver_list.IsZoneLocked(zoneID)) {
			clog(WORLD__CLIENT_ERR,"Enter world failed.  Zone is locked.");
			ZoneUnavail();
			return;
		}

		ServerPacket* pack = new ServerPacket;
		pack->opcode = ServerOP_AcceptWorldEntrance;
		pack->size = sizeof(WorldToZone_Struct);
		pack->pBuffer = new uchar[pack->size];
		memset(pack->pBuffer, 0, pack->size);
		WorldToZone_Struct* wtz = (WorldToZone_Struct*) pack->pBuffer;
		wtz->account_id = GetAccountID();
		wtz->response = 0;
		zs->SendPacket(pack);
		delete pack;
	}
	else {	// if they havent seen character select screen, we can assume this is a zone
			// to zone movement, which should be preauthorized before they leave the previous zone
		Clearance(1);
	}
}

void Client::Clearance(sint8 response)
{
	ZoneServer* zs = NULL;
	if(instanceID > 0)
	{
		zs = zoneserver_list.FindByInstanceID(instanceID);
	}
	else
	{
		zs = zoneserver_list.FindByZoneID(zoneID);
	}

    if(zs == 0 || response == -1 || response == 0)
    {
        if (zs == 0)
        {
            clog(WORLD__CLIENT_ERR,"Unable to find zoneserver in Client::Clearance!!");
        } else {
        	clog(WORLD__CLIENT_ERR, "Invalid response %d in Client::Clearance", response);
        }

        ZoneUnavail();
        return;
    }

	EQApplicationPacket* outapp;

    if (zs->GetCAddress() == NULL) {
        clog(WORLD__CLIENT_ERR, "Unable to do zs->GetCAddress() in Client::Clearance!!");
        ZoneUnavail();
        return;
    }

    if (zoneID == 0) {
        clog(WORLD__CLIENT_ERR, "zoneID is NULL in Client::Clearance!!");
        ZoneUnavail();
        return;
    }

	const char* zonename = database.GetZoneName(zoneID);
    if (zonename == 0) {
        clog(WORLD__CLIENT_ERR, "zonename is NULL in Client::Clearance!!");
        ZoneUnavail();
        return;
    }

	// @bp This is the chat server
	/*
	char packetData[] = "64.37.148.34.9876,MyServer,Testchar,23cd2c95";
	outapp = new EQApplicationPacket(OP_0x0282, sizeof(packetData));
	strcpy((char*)outapp->pBuffer, packetData);
	QueuePacket(outapp);
	delete outapp;
	*/

	// Send zone server IP data
	outapp = new EQApplicationPacket(OP_ZoneServerInfo, sizeof(ZoneServerInfo_Struct));
	ZoneServerInfo_Struct* zsi = (ZoneServerInfo_Struct*)outapp->pBuffer;
	const char *zs_addr=zs->GetCAddress();
	if (!zs_addr[0]) {
		if (cle->IsLocalClient()) {
			struct in_addr  in;
			in.s_addr = zs->GetIP();
			zs_addr=inet_ntoa(in);
			if (!strcmp(zs_addr,"127.0.0.1"))
				zs_addr=WorldConfig::get()->LocalAddress.c_str();
		} else {
			zs_addr=WorldConfig::get()->WorldAddress.c_str();
		}
	}
	strcpy(zsi->ip, zs_addr);
	zsi->port =zs->GetCPort();
	clog(WORLD__CLIENT,"Sending client to zone %s (%d:%d) at %s:%d",zonename,zoneID,instanceID,zsi->ip,zsi->port);
	QueuePacket(outapp);
	safe_delete(outapp);

	if (cle)
		cle->SetOnline(CLE_Status_Zoning);
}

void Client::ZoneUnavail() {
	EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZoneUnavail, sizeof(ZoneUnavail_Struct));
	ZoneUnavail_Struct* ua = (ZoneUnavail_Struct*)outapp->pBuffer;
	const char* zonename = database.GetZoneName(zoneID);
	if (zonename)
		strcpy(ua->zonename, zonename);
	QueuePacket(outapp);
	delete outapp;

	zoneID = 0;
	pwaitingforbootup = 0;
	autobootup_timeout.Disable();
}

bool Client::GenPassKey(char* key) {
	char* passKey=NULL;
	*passKey += ((char)('A'+((int)(rand()%26))));
	*passKey += ((char)('A'+((int)(rand()%26))));
	memcpy(key, passKey, strlen(passKey));
	return true;
}

void Client::QueuePacket(const EQApplicationPacket* app, bool ack_req) {
	clog(WORLD__CLIENT_TRACE, "Sending EQApplicationPacket OpCode 0x%04x",app->GetOpcode());
	_pkt(WORLD__CLIENT_TRACE, app);

	ack_req = true;	// It's broke right now, dont delete this line till fix it. =P
	eqs->QueuePacket(app, ack_req);
}

void Client::SendGuildList() {
	EQApplicationPacket *outapp;
	outapp = new EQApplicationPacket(OP_GuildsList);

	//ask the guild manager to build us a nice guild list packet
	outapp->pBuffer = guild_mgr.MakeGuildList("", outapp->size);
	if(outapp->pBuffer == NULL) {
		clog(GUILDS__ERROR, "Unable to make guild list!");
		return;
	}

	clog(GUILDS__OUT_PACKETS, "Sending OP_GuildsList of length %d", outapp->size);
//	_pkt(GUILDS__OUT_PACKET_TRACE, outapp);

	eqs->FastQueuePacket((EQApplicationPacket **)&outapp);
}

// @merth: I have no idea what this struct is for, so it's hardcoded for now
void Client::SendApproveWorld()
{
	EQApplicationPacket* outapp;

	// Send OPCode: OP_ApproveWorld, size: 544
	outapp = new EQApplicationPacket(OP_ApproveWorld, sizeof(ApproveWorld_Struct));
	ApproveWorld_Struct* aw = (ApproveWorld_Struct*)outapp->pBuffer;
	uchar foo[] = {
//0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x95,0x5E,0x30,0xA5,0xCA,0xD4,0xEA,0xF5,
//0xCB,0x14,0xFC,0xF7,0x78,0xE2,0x73,0x15,0x90,0x17,0xCE,0x7A,0xEB,0xEC,0x3C,0x34,
//0x5C,0x6D,0x10,0x05,0xFC,0xEA,0xED,0x19,0xC5,0x0D,0x7A,0x82,0x17,0xCC,0xCC,0x71,
//0x56,0x38,0xDF,0x78,0x8D,0xE6,0x44,0xD3,0x6F,0xDB,0xE3,0xCF,0x21,0x30,0x75,0x2F,
//0xCD,0xDC,0xE9,0xB4,0xA4,0x4E,0x58,0xDE,0xEE,0x54,0xDD,0x87,0xDA,0xE9,0xC6,0xC8,
//0x02,0xDD,0xC4,0xFD,0x94,0x36,0x32,0xAD,0x1B,0x39,0x0F,0x00,0x00,0x00,0x00,0x00,

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x37,0x87,0x13,0xbe,0xc8,0xa7,0x77,0xcb,
0x27,0xed,0xe1,0xe6,0x5d,0x1c,0xaa,0xd3,0x3c,0x26,0x3b,0x6d,0x8c,0xdb,0x36,0x8d,
0x91,0x72,0xf5,0xbb,0xe0,0x5c,0x50,0x6f,0x09,0x6d,0xc9,0x1e,0xe7,0x2e,0xf4,0x38,
0x1b,0x5e,0xa8,0xc2,0xfe,0xb4,0x18,0x4a,0xf7,0x72,0x85,0x13,0xf5,0x63,0x6c,0x16,
0x69,0xf4,0xe0,0x17,0xff,0x87,0x11,0xf3,0x2b,0xb7,0x73,0x04,0x37,0xca,0xd5,0x77,
0xf8,0x03,0x20,0x0a,0x56,0x8b,0xfb,0x35,0xff,0x59,0x00,0x00,0x00,0x00,0x00,0x00,

//0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x42,0x69,0x2a,0x87,0xdd,0x04,0x3d,
//0x7f,0xb1,0xb3,0xbb,0xde,0xd5,0x5f,0xfc,0x1f,0xb3,0x25,0x94,0x16,0xd5,0xf3,0x97,
//0x43,0xdf,0xb9,0x69,0x68,0xdf,0x2b,0x64,0x98,0xf5,0x44,0xbe,0x38,0x65,0xef,0xff,
//0x36,0x89,0x90,0xcf,0x26,0xbb,0x9f,0x76,0xd5,0xaf,0x6d,0xf2,0x08,0xbe,0xce,0xd8,
//0x3e,0x4b,0x53,0x8a,0xf3,0x44,0x7c,0x19,0x49,0x5d,0x97,0x99,0xd8,0x8b,0xee,0x10,
//0x1a,0x7d,0xb7,0x8b,0x49,0x9b,0x40,0x8c,0xea,0x49,0x09,0x00,0x00,0x00,0x00,0x00,
//
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x53,0xC3,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00
};
	memcpy(aw->unknown544, foo, sizeof(foo));
	QueuePacket(outapp);
	safe_delete(outapp);
}

bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
{
	PlayerProfile_Struct pp;
	ExtendedProfile_Struct ext;
	Inventory inv;
	time_t bday = time(NULL);
	char startzone[50]={0};
	uint32 i;
	struct in_addr	in;


	int stats_sum = cc->STR + cc->STA + cc->AGI + cc->DEX +
		cc->WIS + cc->INT + cc->CHA;

	in.s_addr = GetIP();
	clog(WORLD__CLIENT,"Character creation request from %s LS#%d (%s:%d) : ", GetCLE()->LSName(), GetCLE()->LSID(), inet_ntoa(in), GetPort());
	clog(WORLD__CLIENT,"Name: %s", name);
	clog(WORLD__CLIENT,"Race: %d  Class: %d  Gender: %d  Deity: %d  Start zone: %d",
		cc->race, cc->class_, cc->gender, cc->deity, cc->start_zone);
	clog(WORLD__CLIENT,"STR  STA  AGI  DEX  WIS  INT  CHA    Total");
	clog(WORLD__CLIENT,"%3d  %3d  %3d  %3d  %3d  %3d  %3d     %3d",
		cc->STR, cc->STA, cc->AGI, cc->DEX, cc->WIS, cc->INT, cc->CHA,
		stats_sum);
	clog(WORLD__CLIENT,"Face: %d  Eye colors: %d %d", cc->face, cc->eyecolor1, cc->eyecolor2);
	clog(WORLD__CLIENT,"Hairstyle: %d  Haircolor: %d", cc->hairstyle, cc->haircolor);
	clog(WORLD__CLIENT,"Beard: %d  Beardcolor: %d", cc->beard, cc->beardcolor);

	// validate the char creation struct
	if(!CheckCharCreateInfo(cc))
	{
		clog(WORLD__CLIENT_ERR,"CheckCharCreateInfo did not validate the request (bad race/class/stats)");
		return false;
	}

	// Convert incoming cc_s to the new PlayerProfile_Struct
	memset(&pp, 0, sizeof(PlayerProfile_Struct));	// start building the profile

	InitExtendedProfile(&ext);

	strncpy(pp.name, name, 63);
	// clean the capitalization of the name
#if 0	// on second thought, don't - this will just make the creation fail
// because the name won't match what was already reserved earlier
	for (i = 0; pp.name[i] && i < 63; i++)
	{
		if(!isalpha(pp.name[i]))
			return false;
		pp.name[i] = tolower(pp.name[i]);
	}
	pp.name[0] = toupper(pp.name[0]);
#endif

	pp.race				= cc->race;
	pp.class_			= cc->class_;
	pp.gender			= cc->gender;
	pp.deity			= cc->deity;
	pp.STR				= cc->STR;
	pp.STA				= cc->STA;
	pp.AGI				= cc->AGI;
	pp.DEX				= cc->DEX;
	pp.WIS				= cc->WIS;
	pp.INT				= cc->INT;
	pp.CHA				= cc->CHA;
	pp.face				= cc->face;
	pp.eyecolor1		= cc->eyecolor1;
	pp.eyecolor2		= cc->eyecolor2;
	pp.hairstyle		= cc->hairstyle;
	pp.haircolor		= cc->haircolor;
	pp.beard		 	= cc->beard;
	pp.beardcolor		= cc->beardcolor;
	pp.drakkin_heritage		= cc->drakkin_heritage;
	pp.drakkin_tattoo		= cc->drakkin_tattoo;
	pp.drakkin_details		= cc->drakkin_details;
	pp.birthday		= bday;
	pp.lastlogin	= bday;
	pp.level			= 1;
	pp.points			= 5;
	pp.cur_hp			= 1000; // 1k hp during dev only
	//what was the point of this? zone dosent handle this:
	//pp.expAA			= 0xFFFFFFFF;

	pp.hunger_level = 6000;
	pp.thirst_level = 6000;


	// FIXME: FV roleplay, database goodness...

	// Racial Languages
	SetRacialLanguages( &pp ); // bUsh
	SetRaceStartingSkills( &pp ); // bUsh
	SetClassStartingSkills( &pp ); // bUsh
	pp.skills[SENSE_HEADING] = 200;
	// Some one fucking fix this to use a field name. -Doodman
	//pp.unknown3596[28] = 15; // @bp: This is to enable disc usage
//	strcpy(pp.servername, WorldConfig::get()->ShortName.c_str());


	for(i = 0; i < MAX_PP_SPELLBOOK; i++)
		pp.spell_book[i] = 0xFFFFFFFF;

	for(i = 0; i < MAX_PP_MEMSPELL; i++)
		pp.mem_spells[i] = 0xFFFFFFFF;

	for(i = 0; i < BUFF_COUNT; i++)
		pp.buffs[i].spellid = 0xFFFF;


	//was memset(pp.unknown3704, 0xffffffff, 8);
	//but I dont think thats what you really wanted to do...
	//memset is byte based

	//If server is PVP by default, make all character set to it.
	pp.pvp = database.GetServerType() == 1 ? 1 : 0;

	//If it is an SoF Client and the SoF Start Zone rule is set, send new chars there
	if(SoFClient && (RuleI(World, SoFStartZoneID) > 0)) {
		clog(WORLD__CLIENT,"Found 'SoFStartZoneID' rule setting: %i", (RuleI(World, SoFStartZoneID)));
		pp.zone_id = (RuleI(World, SoFStartZoneID));
		if(pp.zone_id)
			database.GetSafePoints(pp.zone_id, &pp.x, &pp.y, &pp.z);
		else
			clog(WORLD__CLIENT_ERR,"Error getting zone id for Zone ID %i", (RuleI(World, SoFStartZoneID)));
	}
	else
	{
		// if there's a startzone variable put them in there
		if(database.GetVariable("startzone", startzone, 50))
		{
			clog(WORLD__CLIENT,"Found 'startzone' variable setting: %s", startzone);
			pp.zone_id = database.GetZoneID(startzone);
			if(pp.zone_id)
				database.GetSafePoints(pp.zone_id, &pp.x, &pp.y, &pp.z);
			else
				clog(WORLD__CLIENT_ERR,"Error getting zone id for '%s'", startzone);
		}
		else   // otherwise use normal starting zone logic
		{
			if(!SoFClient)
				database.GetStartZone(&pp, cc);
			else
				database.GetStartZoneSoF(&pp, cc);
		}
	}

	if(!pp.zone_id)
	{
		pp.zone_id = 1;		// qeynos
		pp.x = pp.y = pp.z = -1;
	}

	if(!pp.binds[0].zoneId)
	{
		pp.binds[0].zoneId = pp.zone_id;
		pp.binds[0].x = pp.x;
		pp.binds[0].y = pp.y;
		pp.binds[0].z = pp.z;
		pp.binds[0].heading = pp.heading;
 	}

	// set starting city location to the initial bind point
	pp.binds[4] = pp.binds[0];


	clog(WORLD__CLIENT,"Current location: %s  %0.2f, %0.2f, %0.2f",
		database.GetZoneName(pp.zone_id), pp.x, pp.y, pp.z);
	clog(WORLD__CLIENT,"Bind location: %s  %0.2f, %0.2f, %0.2f",
		database.GetZoneName(pp.binds[0].zoneId), pp.binds[0].x, pp.binds[0].y, pp.binds[0].z);


	// Starting Items inventory
	database.SetStartingItems(&pp, &inv, pp.race, pp.class_, pp.deity, pp.zone_id, pp.name, GetAdmin());


	// now we give the pp and the inv we made to StoreCharacter
	// to see if we can store it
	if (!database.StoreCharacter(GetAccountID(), &pp, &inv, &ext))
	{
		clog(WORLD__CLIENT_ERR,"Character creation failed: %s", pp.name);
		return false;
	}
	else
	{
		clog(WORLD__CLIENT,"Character creation successful: %s", pp.name);
		return true;
	}
}

// returns true if the request is ok, false if there's an error
bool CheckCharCreateInfo(CharCreate_Struct *cc)
{
	int32 bSTR, bSTA, bAGI, bDEX, bWIS, bINT, bCHA, bTOTAL, cTOTAL, stat_points;	//these are all int32 in CharCreate_Struct, so we'll make them int32 here to make the compiler shut up
	int classtemp, racetemp;
	int Charerrors = 0;


// solar: if this is increased you'll have to add a column to the classrace
// table below
#define _TABLE_RACES	16

	static const int BaseRace[_TABLE_RACES][7] =
	{            /* STR  STA  AGI  DEX  WIS  INT  CHR */
	{ /*Human*/      75,  75,  75,  75,  75,  75,  75},
	{ /*Barbarian*/ 103,  95,  82,  70,  70,  60,  55},
	{ /*Erudite*/    60,  70,  70,  70,  83, 107,  70},
	{ /*Wood Elf*/   65,  65,  95,  80,  80,  75,  75},
	{ /*High Elf*/   55,  65,  85,  70,  95,  92,  80},
	{ /*Dark Elf*/   60,  65,  90,  75,  83,  99,  60},
	{ /*Half Elf*/   70,  70,  90,  85,  60,  75,  75},
	{ /*Dwarf*/      90,  90,  70,  90,  83,  60,  45},
	{ /*Troll*/     108, 109,  83,  75,  60,  52,  40},
	{ /*Ogre*/      130, 122,  70,  70,  67,  60,  37},
	{ /*Halfling*/   70,  75,  95,  90,  80,  67,  50},
	{ /*Gnome*/      60,  70,  85,  85,  67,  98,  60},
	{ /*Iksar*/      70,  70,  90,  85,  80,  75,  55},
	{ /*Vah Shir*/   90,  75,  90,  70,  70,  65,  65},
	{ /*Froglok*/    70,  80, 100, 100,  75,  75,  50},
	{ /*Drakkin*/    70,  80,  85,  75,  80,  85,  75}
	};

	static const int BaseClass[PLAYER_CLASS_COUNT][8] =
	{              /* STR  STA  AGI  DEX  WIS  INT  CHR  ADD*/
	{ /*Warrior*/      10,  10,   5,   0,   0,   0,   0,  25},
	{ /*Cleric*/        5,   5,   0,   0,  10,   0,   0,  30},
	{ /*Paladin*/      10,   5,   0,   0,   5,   0,  10,  20},
	{ /*Ranger*/        5,  10,  10,   0,   5,   0,   0,  20},
	{ /*ShadowKnight*/ 10,   5,   0,   0,   0,   10,  5,  20},
	{ /*Druid*/         0,  10,   0,   0,  10,   0,   0,  30},
	{ /*Monk*/          5,   5,  10,  10,   0,   0,   0,  20},
	{ /*Bard*/          5,   0,   0,  10,   0,   0,  10,  25},
	{ /*Rouge*/         0,   0,  10,  10,   0,   0,   0,  30},
	{ /*Shaman*/        0,   5,   0,   0,  10,   0,   5,  30},
	{ /*Necromancer*/   0,   0,   0,  10,   0,  10,   0,  30},
	{ /*Wizard*/        0,  10,   0,   0,   0,  10,   0,  30},
	{ /*Magician*/      0,  10,   0,   0,   0,  10,   0,  30},
	{ /*Enchanter*/     0,   0,   0,   0,   0,  10,  10,  30},
	{ /*Beastlord*/     0,  10,   5,   0,  10,   0,   5,  20},
	{ /*Berserker*/    10,   5,   0,  10,   0,   0,   0,  25}
	};

	static const bool ClassRaceLookupTable[PLAYER_CLASS_COUNT][_TABLE_RACES]=
	{                   /*Human  Barbarian Erudite Woodelf Highelf Darkelf Halfelf Dwarf  Troll  Ogre   Halfling Gnome  Iksar  Vahshir Froglok Drakkin*/
	{ /*Warrior*/         true,  true,     false,  true,   false,  true,   true,   true,  true,  true,  true,    true,  true,  true,   true,   true},
	{ /*Cleric*/          true,  false,    true,   false,  true,   true,   true,   true,  false, false, true,    true,  false, false,  true,   true},
	{ /*Paladin*/         true,  false,    true,   false,  true,   false,  true,   true,  false, false, true,    true,  false, false,  true,   true},
	{ /*Ranger*/          true,  false,    false,  true,   false,  false,  true,   false, false, false, true,    false, false, false,  false,  true},
	{ /*ShadowKnight*/    true,  false,    true,   false,  false,  true,   false,  false, true,  true,  false,   true,  true,  false,  true,   true},
	{ /*Druid*/           true,  false,    false,  true,   false,  false,  true,   false, false, false, true,    false, false, false,  false,  true},
	{ /*Monk*/            true,  false,    false,  false,  false,  false,  false,  false, false, false, false,   false, true,  false,  false,  true},
	{ /*Bard*/            true,  false,    false,  true,   false,  false,  true,   false, false, false, false,   false, false, true,   false,  true},
	{ /*Rogue*/           true,  true,     false,  true,   false,  true,   true,   true,  false, false, true,    true,  false, true,   true,   true},
	{ /*Shaman*/          false, true,     false,  false,  false,  false,  false,  false, true,  true,  false,   false, true,  true,   true,   false},
	{ /*Necromancer*/     true,  false,    true,   false,  false,  true,   false,  false, false, false, false,   true,  true,  false,  true,   true},
	{ /*Wizard*/          true,  false,    true,   false,  true,   true,   false,  false, false, false, false,   true,  false, false,  true,   true},
	{ /*Magician*/        true,  false,    true,   false,  true,   true,   false,  false, false, false, false,   true,  false, false,  false,  true},
	{ /*Enchanter*/       true,  false,    true,   false,  true,   true,   false,  false, false, false, false,   true,  false, false,  false,  true},
	{ /*Beastlord*/       false, true,     false,  false,  false,  false,  false,  false, true,  true,  false,   false, true,  true,   false,  false},
	{ /*Berserker*/       false, true,     false,  false,  false,  false,  false,  true,  true,  true,  false,   false, false, true,   false,  false}
	};//Initial table by kathgar, editted by Wiz for accuracy, solar too

	if(!cc) return false;

	_log(WORLD__CLIENT,"Validating char creation info...");

	classtemp = cc->class_ - 1;
	racetemp = cc->race - 1;
	// these have non sequential race numbers so they need to be mapped
	if (cc->race == FROGLOK) racetemp = 14;
	if (cc->race == VAHSHIR) racetemp = 13;
	if (cc->race == IKSAR) racetemp = 12;
	if (cc->race == DRAKKIN) racetemp = 15;

	// if out of range looking it up in the table would crash stuff
	// so we return from these
	if(classtemp >= PLAYER_CLASS_COUNT)
	{
		_log(WORLD__CLIENT_ERR,"  class is out of range");
		return false;
	}
	if(racetemp >= _TABLE_RACES)
	{
		_log(WORLD__CLIENT_ERR,"  race is out of range");
		return false;
	}

	if(!ClassRaceLookupTable[classtemp][racetemp]) //Lookup table better than a bunch of ifs?
	{
		_log(WORLD__CLIENT_ERR,"  invalid race/class combination");
		// we return from this one, since if it's an invalid combination our table
		// doesn't have meaningful values for the stats
		return false;
	}

	// solar: add up the base values for this class/race
	// this is what they start with, and they have stat_points more
	// that can distributed
	bSTR = BaseClass[classtemp][0] + BaseRace[racetemp][0];
	bSTA = BaseClass[classtemp][1] + BaseRace[racetemp][1];
	bAGI = BaseClass[classtemp][2] + BaseRace[racetemp][2];
	bDEX = BaseClass[classtemp][3] + BaseRace[racetemp][3];
	bWIS = BaseClass[classtemp][4] + BaseRace[racetemp][4];
	bINT = BaseClass[classtemp][5] + BaseRace[racetemp][5];
	bCHA = BaseClass[classtemp][6] + BaseRace[racetemp][6];
	stat_points = BaseClass[classtemp][7];
	bTOTAL = bSTR + bSTA + bAGI + bDEX + bWIS + bINT + bCHA;
	cTOTAL = cc->STR + cc->STA + cc->AGI + cc->DEX + cc->WIS + cc->INT + cc->CHA;

	// solar: the first check makes sure the total is exactly what was expected.
	// this will catch all the stat cheating, but there's still the issue
	// of reducing CHA or INT or something, to use for STR, so we check
	// that none are lower than the base or higher than base + stat_points
	// NOTE: these could just be else if, but i want to see all the stats
	// that are messed up not just the first hit

	if(bTOTAL + stat_points != cTOTAL)
	{
		_log(WORLD__CLIENT_ERR,"  stat points total doesn't match expected value: expecting %d got %d", bTOTAL + stat_points, cTOTAL);
		Charerrors++;
	}

	if(cc->STR > bSTR + stat_points || cc->STR < bSTR)
	{
		_log(WORLD__CLIENT_ERR,"  stat STR is out of range");
		Charerrors++;
	}
	if(cc->STA > bSTA + stat_points || cc->STA < bSTA)
	{
		_log(WORLD__CLIENT_ERR,"  stat STA is out of range");
		Charerrors++;
	}
	if(cc->AGI > bAGI + stat_points || cc->AGI < bAGI)
	{
		_log(WORLD__CLIENT_ERR,"  stat AGI is out of range");
		Charerrors++;
	}
	if(cc->DEX > bDEX + stat_points || cc->DEX < bDEX)
	{
		_log(WORLD__CLIENT_ERR,"  stat DEX is out of range");
		Charerrors++;
	}
	if(cc->WIS > bWIS + stat_points || cc->WIS < bWIS)
	{
		_log(WORLD__CLIENT_ERR,"  stat WIS is out of range");
		Charerrors++;
	}
	if(cc->INT > bINT + stat_points || cc->INT < bINT)
	{
		_log(WORLD__CLIENT_ERR,"  stat INT is out of range");
		Charerrors++;
	}
	if(cc->CHA > bCHA + stat_points || cc->CHA < bCHA)
	{
		_log(WORLD__CLIENT_ERR,"  stat CHA is out of range");
		Charerrors++;
	}

	/*TODO: Check for deity/class/race.. it'd be nice, but probably of any real use to hack(faction, deity based items are all I can think of)
	I am NOT writing those tables - kathgar*/

	_log(WORLD__CLIENT,"Found %d errors in character creation request", Charerrors);

	return Charerrors == 0;
}

void Client::SetClassStartingSkills( PlayerProfile_Struct *pp )
{
   switch( pp->class_ )
   {
   case BARD:
      {
         pp->skills[_1H_SLASHING] = 5;
         pp->skills[SINGING] = 5;
         break;
      }
   case BEASTLORD:
      {
         pp->skills[HAND_TO_HAND] = 5;
         break;
      }
   case BERSERKER: // A Guess
      {
         pp->skills[_2H_SLASHING] = 5;
         break;
      }
   case CLERIC:
      {
         pp->skills[_1H_BLUNT] = 5;
         break;
      }
   case DRUID:
      {
         pp->skills[_1H_BLUNT] = 5;
         break;
      }
   case ENCHANTER:
      {
         pp->skills[PIERCING] = 5;
         break;
      }
   case MAGICIAN:
      {
         pp->skills[PIERCING] = 5;
         break;
      }
   case MONK:
      {
         pp->skills[DODGE] = 5;
         pp->skills[DUAL_WIELD] = 5;
         pp->skills[HAND_TO_HAND] = 5;
         break;
      }
   case NECROMANCER:
      {
         pp->skills[PIERCING] = 5;
         break;
      }
   case PALADIN:
      {
         pp->skills[_1H_SLASHING] = 5;
         break;
      }
   case RANGER:
      {
         pp->skills[_1H_SLASHING] = 5;
         break;
      }
   case ROGUE:
      {
         pp->skills[PIERCING] = 5;
         pp->languages[LANG_THIEVES_CANT] = 100; // Thieves Cant
         break;
      }
   case SHADOWKNIGHT:
      {
         pp->skills[_1H_SLASHING] = 5;
         break;
      }
   case SHAMAN:
      {
         pp->skills[_1H_BLUNT] = 5;
         break;
      }
   case WARRIOR:
      {
         pp->skills[_1H_SLASHING] = 5;
         break;
      }
   case WIZARD:
      {
         pp->skills[PIERCING] = 5;
         break;
      }
   }
}

void Client::SetRaceStartingSkills( PlayerProfile_Struct *pp )
{
   switch( pp->race )
   {
   case BARBARIAN:
   case DWARF:
   case ERUDITE:
   case HALF_ELF:
   case HIGH_ELF:
   case HUMAN:
   case OGRE:
   case TROLL:
   case DRAKKIN:	//Drakkin are supposed to get a starting AA Skill
      {
         // No Race Specific Skills
         break;
      }
   case DARK_ELF:
      {
         pp->skills[HIDE] = 50;
         break;
      }
   case FROGLOK:
      {
         pp->skills[SWIMMING] = 125;
         break;
      }
   case GNOME:
      {
         pp->skills[TINKERING] = 50;
         break;
      }
   case HALFLING:
      {
         pp->skills[HIDE] = 50;
         pp->skills[SNEAK] = 50;
         break;
      }
   case IKSAR:
      {
         pp->skills[FORAGE] = 50;
         pp->skills[SWIMMING] = 100;
         break;
      }
   case WOOD_ELF:
      {
         pp->skills[FORAGE] = 50;
         pp->skills[HIDE] = 50;
         break;
      }
   case VAHSHIR:
      {
         pp->skills[SAFE_FALL] = 50;
         pp->skills[SNEAK] = 50;
         break;
      }
   }
}

void Client::SetRacialLanguages( PlayerProfile_Struct *pp )
{
   switch( pp->race )
   {
   case BARBARIAN:
      {
         pp->languages[LANG_COMMON_TONGUE] = 100;
         pp->languages[LANG_BARBARIAN] = 100;
         break;
      }
   case DARK_ELF:
      {
         pp->languages[LANG_COMMON_TONGUE] = 100;
         pp->languages[LANG_DARK_ELVISH] = 100;
         pp->languages[LANG_DARK_SPEECH] = 100;
         pp->languages[LANG_ELDER_ELVISH] = 100;
         pp->languages[LANG_ELVISH] = 25;
         break;
      }
   case DWARF:
      {
         pp->languages[LANG_COMMON_TONGUE] = 100;
         pp->languages[LANG_DWARVISH] = 100;
         pp->languages[LANG_GNOMISH] = 25;
         break;
      }
   case ERUDITE:
      {
         pp->languages[LANG_COMMON_TONGUE] = 100;
         pp->languages[LANG_ERUDIAN] = 100;
         break;
      }
   case FROGLOK:
      {
         pp->languages[LANG_COMMON_TONGUE] = 100;
         pp->languages[LANG_FROGLOK] = 100;
         pp->languages[LANG_TROLL] = 25;
         break;
      }
   case GNOME:
      {
         pp->languages[LANG_COMMON_TONGUE] = 100;
         pp->languages[LANG_DWARVISH] = 25;
         pp->languages[LANG_GNOMISH] = 100;
         break;
      }
   case HALF_ELF:
      {
         pp->languages[LANG_COMMON_TONGUE] = 100;
         pp->languages[LANG_ELVISH] = 100;
         break;
      }
   case HALFLING:
      {
         pp->languages[LANG_COMMON_TONGUE] = 100;
         pp->languages[LANG_HALFLING] = 100;
         break;
      }
   case HIGH_ELF:
      {
         pp->languages[LANG_COMMON_TONGUE] = 100;
         pp->languages[LANG_DARK_ELVISH] = 25;
         pp->languages[LANG_ELDER_ELVISH] = 25;
         pp->languages[LANG_ELVISH] = 100;
         break;
      }
   case HUMAN:
      {
         pp->languages[LANG_COMMON_TONGUE] = 100;
         break;
      }
   case IKSAR:
      {
         pp->languages[LANG_COMMON_TONGUE] = 95;
         pp->languages[LANG_DARK_SPEECH] = 100;
         pp->languages[LANG_LIZARDMAN] = 100;
         break;
      }
   case OGRE:
      {
         pp->languages[LANG_COMMON_TONGUE] = 95;
         pp->languages[LANG_DARK_SPEECH] = 100;
         pp->languages[LANG_OGRE] = 100;
         break;
      }
   case TROLL:
      {
         pp->languages[LANG_COMMON_TONGUE] = 95;
         pp->languages[LANG_DARK_SPEECH] = 100;
         pp->languages[LANG_TROLL] = 100;
         break;
      }
   case WOOD_ELF:
      {
         pp->languages[LANG_COMMON_TONGUE] = 100;
         pp->languages[LANG_ELVISH] = 100;
         break;
      }
   case VAHSHIR:
      {
         pp->languages[LANG_COMMON_TONGUE] = 100;
         pp->languages[LANG_COMBINE_TONGUE] = 100;
         pp->languages[LANG_ERUDIAN] = 25;
         pp->languages[LANG_VAH_SHIR] = 100;
         break;
      }
   case DRAKKIN:
      {
         pp->languages[LANG_COMMON_TONGUE] = 100;
         pp->languages[LANG_ELDER_DRAGON] = 100;
         pp->languages[LANG_DRAGON] = 100;
         break;
      }
   }
}
Note: client.cpp also includes a fix for dual MOTD, so you need to add the variable;
Code:
INSERT INTO `variables` VALUES ('MOTDEqEmu','Welcome to AXCLASSIC!','Server Message of the Day','2010-01-08 13:19:53');
So you can specify different MOTDs on different LS.

cliententry.cpp
Code:
#include "../common/debug.h"
#include "cliententry.h"
#include "clientlist.h"
#include "LoginServer.h"
#include "worlddb.h"
#include "zoneserver.h"
#include "WorldConfig.h"
#include "../common/guilds.h"
extern int32 numplayers;
extern LoginServer loginserver;
extern ClientList		client_list;
extern volatile bool RunLoops;

ClientListEntry::ClientListEntry(int32 in_id, int32 iLSID, const char* iLoginName, const char* iLoginKey, sint16 iWorldAdmin, int32 ip, uint8 local)
: id(in_id)
{
	ClearVars(true);
	
	pIP = ip;
	pLSID = iLSID;
	if(iLSID > 0)
		paccountid = database.GetAccountIDFromLSID(iLSID, paccountname, &padmin);
	strn0cpy(plsname, iLoginName, sizeof(plsname));
	strn0cpy(plskey, iLoginKey, sizeof(plskey));
	pworldadmin = iWorldAdmin;
	plocal=(local==1);

	pinstance = 0;
}

ClientListEntry::ClientListEntry(int32 in_id, int32 iAccID, const char* iAccName, MD5& iMD5Pass, sint16 iAdmin)
: id(in_id)
{
	ClearVars(true);
	
	pIP = 0;
	pLSID = 0;
	pworldadmin = 0;

	paccountid = iAccID;
	strn0cpy(paccountname, iAccName, sizeof(paccountname));
	pMD5Pass = iMD5Pass;
	padmin = iAdmin;

	pinstance = 0;
}

ClientListEntry::ClientListEntry(int32 in_id, ZoneServer* iZS, ServerClientList_Struct* scl, sint8 iOnline)
: id(in_id)
{
	ClearVars(true);
	
	pIP = 0;
	pLSID = scl->LSAccountID;
	strn0cpy(plsname, scl->name, sizeof(plsname));
	strn0cpy(plskey, scl->lskey, sizeof(plskey));
	pworldadmin = 0;

	paccountid = scl->AccountID;
	strn0cpy(paccountname, scl->AccountName, sizeof(paccountname));
	padmin = scl->Admin;

	pinstance = 0;

	if (iOnline >= CLE_Status_Zoning)
		Update(iZS, scl, iOnline);
	else
		SetOnline(iOnline);
}

ClientListEntry::~ClientListEntry() {
	if (RunLoops) {
		Camp(); // updates zoneserver's numplayers
		client_list.RemoveCLEReferances(this);
	}
}

void ClientListEntry::SetChar(int32 iCharID, const char* iCharName) {
	pcharid = iCharID;
	strn0cpy(pname, iCharName, sizeof(pname));
}

void ClientListEntry::SetOnline(ZoneServer* iZS, sint8 iOnline) {
	if (iZS == this->Server())
		SetOnline(iOnline);
}

void ClientListEntry::SetOnline(sint8 iOnline) {
	if (iOnline >= CLE_Status_Online && pOnline < CLE_Status_Online)
		numplayers++;
	else if (iOnline < CLE_Status_Online && pOnline >= CLE_Status_Online) {
		numplayers--;
	}
	if (iOnline != CLE_Status_Online || pOnline < CLE_Status_Online)
		pOnline = iOnline;
	if (iOnline < CLE_Status_Zoning)
		Camp();
	if (pOnline >= CLE_Status_Online)
		stale = 0;
}
void ClientListEntry::LSUpdate(ZoneServer* iZS){
	if(WorldConfig::get()->UpdateStats){
		ServerPacket* pack = new ServerPacket;
		pack->opcode = ServerOP_LSZoneInfo;
		pack->size = sizeof(ZoneInfo_Struct);
		pack->pBuffer = new uchar[pack->size];
		ZoneInfo_Struct* zone =(ZoneInfo_Struct*)pack->pBuffer;
		zone->count=iZS->NumPlayers();
		zone->zone = iZS->GetZoneID();
		zone->zone_wid = iZS->GetID();

#ifdef DUAL_SERVER
		loginserver.SendPacket2(pack);
#else
		loginserver.SendPacket(pack);
#endif

		safe_delete(pack);
	}
}
void ClientListEntry::LSZoneChange(ZoneToZone_Struct* ztz){
	if(WorldConfig::get()->UpdateStats){
		ServerPacket* pack = new ServerPacket;
		pack->opcode = ServerOP_LSPlayerZoneChange;
		pack->size = sizeof(ServerLSPlayerZoneChange_Struct);
		pack->pBuffer = new uchar[pack->size];
		ServerLSPlayerZoneChange_Struct* zonechange =(ServerLSPlayerZoneChange_Struct*)pack->pBuffer;
		zonechange->lsaccount_id = LSID();
		zonechange->from = ztz->current_zone_id;
		zonechange->to = ztz->requested_zone_id;
		loginserver.SendPacket(pack);

#ifdef DUAL_SERVER
		loginserver.SendPacket2(pack);
#endif

		safe_delete(pack);
	}
}
void ClientListEntry::Update(ZoneServer* iZS, ServerClientList_Struct* scl, sint8 iOnline) {
	if (pzoneserver != iZS) {
		if (pzoneserver){
			pzoneserver->RemovePlayer();
			LSUpdate(pzoneserver);
		}
		if (iZS){
			iZS->AddPlayer();
			LSUpdate(iZS);
		}
	}
	pzoneserver = iZS;
	pzone = scl->zone;
	pinstance = scl->instance_id;
	pcharid = scl->charid;

	strcpy(pname, scl->name);
	if (paccountid == 0) {
		paccountid = scl->AccountID;
		strcpy(paccountname, scl->AccountName);
		strcpy(plsname, scl->AccountName);
		pIP = scl->IP;
		pLSID = scl->LSAccountID;
		strn0cpy(plskey, scl->lskey, sizeof(plskey));
	}
	padmin = scl->Admin;
	plevel = scl->level;
	pclass_ = scl->class_;
	prace = scl->race;
	panon = scl->anon;
	ptellsoff = scl->tellsoff;
	pguild_id = scl->guild_id;
	pLFG = scl->LFG;
	gm = scl->gm;
	// Fields from the LFG Window
	if((scl->LFGFromLevel != 0) && (scl->LFGToLevel != 0)) {
		pLFGFromLevel = scl->LFGFromLevel;
		pLFGToLevel = scl->LFGToLevel;
		pLFGMatchFilter = scl->LFGMatchFilter;
		memcpy(pLFGComments, scl->LFGComments, sizeof(pLFGComments));
	}

	SetOnline(iOnline);
}

void ClientListEntry::LeavingZone(ZoneServer* iZS, sint8 iOnline) {
	if (iZS != 0 && iZS != pzoneserver)
		return;
	SetOnline(iOnline);

	if (pzoneserver){
		pzoneserver->RemovePlayer();
		LSUpdate(pzoneserver);
	}
	pzoneserver = 0;
	pzone = 0;
}

void ClientListEntry::ClearVars(bool iAll) {
	if (iAll) {
		pOnline = CLE_Status_Never;
		stale = 0;

		pLSID = 0;
		memset(plsname, 0, sizeof(plsname));
		memset(plskey, 0, sizeof(plskey));
		pworldadmin = 0;

		paccountid = 0;
		memset(paccountname, 0, sizeof(paccountname));
		padmin = 0;
	}
	pzoneserver = 0;
	pzone = 0;
	pcharid = 0;
	memset(pname, 0, sizeof(pname));
	plevel = 0;
	pclass_ = 0;
	prace = 0;
	panon = 0;
	ptellsoff = 0;
	pguild_id = GUILD_NONE;
	pLFG = 0;
	gm = 0;
}

void ClientListEntry::Camp(ZoneServer* iZS) {
	if (iZS != 0 && iZS != pzoneserver)
		return;
	if (pzoneserver){
		pzoneserver->RemovePlayer();
		LSUpdate(pzoneserver);
	}

	ClearVars();

	stale = 0;
}

bool ClientListEntry::CheckStale() {
	stale++;
	if (stale >= 3) {
		if (pOnline > CLE_Status_Offline)
			SetOnline(CLE_Status_Offline);
		else
			return true;
	}
	return false;
}

bool ClientListEntry::CheckAuth(int32 iLSID, const char* iKey) {
//	if (LSID() == iLSID && strncmp(plskey, iKey,10) == 0) {
	if (strncmp(plskey, iKey,10) == 0) {
		if (paccountid == 0 && LSID()>0) {
			sint16 tmpStatus = WorldConfig::get()->DefaultStatus;
			paccountid = database.CreateAccount(plsname, 0, tmpStatus, LSID());
			if (!paccountid) {
				_log(WORLD__CLIENTLIST_ERR,"Error adding local account for LS login: '%s', either duplicate name or adding a second LS account will do this, if latter, try again." ,plsname);
				return false;
			}
			strn0cpy(paccountname, plsname, sizeof(paccountname));
			padmin = tmpStatus;
		}
		char lsworldadmin[15] = "0";
		database.GetVariable("honorlsworldadmin", lsworldadmin, sizeof(lsworldadmin));
		if (atoi(lsworldadmin) == 1 && pworldadmin != 0 && (padmin < pworldadmin || padmin == 0))
			padmin = pworldadmin;
		return true;
	}
	return false;
}

bool ClientListEntry::CheckAuth(const char* iName, MD5& iMD5Password) {
	if (LSAccountID() == 0 && strcmp(paccountname, iName) == 0 && pMD5Pass == iMD5Password)
		return true;
	return false;
}

bool ClientListEntry::CheckAuth(int32 id, const char* iKey, int32 ip) {
	if (pIP==ip && strncmp(plskey, iKey,10) == 0){
		paccountid = id;
		database.GetAccountFromID(id,paccountname,&padmin);
		return true;
	}
	return false;
}
zoneserver.cpp
Code:
#include "../common/debug.h"
#include "zoneserver.h"
#include "clientlist.h"
#include "LoginServer.h"
#include "zonelist.h"
#include "worlddb.h"
#include "console.h"
#include "client.h"
#include "../common/md5.h"
#include "WorldConfig.h"
#include "../common/guilds.h"
#include "../common/packet_dump.h"
#include "../common/misc.h"
#include "cliententry.h"
#include "wguild_mgr.h"
#include "lfplist.h"
extern ClientList	client_list;
extern GroupLFPList LFPGroupList;
extern ZSList		zoneserver_list;
extern ConsoleList		console_list;
extern LoginServer loginserver;
extern volatile bool RunLoops;

ZoneServer::ZoneServer(EmuTCPConnection* itcpc) 
: WorldTCPConnection(), tcpc(itcpc), ls_zboot(5000) {
	ID = zoneserver_list.GetNextID();
	memset(zone_name, 0, sizeof(zone_name));
	memset(compiled, 0, sizeof(compiled));
	zoneID = 0;
	instanceID = 0;

	memset(clientaddress, 0, sizeof(clientaddress));
	clientport = 0;
	BootingUp = false;
	authenticated = false;
	staticzone = false;
	pNumPlayers = 0;
}

ZoneServer::~ZoneServer() {
	if (RunLoops)
		client_list.CLERemoveZSRef(this);
	tcpc->Free();
}

bool ZoneServer::SetZone(int32 iZoneID, int32 iInstanceID, bool iStaticZone) {
	BootingUp = false;
	
	const char* zn = MakeLowerString(database.GetZoneName(iZoneID));
	char*	longname;

	if (iZoneID)
		zlog(WORLD__ZONE,"Setting to '%s' (%d:%d)%s",(zn) ? zn : "",iZoneID, iInstanceID, 
			iStaticZone ? " (Static)" : "");

	zoneID = iZoneID;
	instanceID = iInstanceID;
	if(iZoneID!=0)
		oldZoneID = iZoneID;
	if (zoneID == 0) {
		client_list.CLERemoveZSRef(this);
		pNumPlayers = 0;
		LSSleepUpdate(GetPrevZoneID());
	}

	staticzone = iStaticZone;

	if (zn)
	{
		strcpy(zone_name, zn);
		if( database.GetZoneLongName( (char*)zone_name, &longname, NULL, NULL, NULL, NULL, NULL, NULL ) )
		{
			strcpy(long_name, longname);
			safe_delete( longname );
		}
		else
			strcpy(long_name, "");
	}
	else
	{
		strcpy(zone_name, "");
		strcpy(long_name, "");
	}	

	client_list.ZoneBootup(this);
	ls_zboot.Start();

	return true;
}

void ZoneServer::LSShutDownUpdate(int32 zoneid){
	if(WorldConfig::get()->UpdateStats){
		ServerPacket* pack = new ServerPacket;
		pack->opcode = ServerOP_LSZoneShutdown;
		pack->size = sizeof(ZoneShutdown_Struct);
		pack->pBuffer = new uchar[pack->size];
		memset(pack->pBuffer,0,pack->size);
		ZoneShutdown_Struct* zsd =(ZoneShutdown_Struct*)pack->pBuffer;
		if(zoneid==0)
			zsd->zone = GetPrevZoneID();
		else
			zsd->zone = zoneid;
		zsd->zone_wid = GetID();
		loginserver.SendPacket(pack);
		
#ifdef DUAL_SERVER
		loginserver.SendPacket2(pack);
#endif

		safe_delete(pack);
	}
}
void ZoneServer::LSBootUpdate(int32 zoneid, int32 instanceid, bool startup){
	if(WorldConfig::get()->UpdateStats){
		ServerPacket* pack = new ServerPacket;
		if(startup)
			pack->opcode = ServerOP_LSZoneStart;
		else
			pack->opcode = ServerOP_LSZoneBoot;
		pack->size = sizeof(ZoneBoot_Struct);
		pack->pBuffer = new uchar[pack->size];
		memset(pack->pBuffer,0,pack->size);
		ZoneBoot_Struct* bootup =(ZoneBoot_Struct*)pack->pBuffer;
		if(startup)
			strcpy(bootup->compile_time,GetCompileTime());
		bootup->zone = zoneid;
		bootup->zone_wid = GetID();
		bootup->instance = instanceid;
		loginserver.SendPacket(pack);

#ifdef DUAL_SERVER
		loginserver.SendPacket2(pack);
#endif
		safe_delete(pack);
	}
}

void ZoneServer::LSSleepUpdate(int32 zoneid){
	if(WorldConfig::get()->UpdateStats){
		ServerPacket* pack = new ServerPacket;
		pack->opcode = ServerOP_LSZoneSleep;
		pack->size = sizeof(ServerLSZoneSleep_Struct);
		pack->pBuffer = new uchar[pack->size];
		memset(pack->pBuffer,0,pack->size);
		ServerLSZoneSleep_Struct* sleep =(ServerLSZoneSleep_Struct*)pack->pBuffer;
		sleep->zone = zoneid;
		sleep->zone_wid = GetID();
		loginserver.SendPacket(pack);

#ifdef DUAL_SERVER
		loginserver.SendPacket2(pack);
#endif

		safe_delete(pack);
	}
}

bool ZoneServer::Process() {
	if (!tcpc->Connected())
		return false;
	if(ls_zboot.Check()){
		LSBootUpdate(GetZoneID(), true);
		ls_zboot.Disable();
	}
	ServerPacket *pack = 0;
	while((pack = tcpc->PopPacket())) {
		_hex(WORLD__ZONE_TRACE,pack->pBuffer,pack->size);
		if (!authenticated) {
			if (WorldConfig::get()->SharedKey.length() > 0) {
				if (pack->opcode == ServerOP_ZAAuth && pack->size == 16) {
					int8 tmppass[16];
					MD5::Generate((const uchar*) WorldConfig::get()->SharedKey.c_str(), WorldConfig::get()->SharedKey.length(), tmppass);
					if (memcmp(pack->pBuffer, tmppass, 16) == 0)
						authenticated = true;
					else {
						struct in_addr  in;
						in.s_addr = GetIP();
						zlog(WORLD__ZONE_ERR,"Zone authorization failed.");
						ServerPacket* pack = new ServerPacket(ServerOP_ZAAuthFailed);
						SendPacket(pack);
						delete pack;
						Disconnect();
						return false;
					}
				}
				else {
					struct in_addr  in;
					in.s_addr = GetIP();
					zlog(WORLD__ZONE_ERR,"Zone authorization failed.");
					ServerPacket* pack = new ServerPacket(ServerOP_ZAAuthFailed);
					SendPacket(pack);
					delete pack;
					Disconnect();
					return false;
				}
			}
			else
			{
				_log(WORLD__ZONE,"**WARNING** You have not configured a world shared key in your config file. You should add a <key>STRING</key> element to your <world> element to prevent unauthroized zone access.");
				authenticated = true;
			}
		}
		switch(pack->opcode) {
		case 0:
			break;
		case ServerOP_KeepAlive: {
			// ignore this
			break;
		}
		case ServerOP_ZAAuth: {
			break;
		}
		case ServerOP_LSZoneBoot:{
			if(pack->size==sizeof(ZoneBoot_Struct)){
				ZoneBoot_Struct* zbs= (ZoneBoot_Struct*)pack->pBuffer;
				SetCompile(zbs->compile_time);
			}
			break;
		}
		/*
		case ServerOP_SendGroup: {
			SendGroup_Struct* sgs=(SendGroup_Struct*)pack->pBuffer;
			ZoneServer* zs=zoneserver_list.FindByZoneID(sgs->zoneid);
			if(!zs)
				zlog(WORLD__ZONE,"Could not find zone id: %i running to transfer group to!",sgs->zoneid);
			else{
				zs->SendPacket(pack);
			}
			break;
		}*/
		case ServerOP_GroupIDReq: {
			SendGroupIDs();
			break;
		}
		case ServerOP_GroupLeave: {
			if(pack->size != sizeof(ServerGroupLeave_Struct))
				break;
			zoneserver_list.SendPacket(pack); //bounce it to all zones
			break;
		}

		case ServerOP_GroupJoin: {
			if(pack->size != sizeof(ServerGroupJoin_Struct))
				break;
			zoneserver_list.SendPacket(pack); //bounce it to all zones
			break;
		}

		case ServerOP_ForceGroupUpdate: {
			if(pack->size != sizeof(ServerForceGroupUpdate_Struct))
				break;
			zoneserver_list.SendPacket(pack); //bounce it to all zones
			break;
		}

		case ServerOP_OOZGroupMessage: {
			zoneserver_list.SendPacket(pack); //bounce it to all zones
			break;
		}

		case ServerOP_DisbandGroup: {
			if(pack->size != sizeof(ServerDisbandGroup_Struct))
				break;
			zoneserver_list.SendPacket(pack); //bounce it to all zones
			break;
		}

		case ServerOP_RaidAdd:{
			if(pack->size != sizeof(ServerRaidGeneralAction_Struct))
				break;

			zoneserver_list.SendPacket(pack);
			break;
		}

		case ServerOP_RaidRemove: {
			if(pack->size != sizeof(ServerRaidGeneralAction_Struct))
				break;

			zoneserver_list.SendPacket(pack);
			break;
		}

		case ServerOP_RaidDisband: {
			if(pack->size != sizeof(ServerRaidGeneralAction_Struct))
				break;

			zoneserver_list.SendPacket(pack);
			break;
		}

		case ServerOP_RaidLockFlag: {
			if(pack->size != sizeof(ServerRaidGeneralAction_Struct))
				break;

			zoneserver_list.SendPacket(pack);
			break;
		}

		case ServerOP_RaidChangeGroup: {
			if(pack->size != sizeof(ServerRaidGeneralAction_Struct))
				break;

			zoneserver_list.SendPacket(pack);
			break;
		}

		case ServerOP_UpdateGroup: {
			if(pack->size != sizeof(ServerRaidGeneralAction_Struct))
				break;

			zoneserver_list.SendPacket(pack);
			break;
		}

		case ServerOP_RaidGroupDisband: {
			if(pack->size != sizeof(ServerRaidGeneralAction_Struct))
				break;

			zoneserver_list.SendPacket(pack);
			break;
		}

		case ServerOP_RaidGroupAdd: {
			if(pack->size != sizeof(ServerRaidGroupAction_Struct))
				break;

			zoneserver_list.SendPacket(pack);
			break;
		}

		case ServerOP_RaidGroupRemove: {
			if(pack->size != sizeof(ServerRaidGroupAction_Struct))
				break;

			zoneserver_list.SendPacket(pack);
			break;
		}

		case ServerOP_RaidGroupSay: {
			zoneserver_list.SendPacket(pack);
			break;
		}

		case ServerOP_RaidSay: {
			zoneserver_list.SendPacket(pack);
			break;
		}

		case ServerOP_RaidGroupLeader: {
			if(pack->size != sizeof(ServerRaidGeneralAction_Struct))
				break;

			zoneserver_list.SendPacket(pack);
			break;
		}

		case ServerOP_RaidLeader: {
			if(pack->size != sizeof(ServerRaidGeneralAction_Struct))
				break;

			zoneserver_list.SendPacket(pack);
			break;
		}

		case ServerOP_DetailsChange: {
			if(pack->size != sizeof(ServerRaidGeneralAction_Struct))
				break;

			zoneserver_list.SendPacket(pack);
			break;
		}

		case ServerOP_SpawnCondition: {
			if(pack->size != sizeof(ServerSpawnCondition_Struct))
				break;
			//bounce the packet to the correct zone server, if its up
			ServerSpawnCondition_Struct* ssc = (ServerSpawnCondition_Struct*)pack->pBuffer;
			zoneserver_list.SendPacket(ssc->zoneID, 0, pack);
			break;
		}
		case ServerOP_SpawnEvent: {
			if(pack->size != sizeof(ServerSpawnEvent_Struct))
				break;
			//bounce the packet to the correct zone server, if its up
			ServerSpawnEvent_Struct* sse = (ServerSpawnEvent_Struct*)pack->pBuffer;
			zoneserver_list.SendPacket(sse->zoneID, 0, pack);
			break;
		}
		case ServerOP_ChannelMessage: {
			ServerChannelMessage_Struct* scm = (ServerChannelMessage_Struct*) pack->pBuffer;
			if (scm->chan_num == 7 || scm->chan_num == 14) {
				if (scm->deliverto[0] == '*') {
					Console* con = 0;
					con = console_list.FindByAccountName(&scm->deliverto[1]);
					if (((!con) || (!con->SendChannelMessage(scm))) && (!scm->noreply))
						zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to);
					break;
				}
				ClientListEntry* cle = client_list.FindCharacter(scm->deliverto);
				if (cle == 0 || cle->Online() < CLE_Status_Zoning || (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) {
					if (!scm->noreply)
						zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to);
				}
				else if (cle->Online() == CLE_Status_Zoning) {
					if (!scm->noreply) {
						char errbuf[MYSQL_ERRMSG_SIZE];
						char *query = 0;
						MYSQL_RES *result;
						//MYSQL_ROW row;   Trumpcard - commenting.  Currently unused.
						time_t rawtime;
						struct tm * timeinfo;
						time ( &rawtime );
						timeinfo = localtime ( &rawtime );
						char *telldate=asctime(timeinfo);
						if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT name from character_ where name='%s'",scm->deliverto), errbuf, &result)) {
							safe_delete(query);
							if (result!=0) {
								if (database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO tellque (Date,Receiver,Sender,Message) values('%s','%s','%s','%s')",telldate,scm->deliverto,scm->from,scm->message), errbuf, &result))
									zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "Your message has been added to the %s's que.", scm->to);
								else
									zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to);
								safe_delete(query);
							}
							else
								zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to);
							mysql_free_result(result);
						}
						else
							safe_delete(query);
					}
				//		zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to);
				}
				else if (cle->Server() == 0) {
					if (!scm->noreply)
						zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not contactable at this time'", scm->to, scm->to);
				}
				else
					cle->Server()->SendPacket(pack);
			}
			else {
				if (scm->chan_num == 5 || scm->chan_num == 6 || scm->chan_num == 11) {
					console_list.SendChannelMessage(scm);
				}
				zoneserver_list.SendPacket(pack);
			}
			break;
		}
		case ServerOP_EmoteMessage: {
			ServerEmoteMessage_Struct* sem = (ServerEmoteMessage_Struct*) pack->pBuffer;
			zoneserver_list.SendEmoteMessageRaw(sem->to, sem->guilddbid, sem->minstatus, sem->type, sem->message);
			break;
		}
		case ServerOP_VoiceMacro: {

			ServerVoiceMacro_Struct* svm = (ServerVoiceMacro_Struct*) pack->pBuffer;
			
			if(svm->Type == VoiceMacroTell) {

				ClientListEntry* cle = client_list.FindCharacter(svm->To);

				if (!cle || (cle->Online() < CLE_Status_Zoning) || !cle->Server())  {

					zoneserver_list.SendEmoteMessage(svm->From, 0, 0, 0, "'%s is not online at this time'", svm->To);

					break;
				}

				cle->Server()->SendPacket(pack);
			}
			else
				zoneserver_list.SendPacket(pack);

			break;
		}

		case ServerOP_RezzPlayerAccept: {
			zoneserver_list.SendPacket(pack);
			break;
		}
		case ServerOP_RezzPlayer: {

			RezzPlayer_Struct* sRezz = (RezzPlayer_Struct*) pack->pBuffer;
			if (zoneserver_list.SendPacket(pack)){
				zlog(WORLD__ZONE,"Sent Rez packet for %s",sRezz->rez.your_name);
			}
			else {
				zlog(WORLD__ZONE,"Could not send Rez packet for %s",sRezz->rez.your_name);
			}
			break;
		}
		case ServerOP_MultiLineMsg: {
			ServerMultiLineMsg_Struct* mlm = (ServerMultiLineMsg_Struct*) pack->pBuffer;
			client_list.SendPacket(mlm->to, pack);
			break;
		}
		case ServerOP_SetZone: {
			if(pack->size != sizeof(SetZone_Struct))
				break;

			SetZone_Struct* szs = (SetZone_Struct*) pack->pBuffer;	
			if (szs->zoneid != 0) {
				if(database.GetZoneName(szs->zoneid))
					SetZone(szs->zoneid, szs->instanceid, szs->staticzone);
				else
					SetZone(0);
			}
			else
				SetZone(0);

			break;
		}
		case ServerOP_SetConnectInfo: {
			if (pack->size != sizeof(ServerConnectInfo))
					break;
			ServerConnectInfo* sci = (ServerConnectInfo*) pack->pBuffer;

			if (!sci->port) {
				clientport=zoneserver_list.GetAvailableZonePort();

				ServerPacket p(ServerOP_SetConnectInfo, sizeof(ServerConnectInfo));
				memset(p.pBuffer,0,sizeof(ServerConnectInfo));
				ServerConnectInfo* sci = (ServerConnectInfo*) p.pBuffer;
				sci->port = clientport;
				SendPacket(&p);
				zlog(WORLD__ZONE,"Auto zone port configuration.  Telling zone to use port %d",clientport);
			} else {
				clientport=sci->port;
				zlog(WORLD__ZONE,"Zone specified port %d, must be a previously allocated zone reconnecting.",clientport);
			}

		}
		case ServerOP_SetLaunchName: {
			if(pack->size != sizeof(LaunchName_Struct))
				break;
			const LaunchName_Struct* ln = (const LaunchName_Struct*)pack->pBuffer;
			launcher_name = ln->launcher_name;
			launched_name = ln->zone_name;
			zlog(WORLD__ZONE, "Zone started with name %s by launcher %s", launched_name.c_str(), launcher_name.c_str());
			break;
		}
		case ServerOP_ShutdownAll: {
			if(pack->size==0){
				zoneserver_list.SendPacket(pack);
				zoneserver_list.Process();
				CatchSignal(2);
			}
			else{
				WorldShutDown_Struct* wsd=(WorldShutDown_Struct*)pack->pBuffer;
				if(wsd->time==0 && wsd->interval==0 && zoneserver_list.shutdowntimer->Enabled()){
					zoneserver_list.shutdowntimer->Disable();
					zoneserver_list.reminder->Disable();
				}
				else{
					zoneserver_list.shutdowntimer->SetTimer(wsd->time);
					zoneserver_list.reminder->SetTimer(wsd->interval-1000);
					zoneserver_list.reminder->SetAtTrigger(wsd->interval);
					zoneserver_list.shutdowntimer->Start();
					zoneserver_list.reminder->Start();
				}
			}
			break;
		}
		case ServerOP_ZoneShutdown: {
			ServerZoneStateChange_struct* s = (ServerZoneStateChange_struct *) pack->pBuffer;
			ZoneServer* zs = 0;
			if (s->ZoneServerID != 0)
				zs = zoneserver_list.FindByID(s->ZoneServerID);
			else if (s->zoneid != 0)
				zs = zoneserver_list.FindByName(database.GetZoneName(s->zoneid));
			else
				zoneserver_list.SendEmoteMessage(s->adminname, 0, 0, 0, "Error: SOP_ZoneShutdown: neither ID nor name specified");

			if (zs == 0)
				zoneserver_list.SendEmoteMessage(s->adminname, 0, 0, 0, "Error: SOP_ZoneShutdown: zoneserver not found");
			else
				zs->SendPacket(pack);
			break;
		}
		case ServerOP_ZoneBootup: {
			ServerZoneStateChange_struct* s = (ServerZoneStateChange_struct *) pack->pBuffer;
			zoneserver_list.SOPZoneBootup(s->adminname, s->ZoneServerID, database.GetZoneName(s->zoneid), s->makestatic);
			break;
		}
		case ServerOP_ZoneStatus: {
			if (pack->size >= 1)
				zoneserver_list.SendZoneStatus((char *) &pack->pBuffer[1], (int8) pack->pBuffer[0], this);
			break;

		}
		case ServerOP_AcceptWorldEntrance: {
			if(pack->size != sizeof(WorldToZone_Struct))
				break;

			WorldToZone_Struct* wtz = (WorldToZone_Struct*) pack->pBuffer;
			Client* client = 0;
			client = client_list.FindByAccountID(wtz->account_id);
			if(client != 0)
				client->Clearance(wtz->response);
		}
		case ServerOP_ZoneToZoneRequest: {
		//
		// solar: ZoneChange is received by the zone the player is in, then the
		// zone sends a ZTZ which ends up here.  This code then find the target
		// (ingress point) and boots it if needed, then sends the ZTZ to it.
		// The ingress server will decide wether the player can enter, then will
		// send back the ZTZ to here.  This packet is passed back to the egress
		// server, which will send a ZoneChange response back to the client
		// which can be an error, or a success, in which case the client will
		// disconnect, and their zone location will be saved when ~Client is
		// called, so it will be available when they ask to zone.
		//

			
			if(pack->size != sizeof(ZoneToZone_Struct))
				break;
			ZoneToZone_Struct* ztz = (ZoneToZone_Struct*) pack->pBuffer;
			ClientListEntry* client = NULL;
			if(WorldConfig::get()->UpdateStats)
				client = client_list.FindCharacter(ztz->name);

			zlog(WORLD__ZONE,"ZoneToZone request for %s current zone %d req zone %d\n",
				ztz->name, ztz->current_zone_id, ztz->requested_zone_id);

			if(GetZoneID() == ztz->current_zone_id && GetInstanceID() == ztz->current_instance_id)	// this is a request from the egress zone
			{
				zlog(WORLD__ZONE,"Processing ZTZ for egress from zone for client %s\n", ztz->name);

				if
				(
					ztz->admin < 80 &&
					ztz->ignorerestrictions < 2 &&
					zoneserver_list.IsZoneLocked(ztz->requested_zone_id)
				)
				{
					ztz->response = 0;
					SendPacket(pack);
					break;
				}

				ZoneServer *ingress_server = NULL;
				if(ztz->requested_instance_id > 0)
				{
					ingress_server = zoneserver_list.FindByInstanceID(ztz->requested_instance_id);
				}
				else
				{
					ingress_server = zoneserver_list.FindByZoneID(ztz->requested_zone_id);

				}

				if(ingress_server)	// found a zone already running
				{
					_log(WORLD__ZONE,"Found a zone already booted for %s\n", ztz->name);
					ztz->response = 1;
				}
				else	// need to boot one
				{
					int server_id;
					if ((server_id = zoneserver_list.TriggerBootup(ztz->requested_zone_id, ztz->requested_instance_id))){
						_log(WORLD__ZONE,"Successfully booted a zone for %s\n", ztz->name);
						// bootup successful, ready to rock
						ztz->response = 1;
						ingress_server = zoneserver_list.FindByID(server_id);
					}
					else
					{
						_log(WORLD__ZONE_ERR,"FAILED to boot a zone for %s\n", ztz->name);
						// bootup failed, send back error code 0
						ztz->response = 0;
					}
				}
				if(ztz->response!=0 && client)
					client->LSZoneChange(ztz);
				SendPacket(pack);	// send back to egress server
				if(ingress_server)	// if we couldn't boot one, this is 0
				{
					ingress_server->SendPacket(pack);	// inform target server
				}
			}
			else	// this is response from the ingress server, route it back to the egress server
			{
				zlog(WORLD__ZONE,"Processing ZTZ for ingress to zone for client %s\n", ztz->name);
				ZoneServer *egress_server = NULL;
				if(ztz->current_instance_id > 0)
				{
					egress_server = zoneserver_list.FindByInstanceID(ztz->current_instance_id);
				}
				else
				{
					egress_server = zoneserver_list.FindByZoneID(ztz->current_zone_id);
				}

				if(egress_server)
				{
					egress_server->SendPacket(pack);
				}
			}

			break;
		}
		case ServerOP_ClientList: {
			if (pack->size != sizeof(ServerClientList_Struct)) {
				zlog(WORLD__ZONE_ERR,"Wrong size on ServerOP_ClientList. Got: %d, Expected: %d",pack->size,sizeof(ServerClientList_Struct));
				break;
			}
			client_list.ClientUpdate(this, (ServerClientList_Struct*) pack->pBuffer);
			break;
		}
		case ServerOP_ClientListKA: {
			ServerClientListKeepAlive_Struct* sclka = (ServerClientListKeepAlive_Struct*) pack->pBuffer;
			if (pack->size < 4 || pack->size != 4 + (4 * sclka->numupdates)) {
				zlog(WORLD__ZONE_ERR,"Wrong size on ServerOP_ClientListKA. Got: %d, Expected: %d",pack->size, (4 + (4 * sclka->numupdates)));
				break;
			}
			client_list.CLEKeepAlive(sclka->numupdates, sclka->wid);
			break;
		}
		case ServerOP_Who: {
			ServerWhoAll_Struct* whoall = (ServerWhoAll_Struct*) pack->pBuffer;
			Who_All_Struct* whom = new Who_All_Struct;
			memset(whom,0,sizeof(Who_All_Struct));
			whom->gmlookup = whoall->gmlookup;
			whom->lvllow = whoall->lvllow;
			whom->lvlhigh = whoall->lvlhigh;
			whom->wclass = whoall->wclass;
			whom->wrace = whoall->wrace;
			strcpy(whom->whom,whoall->whom);
			client_list.SendWhoAll(whoall->fromid,whoall->from, whoall->admin, whom, this);
			delete whom;
			break;
		}
		case ServerOP_FriendsWho: {
			ServerFriendsWho_Struct* FriendsWho = (ServerFriendsWho_Struct*) pack->pBuffer;
			client_list.SendFriendsWho(FriendsWho, this);
			break;
		}
		case ServerOP_LFGMatches: {
			ServerLFGMatchesRequest_Struct* smrs = (ServerLFGMatchesRequest_Struct*) pack->pBuffer;
			client_list.SendLFGMatches(smrs);
			break;
		}
		case ServerOP_LFPMatches: {
			ServerLFPMatchesRequest_Struct* smrs = (ServerLFPMatchesRequest_Struct*) pack->pBuffer;
			LFPGroupList.SendLFPMatches(smrs);
			break;
		}
		case ServerOP_LFPUpdate: {
			ServerLFPUpdate_Struct* sus = (ServerLFPUpdate_Struct*) pack->pBuffer;	
			if(sus->Action) 
				LFPGroupList.UpdateGroup(sus);
			else
				LFPGroupList.RemoveGroup(sus);
			break;
		}
		case ServerOP_ZonePlayer: {
			//ServerZonePlayer_Struct* szp = (ServerZonePlayer_Struct*) pack->pBuffer;
			zoneserver_list.SendPacket(pack);
			break;
		}
		case ServerOP_KickPlayer: {
			zoneserver_list.SendPacket(pack);
			break;
		}
		case ServerOP_KillPlayer: {
			zoneserver_list.SendPacket(pack);
			break;
		}
		
		//these opcodes get processed by the guild manager.
		case ServerOP_RefreshGuild:
		case ServerOP_DeleteGuild:
		case ServerOP_GuildCharRefresh:
		case ServerOP_GuildMemberUpdate: {
			guild_mgr.ProcessZonePacket(pack);
			break;
		}
		
		case ServerOP_FlagUpdate: {
			ClientListEntry* cle = client_list.FindCLEByAccountID(*((int32*) pack->pBuffer));
			if (cle)
				cle->SetAdmin(*((sint16*) &pack->pBuffer[4]));
			zoneserver_list.SendPacket(pack);
			break;
		}
		case ServerOP_GMGoto: {
			if (pack->size != sizeof(ServerGMGoto_Struct)) {
				zlog(WORLD__ZONE_ERR,"Wrong size on ServerOP_GMGoto. Got: %d, Expected: %d",pack->size,sizeof(ServerGMGoto_Struct));
				break;
			}
			ServerGMGoto_Struct* gmg = (ServerGMGoto_Struct*) pack->pBuffer;
			ClientListEntry* cle = client_list.FindCharacter(gmg->gotoname);
			if (cle != 0) {
				if (cle->Server() == 0)
					this->SendEmoteMessage(gmg->myname, 0, 0, 13, "Error: Cannot identify %s's zoneserver.", gmg->gotoname);
				else if (cle->Anon() == 1 && cle->Admin() > gmg->admin) // no snooping for anon GMs
					this->SendEmoteMessage(gmg->myname, 0, 0, 13, "Error: %s not found", gmg->gotoname);
				else
					cle->Server()->SendPacket(pack);
			}
			else {
				this->SendEmoteMessage(gmg->myname, 0, 0, 13, "Error: %s not found", gmg->gotoname);
			}
			break;
		}
		case ServerOP_Lock: {
			if (pack->size != sizeof(ServerLock_Struct)) {
				zlog(WORLD__ZONE_ERR,"Wrong size on ServerOP_Lock. Got: %d, Expected: %d",pack->size,sizeof(ServerLock_Struct));
				break;
			}
			ServerLock_Struct* slock = (ServerLock_Struct*) pack->pBuffer;
  			if (slock->mode >= 1) 
				WorldConfig::LockWorld();
			else
				WorldConfig::UnlockWorld();
			if (loginserver.Connected()) {
				loginserver.SendStatus();
				if (slock->mode >= 1)
					this->SendEmoteMessage(slock->myname, 0, 0, 13, "World locked");
				else
					this->SendEmoteMessage(slock->myname, 0, 0, 13, "World unlocked");
			}
			else {
				if (slock->mode >= 1)
					this->SendEmoteMessage(slock->myname, 0, 0, 13, "World locked, but login server not connected.");
				else 
					this->SendEmoteMessage(slock->myname, 0, 0, 13, "World unlocked, but login server not conencted.");
			}
			break;
							}
		case ServerOP_Motd: {
			if (pack->size != sizeof(ServerMotd_Struct)) {
				zlog(WORLD__ZONE_ERR,"Wrong size on ServerOP_Motd. Got: %d, Expected: %d",pack->size,sizeof(ServerMotd_Struct));
				break;
			}
			ServerMotd_Struct* smotd = (ServerMotd_Struct*) pack->pBuffer;
			database.SetVariable("MOTD",smotd->motd);
			//this->SendEmoteMessage(smotd->myname, 0, 0, 13, "Updated Motd.");
			zoneserver_list.SendPacket(pack);
			break;
		}
		case ServerOP_Uptime: {
			if (pack->size != sizeof(ServerUptime_Struct)) {
				zlog(WORLD__ZONE_ERR,"Wrong size on ServerOP_Uptime. Got: %d, Expected: %d",pack->size,sizeof(ServerUptime_Struct));
				break;
			}
			ServerUptime_Struct* sus = (ServerUptime_Struct*) pack->pBuffer;
			if (sus->zoneserverid == 0) {
				ZSList::ShowUpTime(this, sus->adminname);
			}
			else {
				ZoneServer* zs = zoneserver_list.FindByID(sus->zoneserverid);
				if (zs)
					zs->SendPacket(pack);
			}
			break;
							  }
		case ServerOP_Petition: {
			zoneserver_list.SendPacket(pack);
			break;
								}
		case ServerOP_GetWorldTime: {
			zlog(WORLD__ZONE,"Broadcasting a world time update");
			ServerPacket* pack = new ServerPacket;
			
			pack->opcode = ServerOP_SyncWorldTime;
			pack->size = sizeof(eqTimeOfDay);
			pack->pBuffer = new uchar[pack->size];
			memset(pack->pBuffer, 0, pack->size);
			eqTimeOfDay* tod = (eqTimeOfDay*) pack->pBuffer;
			tod->start_eqtime=zoneserver_list.worldclock.getStartEQTime();
			tod->start_realtime=zoneserver_list.worldclock.getStartRealTime();
			SendPacket(pack);
			delete pack;
			break;
									}
		case ServerOP_SetWorldTime: {
			zlog(WORLD__ZONE,"Received SetWorldTime");
			eqTimeOfDay* newtime = (eqTimeOfDay*) pack->pBuffer;
			zoneserver_list.worldclock.setEQTimeOfDay(newtime->start_eqtime, newtime->start_realtime);
			zlog(WORLD__ZONE,"New time = %d-%d-%d %d:%d (%d)\n", newtime->start_eqtime.year, newtime->start_eqtime.month, (int)newtime->start_eqtime.day, (int)newtime->start_eqtime.hour, (int)newtime->start_eqtime.minute, (int)newtime->start_realtime);
			zoneserver_list.worldclock.saveFile(WorldConfig::get()->EQTimeFile.c_str());
			zoneserver_list.SendTimeSync();
			break;
		}
		case ServerOP_IPLookup: {
			if (pack->size < sizeof(ServerGenericWorldQuery_Struct)) {
				zlog(WORLD__ZONE_ERR,"Wrong size on ServerOP_IPLookup. Got: %d, Expected (at least): %d",pack->size,sizeof(ServerGenericWorldQuery_Struct));
				break;
			}
			ServerGenericWorldQuery_Struct* sgwq = (ServerGenericWorldQuery_Struct*) pack->pBuffer;
			if (pack->size == sizeof(ServerGenericWorldQuery_Struct))
				client_list.SendCLEList(sgwq->admin, sgwq->from, this);
			else
				client_list.SendCLEList(sgwq->admin, sgwq->from, this, sgwq->query);
			break;
		}
		case ServerOP_LockZone: {
			if (pack->size < sizeof(ServerLockZone_Struct)) {
				zlog(WORLD__ZONE_ERR,"Wrong size on ServerOP_LockZone. Got: %d, Expected: %d",pack->size,sizeof(ServerLockZone_Struct));
				break;
			}
			ServerLockZone_Struct* s = (ServerLockZone_Struct*) pack->pBuffer;
			switch (s->op) {
				case 0:
					zoneserver_list.ListLockedZones(s->adminname, this);
					break;
				case 1:
					if (zoneserver_list.SetLockedZone(s->zoneID, true))
						zoneserver_list.SendEmoteMessage(0, 0, 80, 15, "Zone locked: %s", database.GetZoneName(s->zoneID));
					else
						this->SendEmoteMessageRaw(s->adminname, 0, 0, 0, "Failed to change lock");
					break;
				case 2:
					if (zoneserver_list.SetLockedZone(s->zoneID, false))
						zoneserver_list.SendEmoteMessage(0, 0, 80, 15, "Zone unlocked: %s", database.GetZoneName(s->zoneID));
					else
						this->SendEmoteMessageRaw(s->adminname, 0, 0, 0, "Failed to change lock");
					break;
			}
			break;
		}
		case ServerOP_ItemStatus: {
			zoneserver_list.SendPacket(pack);
			break;
		}
		case ServerOP_OOCMute: {
			zoneserver_list.SendPacket(pack);
			break;
		}
		case ServerOP_Revoke: {
			RevokeStruct* rev = (RevokeStruct*)pack->pBuffer;
			ClientListEntry* cle = client_list.FindCharacter(rev->name);
			if (cle != 0 && cle->Server() != 0)
			{
				cle->Server()->SendPacket(pack);
			}
			break;
		}
		case ServerOP_SpawnPlayerCorpse: {
			SpawnPlayerCorpse_Struct* s = (SpawnPlayerCorpse_Struct*)pack->pBuffer;
			ZoneServer* zs = zoneserver_list.FindByZoneID(s->zone_id);
			if(zs) {
				if (zs->SendPacket(pack)) {
					zlog(WORLD__ZONE,"Sent request to spawn player corpse id %i in zone %u.",s->player_corpse_id, s->zone_id);
				}
				else {
					zlog(WORLD__ZONE_ERR,"Could not send request to spawn player corpse id %i in zone %u.",s->player_corpse_id, s->zone_id);
				}
			}
			break;
		}
	    case ServerOP_Consent: {
			// Message string id's likely to be used here are:
			// CONSENT_YOURSELF = 399
			// CONSENT_INVALID_NAME = 397
			// TARGET_NOT_FOUND = 101
			ZoneServer* zs;
			ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer;
			ClientListEntry* cle = client_list.FindCharacter(s->grantname);
			if(cle) {
				if(cle->instance() != 0)
				{
					zs = zoneserver_list.FindByInstanceID(cle->instance());
					if(zs) {
						if(zs->SendPacket(pack)) {
							zlog(WORLD__ZONE, "Sent consent packet from player %s to player %s in zone %u.", s->ownername, s->grantname, cle->instance());
						}
						else {
							zlog(WORLD__ZONE_ERR, "Unable to locate zone record for instance id %u in zoneserver list for ServerOP_Consent operation.", s->instance_id);
						}
					}
					else
					{
						delete pack;
						pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct));
						ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer;
						strcpy(scs->grantname, s->grantname);
						strcpy(scs->ownername, s->ownername);
						scs->permission = s->permission;
						scs->zone_id = s->zone_id;
						scs->instance_id = s->instance_id;
						scs->message_string_id = 101;
						zs = zoneserver_list.FindByInstanceID(s->instance_id);
						if(zs) {
							if(!zs->SendPacket(pack))
								zlog(WORLD__ZONE_ERR, "Unable to send consent response back to player %s in instance %u.", s->ownername, zs->GetInstanceID());
						}
						else {
							zlog(WORLD__ZONE_ERR, "Unable to locate zone record for instance id %u in zoneserver list for ServerOP_Consent_Response operation.", s->instance_id);
						}
					}
				}
				else
				{
					zs = zoneserver_list.FindByZoneID(cle->zone());
					if(zs) {
						if(zs->SendPacket(pack)) {
							zlog(WORLD__ZONE, "Sent consent packet from player %s to player %s in zone %u.", s->ownername, s->grantname, cle->zone());
						}
						else {
							zlog(WORLD__ZONE_ERR, "Unable to locate zone record for zone id %u in zoneserver list for ServerOP_Consent operation.", s->zone_id);
						}
					}
					else {
						// send target not found back to requester
						delete pack;
						pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct));
						ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer;
						strcpy(scs->grantname, s->grantname);
						strcpy(scs->ownername, s->ownername);
						scs->permission = s->permission;
						scs->zone_id = s->zone_id;
						scs->message_string_id = 101;
						zs = zoneserver_list.FindByZoneID(s->zone_id);
						if(zs) {
							if(!zs->SendPacket(pack))
								zlog(WORLD__ZONE_ERR, "Unable to send consent response back to player %s in zone %s.", s->ownername, zs->GetZoneName());
						}
						else {
							zlog(WORLD__ZONE_ERR, "Unable to locate zone record for zone id %u in zoneserver list for ServerOP_Consent_Response operation.", s->zone_id);
						}
					}
				}
			}
			else {
				// send target not found back to requester
				delete pack;
				pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct));
				ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer;
				strcpy(scs->grantname, s->grantname);
				strcpy(scs->ownername, s->ownername);
				scs->permission = s->permission;
				scs->zone_id = s->zone_id;
				scs->message_string_id = 397;
				zs = zoneserver_list.FindByZoneID(s->zone_id);
				if(zs) {
					if(!zs->SendPacket(pack))
						zlog(WORLD__ZONE_ERR, "Unable to send consent response back to player %s in zone %s.", s->ownername, zs->GetZoneName());
				}
				else {
					zlog(WORLD__ZONE_ERR, "Unable to locate zone record for zone id %u in zoneserver list for ServerOP_Consent_Response operation.", s->zone_id);
				}
			}
			break;
		}
		case ServerOP_Consent_Response: {
			// Message string id's likely to be used here are:
			// CONSENT_YOURSELF = 399
			// CONSENT_INVALID_NAME = 397
			// TARGET_NOT_FOUND = 101
			ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer;
			if(s->instance_id != 0)
			{
				ZoneServer* zs = zoneserver_list.FindByInstanceID(s->instance_id);
				if(zs) {
					if(!zs->SendPacket(pack))
						zlog(WORLD__ZONE_ERR, "Unable to send consent response back to player %s in instance %u.", s->ownername, zs->GetInstanceID());
				}
				else {
					zlog(WORLD__ZONE_ERR, "Unable to locate zone record for instance id %u in zoneserver list for ServerOP_Consent_Response operation.", s->instance_id);
				}
			}
			else
			{
				ZoneServer* zs = zoneserver_list.FindByZoneID(s->zone_id);
				if(zs) {
					if(!zs->SendPacket(pack))
						zlog(WORLD__ZONE_ERR, "Unable to send consent response back to player %s in zone %s.", s->ownername, zs->GetZoneName());
				}
				else {
					zlog(WORLD__ZONE_ERR, "Unable to locate zone record for zone id %u in zoneserver list for ServerOP_Consent_Response operation.", s->zone_id);
				}
			}
			break;
		}

		case ServerOP_InstanceUpdateTime :
		{
			ServerInstanceUpdateTime_Struct *iut = (ServerInstanceUpdateTime_Struct*)pack->pBuffer;
			ZoneServer *zm = zoneserver_list.FindByInstanceID(iut->instance_id);
			if(zm)
			{
				zm->SendPacket(pack);
			}
			break;
		}
		case ServerOP_QGlobalUpdate:
		{
			if(pack->size != sizeof(ServerQGlobalUpdate_Struct))
		{
			break;
		}

			zoneserver_list.SendPacket(pack);
			break;
		}

		case ServerOP_QGlobalDelete:
		{
			if(pack->size != sizeof(ServerQGlobalDelete_Struct))
			{
				break;
			}

			zoneserver_list.SendPacket(pack);
			break;
		}

		case ServerOP_AdventureCreate:
		case ServerOP_AdventureAddPlayer:
		case ServerOP_AdventureDestroy: 
		case ServerOP_AdventureUpdate:
		case ServerOP_AdventureCount:
		case ServerOP_AdventureFinish:
		case ServerOP_AdventureMessage:
		case ServerOP_DepopAllPlayersCorpses:
		case ServerOP_ReloadTitles:
		case ServerOP_SpawnStatusChange:
		case ServerOP_ReloadTasks:
		case ServerOP_UpdateSpawn:
		{
			zoneserver_list.SendPacket(pack);
			break;
		}
		default:
		{
			zlog(WORLD__ZONE_ERR,"Unknown ServerOPcode from zone 0x%04x, size %d",pack->opcode,pack->size);
			DumpPacket(pack->pBuffer, pack->size);
			break;
		}
		}

		delete pack;
	} 
	return true;
}

void ZoneServer::SendEmoteMessage(const char* to, int32 to_guilddbid, sint16 to_minstatus, int32 type, const char* message, ...) {
	if (!message)
		return;
	va_list argptr;
	char buffer[1024];

	va_start(argptr, message);
	vsnprintf(buffer, sizeof(buffer), message, argptr);
	va_end(argptr);
	SendEmoteMessageRaw(to, to_guilddbid, to_minstatus, type, buffer);
}

void ZoneServer::SendEmoteMessageRaw(const char* to, int32 to_guilddbid, sint16 to_minstatus, int32 type, const char* message) {
	if (!message)
		return;
	ServerPacket* pack = new ServerPacket;

	pack->opcode = ServerOP_EmoteMessage;
	pack->size = sizeof(ServerEmoteMessage_Struct)+strlen(message)+1;
	pack->pBuffer = new uchar[pack->size];
	memset(pack->pBuffer, 0, pack->size);
	ServerEmoteMessage_Struct* sem = (ServerEmoteMessage_Struct*) pack->pBuffer;
	
	if (to != 0) {
		strcpy((char *) sem->to, to);
	}
	else {
		sem->to[0] = 0;
	}

	sem->guilddbid = to_guilddbid;
	sem->minstatus = to_minstatus;
	sem->type = type;
	strcpy(&sem->message[0], message);
	
	pack->Deflate();
	SendPacket(pack);
	delete pack;
}

void ZoneServer::SendGroupIDs() {
	ServerPacket* pack = new ServerPacket(ServerOP_GroupIDReply, sizeof(ServerGroupIDReply_Struct));
	ServerGroupIDReply_Struct* sgi = (ServerGroupIDReply_Struct*)pack->pBuffer;
	zoneserver_list.NextGroupIDs(sgi->start, sgi->end);
	SendPacket(pack);
	delete pack;
}

void ZoneServer::ChangeWID(int32 iCharID, int32 iWID) {
	ServerPacket* pack = new ServerPacket(ServerOP_ChangeWID, sizeof(ServerChangeWID_Struct));
	ServerChangeWID_Struct* scw = (ServerChangeWID_Struct*) pack->pBuffer;
	scw->charid = iCharID;
	scw->newwid = iWID;
	zoneserver_list.SendPacket(pack);
	delete pack;
}


void ZoneServer::TriggerBootup(int32 iZoneID, int32 iInstanceID, const char* adminname, bool iMakeStatic) {
	BootingUp = true;
	zoneID = iZoneID;
	instanceID = iInstanceID;

	ServerPacket* pack = new ServerPacket(ServerOP_ZoneBootup, sizeof(ServerZoneStateChange_struct));
	ServerZoneStateChange_struct* s = (ServerZoneStateChange_struct *) pack->pBuffer;
	s->ZoneServerID = ID;
	if (adminname != 0)
		strcpy(s->adminname, adminname);
	
	if (iZoneID == 0)
		s->zoneid = this->GetZoneID();
	else
		s->zoneid = iZoneID;

	s->instanceid = iInstanceID;
	s->makestatic = iMakeStatic;
	SendPacket(pack);
	delete pack;
	LSBootUpdate(iZoneID, iInstanceID);
}

void ZoneServer::IncommingClient(Client* client) {
	BootingUp = true;
	ServerPacket* pack = new ServerPacket(ServerOP_ZoneIncClient, sizeof(ServerZoneIncommingClient_Struct));
	ServerZoneIncommingClient_Struct* s = (ServerZoneIncommingClient_Struct*) pack->pBuffer;
	s->zoneid = GetZoneID();
	s->instanceid = GetInstanceID();
	s->wid = client->GetWID();
	s->ip = client->GetIP();
	s->accid = client->GetAccountID();
	s->admin = client->GetAdmin();
	s->charid = client->GetCharID();
	if (client->GetCLE())
		s->tellsoff = client->GetCLE()->TellsOff();
	strn0cpy(s->charname, client->GetCharName(), sizeof(s->charname));
	strn0cpy(s->lskey, client->GetLSKey(), sizeof(s->lskey));
	SendPacket(pack);
	delete pack;
}
This was WORLD, COMMON will be on next post

Last edited by Angelox; 04-17-2010 at 08:51 PM..
Reply With Quote