1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #endif // HAVE_CONFIG_H
24 #ifndef SNOWBALLS_CONFIG
25 #define SNOWBALLS_CONFIG ""
26 #endif // SNOWBALLS_CONFIG
28 #ifndef SNOWBALLS_LOGS
29 #define SNOWBALLS_LOGS ""
30 #endif // SNOWBALLS_LOGS
32 // This include is mandatory to use NeL. It include NeL types.
33 #include <nel/misc/types_nl.h>
35 #include <nel/misc/vector.h>
37 // We're using the NeL Service framework and layer 5
38 #include <nel/net/service.h>
39 #include <nel/net/login_server.h>
48 using namespace NLMISC
;
49 using namespace NLNET
;
52 CCallbackServer
*Clients
= 0;
57 * Keep a list of the players connected to that Frontend. Only map Id
68 CPlayer(uint32 Id
, TSockId Con
) : id(Id
), con(Con
), State(IDENTIFYING
) { }
74 typedef map
<uint32
, CPlayer
> _pmap
;
79 /****************************************************************************
82 * Receive chat messages from a client and send it to the Chat Service.
83 ****************************************************************************/
84 void cbChatClient ( CMessage
& msgin
, TSockId from
, CCallbackNetBase
& clientcb
)
88 // Input from the client is stored.
89 msgin
.serial (message
);
91 // Prepare the message to send to the CHAT service
92 CMessage
msgout ("CHAT");
93 msgout
.serial (message
);
96 * The incomming message from the client is sent to the CHAT service
97 * under the "CHAT" identification.
99 CUnifiedNetwork::getInstance ()->send( "CHAT", msgout
);
101 nldebug("SB: Received CHAT message \"%s\" from client \"%s\"", message
.c_str(), clientcb
.hostAddress(from
).asString().c_str());
105 /****************************************************************************
108 * Receive chat messages from the Chat Service to send it to all the clients.
109 ****************************************************************************/
110 void cbChatService (CMessage
&msgin
, const std::string
&serviceName
, TServiceId sid
)
114 // Input: process the reply of the chat service
115 msgin
.serial (message
);
117 // Output: send the reply to the client
118 CMessage
msgout ("CHAT");
119 msgout
.serial (message
);
121 // Send the message to all connected clients
122 Clients
->send (msgout
, InvalidSockId
);
124 nldebug( "SB: Sent chat message \"%s\" to all clients", message
.c_str());
128 /****************************************************************************
131 * Receive position messages from a client and send it to the Position Service.
132 ****************************************************************************/
133 void cbPosClient ( CMessage
& msgin
, TSockId from
, CCallbackNetBase
& clientcb
)
140 // Input from the client is stored.
143 msgin
.serial( angle
);
144 msgin
.serial( state
);
146 // Prepare the message to send to the Position service
147 CMessage
msgout ("ENTITY_POS");
149 msgout
.serial( pos
);
150 msgout
.serial( angle
);
151 msgout
.serial( state
);
154 * The incomming message from the client is sent to the Position service
155 * under the "POS" identification.
157 CUnifiedNetwork::getInstance ()->send( "POS", msgout
);
159 //nldebug( "SB: Received ENTITY_POS from the client");
163 /****************************************************************************
166 * Receive position messages from the Position Service to send it to all the
168 ****************************************************************************/
169 void cbPosService (CMessage
&msgin
, const std::string
&serviceName
, TServiceId sid
)
176 // Input: process the reply of the position service
179 msgin
.serial( angle
);
180 msgin
.serial( state
);
182 // Output: send the reply to the client
183 CMessage
msgout( "ENTITY_POS" );
185 msgout
.serial( pos
);
186 msgout
.serial( angle
);
187 msgout
.serial( state
);
189 // Send the message to all connected clients
190 Clients
->send(msgout
, InvalidSockId
);
192 //nldebug( "SB: Sent ENTITY_POS message to all the connected clients");
195 /****************************************************************************
199 ****************************************************************************/
200 void cbTeleportService (CMessage
&msgin
, const std::string
&serviceName
, TServiceId sid
)
205 msgin
.serial(position
);
206 CMessage
msgout("ENTITY_TP");
208 msgout
.serial(position
);
209 Clients
->send(msgout
, InvalidSockId
);
210 nldebug("SB: Sent ENTITY_TP message to all the connected clients");
214 /****************************************************************************
217 * Receive an ADD_ENTITY message from a client and send it to the Position
219 ****************************************************************************/
220 void cbAddClient ( CMessage
& msgin
, TSockId from
, CCallbackNetBase
& clientcb
)
225 CVector
start(1840.0f
+ ((float)(rand() % 100) / 10.0f
), -970.0f
+ ((float)(rand() % 100) / 10.0f
), -23.0f
); // kaetemi_todo: from config
227 // Input from the client is stored.
233 if(from
->appId() != 0)
235 CPlayer
*p
= (CPlayer
*)(void *)from
->appId();
237 p
->State
= CPlayer::ONLINE
;
240 // Prepare the message to send to the Position service
241 CMessage
msgout("ADD_ENTITY");
245 msgout
.serial(start
);
248 * The incoming message from the client is sent to the Position service
249 * under the "POS" identification.
251 CUnifiedNetwork::getInstance()->send("POS", msgout
);
253 nldebug("SB: Received ADD_ENTITY from the client");
255 // kaetemi_todo: from config
256 msgout
= CMessage("CHAT");
257 std::string
chat_msg(std::string(">>>> Welcome to Snowballs, ") + name
+ std::string("!"));
258 msgout
.serial(chat_msg
);
259 Clients
->send(msgout
, from
);
263 /****************************************************************************
266 * Receive an ADD_ENTITY messages from the Position Service to send it to all
268 ****************************************************************************/
269 void cbAddService (CMessage
&msgin
, const std::string
&serviceName
, TServiceId sid
)
278 // Input: process the reply of the position service
282 msgin
.serial( name
);
283 msgin
.serial( race
);
284 msgin
.serial( start
);
286 // Output: prepare the reply to the clients
287 CMessage
msgout( "ADD_ENTITY" );
289 msgout
.serial( name
);
290 msgout
.serial( race
);
291 msgout
.serial( start
);
295 // Send the message to all connected clients
296 Clients
->send(msgout
, InvalidSockId
);
298 nldebug( "SB: Sent ADD_ENTITY message to all the connected clients");
302 // Send the message about a former connected client to the new client
303 _pmap::iterator ItPlayer
;
304 ItPlayer
= localPlayers
.find(to
);
305 if ( ItPlayer
== localPlayers
.end() )
307 nlwarning( "New player id %u not found !", to
);
311 TSockId conToClient
= ((*ItPlayer
).second
).con
;
312 Clients
->send( msgout
, conToClient
);
314 nldebug( "SB: Sent ADD_ENTITY about all the connected clients to the new client.");
321 /****************************************************************************
324 * Receive an REMOVE_ENTITY message from a client and send it to the Position
326 ****************************************************************************/
327 void cbRemoveClient ( CMessage
& msgin
, TSockId from
, CCallbackNetBase
& clientcb
)
331 // Input from the client is stored.
334 // Prepare the message to send to the Position service
335 CMessage
msgout( "REMOVE_ENTITY" );
339 * The incomming message from the client is sent to the Position service
340 * under the "POS" identification.
342 CUnifiedNetwork::getInstance ()->send( "POS", msgout
);
344 nldebug( "SB: Received REMOVE_ENTITY from the client");
348 /****************************************************************************
351 * Receive an REMOVE_ENTITY messages from the Position Service to send it to all
353 ****************************************************************************/
354 void cbRemoveService (CMessage
&msgin
, const std::string
&serviceName
, TServiceId sid
)
358 // Input: process the reply of the position service
361 // Output: send the reply to the client
362 CMessage
msgout( "REMOVE_ENTITY" );
365 // Send the message to all connected clients
366 Clients
->send( msgout
, InvalidSockId
);
368 nldebug( "SB: Sent REMOVE_ENTITY message to all the connected clients");
372 /****************************************************************************
375 * Receive an SNOWBALL messages from the Position Service to send it to all
377 ****************************************************************************/
378 void cbSnowballService (CMessage
&msgin
, const std::string
&serviceName
, TServiceId sid
)
387 // Input: process the reply of the position service
389 msgin
.serial( playerId
);
390 msgin
.serial( start
);
391 msgin
.serial( target
);
392 msgin
.serial( speed
);
393 msgin
.serial( explosionRadius
);
395 // Output: send the reply to the client
396 CMessage
msgout( "SNOWBALL" );
398 msgout
.serial( playerId
);
399 msgout
.serial( start
);
400 msgout
.serial( target
);
401 msgout
.serial( speed
);
402 msgout
.serial( explosionRadius
);
404 // Send the message to all connected clients
405 Clients
->send( msgout
, InvalidSockId
);
407 nldebug( "SB: Sent SNOWBALL message to all the connected clients");
411 /****************************************************************************
414 * Receive an SNOWBALL message from a client and send it to the Position
416 ****************************************************************************/
417 void cbSnowballClient ( CMessage
& msgin
, TSockId from
, CCallbackNetBase
& clientcb
)
425 // Input from the client is stored.
426 msgin
.serial( playerId
);
427 msgin
.serial( start
);
428 msgin
.serial( target
);
429 msgin
.serial( speed
);
430 msgin
.serial( explosionRadius
);
432 // Prepare the message to send to the Position service
433 CMessage
msgout( "SNOWBALL" );
434 msgout
.serial( playerId
);
435 msgout
.serial( start
);
436 msgout
.serial( target
);
437 msgout
.serial( speed
);
438 msgout
.serial( explosionRadius
);
441 * The incomming message from the client is sent to the Position service
442 * under the "POS" identification.
444 CUnifiedNetwork::getInstance ()->send( "POS", msgout
);
446 nldebug( "SB: Received SNOWBALL from the client");
450 /****************************************************************************
453 * Receive an HIT messages from the Position Service to send it to all
455 ****************************************************************************/
456 void cbHitService (CMessage
&msgin
, const std::string
&serviceName
, TServiceId sid
)
462 // Input: process the reply of the position service
463 msgin
.serial( snowballId
);
464 msgin
.serial( victimId
);
465 msgin
.serial( direct
);
467 // Output: send the reply to the client
468 CMessage
msgout( "HIT" );
469 msgout
.serial( snowballId
);
470 msgout
.serial( victimId
);
471 msgout
.serial( direct
);
473 // Send the message to all connected clients
474 Clients
->send( msgout
, InvalidSockId
);
476 nldebug( "SB: Sent HIT message to all the connected clients");
481 * Contains all callbacks from client
483 TCallbackItem ClientCallbackArray
[] =
485 { "ADD_ENTITY", cbAddClient
},
486 { "ENTITY_POS", cbPosClient
},
487 { "CHAT", cbChatClient
},
488 { "REMOVE_ENTITY", cbRemoveClient
},
489 { "SNOWBALL", cbSnowballClient
},
494 * Contains *all* callbacks from the shard
496 TUnifiedCallbackItem CallbackArray
[] =
498 { "CHAT", cbChatService
},
499 { "ADD_ENTITY", cbAddService
},
500 { "ENTITY_POS", cbPosService
},
501 { "ENTITY_TP", cbTeleportService
},
502 { "REMOVE_ENTITY", cbRemoveService
},
503 { "SNOWBALL", cbSnowballService
},
504 { "HIT", cbHitService
},
508 /****************************************************************************
509 * Connection callback for the Chat service
510 ****************************************************************************/
511 void onReconnectChat (const std::string
&serviceName
, TServiceId sid
, void *arg
)
513 nldebug( "SB: Chat Service reconnected" );
517 /****************************************************************************
518 * Disconnection callback for the Chat service
519 ****************************************************************************/
520 void onDisconnectChat (const std::string
&serviceName
, TServiceId sid
, void *arg
)
522 /* Note: messages already forwarded should get no reply, but it may occure
523 * (e.g. if the server reconnects before the forwarding of a message and
524 * the reconnection callbacks is called after that). Then onReconnectChat()
525 * may send messagess that have already been sent and the front-end may get
526 * the same message twice. This is partially handled in cbChatService.
529 nldebug( "SB: Chat Service disconnecting: messages will be delayed until reconnection" );
533 /****************************************************************************
534 * Connection callback for the Position service
535 ****************************************************************************/
536 void onReconnectPosition (const std::string
&serviceName
, TServiceId sid
, void *arg
)
538 nldebug( "SB: Position Service reconnected" );
542 /****************************************************************************
543 * Disconnection callback for the Position service
544 ****************************************************************************/
545 void onDisconnectPosition (const std::string
&serviceName
, TServiceId sid
, void *arg
)
547 /* Note: messages already forwarded should get no reply, but it may occure
548 * (e.g. if the server reconnects before the forwarding of a message and
549 * the reconnection callbacks is called after that). Then onReconnectChat()
550 * may send messagess that have already been sent and the front-end may get
551 * the same message twice. This is partially handled in cbPositionService.
554 nldebug( "SB: Position Service disconnecting: messages will be delayed until reconnection" );
558 /****************************************************************************
559 * Connection callback for a client
560 ****************************************************************************/
561 void onConnectionClient (TSockId from
, const CLoginCookie
&cookie
)
565 id
= cookie
.getUserId();
567 nlinfo( "The client with unique Id %u is connected", id
);
569 // Add new client to the list of player managed by this FrontEnd
570 pair
<_pmap::iterator
, bool> player
= localPlayers
.insert( make_pair( id
, CPlayer( id
, from
)));
572 // store the player info in appId
574 _pmap::iterator it
= player
.first
;
575 CPlayer
*p
= &((*it
).second
);
576 from
->setAppId((uint64
)(uintptr_t)p
);
578 // Output: send the IDENTIFICATION number to the new connected client
579 CMessage
msgout( "IDENTIFICATION" );
582 // Send the message to connected client "from"
583 Clients
->send( msgout
, from
);
585 nldebug( "SB: Sent IDENTIFICATION message to the new client");
589 /****************************************************************************
590 * Disconnection callback for a client
591 ****************************************************************************/
592 void onDisconnectClient ( TSockId from
, void *arg
)
596 uintptr_t i
= from
->appId();
601 CPlayer
*p
= (CPlayer
*)(void *)i
;
604 nlinfo( "A client with unique Id %u has disconnected", id
);
606 // tell the login system that this client is disconnected
607 CLoginServer::clientDisconnected ( id
);
609 // remove the player from the local player list
610 localPlayers
.erase( id
);
612 // don't send remove messages for entities that haven't been created.
613 if(p
->State
== CPlayer::ONLINE
)
615 // Output: send the REMOVE_ENTITY to the position manager.
616 CMessage
msgout( "REMOVE_ENTITY" );
619 // Send the message to the position manager
620 CUnifiedNetwork::getInstance ()->send( "POS", msgout
);
621 nldebug( "SB: Sent REMOVE_ENTITY message to the position manager.");
627 /****************************************************************************
629 ****************************************************************************/
630 class CFrontEndService
: public IService
638 // Create the server where the client must connect into
639 // In a real game, it should be an UDP server with specific protocol to manage packet lost and so on.
641 Clients
= new CCallbackServer ();
642 nlassert (Clients
!= 0);
644 // Set the callbacks for that connection (comming from the Chat service)
645 Clients
->addCallbackArray (ClientCallbackArray
, sizeof(ClientCallbackArray
)/sizeof(ClientCallbackArray
[0]));
647 // Set the callbacks for the client disconnection of the Frontend
648 Clients
->setDisconnectionCallback (onDisconnectClient
, 0);
650 Clients
->init (37000);
652 // Connect the frontend to the login system
653 CLoginServer::init( *Clients
, onConnectionClient
);
656 * Set the callback function when the Chat service reconnect to the
659 CUnifiedNetwork::getInstance ()->setServiceUpCallback ("CHAT", onReconnectChat
, 0);
662 * Set the callback function when the Chat service disconnect from
665 CUnifiedNetwork::getInstance ()->setServiceDownCallback ("CHAT", onDisconnectChat
, 0);
668 * Set the callback function when the Position service reconnect to the
671 CUnifiedNetwork::getInstance ()->setServiceUpCallback ("POS", onReconnectPosition
, 0);
674 * Set the callback function when the Position service disconnect from
677 CUnifiedNetwork::getInstance ()->setServiceDownCallback ("POS", onDisconnectPosition
, 0);
682 // Manage messages from clients
685 // we want to continue
697 /****************************************************************************
698 * SNOWBALLS FRONTEND SERVICE MAIN Function
700 * This call create a main function for a service:
702 * - based on the "CFrontEndService" class
703 * - having the short name "FS"
704 * - having the long name "frontend_service"
705 * - listening on the port "0" (dynamically determined)
706 * - and shard callback set to "CallbackArray"
708 ****************************************************************************/
709 NLNET_SERVICE_MAIN (CFrontEndService
, "FS", "frontend_service", 0, CallbackArray
, SNOWBALLS_CONFIG
, SNOWBALLS_LOGS
)