Merge branch 'ryzom/marauder-gameplay' into main/gingo-test
[ryzomcore.git] / nelns / login_service / connection_ws.cpp
blobc7ca9d3b7837c291bb41ba113ed7bae4c4511019
1 // NeLNS - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
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.
8 //
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"
19 #include <cstdio>
20 #include <ctype.h>
21 #include <cmath>
23 #include <vector>
24 #include <map>
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"
39 // Namespaces
42 using namespace std;
43 using namespace NLMISC;
44 using namespace NLNET;
48 // Variables
51 static uint RecordNbPlayers = 0;
52 uint NbPlayers = 0;
56 // Functions
59 void refuseShard (TServiceId sid, const char *format, ...)
61 string reason;
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)
71 TSockId from;
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)
79 return;
81 string reason;
82 CMysqlResult result;
83 MYSQL_ROW row;
84 sint32 nbrow;
86 string query = "select * from shard where WSAddr='"+ia.ipAddress()+"'";
87 reason = sqlQuery(query, nbrow, row, result);
88 if (!reason.empty())
90 refuseShard (sid, "mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
91 return;
94 if (nbrow == 0)
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 ());
99 return;
103 static void cbWSDisconnection (const std::string &serviceName, TServiceId sid, void *arg)
105 TSockId from;
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 ());
121 if (ret != 0)
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 ());
134 if (ret != 0)
136 nlwarning ("mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
139 Shards.erase (Shards.begin () + i);
141 return;
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)
150 uint8 res;
151 string IP;
152 uint32 Key, Id;
153 msgin.serial (IP);
154 msgin.serial (Key);
155 msgin.serial (Id);
156 msgin.serial (res);
158 CMessage msgout (netbase.getSIDA (), "ACC");
159 msgout.serial (res);
161 if(res)
163 // the shard accept the user
164 msgout.serial (IP);
165 msgout.serial (Key);
167 else
169 // the shard don't want him!
170 string reason;
171 msgin.serial (reason);
172 msgout.serial (reason);
175 // find the user
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;
184 return;
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);
210 else
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 ());
218 return;
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 ());
228 writeConfigFile ();
229 #else
230 nlwarning("It's not a authorized shard, disconnect it");
231 netbase.close (from);
232 #endif
237 static void cbWSIdentification (CMessage &msgin, const std::string &serviceName, TServiceId sid)
239 TSockId from;
240 CCallbackNetBase *cnb = CUnifiedNetwork::getInstance ()->getNetBase (sid, from);
241 const CInetAddress &ia = cnb->hostAddress (from);
243 sint32 shardId;
244 msgin.serial(shardId);
245 string application;
246 try {
247 msgin.serial(application);
248 } catch (Exception &) { }
249 nldebug("shard identification, It says to be ShardId %d, let's check that!", shardId);
251 string reason;
253 CMysqlResult result;
254 MYSQL_ROW row;
255 sint32 nbrow;
256 string query = "select * from shard where ShardId="+toString(shardId);
257 reason = sqlQuery(query, nbrow, row, result);
258 if (!reason.empty())
260 refuseShard (sid, "mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
261 return;
264 if (nbrow == 0)
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);
271 if (!reason.empty())
273 refuseShard (sid, "mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
275 else
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));
281 else
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);
286 return;
288 else if (nbrow == 1)
290 // check that the ip is ok
291 CInetAddress iadb;
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 ());
298 return;
301 sint32 s = findShard (shardId);
302 if (s != -1)
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");
307 Shards[s].SId = sid;
308 Shards[s].ShardId = shardId;
310 else
312 string query = "update shard set Online=1 where ShardId="+toString(shardId);
313 sint ret = mysql_query (DatabaseConnection, query.c_str ());
314 if (ret != 0)
316 refuseShard (sid, "mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
317 return;
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 ());
325 return;
327 else
329 refuseShard (sid, "mysql problem, There's more than 1 shard with the shardId %d in the database", shardId);
330 return;
333 nlstop;
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
343 // find the user
344 uint32 Id;
345 uint8 con;
346 msgin.serial (Id);
347 msgin.serial (con); // con=1 means a client is connected on the shard, 0 means a client disconnected
349 if(con)
350 nlinfo ("Received a validation that a client is connected on the frontend");
351 else
352 nlinfo ("Received a validation that a client is disconnected on the frontend");
354 string reason;
355 CMysqlResult result;
356 MYSQL_ROW row;
357 sint32 nbrow;
359 string query = "select * from user where UId="+toString(Id);
360 reason = sqlQuery(query, nbrow, row, result);
361 if(!reason.empty()) return;
363 if(nbrow == 0)
365 nlwarning ("Id %d doesn't exist", Id);
366 Output->displayNL ("###: %3d UId doesn't exist", Id);
367 return;
369 else if (nbrow > 1)
371 nlerror ("Id %d have more than one entry!!!", Id);
372 return;
375 // row[4] = State
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]);
380 return;
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]);
386 return;
389 sint ShardPos = findShardWithSId (sid);
391 if (con == 1)
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;
400 if (ShardPos != -1)
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;
409 else
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);
415 NbPlayers++;
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);
423 else
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;
432 if (ShardPos != -1)
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;
441 else
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);
447 NbPlayers--;
452 static void cbWSReportFSState(CMessage &msgin, const std::string &serviceName, TServiceId sid)
454 sint shardPos = findShardWithSId (sid);
456 if (shardPos == -1)
458 nlwarning ("unknown WS %d reported state of a fs", sid.get());
459 return;
462 CShard& shard = Shards[shardPos];
464 TServiceId FSSId;
465 bool alive;
466 bool patching;
467 std::string patchURI;
469 msgin.serial(FSSId);
470 msgin.serial(alive);
471 msgin.serial(patching);
472 msgin.serial(patchURI);
474 if (!alive)
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);
483 break;
487 else
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)
495 // update FS state
496 CFrontEnd& fs = (*itfs);
497 fs.Patching = patching;
498 fs.PatchURI = patchURI;
500 updateFS = &fs;
502 break;
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;
523 uint i;
524 for (i=0; i<shard.FrontEnds.size(); ++i)
526 if (shard.FrontEnds[i].Patching)
528 if (!dynPatchURL.empty())
529 dynPatchURL += ' ';
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 ());
536 if (ret != 0)
538 nlwarning ("mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
539 return;
543 static void cbWSReportNoPatch(CMessage &msgin, const std::string &serviceName, TServiceId sid)
545 sint shardPos = findShardWithSId (sid);
547 if (shardPos == -1)
549 nlwarning ("unknown WS %d reported state of a fs", sid.get());
550 return;
553 CShard& shard = Shards[shardPos];
555 uint i;
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 ());
563 if (ret != 0)
565 nlwarning ("mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
566 return;
570 static void cbWSSetShardOpen(CMessage &msgin, const std::string &serviceName, TServiceId sid)
572 sint shardPos = findShardWithSId (sid);
574 if (shardPos == -1)
576 nlwarning ("unknown WS %d reported shard open state", sid.get());
577 return;
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 ());
587 if (ret != 0)
589 nlwarning ("mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
590 return;
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 },
607 // Functions
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++)
636 TSockId from;
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 ());
645 if (ret != 0)
647 nlwarning ("mysql_query (%s) failed: %s", query.c_str (), mysql_error(DatabaseConnection));
650 // put users connected on this shard offline
652 Shards.clear ();