// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #ifdef HAVE_CONFIG_H #include "config.h" #endif // HAVE_CONFIG_H #ifndef SNOWBALLS_CONFIG #define SNOWBALLS_CONFIG "" #endif // SNOWBALLS_CONFIG #ifndef SNOWBALLS_LOGS #define SNOWBALLS_LOGS "" #endif // SNOWBALLS_LOGS // This include is mandatory to use NeL. It include NeL types. #include #include #include // We're using the NeL Service framework, and layer 5 #include #include #include #include "physics.h" #ifdef NL_OS_WINDOWS #include #endif using namespace NLMISC; using namespace NLNET; using namespace std; #define PLAYER_RADIUS 1.0f #define SNOWBALL_RADIUS 0.1f #define START_SNOW_ID 2000000000 #define THROW_ANIM_OFFSET 1000 // Define information used for all connected players to the shard. struct _player { _player( uint32 Id, string Name, uint8 Race, CVector Position ) : id( Id ), name( Name ), race( Race ), position( Position ) { } uint32 id; string name; uint8 race; CVector position; }; // List of all the players connected to the shard. typedef map _pmap; _pmap playerList; // Define informations used for the snowballs management struct _snowball { _snowball( uint32 Id, uint32 Owner, CTrajectory Traj, float ExplosionRadius ) : id( Id ), owner( Owner ), traj( Traj ), explosionRadius( ExplosionRadius ) { } uint32 id; uint32 owner; CTrajectory traj; float explosionRadius; }; // List of all the games snowballs list<_snowball> snoList; class CCLSClient { private: struct _entity { _entity() { } _entity(CVector position, float radius) : Position(position), Radius(radius) { } CVector Position; float Radius; }; static map adds; static map positions; static map moves; static map radiuses; static map removes; public: static void cbUpdate(CMessage &msgin, const std::string &serviceName, TServiceId sid) { // nldebug("Received CLS_UPDATE, %s %s", serviceName.c_str(), sid.toString().c_str()); for (map::iterator it = adds.begin(); it != adds.end(); it++) { CMessage msgout("ADD"); msgout.serial((uint32 &)it->first); msgout.serial(it->second.Position); msgout.serial(it->second.Radius); CUnifiedNetwork::getInstance()->send(sid, msgout); } adds.clear(); for (map::iterator it = positions.begin(); it != positions.end(); it++) { CMessage msgout("POSITION"); msgout.serial((uint32 &)it->first); msgout.serial((NLMISC::CVector &)it->second); CUnifiedNetwork::getInstance()->send(sid, msgout); } positions.clear(); for (map::iterator it = moves.begin(); it != moves.end(); it++) { CMessage msgout("MOVE"); msgout.serial((uint32 &)it->first); msgout.serial((NLMISC::CVector &)it->second); CUnifiedNetwork::getInstance()->send(sid, msgout); } moves.clear(); for (map::iterator it = radiuses.begin(); it != radiuses.end(); it++) { CMessage msgout("RADIUS"); msgout.serial((uint32 &)it->first); msgout.serial((float &)it->second); CUnifiedNetwork::getInstance()->send(sid, msgout); } radiuses.clear(); for (map::iterator it = removes.begin(); it != removes.end(); it++) { CMessage msgout("REMOVE"); msgout.serial((uint32 &)it->first); CUnifiedNetwork::getInstance()->send(sid, msgout); } removes.clear(); } static void cbPosition(CMessage &msgin, const std::string &serviceName, TServiceId sid) { } static void addEntity(uint32 id, CVector position, float radius) { adds[id] = _entity(position, radius); } static void removeEntity(uint32 id) { if (positions.find(id) != positions.end()) positions.erase(id); if (moves.find(id) != moves.end()) moves.erase(id); if (radiuses.find(id) != radiuses.end()) radiuses.erase(id); if (adds.find(id) == adds.end()) removes[id] = 1; else adds.erase(id); } static void movePosition(uint32 id, CVector position) { moves[id] = position; } static void setPosition(uint32 id, CVector position) { if (moves.find(id) != moves.end()) moves.erase(id); if (adds.find(id) == adds.end()) positions[id] = position; else adds[id].Position = position; } static void setRadius(uint32 id, float radius) { if (adds.find(id) == adds.end()) radiuses[id] = radius; else adds[id].Radius = radius; } static void msgRegister(TServiceId sid) { CMessage msgout("REGISTER"); CUnifiedNetwork::getInstance()->send(sid, msgout); } static void clear() { adds.clear(); positions.clear(); moves.clear(); radiuses.clear(); removes.clear(); } }; map CCLSClient::adds; map CCLSClient::positions; map CCLSClient::moves; map CCLSClient::radiuses; map CCLSClient::removes; class CCLSClientPOS : public CCLSClient { public: static void cbPosition(CMessage &msgin, const std::string &serviceName, TServiceId sid) { // temp uint32 id; CVector position; msgin.serial(id); msgin.serial(position); nldebug("Received CLS_POSITION, %s %s", serviceName.c_str(), sid.toString().c_str()); // Update position information in the player list _pmap::iterator ItPlayer; ItPlayer = playerList.find( id ); if ( ItPlayer == playerList.end() ) { nlwarning( "Player id %u not found !", id ); } else { ((*ItPlayer).second).position = position; //nldebug( "SB: Player position updated" ); } CMessage msgout("ENTITY_TP"); msgout.serial(id); msgout.serial(position); CUnifiedNetwork::getInstance()->send("FS", msgout); } /**************************************************************************** * Connection callback for the collision service ****************************************************************************/ static void cbCollisionServiceUp(const std::string &serviceName, TServiceId sid, void *arg) { nldebug("SB: Collision Service UP, %s %s", serviceName.c_str(), sid.toString().c_str()); clear(); msgRegister(sid); for (_pmap::iterator it = playerList.begin(); it != playerList.end(); it++) addEntity(it->second.id, it->second.position, 1.0f); } }; /**************************************************************************** * Function: cbAddEntity * Callback function called when the Position Service receive a * "ADD_ENTITY" message ****************************************************************************/ void cbAddEntity (CMessage &msgin, const std::string &serviceName, TServiceId sid) { bool all; uint32 id; string name; uint8 race; CVector startPoint; // Extract the incomming message content from the Frontend and print it msgin.serial( id ); msgin.serial( name ); msgin.serial( race ); msgin.serial( startPoint ); nldebug( "SB: Received ADD_ENTITY line." ); // Prepare to send back the message. all = true; CMessage msgout( "ADD_ENTITY" ); msgout.serial( all ); msgout.serial( id ); msgout.serial( id ); msgout.serial( name ); msgout.serial( race ); msgout.serial( startPoint ); /* * Send the message to all the connected Frontend. If we decide to send * it back to the sender, that last argument should be 'from' inteed of '0' */ CUnifiedNetwork::getInstance ()->send( "FS", msgout ); CCLSClientPOS::addEntity(id, startPoint, /* 10.0f, */ 1.0f); nldebug( "SB: Send back ADD_ENTITY line." ); // Send ADD_ENTITY message about all already connected client to the new one. all = false; _pmap::iterator ItPlayer; for (ItPlayer = playerList.begin(); ItPlayer != playerList.end(); ++ItPlayer) { CMessage msgout( "ADD_ENTITY" ); msgout.serial( all ); msgout.serial( id ); msgout.serial( ((*ItPlayer).second).id ); msgout.serial( ((*ItPlayer).second).name ); msgout.serial( ((*ItPlayer).second).race ); msgout.serial( ((*ItPlayer).second).position ); CUnifiedNetwork::getInstance ()->send( sid, msgout); } nldebug( "SB: Send ADD_ENTITY line about all already connected clients to the new one." ); // ADD the current added entity in the player list. playerList.insert( make_pair( id, _player( id, name, race, startPoint ) )); } /**************************************************************************** * Function: cbPosition * Callback function called when the Position Service receive a * "ENTITY_POS" message ****************************************************************************/ void cbPosition (CMessage &msgin, const std::string &serviceName, TServiceId sid) { uint32 id; CVector pos; float angle; uint32 state; // Extract the incomming message content from the Frontend and print it msgin.serial( id ); msgin.serial( pos ); msgin.serial( angle ); msgin.serial( state ); //nldebug( "SB: Received ENTITY_POS line." ); // Update position information in the player list _pmap::iterator ItPlayer; ItPlayer = playerList.find( id ); if ( ItPlayer == playerList.end() ) { nlwarning( "Player id %u not found !", id ); } else { ((*ItPlayer).second).position = pos; //nldebug( "SB: Player position updated" ); } // Prepare to send back the message. CMessage msgout( "ENTITY_POS" ); msgout.serial( id ); msgout.serial( pos ); msgout.serial( angle ); msgout.serial( state ); /* * Send the message to all the connected Frontend. */ CUnifiedNetwork::getInstance ()->send( "FS", msgout ); CCLSClientPOS::movePosition(id, pos); //nldebug( "SB: Send back ENTITY_POS line." ); } /**************************************************************************** * Function: cbRemoveEntity * Callback function called when the Position Service receive a * "REMOVE_ENTITY" message ****************************************************************************/ void cbRemoveEntity (CMessage &msgin, const std::string &serviceName, TServiceId sid) { uint32 id; // Extract the incomming message content from the Frontend and print it msgin.serial( id ); nldebug( "SB: Received REMOVE_ENTITY line." ); // Prepare to send back the message. CMessage msgout( "REMOVE_ENTITY" ); msgout.serial( id ); /* * Send the message to all the connected Frontend. */ CUnifiedNetwork::getInstance ()->send( "FS", msgout ); // Remove player form the player list. playerList.erase( id ); CCLSClientPOS::removeEntity(id); nldebug( "SB: Send back REMOVE_ENTITY line. %d players left ...", playerList.size() ); } /**************************************************************************** * Function: cbSnowball * Callback function called when the Position Service receive a * "SNOWBALL" message ****************************************************************************/ void cbSnowball (CMessage &msgin, const std::string &serviceName, TServiceId sid) { static uint32 snowballId = START_SNOW_ID; uint32 playerId; CVector start, target; float speed, explosionRadius; // Extract the incomming message content from the Frontend and print it msgin.serial( playerId ); msgin.serial( start ); msgin.serial( target ); msgin.serial( speed ); msgin.serial( explosionRadius ); nldebug( "SB: Received SNOWBALL line." ); // Store new snowballs informations CTrajectory traj; traj.init( start, target, speed, CTime::getLocalTime() + THROW_ANIM_OFFSET ); _snowball snowball = _snowball( snowballId, playerId, traj, explosionRadius ); snoList.push_front( snowball ); // Prepare to send back the message. CMessage msgout( "SNOWBALL" ); msgout.serial( snowballId ); msgout.serial( playerId ); msgout.serial( start ); msgout.serial( target ); msgout.serial( speed ); msgout.serial( explosionRadius ); snowballId++; /* * Send the message to all the connected Frontend. */ CUnifiedNetwork::getInstance ()->send( "FS", msgout ); nldebug( "SB: Send back SNOWBALL line." ); } /**************************************************************************** * CallbackArray * * It define the functions to call when receiving a specific message ****************************************************************************/ TUnifiedCallbackItem CallbackArray[] = { { "ADD_ENTITY", cbAddEntity }, { "ENTITY_POS", cbPosition }, { "REMOVE_ENTITY", cbRemoveEntity }, { "SNOWBALL", cbSnowball }, { "CLS_UPDATE", CCLSClientPOS::cbUpdate }, { "CLS_POSITION", CCLSClientPOS::cbPosition } }; /**************************************************************************** * Function: SendHITMsg * Send HIT message to all clients * * Arguments: * - snowball: snowball id * - victim: player touched by the snowball * - direct: define if the hit is direct or by the explosion * area ****************************************************************************/ void SendHITMsg ( uint32 snowball, uint32 victim, bool direct ) { CMessage msgout( "HIT" ); msgout.serial( snowball ); msgout.serial( victim ); msgout.serial( direct ); CUnifiedNetwork::getInstance ()->send( "FS", msgout ); } /**************************************************************************** * CPositionService ****************************************************************************/ class CPositionService : public IService { public: void init() { CUnifiedNetwork::getInstance()->setServiceUpCallback("CLS", CCLSClientPOS::cbCollisionServiceUp, 0); } // Update fonction, called at every frames bool update() { //_snowball snowball; CVector snoPos; float distance; bool removeSnowball; // Get the Current time TTime currentTime = CTime::getLocalTime(); list<_snowball>::iterator ItSnowball; // Check collision of snowballs with players ItSnowball = snoList.begin(); while ( ItSnowball != snoList.end() ) { removeSnowball = false; list<_snowball>::iterator ItSb = ItSnowball++; _snowball snowball = (*ItSb); // Test collision (direct and explosion with players) _pmap::iterator ItPlayer; for (ItPlayer = playerList.begin(); ItPlayer != playerList.end(); ++ItPlayer) { _player player = (*ItPlayer).second; /* * Snowballs can't touch the guy which throw it, Like that * players could not kill them self (intentionally or not :-) */ if ( player.id == snowball.owner ) { continue; } // Get the current snowball position snoPos = snowball.traj.eval( currentTime ); // Test direct collision with players distance = (player.position - snoPos).norm(); if ( distance < ( PLAYER_RADIUS + SNOWBALL_RADIUS ) ) { nldebug( "SB: HIT on player %u by player %u.", player.id, snowball.owner ); // Send HIT message SendHITMsg( snowball.id, player.id, true ); // Flag the snowball to be removed from the list removeSnowball = true; } // Snowballs touch his stop Position if ( snowball.traj.getStopTime() < currentTime ) { // Test for explosion victims distance = (player.position - snoPos).norm(); if ( distance < ( PLAYER_RADIUS + snowball.explosionRadius ) ) { nldebug( "SB: Explosion hit on player %u by player %u.", player.id, snowball.owner ); // Send HIT message SendHITMsg( snowball.id, player.id, false ); } // Flag the snowball to be removed from the list removeSnowball = true; } } // Removed if flaged snowball if ( removeSnowball ) { snoList.erase( ItSb ); nldebug( "SB: Removed outdated SNOWBALL id %u.", snowball.id ); } } return true; } }; /**************************************************************************** * SNOWBALLS POSITION SERVICE MAIN Function * * This call create a main function for the POSITION service: * * - based on the base service class "IService", no need to inherit from it * - having the short name "POSITION" * - having the long name "position_service" * - listening on an automatically allocated port (0) by the naming service * - and callback actions set to "CallbackArray" * ****************************************************************************/ NLNET_SERVICE_MAIN( CPositionService, "POS", "position_service", 0, CallbackArray, SNOWBALLS_CONFIG, SNOWBALLS_LOGS ) /* end of file */