khanat-opennel-code/code/nelns/login_service/connection_ws.cpp
2010-11-02 13:42:25 +01:00

655 lines
18 KiB
C++

// NeLNS - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "nel/misc/types_nl.h"
#include <cstdio>
#include <ctype.h>
#include <cmath>
#include <vector>
#include <map>
#include "nel/misc/debug.h"
#include "nel/misc/config_file.h"
#include "nel/misc/displayer.h"
#include "nel/misc/log.h"
#include "nel/net/service.h"
#include "nel/net/login_cookie.h"
#include "login_service.h"
#include "mysql_helper.h"
//
// Namespaces
//
using namespace std;
using namespace NLMISC;
using namespace NLNET;
//
// Variables
//
static uint RecordNbPlayers = 0;
uint NbPlayers = 0;
//
// Functions
//
void refuseShard (TServiceId sid, const char *format, ...)
{
string reason;
NLMISC_CONVERT_VARGS (reason, format, NLMISC::MaxCStringSize);
nlwarning(reason.c_str ());
CMessage msgout("FAILED");
msgout.serial (reason);
CUnifiedNetwork::getInstance ()->send (sid, msgout);
}
static void cbWSConnection (const std::string &serviceName, TServiceId sid, void *arg)
{
TSockId from;
CCallbackNetBase *cnb = CUnifiedNetwork::getInstance ()->getNetBase (sid, from);
const CInetAddress &ia = cnb->hostAddress (from);
nldebug("new potential shard: %s", ia.asString ().c_str ());
// if we accept external shard, don't need to check if address is valid
if(IService::getInstance ()->ConfigFile.getVar("AcceptExternalShards").asInt () == 1)
return;
string reason;
CMysqlResult result;
MYSQL_ROW row;
sint32 nbrow;
string query = "select * from shard where WSAddr='"+ia.ipAddress()+"'";
reason = sqlQuery(query, nbrow, row, result);
if (!reason.empty())
{
refuseShard (sid, "mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
return;
}
if (nbrow == 0)
{
// if we are here, it s that the shard have not a valid wsaddr in the database
// we can't accept unknown shard
refuseShard (sid, "Bad shard identification, the shard (WSAddr %s) is not in the database and can't be added", ia.ipAddress ().c_str ());
return;
}
}
static void cbWSDisconnection (const std::string &serviceName, TServiceId sid, void *arg)
{
TSockId from;
CCallbackNetBase *cnb = CUnifiedNetwork::getInstance ()->getNetBase (sid, from);
const CInetAddress &ia = cnb->hostAddress (from);
nldebug("shard disconnection: %s", ia.asString ().c_str ());
for (uint32 i = 0; i < Shards.size (); i++)
{
if (Shards[i].SId == sid)
{
// shard disconnected
nlinfo("ShardId %d with IP '%s' is offline!", Shards[i].ShardId, ia.asString ().c_str());
nlinfo("*** ShardId %3d NbPlayers %3d -> %3d", Shards[i].ShardId, Shards[i].NbPlayers, 0);
string query = "update shard set Online=0, NbPlayers=NbPlayers-"+toString(Shards[i].NbPlayers)+" where ShardId="+toString(Shards[i].ShardId);
sint ret = mysql_query (DatabaseConnection, query.c_str ());
if (ret != 0)
{
nlwarning ("mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
}
NbPlayers -= Shards[i].NbPlayers;
Shards[i].NbPlayers = 0;
// put users connected on this shard offline
query = "update user set State='Offline', ShardId=-1 where ShardId="+toString(Shards[i].ShardId);
ret = mysql_query (DatabaseConnection, query.c_str ());
if (ret != 0)
{
nlwarning ("mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
}
Shards.erase (Shards.begin () + i);
return;
}
}
nlwarning("Shard %s goes offline but wasn't online!", ia.asString ().c_str ());
}
/** Shard accepted the new user, so warn the user that he could connect to the shard now */
/*void cbClientShardAcceptedTheUser (CMessage &msgin, TSockId from, CCallbackNetBase &netbase)
{
uint8 res;
string IP;
uint32 Key, Id;
msgin.serial (IP);
msgin.serial (Key);
msgin.serial (Id);
msgin.serial (res);
CMessage msgout (netbase.getSIDA (), "ACC");
msgout.serial (res);
if(res)
{
// the shard accept the user
msgout.serial (IP);
msgout.serial (Key);
}
else
{
// the shard don't want him!
string reason;
msgin.serial (reason);
msgout.serial (reason);
}
// find the user
for (vector<CUser>::iterator it = Users.begin (); it != Users.end (); it++)
{
if ((*it).Authorized && (*it).Key == Key)
{
// send the answer to the user
netbase.send (msgout, (*it).SockId);
(*it).Authorized = false;
return;
}
}
}
*/
/*
void cbShardComesIn (CMessage &msgin, TSockId from, CCallbackNetBase &netbase)
{
const CInetAddress &ia = netbase.hostAddress (from);
// at this time, it could be a new shard or a ne client.
nldebug("new potential shard: %s", ia.asString ().c_str ());
// first, check if it an authorized shard
for (sint32 i = 0; i < (sint32) Shards.size (); i++)
{
if (Shards[i].Address.ipAddress () == ia.ipAddress ())
{
if (Shards[i].Online)
{
nlwarning("Shard with ip '%s' is already online! Disconnect the new one", ia.asString().c_str ());
netbase.disconnect (from);
}
else
{
// new shard connected
Shards[i].Address = ia;
Shards[i].Online = true;
Shards[i].SockId = from;
nlinfo("Shard with ip '%s' is online!", Shards[i].Address.asString().c_str ());
}
return;
}
}
#if ACCEPT_EXTERNAL_SHARD
// New externam shard connected, add it in the file
Shards.push_back (CShard(ia));
sint32 pos = Shards.size()-1;
Shards[pos].Online = true;
Shards[pos].SockId = from;
nlinfo("External shard with ip '%s' is online!", Shards[pos].Address.asString().c_str ());
writeConfigFile ();
#else
nlwarning("It's not a authorized shard, disconnect it");
netbase.close (from);
#endif
}
*/
//
static void cbWSIdentification (CMessage &msgin, const std::string &serviceName, TServiceId sid)
{
TSockId from;
CCallbackNetBase *cnb = CUnifiedNetwork::getInstance ()->getNetBase (sid, from);
const CInetAddress &ia = cnb->hostAddress (from);
sint32 shardId;
msgin.serial(shardId);
string application;
try {
msgin.serial(application);
} catch (Exception &) { }
nldebug("shard identification, It says to be ShardId %d, let's check that!", shardId);
string reason;
CMysqlResult result;
MYSQL_ROW row;
sint32 nbrow;
string query = "select * from shard where ShardId="+toString(shardId);
reason = sqlQuery(query, nbrow, row, result);
if (!reason.empty())
{
refuseShard (sid, "mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
return;
}
if (nbrow == 0)
{
if(IService::getInstance ()->ConfigFile.getVar("AcceptExternalShards").asInt () == 1)
{
// we accept new shard, add it
query = "insert into shard (ShardId, WsAddr, Online, Name, ClientApplication) values ("+toString(shardId)+", '"+ia.ipAddress ()+"', 1, '"+ia.ipAddress ()+"', '"+application+"')";
reason = sqlQuery(query, nbrow, row, result);
if (!reason.empty())
{
refuseShard (sid, "mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
}
else
{
nlinfo("The ShardId %d with ip '%s' was inserted in the database and is online!", shardId, ia.ipAddress ().c_str ());
Shards.push_back (CShard (shardId, sid));
}
}
else
{
// can't accept new shard
refuseShard (sid, "Bad shard identification, The shard %d is not in the database and can't be added", shardId);
}
return;
}
else if (nbrow == 1)
{
// check that the ip is ok
CInetAddress iadb;
iadb.setNameAndPort (row[1]);
nlinfo ("check %s with %s (%s)", ia.ipAddress ().c_str(), iadb.ipAddress().c_str(), row[1]);
if (ia.ipAddress () != iadb.ipAddress())
{
// good shard id but from a bad computer address
refuseShard (sid, "Bad shard identification, ShardId %d should come from '%s' and come from '%s'", shardId, row[1], ia.ipAddress ().c_str ());
return;
}
sint32 s = findShard (shardId);
if (s != -1)
{
// the shard is already online, disconnect the old one and set the new one
refuseShard (Shards[s].SId, "A new shard connects with the same IP/ShardId, you was replaced");
Shards[s].SId = sid;
Shards[s].ShardId = shardId;
}
else
{
string query = "update shard set Online=1 where ShardId="+toString(shardId);
sint ret = mysql_query (DatabaseConnection, query.c_str ());
if (ret != 0)
{
refuseShard (sid, "mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
return;
}
Shards.push_back (CShard (shardId, sid));
}
// ok, the shard is identified correctly
nlinfo("ShardId %d with ip '%s' is online!", shardId, ia.ipAddress ().c_str ());
return;
}
else
{
refuseShard (sid, "mysql problem, There's more than 1 shard with the shardId %d in the database", shardId);
return;
}
nlstop;
}
static void cbWSClientConnected (CMessage &msgin, const std::string &serviceName, TServiceId sid)
{
//
// S16: Receive "CC" message from WS
//
// a WS tells me that a player is connected or disconnected
// find the user
uint32 Id;
uint8 con;
msgin.serial (Id);
msgin.serial (con); // con=1 means a client is connected on the shard, 0 means a client disconnected
if(con)
nlinfo ("Received a validation that a client is connected on the frontend");
else
nlinfo ("Received a validation that a client is disconnected on the frontend");
string reason;
CMysqlResult result;
MYSQL_ROW row;
sint32 nbrow;
string query = "select * from user where UId="+toString(Id);
reason = sqlQuery(query, nbrow, row, result);
if(!reason.empty()) return;
if(nbrow == 0)
{
nlwarning ("Id %d doesn't exist", Id);
Output->displayNL ("###: %3d UId doesn't exist", Id);
return;
}
else if (nbrow > 1)
{
nlerror ("Id %d have more than one entry!!!", Id);
return;
}
// row[4] = State
if (con == 1 && string(row[4]) != string("Waiting"))
{
nlwarning("Id %d is not waiting", Id);
Output->displayNL("###: %3d User isn't waiting, his state is '%s'", Id, row[4]);
return;
}
else if (con == 0 && string(row[4]) != string ("Online"))
{
nlwarning ("Id %d wasn't connected on a shard", Id);
Output->displayNL ("###: %3d User wasn't connected on a shard, his state is '%s'", Id, row[4]);
return;
}
sint ShardPos = findShardWithSId (sid);
if (con == 1)
{
// new client on the shard
string query = "update user set State='Online', ShardId="+toString(Shards[ShardPos].ShardId)+" where UId="+toString(Id);
string rea = sqlQuery(query);
if(!rea.empty()) return;
if (ShardPos != -1)
{
nlinfo("*** ShardId %3d NbPlayers %3d -> %3d", Shards[ShardPos].ShardId, Shards[ShardPos].NbPlayers, Shards[ShardPos].NbPlayers+1);
Shards[ShardPos].NbPlayers++;
string query = "update shard set NbPlayers=NbPlayers+1 where ShardId="+toString(Shards[ShardPos].ShardId);
string rea = sqlQuery(query);
if(!rea.empty()) return;
}
else
nlwarning ("user connected shard isn't in the shard list");
nldebug ("Id %d is connected on the shard", Id);
Output->displayNL ("###: %3d User connected to the shard (%d)", Id, Shards[ShardPos].ShardId);
NbPlayers++;
if (NbPlayers > RecordNbPlayers)
{
RecordNbPlayers = NbPlayers;
beep (2000, 1, 100, 0);
nlwarning("New player number record!!! %d players online on all shards", RecordNbPlayers);
}
}
else
{
// client removed from the shard (true is for potential other client with the same id that wait for a connection)
// disconnectClient (Users[pos], true, false);
string query = "update user set State='Offline', ShardId=-1 where UId="+toString(Id);
string rea = sqlQuery(query);
if(!rea.empty()) return;
if (ShardPos != -1)
{
nlinfo("*** ShardId %3d NbPlayers %3d -> %3d", Shards[ShardPos].ShardId, Shards[ShardPos].NbPlayers, Shards[ShardPos].NbPlayers-1);
Shards[ShardPos].NbPlayers--;
string query = "update shard set NbPlayers=NbPlayers-1 where ShardId="+toString(Shards[ShardPos].ShardId);
string rea = sqlQuery(query);
if(!rea.empty()) return;
}
else
nlwarning ("user disconnected shard isn't in the shard list");
nldebug ("Id %d is disconnected from the shard", Id);
Output->displayNL ("###: %3d User disconnected from the shard (%d)", Id, Shards[ShardPos].ShardId);
NbPlayers--;
}
}
static void cbWSReportFSState(CMessage &msgin, const std::string &serviceName, TServiceId sid)
{
sint shardPos = findShardWithSId (sid);
if (shardPos == -1)
{
nlwarning ("unknown WS %d reported state of a fs", sid.get());
return;
}
CShard& shard = Shards[shardPos];
TServiceId FSSId;
bool alive;
bool patching;
std::string patchURI;
msgin.serial(FSSId);
msgin.serial(alive);
msgin.serial(patching);
msgin.serial(patchURI);
if (!alive)
{
nlinfo("Shard %d frontend %d reported as offline", shard.ShardId, FSSId.get());
std::vector<CFrontEnd>::iterator itfs;
for (itfs=shard.FrontEnds.begin(); itfs!=shard.FrontEnds.end(); ++itfs)
{
if ((*itfs).SId == FSSId)
{
shard.FrontEnds.erase(itfs);
break;
}
}
}
else
{
CFrontEnd* updateFS = NULL;
std::vector<CFrontEnd>::iterator itfs;
for (itfs=shard.FrontEnds.begin(); itfs!=shard.FrontEnds.end(); ++itfs)
{
if ((*itfs).SId == FSSId)
{
// update FS state
CFrontEnd& fs = (*itfs);
fs.Patching = patching;
fs.PatchURI = patchURI;
updateFS = &fs;
break;
}
}
if (itfs == shard.FrontEnds.end())
{
nlinfo("Shard %d frontend %d reported as online", shard.ShardId, FSSId.get());
// unknown fs, create new entry
shard.FrontEnds.push_back(CFrontEnd(FSSId, patching, patchURI));
updateFS = &(shard.FrontEnds.back());
}
if (updateFS != NULL)
{
nlinfo("Shard %d frontend %d status updated: patching=%s patchURI=%s", shard.ShardId, FSSId.get(), (updateFS->Patching ? "yes" : "no"), updateFS->PatchURI.c_str());
}
}
// update DynPatchURLS in database
std::string dynPatchURL;
uint i;
for (i=0; i<shard.FrontEnds.size(); ++i)
{
if (shard.FrontEnds[i].Patching)
{
if (!dynPatchURL.empty())
dynPatchURL += ' ';
dynPatchURL += shard.FrontEnds[i].PatchURI;
}
}
string query = "UPDATE shard SET DynPatchURL='"+dynPatchURL+"' WHERE ShardId='"+toString(shard.ShardId)+"'";
sint ret = mysql_query (DatabaseConnection, query.c_str ());
if (ret != 0)
{
nlwarning ("mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
return;
}
}
static void cbWSReportNoPatch(CMessage &msgin, const std::string &serviceName, TServiceId sid)
{
sint shardPos = findShardWithSId (sid);
if (shardPos == -1)
{
nlwarning ("unknown WS %d reported state of a fs", sid.get());
return;
}
CShard& shard = Shards[shardPos];
uint i;
for (i=0; i<shard.FrontEnds.size(); ++i)
{
shard.FrontEnds[i].Patching = false;
}
string query = "UPDATE shard SET DynPatchURL='' WHERE ShardId='"+toString(shard.ShardId)+"'";
sint ret = mysql_query (DatabaseConnection, query.c_str ());
if (ret != 0)
{
nlwarning ("mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
return;
}
}
static void cbWSSetShardOpen(CMessage &msgin, const std::string &serviceName, TServiceId sid)
{
sint shardPos = findShardWithSId (sid);
if (shardPos == -1)
{
nlwarning ("unknown WS %d reported shard open state", sid.get());
return;
}
uint8 shardOpenState;
msgin.serial(shardOpenState);
CShard& shard = Shards[shardPos];
string query = toString("UPDATE shard SET Online='%d' WHERE ShardId='%d'", shardOpenState+1, shard.ShardId);
sint ret = mysql_query (DatabaseConnection, query.c_str ());
if (ret != 0)
{
nlwarning ("mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
return;
}
}
static const TUnifiedCallbackItem WSCallbackArray[] =
{
{ "CC", cbWSClientConnected },
{ "WS_IDENT", cbWSIdentification },
{ "REPORT_FS_STATE", cbWSReportFSState },
{ "REPORT_NO_PATCH", cbWSReportNoPatch },
{ "SET_SHARD_OPEN", cbWSSetShardOpen },
};
//
// Functions
//
void connectionWSInit ()
{
CUnifiedNetwork::getInstance ()->addCallbackArray (WSCallbackArray, sizeof(WSCallbackArray)/sizeof(WSCallbackArray[0]));
CUnifiedNetwork::getInstance ()->setServiceUpCallback ("WS", cbWSConnection);
CUnifiedNetwork::getInstance ()->setServiceDownCallback ("WS", cbWSDisconnection);
}
void connectionWSUpdate ()
{
}
void connectionWSRelease ()
{
nlinfo ("I'm going down, clean the database");
while (!Shards.empty())
{
cbWSDisconnection ("", Shards[0].SId, NULL);
}
/*
// we remove all shards online from my list
for (uint32 i = 0; i < Shards.size (); i++)
{
TSockId from;
CCallbackNetBase *cnb = CUnifiedNetwork::getInstance ()->getNetBase (Shards[i].SId, from);
const CInetAddress &ia = cnb->hostAddress (from);
// shard disconnected
nlinfo("Set ShardId %d with IP '%s' offline in the database and set %d players to offline", Shards[i].ShardId, ia.asString ().c_str());
string query = "update shard set Online=Online-1, NbPlayers=NbPlayers-"+toString(Shards[i].NbPlayers)+" where ShardId="+toString(Shards[i].ShardId);
sint ret = mysql_query (DatabaseConnection, query.c_str ());
if (ret != 0)
{
nlwarning ("mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
}
// put users connected on this shard offline
}
Shards.clear ();
*/
}