Merge branch '164-crash-on-patching-and-possibly-right-after-login' into main/gingo...
[ryzomcore.git] / ryzom / client / src / gateway_fec_transport.cpp
blobfa15c465b061a3baef52b7cf769c6adeb6b8942e
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
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 "stdpch.h"
18 #include "nel/net/module.h"
19 #include "nel/net/module_manager.h"
20 #include "nel/net/module_gateway.h"
21 // client include
22 #if !defined(RZ_CLIENT_DRONE)
23 # include "net_manager.h"
24 # include "network_connection.h"
25 #else
26 # include "../client_drone/network_connection.h"
27 # include "../client_drone/simulated_client.h"
28 # include "../game_share/generic_xml_msg_mngr.h"
29 #endif
31 using namespace std;
32 using namespace NLMISC;
33 using namespace NLNET;
35 extern CGenericXmlMsgHeaderManager GenericMsgHeaderMngr;
37 /** the specialized route for frontend client transport */
38 class CFEClientRoute : public CGatewayRoute
40 public:
42 // the network connection to use to send data
43 CNetworkConnection *NetConn;
46 CFEClientRoute(IGatewayTransport *transport, CNetworkConnection *netConn)
47 : CGatewayRoute(transport),
48 NetConn(netConn)
50 // warn the server that the transport is open
51 CBitMemStream bms;
52 GenericMsgHeaderMngr.pushNameToStream( "MODULE_GATEWAY:FEOPEN", bms );
54 sendRawMessage(bms);
57 ~CFEClientRoute()
59 // warn the server that the transport is closed
60 CBitMemStream bms;
61 GenericMsgHeaderMngr.pushNameToStream( "MODULE_GATEWAY:FECLOSE", bms );
63 sendRawMessage(bms);
67 void sendMessage(const CMessage &message) const
69 // wrap the message in a transport message
70 CBitMemStream wrapper;
71 GenericMsgHeaderMngr.pushNameToStream( "MODULE_GATEWAY:GATEWAY_MSG", wrapper );
72 // wrapper.serial(message.getName());
73 // wrapper.serialBufferWithSize(const_cast<uint8*>(message.buffer())+message.getHeaderSize(), message.length()-message.getHeaderSize());
74 wrapper.serialBufferWithSize(const_cast<uint8*>(message.buffer()), message.length());
76 sendRawMessage(wrapper);
79 // send a bit mem stream to the server
80 void sendRawMessage(CBitMemStream &message) const
82 NetConn->push(message);
89 #define FE_CLIENT_CLASS_NAME "FEClient"
90 /** Transport for module gateway through front end service, client part. */
91 class CGatewayFEClientTransport : public IGatewayTransport
93 friend class CFEClientRoute;
94 public:
95 /// Invalid command
96 class EInvalidCommand : public NLMISC::Exception
100 /// The established route if any
101 CFEClientRoute *_Route;
103 /// Status of the transport.
104 bool _Open;
105 /// A flag that is false after opening the transport until we receive one message
106 bool _FirstMessageReceived;
108 enum TMessageType
110 mt_route_open,
111 mt_route_close,
112 mt_gw_msg,
114 /// Storage for message pending because or bad ordering
115 struct TWaitingMessage
117 TMessageType MessageType;
118 CBitMemStream Message;
121 typedef map<uint8, TWaitingMessage> TWaitingMessages;
122 /// The list of message waiting because or bad ordering
123 TWaitingMessages _WaitingMessages;
125 /// The message serial number that we are waiting to dispatch
126 uint8 _NextAwaitedMessage;
128 #if !defined(RZ_CLIENT_DRONE)
129 // store the unique active transport (only one transport of this type can be activated at a time)
130 static CGatewayFEClientTransport *&OpenTransport()
132 static CGatewayFEClientTransport *openTransport = NULL;
134 return openTransport;
136 #else
137 CSimulatedClient *SimClient;
138 #endif
140 /// Constructor
141 CGatewayFEClientTransport(const IGatewayTransport::TCtorParam &param)
142 : IGatewayTransport(param),
143 _Route(NULL),
144 _Open(false),
145 _FirstMessageReceived(false),
146 _NextAwaitedMessage(0)
148 #if defined(RZ_CLIENT_DRONE)
149 SimClient = CSimulatedClient::currentContext();
150 #endif
153 ~CGatewayFEClientTransport()
155 #if !defined(RZ_CLIENT_DRONE)
156 if (OpenTransport() == this)
158 // the transport is still open, close it before destruction
159 close();
161 #else
162 // close anyway
163 close();
164 #endif
167 const std::string &getClassName() const
169 static string className(FE_CLIENT_CLASS_NAME);
170 return className;
173 virtual void update()
177 virtual uint32 getRouteCount() const
179 return _Route != NULL ? 1 : 0;
182 void dump(NLMISC::CLog &log) const
184 IModuleManager &mm = IModuleManager::getInstance();
185 log.displayNL(" Frontend service transport, client part");
186 if (!_Open)
188 log.displayNL(" The connection is currently closed.");
190 else
192 log.displayNL(" The connection is open :");
194 if (_Route == NULL)
196 log.displayNL(" There is no route.");
198 else
200 CFEClientRoute *route = _Route;
201 log.displayNL(" The route has %u entries in the proxy translation table :",
202 route->ForeignToLocalIdx.getAToBMap().size());
204 CGatewayRoute::TForeignToLocalIdx::TAToBMap::const_iterator first(route->ForeignToLocalIdx.getAToBMap().begin()), last(route->ForeignToLocalIdx.getAToBMap().end());
205 for (; first != last; ++first)
207 IModuleProxy *modProx = mm.getModuleProxy(first->second);
209 log.displayNL(" - Proxy '%s' : local proxy id %u => foreign module id %u",
210 modProx != NULL ? modProx->getModuleName().c_str() : "ERROR, invalid module",
211 first->second,
212 first->first);
219 void onCommand(const CMessage &/* command */)
221 // nothing done for now
222 throw EInvalidCommand();
224 /// The gateway send a textual command to the transport
225 bool onCommand(const TParsedCommandLine &command)
227 if (command.SubParams.size() < 1)
228 throw EInvalidCommand();
230 const std::string &commandName = command.SubParams[0]->ParamName;
231 if (commandName == "open")
233 if (_Open)
235 nlwarning("The transport is already open");
236 return false;
238 #if !defined(RZ_CLIENT_DRONE)
239 if (OpenTransport() != NULL)
241 nlwarning("A transport is already open, only one transport can be open at a time");
242 return false;
244 #endif
246 open();
248 else if (commandName == "close")
250 close();
252 else
253 return false;
255 return true;
258 /// Open the connection by intercepting client gateway message
259 void open()
261 if (_Open)
263 nlwarning("Transport already open");
264 return;
266 #if !defined(RZ_CLIENT_DRONE)
267 if (OpenTransport() != NULL)
268 throw ETransportError("connect : a transport is already connected !");
270 // set this transport as the open transport
271 OpenTransport() = this;
272 #else
273 // associate this transport with the simulated client for later dispatching
274 SimClient->setGatewayTransport(this);
275 #endif
277 _Open = true;
278 _FirstMessageReceived = false;
279 // create a route
280 onConnection();
283 /// Close the server, this will close the listing socket and any active connection
284 void close()
286 if (!_Open)
288 nlwarning("Transport not open");
289 return;
291 #if !defined(RZ_CLIENT_DRONE)
292 if (OpenTransport() != this)
293 throw ETransportError("close : The connection is not open");
295 #else
296 // associate this transport with the simulated client for later dispatching
297 SimClient->setGatewayTransport(NULL);
298 #endif
300 if( _Route != NULL)
301 onDisconnection();
303 _Open = false;
304 _FirstMessageReceived = false;
306 #if !defined(RZ_CLIENT_DRONE)
307 // this transport is no longer open
308 OpenTransport() = NULL;
309 #endif
313 /***************************************************/
314 /** Event management **/
315 /***************************************************/
317 // handle the connection of the client
318 void onConnection ( )
320 if (_Route != NULL)
322 nlwarning("onConnection : route already created !");
323 return;
326 CNetworkConnection *netConn;
327 #if defined(RZ_CLIENT_DRONE)
328 netConn = &SimClient->getNetworkConnection();
329 #else
330 netConn = &NetMngr;
331 #endif
332 // Create a new route for this connection
333 _Route = new CFEClientRoute(this, netConn);
335 // callback the gateway
336 _Gateway->onRouteAdded(_Route);
339 // handle the deconnection of a new client on the client
340 void onDisconnection ()
342 if (_Route == NULL)
344 nlwarning("onDisconnection : route not created !");
345 return;
348 // callback the gateway that this route is no more
349 _Gateway->onRouteRemoved(_Route);
351 // delete the route
352 delete _Route;
353 _Route = NULL;
356 // Called to dispatch an incoming message to the gateway
357 void onDispatchMessage(NLMISC::CBitMemStream &bms)
359 if (_Route == NULL)
361 nlwarning("onDispatchMessage : no route created, message will be discarded");
362 return;
365 // now, we have received a message
366 _FirstMessageReceived = true;
368 // Build a CMessage from the bit mem stream
369 // string msgName;
370 // bms.serial(msgName);
372 // create an input stream for dispatching
373 CMessage msg("", true);
374 uint32 len;
375 bms.serial(len);
376 bms.serialBuffer(msg.bufferToFill(len), len);
377 msg.readType();
379 // forward to gateway
380 _Gateway->onReceiveMessage(_Route, msg);
383 // a message has been processed, try to process waiting message
384 void processPendingMessage()
386 if (_WaitingMessages.empty())
387 return;
389 TWaitingMessages::iterator it(_WaitingMessages.find(_NextAwaitedMessage));
390 while (it != _WaitingMessages.end())
392 TWaitingMessage &wm = _WaitingMessages.find(_NextAwaitedMessage)->second;
394 switch (wm.MessageType)
396 case mt_route_open:
397 impulsionGatewayOpen(wm.Message, false);
398 break;
399 case mt_route_close:
400 impulsionGatewayClose(wm.Message, false);
401 break;
402 case mt_gw_msg:
403 impulsionGatewayMessage(wm.Message, false);
404 break;
407 // remove the processed message
408 _WaitingMessages.erase(it);
409 // advance to next message, _NextAwaitedMessage is incremented by impulsion handler
410 it = _WaitingMessages.find(_NextAwaitedMessage);
414 // impulsion gateway open callback handler
415 void impulsionGatewayOpen(NLMISC::CBitMemStream &bms, bool readSerialNumber)
417 uint8 serialNumber;
418 if (readSerialNumber)
419 bms.serial(serialNumber);
420 else
421 serialNumber = _NextAwaitedMessage;
423 if (serialNumber != _NextAwaitedMessage)
425 // store the message for later processing
426 nlassert(_WaitingMessages.find(serialNumber) == _WaitingMessages.end());
427 _WaitingMessages[serialNumber].Message.swap(bms);
428 // _WaitingMessages[serialNumber].Message = bms;
429 _WaitingMessages[serialNumber].MessageType = mt_route_open;
431 return;
434 // advance to next message for next reception
435 ++_NextAwaitedMessage;
437 if (_Route != NULL && _FirstMessageReceived)
439 // there is already a route here, and it have received some message,
440 // we need to delete it
441 onDisconnection();
444 if (_Route == NULL)
445 // if there is no route, create one
446 onConnection();
448 // try to process some messages
449 if (readSerialNumber)
450 processPendingMessage();
453 // impulsion gateway message callback handler
454 void impulsionGatewayMessage(NLMISC::CBitMemStream &bms, bool readSerialNumber)
456 uint8 serialNumber;
457 if (readSerialNumber)
458 bms.serial(serialNumber);
459 else
460 serialNumber = _NextAwaitedMessage;
462 if (serialNumber != _NextAwaitedMessage)
464 // store the message for later processing
465 nlassert(_WaitingMessages.find(serialNumber) == _WaitingMessages.end());
466 _WaitingMessages[serialNumber].Message.swap(bms);
467 // _WaitingMessages[serialNumber].Message = bms;
468 _WaitingMessages[serialNumber].MessageType = mt_gw_msg;
470 return;
473 // advance to next message for next reception
474 ++_NextAwaitedMessage;
476 onDispatchMessage(bms);
478 if (readSerialNumber)
479 processPendingMessage();
483 // impulsion gateway open callback handler
484 void impulsionGatewayClose(NLMISC::CBitMemStream &bms, bool readSerialNumber)
486 uint8 serialNumber;
487 if (readSerialNumber)
488 bms.serial(serialNumber);
489 else
490 serialNumber = _NextAwaitedMessage;
492 if (serialNumber != _NextAwaitedMessage)
494 // store the message for later processing
495 nlassert(_WaitingMessages.find(serialNumber) == _WaitingMessages.end());
496 _WaitingMessages[serialNumber].Message.swap(bms);
497 // _WaitingMessages[serialNumber].Message = bms;
498 _WaitingMessages[serialNumber].MessageType = mt_route_close;
500 return;
503 // advance to next message for next reception
504 ++_NextAwaitedMessage;
506 if (_Route == NULL)
508 // there is no route open
509 return;
512 onDisconnection();
514 if (readSerialNumber)
515 processPendingMessage();
519 /***************************************************/
521 static CGatewayFEClientTransport *getCurrentTransport()
523 #if defined(RZ_CLIENT_DRONE)
524 // the current transport is set to the current context of the client drone
525 if (CSimulatedClient::currentContext() == NULL)
526 return NULL;
527 if (CSimulatedClient::currentContext()->getGatewayTransport() == NULL)
528 return NULL;
530 return static_cast<CGatewayFEClientTransport*>(CSimulatedClient::currentContext()->getGatewayTransport());
531 #else
532 // normal client mode, there is only one transport
533 return OpenTransport();
534 #endif
541 void cbImpulsionGatewayOpen(NLMISC::CBitMemStream &bms)
543 if (CGatewayFEClientTransport::getCurrentTransport() != NULL)
544 CGatewayFEClientTransport::getCurrentTransport()->impulsionGatewayOpen(bms, true);
548 // impulsion gateway message callback handler
549 void cbImpulsionGatewayMessage(NLMISC::CBitMemStream &bms)
551 if (CGatewayFEClientTransport::getCurrentTransport() != NULL)
552 CGatewayFEClientTransport::getCurrentTransport()->impulsionGatewayMessage(bms, true);
555 // impulsion gateway close callback handler
556 void cbImpulsionGatewayClose(NLMISC::CBitMemStream &bms)
558 if (CGatewayFEClientTransport::getCurrentTransport() != NULL)
559 CGatewayFEClientTransport::getCurrentTransport()->impulsionGatewayClose(bms, true);
564 // register this class in the transport factory
565 NLMISC_REGISTER_OBJECT(IGatewayTransport, CGatewayFEClientTransport, std::string, string(FE_CLIENT_CLASS_NAME));