1 // NeLNS - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "nel/misc/types_nl.h"
26 #include "nel/misc/debug.h"
27 #include "nel/misc/config_file.h"
28 #include "nel/misc/displayer.h"
29 #include "nel/misc/log.h"
31 #include "nel/net/service.h"
32 #include "nel/net/login_cookie.h"
34 #include "login_service.h"
35 #include "mysql_helper.h"
43 using namespace NLMISC
;
44 using namespace NLNET
;
51 static uint RecordNbPlayers
= 0;
59 void refuseShard (TServiceId sid
, const char *format
, ...)
62 NLMISC_CONVERT_VARGS (reason
, format
, NLMISC::MaxCStringSize
);
63 nlwarning(reason
.c_str ());
64 CMessage
msgout("FAILED");
65 msgout
.serial (reason
);
66 CUnifiedNetwork::getInstance ()->send (sid
, msgout
);
69 static void cbWSConnection (const std::string
&serviceName
, TServiceId sid
, void *arg
)
72 CCallbackNetBase
*cnb
= CUnifiedNetwork::getInstance ()->getNetBase (sid
, from
);
73 const CInetAddress
&ia
= cnb
->hostAddress (from
);
75 nldebug("new potential shard: %s", ia
.asString ().c_str ());
77 // if we accept external shard, don't need to check if address is valid
78 if(IService::getInstance ()->ConfigFile
.getVar("AcceptExternalShards").asInt () == 1)
86 string query
= "select * from shard where WSAddr='"+ia
.ipAddress()+"'";
87 reason
= sqlQuery(query
, nbrow
, row
, result
);
90 refuseShard (sid
, "mysql_query (%s) failed: %s", query
.c_str (), mysql_error(DatabaseConnection
));
96 // if we are here, it s that the shard have not a valid wsaddr in the database
97 // we can't accept unknown shard
98 refuseShard (sid
, "Bad shard identification, the shard (WSAddr %s) is not in the database and can't be added", ia
.ipAddress ().c_str ());
103 static void cbWSDisconnection (const std::string
&serviceName
, TServiceId sid
, void *arg
)
106 CCallbackNetBase
*cnb
= CUnifiedNetwork::getInstance ()->getNetBase (sid
, from
);
107 const CInetAddress
&ia
= cnb
->hostAddress (from
);
109 nldebug("shard disconnection: %s", ia
.asString ().c_str ());
111 for (uint32 i
= 0; i
< Shards
.size (); i
++)
113 if (Shards
[i
].SId
== sid
)
115 // shard disconnected
116 nlinfo("ShardId %d with IP '%s' is offline!", Shards
[i
].ShardId
, ia
.asString ().c_str());
117 nlinfo("*** ShardId %3d NbPlayers %3d -> %3d", Shards
[i
].ShardId
, Shards
[i
].NbPlayers
, 0);
119 string query
= "update shard set Online=0, NbPlayers=NbPlayers-"+toString(Shards
[i
].NbPlayers
)+" where ShardId="+toString(Shards
[i
].ShardId
);
120 sint ret
= mysql_query (DatabaseConnection
, query
.c_str ());
123 nlwarning ("mysql_query (%s) failed: %s", query
.c_str (), mysql_error(DatabaseConnection
));
126 NbPlayers
-= Shards
[i
].NbPlayers
;
128 Shards
[i
].NbPlayers
= 0;
130 // put users connected on this shard offline
132 query
= "update user set State='Offline', ShardId=-1 where ShardId="+toString(Shards
[i
].ShardId
);
133 ret
= mysql_query (DatabaseConnection
, query
.c_str ());
136 nlwarning ("mysql_query (%s) failed: %s", query
.c_str (), mysql_error(DatabaseConnection
));
139 Shards
.erase (Shards
.begin () + i
);
144 nlwarning("Shard %s goes offline but wasn't online!", ia
.asString ().c_str ());
147 /** Shard accepted the new user, so warn the user that he could connect to the shard now */
148 /*void cbClientShardAcceptedTheUser (CMessage &msgin, TSockId from, CCallbackNetBase &netbase)
158 CMessage msgout (netbase.getSIDA (), "ACC");
163 // the shard accept the user
169 // the shard don't want him!
171 msgin.serial (reason);
172 msgout.serial (reason);
176 for (vector<CUser>::iterator it = Users.begin (); it != Users.end (); it++)
178 if ((*it).Authorized && (*it).Key == Key)
180 // send the answer to the user
181 netbase.send (msgout, (*it).SockId);
183 (*it).Authorized = false;
192 void cbShardComesIn (CMessage &msgin, TSockId from, CCallbackNetBase &netbase)
194 const CInetAddress &ia = netbase.hostAddress (from);
196 // at this time, it could be a new shard or a ne client.
198 nldebug("new potential shard: %s", ia.asString ().c_str ());
200 // first, check if it an authorized shard
201 for (sint32 i = 0; i < (sint32) Shards.size (); i++)
203 if (Shards[i].Address.ipAddress () == ia.ipAddress ())
205 if (Shards[i].Online)
207 nlwarning("Shard with ip '%s' is already online! Disconnect the new one", ia.asString().c_str ());
208 netbase.disconnect (from);
212 // new shard connected
213 Shards[i].Address = ia;
214 Shards[i].Online = true;
215 Shards[i].SockId = from;
216 nlinfo("Shard with ip '%s' is online!", Shards[i].Address.asString().c_str ());
221 #if ACCEPT_EXTERNAL_SHARD
222 // New externam shard connected, add it in the file
223 Shards.push_back (CShard(ia));
224 sint32 pos = Shards.size()-1;
225 Shards[pos].Online = true;
226 Shards[pos].SockId = from;
227 nlinfo("External shard with ip '%s' is online!", Shards[pos].Address.asString().c_str ());
230 nlwarning("It's not a authorized shard, disconnect it");
231 netbase.close (from);
237 static void cbWSIdentification (CMessage
&msgin
, const std::string
&serviceName
, TServiceId sid
)
240 CCallbackNetBase
*cnb
= CUnifiedNetwork::getInstance ()->getNetBase (sid
, from
);
241 const CInetAddress
&ia
= cnb
->hostAddress (from
);
244 msgin
.serial(shardId
);
247 msgin
.serial(application
);
248 } catch (Exception
&) { }
249 nldebug("shard identification, It says to be ShardId %d, let's check that!", shardId
);
256 string query
= "select * from shard where ShardId="+toString(shardId
);
257 reason
= sqlQuery(query
, nbrow
, row
, result
);
260 refuseShard (sid
, "mysql_query (%s) failed: %s", query
.c_str (), mysql_error(DatabaseConnection
));
266 if(IService::getInstance ()->ConfigFile
.getVar("AcceptExternalShards").asInt () == 1)
268 // we accept new shard, add it
269 query
= "insert into shard (ShardId, WsAddr, Online, Name, ClientApplication) values ("+toString(shardId
)+", '"+ia
.ipAddress ()+"', 1, '"+ia
.ipAddress ()+"', '"+application
+"')";
270 reason
= sqlQuery(query
, nbrow
, row
, result
);
273 refuseShard (sid
, "mysql_query (%s) failed: %s", query
.c_str (), mysql_error(DatabaseConnection
));
277 nlinfo("The ShardId %d with ip '%s' was inserted in the database and is online!", shardId
, ia
.ipAddress ().c_str ());
278 Shards
.push_back (CShard (shardId
, sid
));
283 // can't accept new shard
284 refuseShard (sid
, "Bad shard identification, The shard %d is not in the database and can't be added", shardId
);
290 // check that the ip is ok
292 iadb
.setNameAndPort (row
[1]);
293 nlinfo ("check %s with %s (%s)", ia
.ipAddress ().c_str(), iadb
.ipAddress().c_str(), row
[1]);
294 if (ia
.ipAddress () != iadb
.ipAddress())
296 // good shard id but from a bad computer address
297 refuseShard (sid
, "Bad shard identification, ShardId %d should come from '%s' and come from '%s'", shardId
, row
[1], ia
.ipAddress ().c_str ());
301 sint32 s
= findShard (shardId
);
304 // the shard is already online, disconnect the old one and set the new one
305 refuseShard (Shards
[s
].SId
, "A new shard connects with the same IP/ShardId, you was replaced");
308 Shards
[s
].ShardId
= shardId
;
312 string query
= "update shard set Online=1 where ShardId="+toString(shardId
);
313 sint ret
= mysql_query (DatabaseConnection
, query
.c_str ());
316 refuseShard (sid
, "mysql_query (%s) failed: %s", query
.c_str (), mysql_error(DatabaseConnection
));
320 Shards
.push_back (CShard (shardId
, sid
));
323 // ok, the shard is identified correctly
324 nlinfo("ShardId %d with ip '%s' is online!", shardId
, ia
.ipAddress ().c_str ());
329 refuseShard (sid
, "mysql problem, There's more than 1 shard with the shardId %d in the database", shardId
);
336 static void cbWSClientConnected (CMessage
&msgin
, const std::string
&serviceName
, TServiceId sid
)
339 // S16: Receive "CC" message from WS
342 // a WS tells me that a player is connected or disconnected
347 msgin
.serial (con
); // con=1 means a client is connected on the shard, 0 means a client disconnected
350 nlinfo ("Received a validation that a client is connected on the frontend");
352 nlinfo ("Received a validation that a client is disconnected on the frontend");
359 string query
= "select * from user where UId="+toString(Id
);
360 reason
= sqlQuery(query
, nbrow
, row
, result
);
361 if(!reason
.empty()) return;
365 nlwarning ("Id %d doesn't exist", Id
);
366 Output
->displayNL ("###: %3d UId doesn't exist", Id
);
371 nlerror ("Id %d have more than one entry!!!", Id
);
376 if (con
== 1 && string(row
[4]) != string("Waiting"))
378 nlwarning("Id %d is not waiting", Id
);
379 Output
->displayNL("###: %3d User isn't waiting, his state is '%s'", Id
, row
[4]);
382 else if (con
== 0 && string(row
[4]) != string ("Online"))
384 nlwarning ("Id %d wasn't connected on a shard", Id
);
385 Output
->displayNL ("###: %3d User wasn't connected on a shard, his state is '%s'", Id
, row
[4]);
389 sint ShardPos
= findShardWithSId (sid
);
393 // new client on the shard
396 string query
= "update user set State='Online', ShardId="+toString(Shards
[ShardPos
].ShardId
)+" where UId="+toString(Id
);
397 string rea
= sqlQuery(query
);
398 if(!rea
.empty()) return;
402 nlinfo("*** ShardId %3d NbPlayers %3d -> %3d", Shards
[ShardPos
].ShardId
, Shards
[ShardPos
].NbPlayers
, Shards
[ShardPos
].NbPlayers
+1);
403 Shards
[ShardPos
].NbPlayers
++;
405 string query
= "update shard set NbPlayers=NbPlayers+1 where ShardId="+toString(Shards
[ShardPos
].ShardId
);
406 string rea
= sqlQuery(query
);
407 if(!rea
.empty()) return;
410 nlwarning ("user connected shard isn't in the shard list");
412 nldebug ("Id %d is connected on the shard", Id
);
413 Output
->displayNL ("###: %3d User connected to the shard (%d)", Id
, Shards
[ShardPos
].ShardId
);
416 if (NbPlayers
> RecordNbPlayers
)
418 RecordNbPlayers
= NbPlayers
;
419 beep (2000, 1, 100, 0);
420 nlwarning("New player number record!!! %d players online on all shards", RecordNbPlayers
);
425 // client removed from the shard (true is for potential other client with the same id that wait for a connection)
426 // disconnectClient (Users[pos], true, false);
428 string query
= "update user set State='Offline', ShardId=-1 where UId="+toString(Id
);
429 string rea
= sqlQuery(query
);
430 if(!rea
.empty()) return;
434 nlinfo("*** ShardId %3d NbPlayers %3d -> %3d", Shards
[ShardPos
].ShardId
, Shards
[ShardPos
].NbPlayers
, Shards
[ShardPos
].NbPlayers
-1);
435 Shards
[ShardPos
].NbPlayers
--;
437 string query
= "update shard set NbPlayers=NbPlayers-1 where ShardId="+toString(Shards
[ShardPos
].ShardId
);
438 string rea
= sqlQuery(query
);
439 if(!rea
.empty()) return;
442 nlwarning ("user disconnected shard isn't in the shard list");
444 nldebug ("Id %d is disconnected from the shard", Id
);
445 Output
->displayNL ("###: %3d User disconnected from the shard (%d)", Id
, Shards
[ShardPos
].ShardId
);
452 static void cbWSReportFSState(CMessage
&msgin
, const std::string
&serviceName
, TServiceId sid
)
454 sint shardPos
= findShardWithSId (sid
);
458 nlwarning ("unknown WS %d reported state of a fs", sid
.get());
462 CShard
& shard
= Shards
[shardPos
];
467 std::string patchURI
;
471 msgin
.serial(patching
);
472 msgin
.serial(patchURI
);
476 nlinfo("Shard %d frontend %d reported as offline", shard
.ShardId
, FSSId
.get());
477 std::vector
<CFrontEnd
>::iterator itfs
;
478 for (itfs
=shard
.FrontEnds
.begin(); itfs
!=shard
.FrontEnds
.end(); ++itfs
)
480 if ((*itfs
).SId
== FSSId
)
482 shard
.FrontEnds
.erase(itfs
);
489 CFrontEnd
* updateFS
= NULL
;
490 std::vector
<CFrontEnd
>::iterator itfs
;
491 for (itfs
=shard
.FrontEnds
.begin(); itfs
!=shard
.FrontEnds
.end(); ++itfs
)
493 if ((*itfs
).SId
== FSSId
)
496 CFrontEnd
& fs
= (*itfs
);
497 fs
.Patching
= patching
;
498 fs
.PatchURI
= patchURI
;
506 if (itfs
== shard
.FrontEnds
.end())
508 nlinfo("Shard %d frontend %d reported as online", shard
.ShardId
, FSSId
.get());
509 // unknown fs, create new entry
510 shard
.FrontEnds
.push_back(CFrontEnd(FSSId
, patching
, patchURI
));
512 updateFS
= &(shard
.FrontEnds
.back());
515 if (updateFS
!= NULL
)
517 nlinfo("Shard %d frontend %d status updated: patching=%s patchURI=%s", shard
.ShardId
, FSSId
.get(), (updateFS
->Patching
? "yes" : "no"), updateFS
->PatchURI
.c_str());
521 // update DynPatchURLS in database
522 std::string dynPatchURL
;
524 for (i
=0; i
<shard
.FrontEnds
.size(); ++i
)
526 if (shard
.FrontEnds
[i
].Patching
)
528 if (!dynPatchURL
.empty())
530 dynPatchURL
+= shard
.FrontEnds
[i
].PatchURI
;
534 string query
= "UPDATE shard SET DynPatchURL='"+dynPatchURL
+"' WHERE ShardId='"+toString(shard
.ShardId
)+"'";
535 sint ret
= mysql_query (DatabaseConnection
, query
.c_str ());
538 nlwarning ("mysql_query (%s) failed: %s", query
.c_str (), mysql_error(DatabaseConnection
));
543 static void cbWSReportNoPatch(CMessage
&msgin
, const std::string
&serviceName
, TServiceId sid
)
545 sint shardPos
= findShardWithSId (sid
);
549 nlwarning ("unknown WS %d reported state of a fs", sid
.get());
553 CShard
& shard
= Shards
[shardPos
];
556 for (i
=0; i
<shard
.FrontEnds
.size(); ++i
)
558 shard
.FrontEnds
[i
].Patching
= false;
561 string query
= "UPDATE shard SET DynPatchURL='' WHERE ShardId='"+toString(shard
.ShardId
)+"'";
562 sint ret
= mysql_query (DatabaseConnection
, query
.c_str ());
565 nlwarning ("mysql_query (%s) failed: %s", query
.c_str (), mysql_error(DatabaseConnection
));
570 static void cbWSSetShardOpen(CMessage
&msgin
, const std::string
&serviceName
, TServiceId sid
)
572 sint shardPos
= findShardWithSId (sid
);
576 nlwarning ("unknown WS %d reported shard open state", sid
.get());
580 uint8 shardOpenState
;
581 msgin
.serial(shardOpenState
);
583 CShard
& shard
= Shards
[shardPos
];
585 string query
= toString("UPDATE shard SET Online='%d' WHERE ShardId='%d'", shardOpenState
+1, shard
.ShardId
);
586 sint ret
= mysql_query (DatabaseConnection
, query
.c_str ());
589 nlwarning ("mysql_query (%s) failed: %s", query
.c_str (), mysql_error(DatabaseConnection
));
595 static const TUnifiedCallbackItem WSCallbackArray
[] =
597 { "CC", cbWSClientConnected
},
598 { "WS_IDENT", cbWSIdentification
},
600 { "REPORT_FS_STATE", cbWSReportFSState
},
601 { "REPORT_NO_PATCH", cbWSReportNoPatch
},
602 { "SET_SHARD_OPEN", cbWSSetShardOpen
},
610 void connectionWSInit ()
612 CUnifiedNetwork::getInstance ()->addCallbackArray (WSCallbackArray
, sizeof(WSCallbackArray
)/sizeof(WSCallbackArray
[0]));
614 CUnifiedNetwork::getInstance ()->setServiceUpCallback ("WS", cbWSConnection
);
615 CUnifiedNetwork::getInstance ()->setServiceDownCallback ("WS", cbWSDisconnection
);
618 void connectionWSUpdate ()
622 void connectionWSRelease ()
624 nlinfo ("I'm going down, clean the database");
627 while (!Shards
.empty())
629 cbWSDisconnection ("", Shards
[0].SId
, NULL
);
633 // we remove all shards online from my list
634 for (uint32 i = 0; i < Shards.size (); i++)
637 CCallbackNetBase *cnb = CUnifiedNetwork::getInstance ()->getNetBase (Shards[i].SId, from);
638 const CInetAddress &ia = cnb->hostAddress (from);
640 // shard disconnected
641 nlinfo("Set ShardId %d with IP '%s' offline in the database and set %d players to offline", Shards[i].ShardId, ia.asString ().c_str());
643 string query = "update shard set Online=Online-1, NbPlayers=NbPlayers-"+toString(Shards[i].NbPlayers)+" where ShardId="+toString(Shards[i].ShardId);
644 sint ret = mysql_query (DatabaseConnection, query.c_str ());
647 nlwarning ("mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
650 // put users connected on this shard offline