/*
* Ascent MMORPG Server
* Copyright (C) 2005-2007 Ascent Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "StdAfx.h"
#include "svn_revision.h"
#ifdef CLUSTERING
initialiseSingleton(ClusterInterface);
ClusterInterfaceHandler ClusterInterface::PHandlers[IMSG_NUM_TYPES];
void ClusterInterface::InitHandlers()
{
memset(PHandlers, 0, sizeof(void*) * IMSG_NUM_TYPES);
PHandlers[ISMSG_AUTH_REQUEST] = &ClusterInterface::HandleAuthRequest;
PHandlers[ISMSG_AUTH_RESULT] = &ClusterInterface::HandleAuthResult;
PHandlers[ISMSG_REGISTER_RESULT] = &ClusterInterface::HandleRegisterResult;
PHandlers[ISMSG_CREATE_INSTANCE] = &ClusterInterface::HandleCreateInstance;
PHandlers[ISMSG_PLAYER_LOGIN] = &ClusterInterface::HandlePlayerLogin;
PHandlers[ISMSG_WOW_PACKET] = &ClusterInterface::HandleWoWPacket;
}
ClusterInterface::ClusterInterface()
{
ClusterInterface::InitHandlers();
m_connected = false;
}
ClusterInterface::~ClusterInterface()
{
}
string ClusterInterface::GenerateVersionString()
{
char str[200];
snprintf(str, 200, "Ascent r%u/%s-%s-%s", g_getRevision(), CONFIG, PLATFORM_TEXT, ARCH);
return string(str);
}
void ClusterInterface::ForwardWoWPacket(uint16 opcode, uint32 size, const void * data, uint32 sessionid)
{
Log.Debug("ForwardWoWPacket", "Forwarding %s to server", LookupName(opcode, g_worldOpcodeNames));
bool rv;
uint32 size2 = 10 + size;
uint16 opcode2 = ICMSG_WOW_PACKET;
if(!_clientSocket) return; // Shouldn't happen
_clientSocket->BurstBegin();
_clientSocket->BurstSend((const uint8*)&opcode2, 2);
_clientSocket->BurstSend((const uint8*)&size2, 4);
_clientSocket->BurstSend((const uint8*)&sessionid, 4);
_clientSocket->BurstSend((const uint8*)&opcode, 2);
rv=_clientSocket->BurstSend((const uint8*)&size, 4);
if(size&&rv)
rv=_clientSocket->BurstSend((const uint8*)data, size);
if(rv) _clientSocket->BurstPush();
_clientSocket->BurstEnd();
}
void ClusterInterface::ConnectToRealmServer()
{
_lastConnectTime = UNIXTIME;
string hostname;
int port;
string strkey;
if(!Config.MainConfig.GetString("Cluster", "RSHostName", &hostname) || !Config.MainConfig.GetInt("Cluster", "RSPort", &port) || !Config.MainConfig.GetString("Cluster", "Key", &strkey))
{
Log.Error("ClusterInterface", "Could not get necessary fields from config file. Please fix and rehash.");
return;
}
/* hash the key */
Sha1Hash k;
k.UpdateData(strkey);
k.Finalize();
memcpy(key, k.GetDigest(), 20);
Log.Notice("ClusterInterface", "Connecting to %s port %u", hostname.c_str(), port);
WSClient * s = ConnectTCPSocket(hostname.c_str(), port);
if(!s)
{
Log.Error("ClusterInterface", "Could not connect to %s:%u", hostname.c_str(), port);
return;
}
Log.Success("ClusterInterface", "Connected to %s:%u", hostname.c_str(), port);
_clientSocket = s;
m_latency = getMSTime();
m_connected = true;
}
void ClusterInterface::HandleAuthRequest(WorldPacket & pck)
{
uint32 x;
pck >> x;
Log.Debug("ClusterInterface", "Incoming auth request from %s (RS build %u)", _clientSocket->GetRemoteIP().c_str(), x);
WorldPacket data(ICMSG_AUTH_REPLY, 50);
data.append(key, 20);
data << uint32(g_getRevision());
data << GenerateVersionString();
SendPacket(&data);
m_latency = getMSTime() - m_latency;
Log.Notice("ClusterInterface", "Latency between realm server is %u ms", m_latency);
}
void ClusterInterface::HandleAuthResult(WorldPacket & pck)
{
uint32 res;
pck >> res;
Log.Debug("ClusterInterface", "Auth Result: %u", res);
if(!res)
{
Log.Error("ClusterInterface", "Authentication Failed");
_clientSocket->Disconnect();
_clientSocket = 0;
return;
}
/* hardcoded to perfer 0, 530 */
WorldPacket data(ICMSG_REGISTER_WORKER, 4 + 12);
data << uint32(69);
data << uint32(1);
data << uint32(0);
SendPacket(&data);
}
void ClusterInterface::HandleRegisterResult(WorldPacket & pck)
{
uint32 res;
pck >> res;
Log.Debug("ClusterInterface", "Register Result: %u", res);
}
void ClusterInterface::HandleCreateInstance(WorldPacket & pck)
{
uint32 mapid, instanceid;
pck >> mapid >> instanceid;
Log.Debug("ClusterInterface", "Creating Instance %u on Map %u", instanceid, mapid);
Map * pMap = sWorldCreator.GetMap(mapid);
pMap->CreateMapMgrInstance(instanceid);
}
void ClusterInterface::HandleDestroyInstance(WorldPacket & pck)
{
}
void ClusterInterface::HandlePlayerLogin(WorldPacket & pck)
{
/* player x logging into instance y */
uint32 guid, instance, mapid;
uint32 accountid, accountflags, sessionid;
string gmpermissions, accountname;
pck >> guid >> mapid >> instance >> accountid >> accountflags >> sessionid >> gmpermissions >> accountname;
/* find the instance */
Map * ma = sWorldCreator.GetMap(mapid);
ASSERT(ma);
MapMgr * mm = ma->GetInstance(instance);
ASSERT(mm);
/* create the session */
WorldSession * s = sWorld.FindSession(accountid);
ASSERT(!s);
/* create the socket */
WorldSocket * so = new WorldSocket(sessionid);
s = new WorldSession(accountid, accountname, so);
_sessions[sessionid] = s;
sWorld.AddSession(s);
bool login_result = s->PlayerLogin(guid, mapid, instance);
if(login_result)
{
/* login was ok. send a message to the realm server telling him to distribute our info to all other realm server */
WorldPacket data(ICMSG_PLAYER_LOGIN_RESULT, 5);
data << guid << sessionid << uint8(1);
SendPacket(&data);
}
else
{
/* for some reason the login failed */
WorldPacket data(ICMSG_PLAYER_LOGIN_RESULT, 5);
data << guid << sessionid << uint8(0);
SendPacket(&data);
/* tell the client his login failed before deleting the session */
data.Initialize(SMSG_CHARACTER_LOGIN_FAILED);
data << uint8(62);
so->SendPacket(&data);
/* destroy the session */
DestroySession(sessionid);
}
}
void ClusterInterface::HandlePackedPlayerInfo(WorldPacket & pck)
{
RPlayerInfo * pi;
// todo: uncompress
uint32 count;
pck >> count;
for(uint32 i = 0; i < count; ++i)
{
pi = new RPlayerInfo;
pi->Unpack(pck);
_onlinePlayers[pi->Guid] = pi;
}
}
void ClusterInterface::Update()
{
if(!m_connected && UNIXTIME >= (_lastConnectTime + 3))
ConnectToRealmServer();
WorldPacket * pck;
uint16 opcode;
while((pck = _pckQueue.Pop()))
{
opcode = pck->GetOpcode();
if(opcode < IMSG_NUM_TYPES && ClusterInterface::PHandlers[opcode] != 0)
(this->*ClusterInterface::PHandlers[opcode])(*pck);
else
Log.Error("ClusterInterface", "Unhandled packet %u\n", opcode);
}
}
void ClusterInterface::DestroySession(uint32 sid)
{
WorldSession * s = _sessions[sid];
_sessions[sid] = 0;
if(s)
{
/* todo: replace this with an event so we don't remove from the wrong thread */
if(s->GetPlayer())
s->LogoutPlayer(true);
delete s->GetSocket();
delete s;
}
}
void ClusterInterface::HandleWoWPacket(WorldPacket & pck)
{
uint32 size, sid;
uint16 opcode;
pck >> sid >> opcode >> size;
if(!_sessions[sid])
{
Log.Error("HandleWoWPacket", "Invalid session: %u", sid);
return;
}
Log.Debug("HandleWoWPacket", "Forwarding %s to client", LookupName(opcode, g_worldOpcodeNames));
WorldPacket * npck = new WorldPacket(opcode, size);
npck->resize(size);
memcpy((void*)npck->contents(), pck.contents()+10, size);
_sessions[sid]->QueuePacket(npck);
}
void ClusterInterface::HandlePlayerChangedServers(WorldPacket & pck)
{
uint32 sessionid, dsid;
pck >> sessionid >> dsid;
if(!_sessions[dsid])
{
Log.Error("HandlePlayerChangedServers", "Invalid session: %u", sessionid);
return;
}
WorldSession * s = _sessions[sessionid];
Player * plr = s->GetPlayer();
/* build the packet with the players information */
WorldPacket data(ICMSG_PLAYER_CHANGE_SERVER_INFO, 1000);
data << sessionid << plr->GetGUIDLow();
/* pack */
//data << plr->
/* remove the player from our world. */
sEventMgr.AddEvent(plr, &Player::EventRemoveAndDelete, EVENT_GAMEOBJECT_EXPIRE /* meh :P */, 1000, 1);
/* dereference the session */
}
#endif