1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
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/>.
18 #include "nel/net/module.h"
19 #include "nel/net/module_manager.h"
20 #include "nel/net/module_gateway.h"
22 #if !defined(RZ_CLIENT_DRONE)
23 # include "net_manager.h"
24 # include "network_connection.h"
26 # include "../client_drone/network_connection.h"
27 # include "../client_drone/simulated_client.h"
28 # include "../game_share/generic_xml_msg_mngr.h"
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
42 // the network connection to use to send data
43 CNetworkConnection
*NetConn
;
46 CFEClientRoute(IGatewayTransport
*transport
, CNetworkConnection
*netConn
)
47 : CGatewayRoute(transport
),
50 // warn the server that the transport is open
52 GenericMsgHeaderMngr
.pushNameToStream( "MODULE_GATEWAY:FEOPEN", bms
);
59 // warn the server that the transport is closed
61 GenericMsgHeaderMngr
.pushNameToStream( "MODULE_GATEWAY:FECLOSE", 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
;
96 class EInvalidCommand
: public NLMISC::Exception
100 /// The established route if any
101 CFEClientRoute
*_Route
;
103 /// Status of the transport.
105 /// A flag that is false after opening the transport until we receive one message
106 bool _FirstMessageReceived
;
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
;
137 CSimulatedClient
*SimClient
;
141 CGatewayFEClientTransport(const IGatewayTransport::TCtorParam
¶m
)
142 : IGatewayTransport(param
),
145 _FirstMessageReceived(false),
146 _NextAwaitedMessage(0)
148 #if defined(RZ_CLIENT_DRONE)
149 SimClient
= CSimulatedClient::currentContext();
153 ~CGatewayFEClientTransport()
155 #if !defined(RZ_CLIENT_DRONE)
156 if (OpenTransport() == this)
158 // the transport is still open, close it before destruction
167 const std::string
&getClassName() const
169 static string
className(FE_CLIENT_CLASS_NAME
);
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");
188 log
.displayNL(" The connection is currently closed.");
192 log
.displayNL(" The connection is open :");
196 log
.displayNL(" There is no route.");
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",
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")
235 nlwarning("The transport is already open");
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");
248 else if (commandName
== "close")
258 /// Open the connection by intercepting client gateway message
263 nlwarning("Transport already open");
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;
273 // associate this transport with the simulated client for later dispatching
274 SimClient
->setGatewayTransport(this);
278 _FirstMessageReceived
= false;
283 /// Close the server, this will close the listing socket and any active connection
288 nlwarning("Transport not open");
291 #if !defined(RZ_CLIENT_DRONE)
292 if (OpenTransport() != this)
293 throw ETransportError("close : The connection is not open");
296 // associate this transport with the simulated client for later dispatching
297 SimClient
->setGatewayTransport(NULL
);
304 _FirstMessageReceived
= false;
306 #if !defined(RZ_CLIENT_DRONE)
307 // this transport is no longer open
308 OpenTransport() = NULL
;
313 /***************************************************/
314 /** Event management **/
315 /***************************************************/
317 // handle the connection of the client
318 void onConnection ( )
322 nlwarning("onConnection : route already created !");
326 CNetworkConnection
*netConn
;
327 #if defined(RZ_CLIENT_DRONE)
328 netConn
= &SimClient
->getNetworkConnection();
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 ()
344 nlwarning("onDisconnection : route not created !");
348 // callback the gateway that this route is no more
349 _Gateway
->onRouteRemoved(_Route
);
356 // Called to dispatch an incoming message to the gateway
357 void onDispatchMessage(NLMISC::CBitMemStream
&bms
)
361 nlwarning("onDispatchMessage : no route created, message will be discarded");
365 // now, we have received a message
366 _FirstMessageReceived
= true;
368 // Build a CMessage from the bit mem stream
370 // bms.serial(msgName);
372 // create an input stream for dispatching
373 CMessage
msg("", true);
376 bms
.serialBuffer(msg
.bufferToFill(len
), len
);
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())
389 TWaitingMessages::iterator
it(_WaitingMessages
.find(_NextAwaitedMessage
));
390 while (it
!= _WaitingMessages
.end())
392 TWaitingMessage
&wm
= _WaitingMessages
.find(_NextAwaitedMessage
)->second
;
394 switch (wm
.MessageType
)
397 impulsionGatewayOpen(wm
.Message
, false);
400 impulsionGatewayClose(wm
.Message
, false);
403 impulsionGatewayMessage(wm
.Message
, false);
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
)
418 if (readSerialNumber
)
419 bms
.serial(serialNumber
);
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
;
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
445 // if there is no route, create one
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
)
457 if (readSerialNumber
)
458 bms
.serial(serialNumber
);
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
;
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
)
487 if (readSerialNumber
)
488 bms
.serial(serialNumber
);
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
;
503 // advance to next message for next reception
504 ++_NextAwaitedMessage
;
508 // there is no route open
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
)
527 if (CSimulatedClient::currentContext()->getGatewayTransport() == NULL
)
530 return static_cast<CGatewayFEClientTransport
*>(CSimulatedClient::currentContext()->getGatewayTransport());
532 // normal client mode, there is only one transport
533 return OpenTransport();
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
));