diff --git a/data/gui/online/lobby_settings.stkgui b/data/gui/online/lobby_settings.stkgui new file mode 100644 index 000000000..fc5ff8deb --- /dev/null +++ b/data/gui/online/lobby_settings.stkgui @@ -0,0 +1,32 @@ + +
+ +
+ + +
+
+
+ + + +
+
+
+
+ + + + + + + +
+ + + +
diff --git a/data/shaders/lightbeam.frag b/data/shaders/lightbeam.frag index 9ffaf7ba7..a02fad019 100644 --- a/data/shaders/lightbeam.frag +++ b/data/shaders/lightbeam.frag @@ -2,25 +2,20 @@ // Creates a cone lightbeam effect by smoothing edges // Original idea: http://udn.epicgames.com/Three/VolumetricLightbeamTutorial.html // TODO: Soft edges when it intesects geometry -// Some artefacts are still visible - +// Some artefacts are still visible -uniform sampler2D main_texture; +uniform sampler2D tex; uniform float transparency; + varying vec2 uv; varying vec3 eyeVec; varying vec3 normal; void main() { + float inter = dot(normal, eyeVec); + float m = texture2D(tex, vec2(0.5, uv.y)).r; + float alpha = inter * inter * inter * inter * m; - - float inter = dot(normal, eyeVec); - float m = texture2D(main_texture, vec2(0.5, uv.y)).r; - - - gl_FragColor = vec4(1.0,1.0,0.8, 1.0); - - - gl_FragColor.a = inter * inter * inter * inter * m; + gl_FragColor = vec4(1.0, 1.0, 0.8, alpha); } diff --git a/data/shaders/lightbeam.vert b/data/shaders/lightbeam.vert index 5fe4d6917..edd3b3f8e 100644 --- a/data/shaders/lightbeam.vert +++ b/data/shaders/lightbeam.vert @@ -1,8 +1,6 @@ // Jean-manuel clemencon supertuxkart // Creates a cone lightbeam effect by smoothing edges - -uniform float time; varying vec2 uv; varying vec3 eyeVec; varying vec3 normal; @@ -10,13 +8,12 @@ varying vec3 normal; void main() { gl_TexCoord[0] = gl_MultiTexCoord0; - gl_Position = gl_ModelViewMatrix * gl_Vertex; + vec4 viewp = gl_ModelViewMatrix * gl_Vertex; + eyeVec = normalize(-viewp).xyz; + normal = gl_NormalMatrix * gl_Normal; - eyeVec = normalize(-gl_Position).xyz; - normal = gl_NormalMatrix * gl_Normal; + gl_Position = ftransform(); - gl_Position = ftransform(); - - uv = gl_TexCoord[0].st; + uv = gl_TexCoord[0].st; } diff --git a/doc/protocols.xls b/doc/protocols.xls new file mode 100644 index 000000000..70ffa359f Binary files /dev/null and b/doc/protocols.xls differ diff --git a/lib/enet/host.c b/lib/enet/host.c index c44f2ed3c..f0597b1ff 100644 --- a/lib/enet/host.c +++ b/lib/enet/host.c @@ -49,7 +49,7 @@ enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelL memset (host -> peers, 0, peerCount * sizeof (ENetPeer)); host -> socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM); - if (host -> socket == ENET_SOCKET_NULL || (address != NULL && enet_socket_bind (host -> socket, address) < 0)) + if (host -> socket == ENET_SOCKET_NULL || (enet_socket_bind (host -> socket, address) < 0 && address != NULL)) { if (host -> socket != ENET_SOCKET_NULL) enet_socket_destroy (host -> socket); diff --git a/lib/irrlicht/changes.stk b/lib/irrlicht/changes.stk index fe0e93896..22f21c60d 100644 --- a/lib/irrlicht/changes.stk +++ b/lib/irrlicht/changes.stk @@ -6,3 +6,4 @@ The following changes have been made: - materialtype override - skies respect Z - partial backport to expose setCurrentRendertime in the scene mgr +- a workaround for every other RTTs flipping diff --git a/lib/irrlicht/source/Irrlicht/COpenGLDriver.cpp b/lib/irrlicht/source/Irrlicht/COpenGLDriver.cpp index 0f3e1790a..7f084c9d7 100644 --- a/lib/irrlicht/source/Irrlicht/COpenGLDriver.cpp +++ b/lib/irrlicht/source/Irrlicht/COpenGLDriver.cpp @@ -987,7 +987,7 @@ void COpenGLDriver::setTransform(E_TRANSFORMATION_STATE state, const core::matri else { GLfloat glmat[16]; - if (isRTT) + if (isRTT && CurrentTarget == ERT_FRAME_BUFFER) getGLTextureMatrix(glmat, mat * TextureFlipMatrix); else getGLTextureMatrix(glmat, mat); diff --git a/sources.cmake b/sources.cmake index 2ee78af57..cc8e1ef74 100644 --- a/sources.cmake +++ b/sources.cmake @@ -105,6 +105,7 @@ src/karts/controller/ai_base_controller.cpp src/karts/controller/ai_properties.cpp src/karts/controller/controller.cpp src/karts/controller/end_controller.cpp +src/karts/controller/network_player_controller.cpp src/karts/controller/player_controller.cpp src/karts/controller/skidding_ai.cpp src/karts/explosion_animation.cpp @@ -136,15 +137,39 @@ src/modes/tutorial_world.cpp src/modes/world.cpp src/modes/world_status.cpp src/modes/world_with_rank.cpp -src/network/connect_message.cpp -src/network/kart_control_message.cpp -src/network/kart_update_message.cpp -src/network/message.cpp -src/network/network_kart.cpp +src/network/client_network_manager.cpp +src/network/event.cpp +src/network/game_setup.cpp +src/network/http_functions.cpp +src/network/network_interface.cpp src/network/network_manager.cpp -src/network/race_info_message.cpp -src/network/race_result_message.cpp -src/network/race_state.cpp +src/network/network_string.cpp +src/network/network_world.cpp +src/network/protocol.cpp +src/network/protocol_manager.cpp +src/network/protocols/client_lobby_room_protocol.cpp +src/network/protocols/connect_to_peer.cpp +src/network/protocols/connect_to_server.cpp +src/network/protocols/controller_events_protocol.cpp +src/network/protocols/game_events_protocol.cpp +src/network/protocols/get_peer_address.cpp +src/network/protocols/get_public_address.cpp +src/network/protocols/hide_public_address.cpp +src/network/protocols/kart_update_protocol.cpp +src/network/protocols/lobby_room_protocol.cpp +src/network/protocols/ping_protocol.cpp +src/network/protocols/quick_join_protocol.cpp +src/network/protocols/request_connection.cpp +src/network/protocols/server_lobby_room_protocol.cpp +src/network/protocols/show_public_address.cpp +src/network/protocols/start_game_protocol.cpp +src/network/protocols/start_server.cpp +src/network/protocols/stop_server.cpp +src/network/protocols/synchronization_protocol.cpp +src/network/server_network_manager.cpp +src/network/stk_host.cpp +src/network/stk_peer.cpp +src/network/types.cpp src/online/current_user.cpp src/online/http_manager.cpp src/online/messages.cpp @@ -200,6 +225,9 @@ src/states_screens/help_screen_4.cpp src/states_screens/kart_selection.cpp src/states_screens/main_menu_screen.cpp src/states_screens/networking_lobby.cpp +src/states_screens/networking_lobby_settings.cpp +src/states_screens/network_kart_selection.cpp +src/states_screens/offline_kart_selection.cpp src/states_screens/online_screen.cpp src/states_screens/options_screen_audio.cpp src/states_screens/options_screen_input2.cpp @@ -368,6 +396,7 @@ src/karts/controller/ai_properties.hpp src/karts/controller/controller.hpp src/karts/controller/end_controller.hpp src/karts/controller/kart_control.hpp +src/karts/controller/network_player_controller.hpp src/karts/controller/player_controller.hpp src/karts/controller/skidding_ai.hpp src/karts/explosion_animation.hpp @@ -398,25 +427,41 @@ src/modes/tutorial_world.hpp src/modes/world.hpp src/modes/world_status.hpp src/modes/world_with_rank.hpp -src/network/character_confirm_message.hpp -src/network/character_info_message.hpp -src/network/character_selected_message.hpp -src/network/connect_message.hpp -src/network/flyable_info.hpp -src/network/item_info.hpp -src/network/kart_control_message.hpp -src/network/kart_update_message.hpp -src/network/message.hpp -src/network/network_kart.hpp +src/network/client_network_manager.hpp +src/network/event.hpp +src/network/game_setup.hpp +src/network/http_functions.hpp +src/network/network_interface.hpp src/network/network_manager.hpp -src/network/num_players_message.hpp -src/network/race_info_message.hpp -src/network/race_result_ack_message.hpp -src/network/race_result_message.hpp -src/network/race_start_message.hpp -src/network/race_state.hpp +src/network/network_string.hpp +src/network/network_world.hpp +src/network/protocol.hpp +src/network/protocol_manager.hpp +src/network/protocols/client_lobby_room_protocol.hpp +src/network/protocols/connect_to_peer.hpp +src/network/protocols/connect_to_server.hpp +src/network/protocols/controller_events_protocol.hpp +src/network/protocols/game_events_protocol.hpp +src/network/protocols/get_peer_address.hpp +src/network/protocols/get_public_address.hpp +src/network/protocols/hide_public_address.hpp +src/network/protocols/kart_update_protocol.hpp +src/network/protocols/lobby_room_protocol.hpp +src/network/protocols/ping_protocol.hpp +src/network/protocols/quick_join_protocol.hpp +src/network/protocols/request_connection.hpp +src/network/protocols/server_lobby_room_protocol.hpp +src/network/protocols/show_public_address.hpp +src/network/protocols/start_game_protocol.hpp +src/network/protocols/start_server.hpp +src/network/protocols/stop_server.hpp +src/network/protocols/synchronization_protocol.hpp src/network/remote_kart_info.hpp -src/network/world_loaded_message.hpp +src/network/server_network_manager.hpp +src/network/singleton.hpp +src/network/stk_host.hpp +src/network/stk_peer.hpp +src/network/types.hpp src/online/current_user.hpp src/online/http_manager.hpp src/online/messages.hpp @@ -475,6 +520,9 @@ src/states_screens/help_screen_4.hpp src/states_screens/kart_selection.hpp src/states_screens/main_menu_screen.hpp src/states_screens/networking_lobby.hpp +src/states_screens/networking_lobby_settings.hpp +src/states_screens/network_kart_selection.hpp +src/states_screens/offline_kart_selection.hpp src/states_screens/online_screen.hpp src/states_screens/options_screen_audio.hpp src/states_screens/options_screen_input2.hpp diff --git a/src/addons/network_http.hpp b/src/addons/network_http.hpp index 923b90cb2..8b09758ea 100644 --- a/src/addons/network_http.hpp +++ b/src/addons/network_http.hpp @@ -27,7 +27,7 @@ #include #ifdef WIN32 -# include +# include #endif #include diff --git a/src/audio/sfx_manager.cpp b/src/audio/sfx_manager.cpp index 8cc0f9bd8..fe5d2b6e1 100644 --- a/src/audio/sfx_manager.cpp +++ b/src/audio/sfx_manager.cpp @@ -282,7 +282,7 @@ SFXBase* SFXManager::createSoundSource(SFXBuffer* buffer, // race_manager->getNumLocalPlayers(), buffer->isPositional()); #if HAVE_OGGVORBIS - assert( alIsBuffer(buffer->getBufferID()) ); + //assert( alIsBuffer(buffer->getBufferID()) ); crashes on server SFXBase* sfx = new SFXOpenAL(buffer, positional, buffer->getGain(), owns_buffer); #else SFXBase* sfx = new DummySFX(buffer, positional, buffer->getGain(), owns_buffer); diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp index 548c938be..fb2485c55 100644 --- a/src/config/user_config.hpp +++ b/src/config/user_config.hpp @@ -487,13 +487,15 @@ namespace UserConfigParams // not saved to file // ---- Networking - PARAM_PREFIX StringUserConfigParam m_server_address - PARAM_DEFAULT( StringUserConfigParam("localhost", "server_adress", - "Information about last server used") ); PARAM_PREFIX IntUserConfigParam m_server_port - PARAM_DEFAULT( IntUserConfigParam(2305, "server_port", - "Information about last server used") ); - + PARAM_DEFAULT( IntUserConfigParam(7321, "server_port", + "Information about the port to listen on.") ); + + PARAM_PREFIX IntUserConfigParam m_server_max_players + PARAM_DEFAULT( IntUserConfigParam(16, "server_max_players", + "Maximum number of players on the server.") ); + + // ---- Graphic Quality PARAM_PREFIX GroupUserConfigParam m_graphics_quality PARAM_DEFAULT( GroupUserConfigParam("GFX", diff --git a/src/guiengine/engine.cpp b/src/guiengine/engine.cpp index cdbefe733..83235ad63 100644 --- a/src/guiengine/engine.cpp +++ b/src/guiengine/engine.cpp @@ -485,7 +485,7 @@ namespace GUIEngine Used on divs, indicate by how many pixels to pad contents - + \n
\section code Using the engine in code @@ -904,6 +904,25 @@ namespace GUIEngine g_loaded_screens.push_back(cutscene); } // addScreenToList + // ------------------------------------------------------------------------ + + void removeScreen(const char* name) + { + const int screen_amount = g_loaded_screens.size(); + for(int n=0; nunload(); + delete g_current_screen; + g_current_screen = NULL; + g_loaded_screens.remove(n); + break; + } + } + } + // ------------------------------------------------------------------------ void reshowCurrentScreen() { diff --git a/src/guiengine/engine.hpp b/src/guiengine/engine.hpp index 9a91cdd84..d4916d533 100644 --- a/src/guiengine/engine.hpp +++ b/src/guiengine/engine.hpp @@ -195,6 +195,8 @@ namespace GUIEngine /** \brief Add a screen to the list of screens known by the gui engine */ void addScreenToList(Screen* screen); + /** \brief Remove a screen from the list of screens known by the gui engine */ + void removeScreen(const char* name); /** \brief Low-level mean to change current screen. * \note Do not use directly. Use a state manager instead to get higher-level functionnality. diff --git a/src/guiengine/screen.hpp b/src/guiengine/screen.hpp index 017c84b31..59ddc82cb 100644 --- a/src/guiengine/screen.hpp +++ b/src/guiengine/screen.hpp @@ -61,6 +61,7 @@ namespace GUIEngine template class ScreenSingleton { + protected: static SCREEN* singleton; public: diff --git a/src/ide/vc9/supertuxkart.sln b/src/ide/vc9/supertuxkart.sln index d6a3108d1..6c6e65523 100644 --- a/src/ide/vc9/supertuxkart.sln +++ b/src/ide/vc9/supertuxkart.sln @@ -44,8 +44,8 @@ Global {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.debug|Win32.Build.0 = Debug|Win32 {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release|Win32.ActiveCfg = Release|Win32 {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.Release|Win32.Build.0 = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.wiiuse-debug|Win32.ActiveCfg = Release|Win32 - {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.wiiuse-debug|Win32.Build.0 = Release|Win32 + {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.wiiuse-debug|Win32.ActiveCfg = Debug|Win32 + {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.wiiuse-debug|Win32.Build.0 = Debug|Win32 {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.wiiuse-release|Win32.ActiveCfg = wiiuse-release|Win32 {B0E92B97-089A-4D5B-BF17-77F1BC5DAEEF}.wiiuse-release|Win32.Build.0 = wiiuse-release|Win32 {E08E042A-6C45-411B-92BE-3CC31331019F}.debug|Win32.ActiveCfg = Static lib - Debug|Win32 diff --git a/src/input/input_manager.cpp b/src/input/input_manager.cpp index 7ac13e5e0..c56184bdd 100644 --- a/src/input/input_manager.cpp +++ b/src/input/input_manager.cpp @@ -164,12 +164,12 @@ void InputManager::handleStaticAction(int key, int value) if (UserConfigParams::m_artist_debug_mode && world) { AbstractKart* kart = world->getLocalPlayerKart(0); - + if (control_is_pressed) kart->setPowerup(PowerupManager::POWERUP_SWATTER, 10000); else kart->setPowerup(PowerupManager::POWERUP_RUBBERBALL, 10000); - + #ifdef FORCE_RESCUE_ON_FIRST_KART // Can be useful for debugging places where the AI gets into // a rescue loop: rescue, drive, crash, rescue to same place @@ -181,7 +181,7 @@ void InputManager::handleStaticAction(int key, int value) if (UserConfigParams::m_artist_debug_mode && world) { AbstractKart* kart = world->getLocalPlayerKart(0); - + kart->setPowerup(PowerupManager::POWERUP_PLUNGER, 10000); } break; @@ -557,7 +557,7 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, action == PA_MENU_CANCEL ) ) { // returns true if the event was handled - if (KartSelectionScreen::getInstance()->playerQuit( player )) + if (KartSelectionScreen::getRunningInstance()->playerQuit( player )) { return; // we're done here } @@ -592,7 +592,7 @@ void InputManager::dispatchInput(Input::InputType type, int deviceID, if (device != NULL) { - KartSelectionScreen::getInstance()->playerJoin(device, + KartSelectionScreen::getRunningInstance()->playerJoin(device, false ); } } diff --git a/src/io/xml_node.cpp b/src/io/xml_node.cpp index 84bc8448c..264bba0aa 100644 --- a/src/io/xml_node.cpp +++ b/src/io/xml_node.cpp @@ -41,7 +41,7 @@ XMLNode::XMLNode(const std::string &filename) m_file_name = filename; io::IXMLReader *xml = file_manager->createXMLReader(filename); - + if (xml == NULL) { throw std::runtime_error("Cannot find file "+filename); @@ -319,6 +319,22 @@ int XMLNode::get(const std::string &attribute, int64_t *value) const } // get(int64_t) +// ---------------------------------------------------------------------------- +int XMLNode::get(const std::string &attribute, uint16_t *value) const +{ + std::string s; + if(!get(attribute, &s)) return 0; + + if (!StringUtils::parseString(s, value)) + { + fprintf(stderr, "[XMLNode] WARNING: Expected uint but found '%s' for attribute '%s' of node '%s' in file %s\n", + s.c_str(), attribute.c_str(), m_name.c_str(), m_file_name.c_str()); + return 0; + } + + return 1; +} // get(uint32_t) + // ---------------------------------------------------------------------------- int XMLNode::get(const std::string &attribute, uint32_t *value) const { diff --git a/src/io/xml_node.hpp b/src/io/xml_node.hpp index 953d52f8a..37e4ade41 100644 --- a/src/io/xml_node.hpp +++ b/src/io/xml_node.hpp @@ -74,6 +74,7 @@ public: int get(const std::string &attribute, std::string *value) const; int get(const std::string &attribute, core::stringw *value) const; int get(const std::string &attribute, int32_t *value) const; + int get(const std::string &attribute, uint16_t *value) const; int get(const std::string &attribute, uint32_t *value) const; int get(const std::string &attribute, int64_t *value) const; int get(const std::string &attribute, float *value) const; diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index 1bbbdf2cc..7d7fbb6a0 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -33,8 +33,6 @@ #include "karts/kart_properties.hpp" #include "modes/three_strikes_battle.hpp" #include "modes/world.hpp" -#include "network/race_state.hpp" -#include "network/network_manager.hpp" #include "utils/constants.hpp" /** Initialises the attachment each kart has. @@ -258,15 +256,6 @@ void Attachment::hitBanana(Item *item, int new_attachment) new_attachment = m_random.get(3); } // switch - // Save the information about the attachment in the race state - // so that the clients can be updated. - if(network_manager->getMode()==NetworkManager::NW_SERVER) - { - race_state->itemCollected(m_kart->getWorldKartId(), - item->getItemId(), - new_attachment); - } - if (add_a_new_item) { switch (new_attachment) diff --git a/src/items/flyable.cpp b/src/items/flyable.cpp index d0116415d..b1ee43129 100644 --- a/src/items/flyable.cpp +++ b/src/items/flyable.cpp @@ -38,7 +38,6 @@ #include "karts/abstract_kart.hpp" #include "karts/explosion_animation.hpp" #include "modes/world.hpp" -#include "network/flyable_info.hpp" #include "physics/physics.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" @@ -410,19 +409,6 @@ bool Flyable::updateAndDelete(float dt) return false; } // updateAndDelete -// ---------------------------------------------------------------------------- -/** Updates the position of a projectile based on information received frmo the - * server. - */ -void Flyable::updateFromServer(const FlyableInfo &f, float dt) -{ - setXYZ(f.m_xyz); - setRotation(f.m_rotation); - - // Update the graphical position - Moveable::update(dt); -} // updateFromServer - // ---------------------------------------------------------------------------- /** Returns true if the item hit the kart who shot it (to avoid that an item * that's too close to the shoter hits the shoter). diff --git a/src/items/flyable.hpp b/src/items/flyable.hpp index bfd46835f..9030209f3 100644 --- a/src/items/flyable.hpp +++ b/src/items/flyable.hpp @@ -34,7 +34,6 @@ using namespace irr; #include "tracks/terrain_info.hpp" class AbstractKart; -class FlyableInfo; class HitEffect; class PhysicalObject; class XMLNode; @@ -169,7 +168,6 @@ public: virtual bool updateAndDelete(float); virtual const core::stringw getHitString(const AbstractKart *kart) const = 0; virtual HitEffect* getHitEffect() const; - void updateFromServer(const FlyableInfo &f, float dt); bool isOwnerImmunity(const AbstractKart *kart_hit) const; virtual bool hit(AbstractKart* kart, PhysicalObject* obj=NULL); void explode(AbstractKart* kart, PhysicalObject* obj=NULL, diff --git a/src/items/item_manager.cpp b/src/items/item_manager.cpp index 4b7fc0ad1..a76db40fe 100644 --- a/src/items/item_manager.cpp +++ b/src/items/item_manager.cpp @@ -29,7 +29,6 @@ #include "io/file_manager.hpp" #include "karts/abstract_kart.hpp" #include "modes/linear_world.hpp" -#include "network/network_manager.hpp" #include "tracks/quad_graph.hpp" #include "tracks/track.hpp" #include "utils/string_utils.hpp" @@ -289,9 +288,6 @@ void ItemManager::collectedItem(Item *item, AbstractKart *kart, int add_info) */ void ItemManager::checkItemHit(AbstractKart* kart) { - // Only do this on the server - if(network_manager->getMode()==NetworkManager::NW_CLIENT) return; - // We could use m_items_in_quads to to check for item hits: take the quad // of the graph node of the kart, and only check items in that quad. But // then we also need to check for any adjacent quads (since an item just diff --git a/src/items/powerup.cpp b/src/items/powerup.cpp index d6bd8022c..a4264b16a 100644 --- a/src/items/powerup.cpp +++ b/src/items/powerup.cpp @@ -29,8 +29,6 @@ #include "karts/controller/controller.hpp" #include "karts/kart_properties.hpp" #include "modes/world.hpp" -#include "network/network_manager.hpp" -#include "network/race_state.hpp" #include "physics/triangle_mesh.hpp" #include "tracks/track.hpp" #include "utils/string_utils.hpp" diff --git a/src/items/projectile_manager.cpp b/src/items/projectile_manager.cpp index f7846b684..18e299560 100644 --- a/src/items/projectile_manager.cpp +++ b/src/items/projectile_manager.cpp @@ -26,8 +26,6 @@ #include "items/powerup_manager.hpp" #include "items/powerup.hpp" #include "items/rubber_ball.hpp" -#include "network/network_manager.hpp" -#include "network/race_state.hpp" ProjectileManager *projectile_manager=0; @@ -66,14 +64,7 @@ void ProjectileManager::cleanup() /** General projectile update call. */ void ProjectileManager::update(float dt) { - if(network_manager->getMode()==NetworkManager::NW_CLIENT) - { - updateClient(dt); - } - else - { - updateServer(dt); - } + updateServer(dt); HitEffects::iterator he = m_active_hit_effects.begin(); while(he!=m_active_hit_effects.end()) @@ -100,23 +91,10 @@ void ProjectileManager::update(float dt) /** Updates all rockets on the server (or no networking). */ void ProjectileManager::updateServer(float dt) { - // First update all projectiles on the track - if(network_manager->getMode()!=NetworkManager::NW_NONE) - { - race_state->setNumFlyables(m_active_projectiles.size()); - } - Projectiles::iterator p = m_active_projectiles.begin(); while(p!=m_active_projectiles.end()) { bool can_be_deleted = (*p)->updateAndDelete(dt); - if(network_manager->getMode()!=NetworkManager::NW_NONE) - { - race_state->setFlyableInfo(p-m_active_projectiles.begin(), - FlyableInfo((*p)->getXYZ(), - (*p)->getRotation(), - can_be_deleted) ); - } if(can_be_deleted) { HitEffect *he = (*p)->getHitEffect(); @@ -130,32 +108,9 @@ void ProjectileManager::updateServer(float dt) else p++; } // while p!=m_active_projectiles.end() + } // updateServer -// ----------------------------------------------------------------------------- -/** Updates all rockets and hit effects on the client. - * updateClient takes the information in race_state and updates all rockets - * (i.e. position, hit effects etc) */ -void ProjectileManager::updateClient(float dt) -{ - unsigned int num_projectiles = race_state->getNumFlyables(); - if(num_projectiles != m_active_projectiles.size()) - fprintf(stderr, "Warning: num_projectiles %d active %d\n", - num_projectiles, (int)m_active_projectiles.size()); - - unsigned int indx=0; - for(Projectiles::iterator i = m_active_projectiles.begin(); - i != m_active_projectiles.end(); ++i, ++indx) - { - const FlyableInfo &f = race_state->getFlyable(indx); - (*i)->updateFromServer(f, dt); - if(f.m_exploded) - { - (*i)->hit(NULL); - } - } // for i in m_active_projectiles - -} // updateClient // ----------------------------------------------------------------------------- Flyable *ProjectileManager::newProjectile(AbstractKart *kart, Track* track, PowerupManager::PowerupType type) diff --git a/src/items/projectile_manager.hpp b/src/items/projectile_manager.hpp index f0906ca6b..8fb513502 100644 --- a/src/items/projectile_manager.hpp +++ b/src/items/projectile_manager.hpp @@ -53,7 +53,6 @@ private: * being shown or have a sfx playing. */ HitEffects m_active_hit_effects; - void updateClient(float dt); void updateServer(float dt); public: ProjectileManager() {} diff --git a/src/items/rubber_ball.cpp b/src/items/rubber_ball.cpp index 35361ae29..592a7f86d 100644 --- a/src/items/rubber_ball.cpp +++ b/src/items/rubber_ball.cpp @@ -419,6 +419,8 @@ void RubberBall::moveTowardsTarget(Vec3 *next_xyz, float dt) // at it directly, stop interpolating, instead fly straight // towards it. Vec3 diff = m_target->getXYZ()-getXYZ(); + if(diff.length()==0) + printf("diff=0\n"); *next_xyz = getXYZ() + (dt*m_speed/diff.length())*diff; Vec3 old_vec = getXYZ()-m_previous_xyz; diff --git a/src/karts/controller/controller.hpp b/src/karts/controller/controller.hpp index a981176d2..869d22f16 100644 --- a/src/karts/controller/controller.hpp +++ b/src/karts/controller/controller.hpp @@ -37,7 +37,7 @@ class Item; class KartControl; class Material; -/** This is the base class for kart controller - that can be a player +/** This is the base class for kart controller - that can be a player * or a a robot. * \ingroup controller */ @@ -58,7 +58,7 @@ protected: /** The name of the controller, mainly used for debugging purposes. */ std::string m_controller_name; public: - Controller (AbstractKart *kart, + Controller (AbstractKart *kart, StateManager::ActivePlayer *player=NULL); virtual ~Controller () {}; virtual void reset () = 0; @@ -74,20 +74,20 @@ public: virtual bool disableSlipstreamBonus() const = 0; // --------------------------------------------------------------------------- /** Sets the controller name for this controller. */ - virtual void setControllerName(const std::string &name) + virtual void setControllerName(const std::string &name) { m_controller_name = name; } // --------------------------------------------------------------------------- /** Returns the name of this controller. */ const std::string &getControllerName() const { return m_controller_name; } // --------------------------------------------------------------------------- - /** Returns the active player for this controller (NULL + /** Returns the active player for this controller (NULL * if this controller does not belong to a player. */ StateManager::ActivePlayer *getPlayer () {return m_player;} - + // --------------------------------------------------------------------------- /** Returns the player object (or NULL if it's a computer controller). */ const StateManager::ActivePlayer *getPlayer () const { return m_player; } - + // ------------------------------------------------------------------------ /** Default: ignore actions. Only PlayerController get them. */ virtual void action(PlayerAction action, int value) = 0; @@ -101,6 +101,9 @@ public: /** Called whan this controller's kart finishes the last lap. */ virtual void finishedRace(float time) = 0; // ------------------------------------------------------------------------ + /** Get a pointer on the kart controls. */ + virtual KartControl* getControls() { return m_controls; } + // ------------------------------------------------------------------------ }; // Controller #endif diff --git a/src/karts/controller/end_controller.cpp b/src/karts/controller/end_controller.cpp index ccea9e9eb..8b72439c2 100644 --- a/src/karts/controller/end_controller.cpp +++ b/src/karts/controller/end_controller.cpp @@ -43,7 +43,6 @@ #include "karts/max_speed.hpp" #include "karts/rescue_animation.hpp" #include "modes/linear_world.hpp" -#include "network/network_manager.hpp" #include "race/race_manager.hpp" #include "states_screens/race_result_gui.hpp" #include "tracks/quad_graph.hpp" diff --git a/src/karts/controller/kart_control.hpp b/src/karts/controller/kart_control.hpp index 78f789e7c..ed330d820 100644 --- a/src/karts/controller/kart_control.hpp +++ b/src/karts/controller/kart_control.hpp @@ -19,9 +19,8 @@ #ifndef HEADER_KART_CONTROL_HPP #define HEADER_KART_CONTROL_HPP -#include "network/message.hpp" -/** +/** * \ingroup controller */ class KartControl @@ -52,15 +51,6 @@ public: reset(); } // ------------------------------------------------------------------------ - /** Construct kart control from a Message (i.e. unserialise) */ - KartControl(Message *m) - { - m_steer = m->getFloat(); - m_accel = m->getFloat(); - char c = m->getChar(); - setButtonsCompressed(c); - } // KartControl(Message*) - // ------------------------------------------------------------------------ /** Resets all controls. */ void reset() { @@ -74,17 +64,6 @@ public: m_look_back = false; } // reset // ------------------------------------------------------------------------ - /** Return the serialised size in bytes. */ - static int getLength() { return 9; } - // ------------------------------------------------------------------------ - /** Serialises the kart control into a message. */ - void serialise(Message *m) const - { - m->addFloat(m_steer); - m->addFloat(m_accel); - m->addChar(getButtonsCompressed()); - } // compress - // ------------------------------------------------------------------------ void uncompress(char *c) { m_steer = ((float*)c)[0]; diff --git a/src/karts/controller/network_player_controller.cpp b/src/karts/controller/network_player_controller.cpp new file mode 100644 index 000000000..b408727ec --- /dev/null +++ b/src/karts/controller/network_player_controller.cpp @@ -0,0 +1,368 @@ +#include "karts/controller/network_player_controller.hpp" + +#include "config/player.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/post_processing.hpp" +#include "input/input_manager.hpp" +#include "items/attachment.hpp" +#include "items/item.hpp" +#include "items/powerup.hpp" +#include "karts/abstract_kart.hpp" +#include "karts/kart_properties.hpp" +#include "karts/skidding.hpp" +#include "karts/rescue_animation.hpp" +#include "modes/world.hpp" +#include "race/history.hpp" +#include "states_screens/race_gui_base.hpp" +#include "utils/constants.hpp" +#include "utils/log.hpp" +#include "utils/translation.hpp" + +NetworkPlayerController::NetworkPlayerController(AbstractKart *kart, + StateManager::ActivePlayer *player) + : Controller(kart) +{ + assert(player != NULL); + m_player = player; + m_player->setKart(kart); + m_penalty_time = 0.0f; + + reset(); + + Log::info("NetworkPlayerController", "New network player controller."); +} // NetworkPlayerController + +//----------------------------------------------------------------------------- +/** Destructor for a player kart. + */ +NetworkPlayerController::~NetworkPlayerController() +{ +} // ~NetworkPlayerController + +//----------------------------------------------------------------------------- +/** Resets the player kart for a new or restarted race. + */ +void NetworkPlayerController::reset() +{ + m_steer_val_l = 0; + m_steer_val_r = 0; + m_steer_val = 0; + m_prev_brake = 0; + m_prev_accel = 0; + m_prev_nitro = false; + m_penalty_time = 0; +} // reset + +// ---------------------------------------------------------------------------- +/** Resets the state of control keys. This is used after the in-game menu to + * avoid that any keys pressed at the time the menu is opened are still + * considered to be pressed. + */ +void NetworkPlayerController::resetInputState() +{ + m_steer_val_l = 0; + m_steer_val_r = 0; + m_steer_val = 0; + m_prev_brake = 0; + m_prev_accel = 0; + m_prev_nitro = false; + m_controls->reset(); +} // resetInputState + +// ---------------------------------------------------------------------------- +/** This function interprets a kart action and value, and set the corresponding + * entries in the kart control data structure. This function handles esp. + * cases like 'press left, press right, release right' - in this case after + * releasing right, the steering must switch to left again. Similarly it + * handles 'press left, press right, release left' (in which case still + * right must be selected). Similarly for braking and acceleration. + * \param action The action to be executed. + * \param value If 32768, it indicates a digital value of 'fully set' + * if between 1 and 32767, it indicates an analog value, + * and if it's 0 it indicates that the corresponding button + * was released. + */ +void NetworkPlayerController::action(PlayerAction action, int value) +{ + switch (action) + { + case PA_STEER_LEFT: + m_steer_val_l = value; + if (value) + { + m_steer_val = value; + if(m_controls->m_skid==KartControl::SC_NO_DIRECTION) + m_controls->m_skid = KartControl::SC_LEFT; + } + else + m_steer_val = m_steer_val_r; + + break; + case PA_STEER_RIGHT: + m_steer_val_r = -value; + if (value) + { + m_steer_val = -value; + if(m_controls->m_skid==KartControl::SC_NO_DIRECTION) + m_controls->m_skid = KartControl::SC_RIGHT; + } + else + m_steer_val = m_steer_val_l; + + break; + case PA_ACCEL: + m_prev_accel = value; + if (value && !(m_penalty_time > 0.0f)) + { + m_controls->m_accel = value/32768.0f; + m_controls->m_brake = false; + m_controls->m_nitro = m_prev_nitro; + } + else + { + m_controls->m_accel = 0.0f; + m_controls->m_brake = m_prev_brake; + m_controls->m_nitro = false; + } + break; + case PA_BRAKE: + m_prev_brake = value!=0; + // let's consider below that to be a deadzone + if(value > 32768/2) + { + m_controls->m_brake = true; + m_controls->m_accel = 0.0f; + m_controls->m_nitro = false; + } + else + { + m_controls->m_brake = false; + m_controls->m_accel = m_prev_accel/32768.0f; + // Nitro still depends on whether we're accelerating + m_controls->m_nitro = (m_prev_nitro && m_prev_accel); + } + break; + case PA_NITRO: + // This basically keeps track whether the button still is being pressed + m_prev_nitro = (value != 0); + // Enable nitro only when also accelerating + m_controls->m_nitro = ((value!=0) && m_controls->m_accel); + break; + case PA_RESCUE: + m_controls->m_rescue = (value!=0); + break; + case PA_FIRE: + { + m_controls->m_fire = (value!=0); + break; + } + case PA_LOOK_BACK: + m_controls->m_look_back = (value!=0); + break; + case PA_DRIFT: + if(value==0) + m_controls->m_skid = KartControl::SC_NONE; + else + if(m_steer_val==0) + m_controls->m_skid = KartControl::SC_NO_DIRECTION; + else + m_controls->m_skid = m_steer_val<0 + ? KartControl::SC_RIGHT + : KartControl::SC_LEFT; + break; + case PA_PAUSE_RACE: + if (value != 0) StateManager::get()->escapePressed(); + break; + default: + break; + } + +} // action + +//----------------------------------------------------------------------------- +/** Handles steering for a player kart. + */ +void NetworkPlayerController::steer(float dt, int steer_val) +{ + if(stk_config->m_disable_steer_while_unskid && + m_controls->m_skid==KartControl::SC_NONE && + m_kart->getSkidding()->getVisualSkidRotation()!=0) + { + m_controls->m_steer = 0; + } + + + // Amount the steering is changed for digital devices. + // If the steering is 'back to straight', a different steering + // change speed is used. + const float STEER_CHANGE = ( (steer_val<=0 && m_controls->m_steer<0) || + (steer_val>=0 && m_controls->m_steer>0) ) + ? dt/m_kart->getKartProperties()->getTimeResetSteer() + : dt/m_kart->getTimeFullSteer(fabsf(m_controls->m_steer)); + if (steer_val < 0) + { + // If we got analog values do not cumulate. + if (steer_val > -32767) + m_controls->m_steer = -steer_val/32767.0f; + else + m_controls->m_steer += STEER_CHANGE; + } + else if(steer_val > 0) + { + // If we got analog values do not cumulate. + if (steer_val < 32767) + m_controls->m_steer = -steer_val/32767.0f; + else + m_controls->m_steer -= STEER_CHANGE; + } + else + { // no key is pressed + if(m_controls->m_steer>0.0f) + { + m_controls->m_steer -= STEER_CHANGE; + if(m_controls->m_steer<0.0f) m_controls->m_steer=0.0f; + } + else + { // m_controls->m_steer<=0.0f; + m_controls->m_steer += STEER_CHANGE; + if(m_controls->m_steer>0.0f) m_controls->m_steer=0.0f; + } // if m_controls->m_steer<=0.0f + } // no key is pressed + if(UserConfigParams::m_gamepad_debug) + { + Log::debug("PlayerController", " set to: %f\n", m_controls->m_steer); + } + + m_controls->m_steer = std::min(1.0f, std::max(-1.0f, m_controls->m_steer)); + +} // steer + +//----------------------------------------------------------------------------- +/** Callback when the skidding bonus is triggered. The player controller + * resets the current steering to 0, which makes the kart easier to control. + */ +void NetworkPlayerController::skidBonusTriggered() +{ + m_controls->m_steer = 0; +} // skidBonusTriggered + +//----------------------------------------------------------------------------- +/** Updates the player kart, called once each timestep. + */ +void NetworkPlayerController::update(float dt) +{ + if (UserConfigParams::m_gamepad_debug) + { + // Print a dividing line so that it's easier to see which events + // get received in which order in the one frame. + Log::debug("PlayerController", "irr_driver", "-------------------------------------\n"); + } + + // Don't do steering if it's replay. In position only replay it doesn't + // matter, but if it's physics replay the gradual steering causes + // incorrect results, since the stored values are already adjusted. + if (!history->replayHistory()) + steer(dt, m_steer_val); + + if (World::getWorld()->isStartPhase()) + { + if (m_controls->m_accel || m_controls->m_brake || + m_controls->m_fire || m_controls->m_nitro) + { + // Only give penalty time in SET_PHASE. + // Penalty time check makes sure it doesn't get rendered on every + // update. + if (m_penalty_time == 0.0 && + World::getWorld()->getPhase() == WorldStatus::SET_PHASE) + { + RaceGUIBase* m=World::getWorld()->getRaceGUI(); + if (m) + { + m->addMessage(_("Penalty time!!"), m_kart, 2.0f, + video::SColor(255, 255, 128, 0)); + m->addMessage(_("Don't accelerate before go"), m_kart, 2.0f, + video::SColor(255, 210, 100, 50)); + } + + m_penalty_time = stk_config->m_penalty_time; + } // if penalty_time = 0 + + m_controls->m_brake = false; + m_controls->m_accel = 0.0f; + } // if key pressed + + return; + } // if isStartPhase + + if (m_penalty_time>0.0) + { + m_penalty_time-=dt; + return; + } + + // We can't restrict rescue to fulfil isOnGround() (which would be more like + // MK), since e.g. in the City track it is possible for the kart to end + // up sitting on a brick wall, with all wheels in the air :(( + // Only accept rescue if there is no kart animation is already playing + // (e.g. if an explosion happens, wait till the explosion is over before + // starting any other animation). + if ( m_controls->m_rescue && !m_kart->getKartAnimation() ) + { + new RescueAnimation(m_kart); + m_controls->m_rescue=false; + } +} // update + +//----------------------------------------------------------------------------- +/** Checks if the kart was overtaken, and if so plays a sound +*/ +void NetworkPlayerController::setPosition(int p) +{ + if(m_kart->getPosition()getNumKarts(); i++ ) + { + AbstractKart *kart = world->getKart(i); + if(kart->getPosition() == p + 1) + { + kart->beep(); + break; + } + } + } +} // setPosition + +//----------------------------------------------------------------------------- +/** Called when a kart finishes race. + * /param time Finishing time for this kart. + d*/ +void NetworkPlayerController::finishedRace(float time) +{ + +} // finishedRace + +//----------------------------------------------------------------------------- +/** Called when a kart hits or uses a zipper. + */ +void NetworkPlayerController::handleZipper(bool play_sound) +{ + m_kart->showZipperFire(); +} // handleZipper + +//----------------------------------------------------------------------------- +/** Called when a kart hits an item. + * \param item Item that was collected. + * \param add_info Additional info to be used then handling the item. If + * this is -1 (default), the item type is selected + * randomly. Otherwise it contains the powerup or + * attachment for the kart. This is used in network mode to + * let the server determine the powerup/attachment for + * the clients. + */ +void NetworkPlayerController::collectedItem(const Item &item, int add_info, float old_energy) +{ + +} // collectedItem diff --git a/src/karts/controller/network_player_controller.hpp b/src/karts/controller/network_player_controller.hpp new file mode 100644 index 000000000..fba1f70ef --- /dev/null +++ b/src/karts/controller/network_player_controller.hpp @@ -0,0 +1,48 @@ +#ifndef NETWORK_PLAYER_CONTROLLER_HPP +#define NETWORK_PLAYER_CONTROLLER_HPP + +#include "karts/controller/controller.hpp" + +class AbstractKart; +class Player; + +class NetworkPlayerController : public Controller +{ +protected: + int m_steer_val, m_steer_val_l, m_steer_val_r; + int m_prev_accel; + bool m_prev_brake; + bool m_prev_nitro; + + float m_penalty_time; + + void steer(float, int); +public: + NetworkPlayerController (AbstractKart *kart, + StateManager::ActivePlayer *_player); + virtual ~NetworkPlayerController (); + void update (float); + void action (PlayerAction action, int value); + void handleZipper (bool play_sound); + void collectedItem (const Item &item, int add_info=-1, + float previous_energy=0); + virtual void skidBonusTriggered(); + virtual void setPosition (int p); + virtual bool isPlayerController() const { return false; } + virtual bool isNetworkController() const { return true; } + virtual void reset (); + void resetInputState (); + virtual void finishedRace (float time); + virtual void crashed (const AbstractKart *k) {} + virtual void crashed (const Material *m) {} + // ------------------------------------------------------------------------ + /** Callback whenever a new lap is triggered. Used by the AI + * to trigger a recomputation of the way to use, not used for players. */ + virtual void newLap(int lap) {} + // ------------------------------------------------------------------------ + /** Player will always be able to get a slipstream bonus. */ + virtual bool disableSlipstreamBonus() const { return false; } + +}; + +#endif // NETWORK_PLAYER_CONTROLLER_HPP diff --git a/src/karts/controller/player_controller.cpp b/src/karts/controller/player_controller.cpp index f58b72902..258556b08 100644 --- a/src/karts/controller/player_controller.cpp +++ b/src/karts/controller/player_controller.cpp @@ -35,6 +35,7 @@ #include "karts/skidding.hpp" #include "karts/rescue_animation.hpp" #include "modes/world.hpp" +#include "network/network_world.hpp" #include "race/history.hpp" #include "states_screens/race_gui_base.hpp" #include "utils/constants.hpp" @@ -218,6 +219,10 @@ void PlayerController::action(PlayerAction action, int value) default: break; } + if (NetworkWorld::getInstance()->isRunning()) + { + NetworkWorld::getInstance()->controllerAction(this, action, value); + } } // action diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index ef3158c93..b75800b79 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -54,7 +54,6 @@ #include "items/powerup.hpp" #include "modes/linear_world.hpp" #include "modes/profile_world.hpp" -#include "network/network_manager.hpp" #include "race/race_manager.hpp" #include "tracks/quad_graph.hpp" #include "tracks/track.hpp" @@ -297,13 +296,6 @@ void SkiddingAI::update(float dt) return; #endif - // The client does not do any AI computations. - if(network_manager->getMode()==NetworkManager::NW_CLIENT) - { - AIBaseController::update(dt); - return; - } - // If the kart needs to be rescued, do it now (and nothing else) if(isStuck() && !m_kart->getKartAnimation()) { diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index aa2c9b0dd..62d3f0e69 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -57,8 +57,6 @@ #include "karts/max_speed.hpp" #include "karts/skidding.hpp" #include "modes/linear_world.hpp" -#include "network/race_state.hpp" -#include "network/network_manager.hpp" #include "physics/btKart.hpp" #include "physics/btKartRaycast.hpp" #include "physics/btUprightConstraint.hpp" @@ -878,15 +876,6 @@ void Kart::collectedItem(Item *item, int add_info) default : break; } // switch TYPE - // Attachments and powerups are stored in the corresponding - // functions (hit{Red,Green}Item), so only coins need to be - // stored here. - if(network_manager->getMode()==NetworkManager::NW_SERVER && - (type==Item::ITEM_NITRO_BIG || type==Item::ITEM_NITRO_SMALL) ) - { - race_state->itemCollected(getWorldKartId(), item->getItemId()); - } - if ( m_collected_energy > m_kart_properties->getNitroMax()) m_collected_energy = m_kart_properties->getNitroMax(); m_controller->collectedItem(*item, add_info, old_energy); @@ -1018,14 +1007,6 @@ void Kart::update(float dt) m_slipstream->update(dt); - // Store the actual kart controls at the start of update in the server - // state. This makes it easier to reset some fields when they are not used - // anymore (e.g. controls.fire). - if(network_manager->getMode()==NetworkManager::NW_SERVER) - { - race_state->storeKartControls(*this); - } - if (!m_flying) { // When really on air, free fly, when near ground, try to glide / adjust for landing @@ -1884,10 +1865,8 @@ void Kart::updatePhysics(float dt) updateSliding(); - // Only compute the current speed if this is not the client. On a client the - // speed is actually received from the server. - if(network_manager->getMode()!=NetworkManager::NW_CLIENT) - m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length(); + // Compute the speed of the kart. + m_speed = getVehicle()->getRigidBody()->getLinearVelocity().length(); // calculate direction of m_speed const btTransform& chassisTrans = getVehicle()->getChassisWorldTransform(); diff --git a/src/karts/moveable.hpp b/src/karts/moveable.hpp index 4461620ee..d6bafea84 100644 --- a/src/karts/moveable.hpp +++ b/src/karts/moveable.hpp @@ -31,13 +31,14 @@ using namespace irr; #include "physics/user_pointer.hpp" #include "utils/no_copy.hpp" #include "utils/vec3.hpp" +#include "network/types.hpp" class Material; /** * \ingroup karts */ -class Moveable: public NoCopy +class Moveable: public NoCopy, public CallbackObject { private: btVector3 m_velocityLC; /**setMode(NetworkManager::NW_SERVER); UserConfigParams::m_server_port = n; } else if( !strcmp(argv[i], "--server") ) { - network_manager->setMode(NetworkManager::NW_SERVER); + NetworkManager::getInstance(); + Log::info("main", "Creating a server network manager."); } - else if( sscanf(argv[i], "--port=%d", &n) ) + else if( sscanf(argv[i], "--max-players=%d", &n) ) { - UserConfigParams::m_server_port=n; + UserConfigParams::m_server_max_players=n; } - else if( sscanf(argv[i], "--client=%1023s", s) ) + else if( sscanf(argv[i], "--login=%1023s", s) ) { - network_manager->setMode(NetworkManager::NW_CLIENT); - UserConfigParams::m_server_address=s; + login = s; + try_login = true; + } + else if( sscanf(argv[i], "--password=%1023s", s) ) + { + password = s; } else if ( sscanf(argv[i], "--gfx=%d", &n) ) { @@ -981,6 +993,9 @@ int handleCmdLine(int argc, char **argv) race_manager->setNumLaps(999999); // profile end depends on time } else if( !strcmp(argv[i], "--no-graphics") ) + { + } + else if( !strcmp(argv[i], "--with-profile") ) { // Set default profile mode of 1 lap if we haven't already set one if (!ProfileWorld::isProfileMode()) { @@ -1092,6 +1107,19 @@ int handleCmdLine(int argc, char **argv) UserConfigParams::m_music = false;// and music when profiling } + if (try_login) + { + irr::core::stringw s; + Online::CurrentUser::SignInRequest* request = + Online::CurrentUser::get()->requestSignIn(login, password, false, false); + Online::HTTPManager::get()->synchronousRequest(request); + + if (request->isSuccess()) + { + Log::info("Main", "Logged in from command line."); + } + } + return 1; } // handleCmdLine @@ -1173,7 +1201,6 @@ void initRest() powerup_manager = new PowerupManager (); attachment_manager = new AttachmentManager (); highscore_manager = new HighscoreManager (); - network_manager = new NetworkManager (); KartPropertiesManager::addKartSearchDir( file_manager->getAddonsFile("karts/")); track_manager->addTrackSearchDir( @@ -1215,6 +1242,7 @@ void cleanSuperTuxKart() if(Online::HTTPManager::isRunning()) Online::HTTPManager::get()->stopNetworkThread(); //delete in reverse order of what they were created in. + //delete in reverse order of what they were created in. //see InitTuxkart() Referee::cleanup(); if(ReplayPlay::get()) ReplayPlay::destroy(); @@ -1222,7 +1250,8 @@ void cleanSuperTuxKart() INetworkHttp::destroy(); if(news_manager) delete news_manager; if(addons_manager) delete addons_manager; - if(network_manager) delete network_manager; + NetworkManager::kill(); + if(grand_prix_manager) delete grand_prix_manager; if(highscore_manager) delete highscore_manager; if(attachment_manager) delete attachment_manager; @@ -1365,6 +1394,15 @@ int main(int argc, char *argv[] ) //handleCmdLine() needs InitTuxkart() so it can't be called first if(!handleCmdLine(argc, argv)) exit(0); + // load the network manager + // If the server has been created (--server option), this will do nothing (just a warning): + NetworkManager::getInstance(); + NetworkManager::getInstance()->run(); + if (NetworkManager::getInstance()->isServer()) + { + ProtocolManager::getInstance()->requestStart(new ServerLobbyRoomProtocol()); + } + addons_manager->checkInstalledAddons(); // Load addons.xml to get info about addons even when not @@ -1377,6 +1415,20 @@ int main(int argc, char *argv[] ) } } + // no graphics, and no profile mode + if (ProfileWorld::isNoGraphics() && !ProfileWorld::isProfileMode()) + { + // hack to have a running game slot : + PtrVector& players = UserConfigParams::m_all_players; + if (UserConfigParams::m_default_player.toString().size() > 0) + for (int n=0; nsetCurrentSlot(players[n].getUniqueID()); + + main_loop->run(); + throw "salut"; + } + if(!UserConfigParams::m_no_start_screen) { StateManager::get()->pushScreen(StoryModeLobbyScreen::getInstance()); @@ -1439,7 +1491,7 @@ int main(int argc, char *argv[] ) // Create player and associate player with keyboard StateManager::get()->createActivePlayer( - UserConfigParams::m_all_players.get(0), device ); + UserConfigParams::m_all_players.get(0), device, NULL); if (kart_properties_manager->getKart(UserConfigParams::m_default_kart) == NULL) { @@ -1480,7 +1532,7 @@ int main(int argc, char *argv[] ) { // This will setup the race manager etc. history->Load(); - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startNew(false); main_loop->run(); // well, actually run() will never return, since @@ -1489,20 +1541,6 @@ int main(int argc, char *argv[] ) exit(-3); } - // Initialise connection in case that a command line option was set - // configuring a client or server. Otherwise this function does nothing - // here (and will be called again from the network gui). - if(!network_manager->initialiseConnections()) - { - Log::error("main", "Problems initialising network connections,\n" - "Running in non-network mode."); - } - // On the server start with the network information page for now - if(network_manager->getMode()==NetworkManager::NW_SERVER) - { - // TODO - network menu - //menu_manager->pushMenu(MENUID_NETWORK_GUI); - } // Not replaying // ============= if(!ProfileWorld::isProfileMode()) @@ -1512,7 +1550,7 @@ int main(int argc, char *argv[] ) // Quickstart (-N) // =============== // all defaults are set in InitTuxkart() - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startNew(false); } } @@ -1522,7 +1560,7 @@ int main(int argc, char *argv[] ) // ========= race_manager->setMajorMode (RaceManager::MAJOR_MODE_SINGLE); race_manager->setDifficulty(RaceManager::DIFFICULTY_HARD); - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startNew(false); } main_loop->run(); diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 8524dbed0..c8ec85ee9 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -29,7 +29,8 @@ #include "input/wiimote_manager.hpp" #include "modes/profile_world.hpp" #include "modes/world.hpp" -#include "network/network_manager.hpp" +#include "network/protocol_manager.hpp" +#include "network/network_world.hpp" #include "race/race_manager.hpp" #include "states_screens/state_manager.hpp" #include "utils/profiler.hpp" @@ -73,9 +74,9 @@ float MainLoop::getLimitedDt() // Throttle fps if more than maximum, which can reduce // the noise the fan on a graphics card makes. // When in menus, reduce FPS much, it's not necessary to push to the maximum for plain menus - const int max_fps = (StateManager::get()->throttleFPS() ? 35 : UserConfigParams::m_max_fps); + const int max_fps = 35;//(StateManager::get()->throttleFPS() ? 35 : UserConfigParams::m_max_fps); const int current_fps = (int)(1000.0f/dt); - if( current_fps > max_fps && !ProfileWorld::isNoGraphics()) + if( current_fps > max_fps && !ProfileWorld::isProfileMode()) { int wait_time = 1000/max_fps - 1000/current_fps; if(wait_time < 1) wait_time = 1; @@ -94,22 +95,12 @@ float MainLoop::getLimitedDt() */ void MainLoop::updateRace(float dt) { - // Server: Send the current position and previous controls to all clients - // Client: send current controls to server - // But don't do this if the race is in finish phase (otherwise - // messages can be mixed up in the race manager) - if(!World::getWorld()->isFinishPhase()) - network_manager->sendUpdates(); if(ProfileWorld::isProfileMode()) dt=1.0f/60.0f; - // Again, only receive updates if the race isn't over - once the - // race results are displayed (i.e. game is in finish phase) - // messages must be handled by the normal update of the network - // manager - if(!World::getWorld()->isFinishPhase()) - network_manager->receiveUpdates(); - - World::getWorld()->updateWorld(dt); + if (NetworkWorld::getInstance()->isRunning()) + NetworkWorld::getInstance()->update(dt); + else + World::getWorld()->updateWorld(dt); } // updateRace //----------------------------------------------------------------------------- @@ -127,13 +118,8 @@ void MainLoop::run() m_prev_time = m_curr_time; float dt = getLimitedDt(); - network_manager->update(dt); - if (World::getWorld()) // race is active if world exists { - // Busy wait if race_manager is active (i.e. creating of world is done) - // till all clients have reached this state. - if (network_manager->getState()==NetworkManager::NS_READY_SET_GO_BARRIER) continue; updateRace(dt); } // if race is active @@ -162,7 +148,20 @@ void MainLoop::run() PROFILER_PUSH_CPU_MARKER("IrrDriver update", 0x00, 0x00, 0x7F); irr_driver->update(dt); PROFILER_POP_CPU_MARKER(); + + PROFILER_PUSH_CPU_MARKER("Protocol manager update", 0x7F, 0x00, 0x7F); + ProtocolManager::getInstance()->update(); + PROFILER_POP_CPU_MARKER(); + + PROFILER_SYNC_FRAME(); } + else if (!m_abort && ProfileWorld::isNoGraphics()) + { + PROFILER_PUSH_CPU_MARKER("Protocol manager update", 0x7F, 0x00, 0x7F); + ProtocolManager::getInstance()->update(); + PROFILER_POP_CPU_MARKER(); + } + PROFILER_SYNC_FRAME(); PROFILER_POP_CPU_MARKER(); } // while !m_exit diff --git a/src/modes/cutscene_world.cpp b/src/modes/cutscene_world.cpp index ab56220d9..2646d3ea1 100644 --- a/src/modes/cutscene_world.cpp +++ b/src/modes/cutscene_world.cpp @@ -35,7 +35,7 @@ #include "physics/physics.hpp" #include "states_screens/credits.hpp" #include "states_screens/cutscene_gui.hpp" -#include "states_screens/kart_selection.hpp" +#include "states_screens/offline_kart_selection.hpp" #include "states_screens/main_menu_screen.hpp" #include "tracks/track.hpp" #include "tracks/track_object.hpp" @@ -384,7 +384,7 @@ void CutsceneWorld::enterRaceOverState() slot->setFirstTime(false); unlock_manager->save(); - KartSelectionScreen* s = KartSelectionScreen::getInstance(); + KartSelectionScreen* s = OfflineKartSelectionScreen::getInstance(); s->setMultiplayer(false); s->setGoToOverworldNext(); StateManager::get()->pushScreen( s ); diff --git a/src/modes/demo_world.cpp b/src/modes/demo_world.cpp index 681382e7e..35a91def1 100644 --- a/src/modes/demo_world.cpp +++ b/src/modes/demo_world.cpp @@ -21,7 +21,6 @@ #include "guiengine/modaldialog.hpp" #include "input/device_manager.hpp" #include "input/input_manager.hpp" -#include "network/network_manager.hpp" #include "race/race_manager.hpp" #include "tracks/track.hpp" #include "tracks/track_manager.hpp" @@ -141,7 +140,7 @@ bool DemoWorld::updateIdleTimeAndStartDemo(float dt) // Use keyboard 0 by default in --no-start-screen device = input_manager->getDeviceList()->getKeyboard(0); StateManager::get()->createActivePlayer( - UserConfigParams::m_all_players.get(0), device ); + UserConfigParams::m_all_players.get(0), device , NULL); // ASSIGN should make sure that only input from assigned devices // is read. input_manager->getDeviceList()->setAssignMode(ASSIGN); @@ -149,7 +148,7 @@ bool DemoWorld::updateIdleTimeAndStartDemo(float dt) m_do_demo = true; race_manager->setNumKarts(m_num_karts); race_manager->setLocalKartInfo(0, "tux"); - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startSingleRace(m_demo_tracks[0], m_num_laps, false); m_demo_tracks.push_back(m_demo_tracks[0]); m_demo_tracks.erase(m_demo_tracks.begin()); diff --git a/src/modes/linear_world.cpp b/src/modes/linear_world.cpp index c519703c1..81cadfa95 100644 --- a/src/modes/linear_world.cpp +++ b/src/modes/linear_world.cpp @@ -25,7 +25,6 @@ #include "karts/abstract_kart.hpp" #include "karts/controller/controller.hpp" #include "karts/kart_properties.hpp" -#include "network/network_manager.hpp" #include "physics/physics.hpp" #include "race/history.hpp" #include "states_screens/race_gui_base.hpp" @@ -319,13 +318,7 @@ void LinearWorld::newLap(unsigned int kart_index) // Race finished if(kart_info.m_race_lap >= race_manager->getNumLaps() && raceHasLaps()) { - // A client does not detect race finished by itself, it will - // receive a message from the server. So a client does not do - // anything here. - if(network_manager->getMode()!=NetworkManager::NW_CLIENT) - { - kart->finishedRace(getTime()); - } + kart->finishedRace(getTime()); } float time_per_lap; if (kart_info.m_race_lap == 1) // just completed first lap diff --git a/src/modes/overworld.cpp b/src/modes/overworld.cpp index 65cdf8f10..f6d420c57 100644 --- a/src/modes/overworld.cpp +++ b/src/modes/overworld.cpp @@ -27,9 +27,8 @@ #include "karts/rescue_animation.hpp" #include "modes/overworld.hpp" #include "physics/physics.hpp" -#include "network/network_manager.hpp" #include "states_screens/dialogs/select_challenge.hpp" -#include "states_screens/kart_selection.hpp" +#include "states_screens/offline_kart_selection.hpp" #include "states_screens/race_gui_overworld.hpp" #include "tracks/track.hpp" @@ -64,7 +63,7 @@ void OverWorld::enterOverWorld() // Create player and associate player with keyboard StateManager::get()->createActivePlayer(unlock_manager->getCurrentPlayer(), - device); + device, NULL); if (!kart_properties_manager->getKart(UserConfigParams::m_default_kart)) { @@ -83,7 +82,7 @@ void OverWorld::enterOverWorld() ->setSinglePlayer( StateManager::get()->getActivePlayer(0) ); StateManager::get()->enterGameState(); - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startNew(false); if(race_manager->haveKartLastPositionOnOverworld()){ OverWorld *ow = (OverWorld*)World::getWorld(); @@ -129,7 +128,7 @@ void OverWorld::update(float dt) m_return_to_garage = false; delayedSelfDestruct(); race_manager->exitRace(false); - KartSelectionScreen* s = KartSelectionScreen::getInstance(); + KartSelectionScreen* s = OfflineKartSelectionScreen::getInstance(); s->setMultiplayer(false); s->setFromOverworld(true); StateManager::get()->resetAndGoToScreen(s); diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 92e76e05f..69862551b 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -38,12 +38,11 @@ #include "karts/controller/player_controller.hpp" #include "karts/controller/end_controller.hpp" #include "karts/controller/skidding_ai.hpp" +#include "karts/controller/network_player_controller.hpp" #include "karts/kart.hpp" #include "karts/kart_properties_manager.hpp" #include "modes/overworld.hpp" #include "modes/profile_world.hpp" -#include "network/network_manager.hpp" -#include "network/race_state.hpp" #include "physics/btKart.hpp" #include "physics/physics.hpp" #include "physics/triangle_mesh.hpp" @@ -79,8 +78,8 @@ World* World::m_world = NULL; * of all karts is set (i.e. in a normal race the arrival time for karts * will be estimated), highscore is updated, and the race result gui * is being displayed. - * Rescuing is handled via the three functions: - * getNumberOfRescuePositions() - which returns the number of rescue + * Rescuing is handled via the three functions: + * getNumberOfRescuePositions() - which returns the number of rescue * positions defined. * getRescuePositionIndex(AbstractKart *kart) - which determines the * index of the rescue position to be used for the given kart. @@ -133,7 +132,6 @@ World::World() : WorldStatus(), m_clear_color(255,100,101,140) */ void World::init() { - race_state = new RaceState(); m_faster_music_active = false; m_fastest_kart = 0; m_eliminated_karts = 0; @@ -189,8 +187,6 @@ void World::init() if(ReplayPlay::get()) ReplayPlay::get()->Load(); - network_manager->worldLoaded(); - powerup_manager->updateWeightsForRace(num_karts); } // init @@ -300,11 +296,10 @@ AbstractKart *World::createKart(const std::string &kart_ident, int index, m_num_players ++; break; case RaceManager::KT_NETWORK_PLAYER: - break; // Avoid compiler warning about enum not handled. - //controller = new NetworkController(kart_ident, position, init_pos, - // global_player_id); - //m_num_players++; - //break; + controller = new NetworkPlayerController(new_kart, + StateManager::get()->getActivePlayer(local_player_id)); + m_num_players++; + break; case RaceManager::KT_AI: controller = loadAIController(new_kart); break; @@ -375,7 +370,6 @@ World::~World() // gui and this must be deleted. delete m_race_gui; } - delete race_state; for ( unsigned int i = 0 ; i < m_karts.size() ; i++ ) delete m_karts[i]; @@ -491,7 +485,7 @@ void World::resetAllKarts() // Loop over all karts, in case that some karts are dfferent for(unsigned int kart_id=0; kart_idgetBody()->setCenterOfMassTransform(pos); // Project kart to surface of track - // This will set the physics transform + // This will set the physics transform m_track->findGround(kart); } // moveKartTo @@ -748,13 +742,12 @@ void World::updateWorld(float dt) InputDevice* device = input_manager->getDeviceList()->getKeyboard(0); // Create player and associate player with keyboard - StateManager::get() - ->createActivePlayer(unlock_manager->getCurrentPlayer(), - device); + StateManager::get()->createActivePlayer(unlock_manager->getCurrentPlayer(), + device, NULL); if (!kart_properties_manager->getKart(UserConfigParams::m_default_kart)) { - Log::warn("World", + Log::warn("World", "Cannot find kart '%s', will revert to default.", UserConfigParams::m_default_kart.c_str()); UserConfigParams::m_default_kart.revertToDefaults(); @@ -768,7 +761,7 @@ void World::updateWorld(float dt) ->setSinglePlayer( StateManager::get()->getActivePlayer(0) ); StateManager::get()->enterGameState(); - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startNew(true); } else @@ -821,11 +814,8 @@ void World::update(float dt) if(ReplayPlay::get()) ReplayPlay::get()->update(dt); if(history->replayHistory()) dt=history->getNextDelta(); WorldStatus::update(dt); - // Clear race state so that new information can be stored - race_state->clear(); - if(network_manager->getMode()!=NetworkManager::NW_CLIENT && - !history->dontDoPhysics()) + if (!history->dontDoPhysics()) { m_physics->update(dt); } @@ -988,7 +978,8 @@ AbstractKart *World::getPlayerKart(unsigned int n) const unsigned int count=-1; for(unsigned int i=0; igetController()->isPlayerController()) + if(m_karts[i]->getController()->isPlayerController() || + m_karts[i]->getController()->isNetworkController()) { count++; if(count==n) return m_karts[i]; diff --git a/src/modes/world.hpp b/src/modes/world.hpp index c981c960c..d6aaffd95 100644 --- a/src/modes/world.hpp +++ b/src/modes/world.hpp @@ -282,6 +282,9 @@ public: assert(kartId >= 0 && kartId < int(m_karts.size())); return m_karts[kartId]; } // ------------------------------------------------------------------------ + /** Returns all karts. */ + KartList getKarts() const { return m_karts; } + // ------------------------------------------------------------------------ /** Returns the number of currently active (i.e.non-elikminated) karts. */ unsigned int getCurrentNumKarts() const { return (int)m_karts.size() - m_eliminated_karts; } diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp index 522d0e01e..294009f1e 100644 --- a/src/modes/world_status.cpp +++ b/src/modes/world_status.cpp @@ -26,7 +26,6 @@ #include "karts/abstract_kart.hpp" #include "modes/world.hpp" #include "tracks/track.hpp" -#include "network/network_manager.hpp" #include @@ -112,8 +111,6 @@ void WorldStatus::enterRaceOverState() */ void WorldStatus::terminateRace() { - if(network_manager->getMode()==NetworkManager::NW_SERVER) - network_manager->sendRaceResults(); } // terminateRace //----------------------------------------------------------------------------- diff --git a/src/network/character_confirm_message.hpp b/src/network/character_confirm_message.hpp deleted file mode 100644 index 527794928..000000000 --- a/src/network/character_confirm_message.hpp +++ /dev/null @@ -1,71 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_CHARACTER_CONFIRM_MESSAGE_HPP -#define HEADER_CHARACTER_CONFIRM_MESSAGE_HPP - -#include - -#include "network/message.hpp" - - -/** This message is from the server to all clients to inform them about a - * newly selected character. This means that this character is not available - * anymore. The message contains the hostid of the client who selected this - * character (0 in case of server), so that this message acts as a - * confirmation for the corresponding client (or a reject if the message has - * a different hostid, meaning that another client selected the character - * earlier). - */ -class CharacterConfirmMessage : public Message -{ -private: - /** The host id. */ - int m_host_id; - /** Name of the selected kart. */ - std::string m_kart_name; -public: - /** Constructor, takes the name of the kart name and the host id. - * \param kart_name Name of the kart. - * \param host_id Id of the host who selected this character. - */ - CharacterConfirmMessage(const std::string &kart_name, int host_id) - : Message(Message::MT_CHARACTER_CONFIRM) - { - allocate(getStringLength(kart_name) + getCharLength()); - addString(kart_name); - addChar(host_id); - } // CharacterConfirmMessage - - // ------------------------------------------------------------------------ - /** Unpacks a character confirm message. - * \param pkt Received enet packet. - */ - CharacterConfirmMessage(ENetPacket* pkt):Message(pkt, MT_CHARACTER_CONFIRM) - { - m_kart_name = getString(); - m_host_id = getChar(); - } // CharacterConfirmMessage(EnetPacket) - // ------------------------------------------------------------------------ - /** Returns the kart name contained in a received message. */ - const std::string &getKartName() const { return m_kart_name; } - /** Returns the host id contained in a received message. */ - int getHostId() const { return m_host_id; } - -}; // CharacterConfirmMessage -#endif diff --git a/src/network/character_info_message.hpp b/src/network/character_info_message.hpp deleted file mode 100644 index 443a2d670..000000000 --- a/src/network/character_info_message.hpp +++ /dev/null @@ -1,50 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_CHARACTER_INFO_MESSAGE_HPP -#define HEADER_CHARACTER_INFO_MESSAGE_HPP - -#include "karts/kart_properties_manager.hpp" -#include "network/message.hpp" - -/** This message is sent from the server to the clients and contains the list - * of available characters. Additionally, it contains the clients id. - */ -class CharacterInfoMessage : public Message -{ -// Add the remote host id to this message (to avoid sending this separately) -public: - CharacterInfoMessage(int hostid) : Message(Message::MT_CHARACTER_INFO) - { - std::vector all_karts = - kart_properties_manager->getAllAvailableKarts(); - allocate(getCharLength()+getStringVectorLength(all_karts)); - addChar(hostid); - addStringVector(all_karts); - } - // ------------------------------------------------------------------------ - CharacterInfoMessage(ENetPacket* pkt):Message(pkt, MT_CHARACTER_INFO) - { - int hostid=getChar(); - network_manager->setHostId(hostid); - std::vector all_karts; - all_karts = getStringVector(); - kart_properties_manager->setUnavailableKarts(all_karts); - } -}; // CharacterInfoMessage -#endif diff --git a/src/network/character_selected_message.hpp b/src/network/character_selected_message.hpp deleted file mode 100644 index 9e8a61961..000000000 --- a/src/network/character_selected_message.hpp +++ /dev/null @@ -1,105 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_CHARACTER_SELECTED_MESSAGE_HPP -#define HEADER_CHARACTER_SELECTED_MESSAGE_HPP - -#include "network/message.hpp" -#include "network/remote_kart_info.hpp" -#include "race/race_manager.hpp" - -/** This message is send contains information about selected karts. It is send - * from the client to the server to indicate a selected kart, and from the - * server to the clients to indicate that a kart was selected. In the latter - * case it contains the hostid of the successful selecter. This way a client - * selecting a kart can check if its selection was successful or not, and - * other clients are informed that a certain kart is not available anymore. - */ -class CharacterSelectedMessage : public Message -{ -private: - /** Number of local players on a host. If the message is send from the - * server to the clients, this field instead contains the host id of - * the host which selected the kart - */ - int m_num_local_players; - /** Stores information about the selected kart. */ - RemoteKartInfo m_kart_info; - -public: - /** Contains information about a selected kart. When send from the client - * to the server, it contains the number of local players (which - * technically needs only to be sent once); when send from from the server - * to the clients this field instead contains the host id of the host - * selected the character. This allows the client to detect if a selected - * kart was not confirmed by the server (i.e. another client or the server - * has selected the kart first - * \param player_id The local player id. - * \param host_id If this value is specified (>-1), then this value is - * used in the message instead of the number of local - * players. - */ - CharacterSelectedMessage(int player_id, int host_id=-1) - : Message(Message::MT_CHARACTER_INFO) - { - m_kart_info = race_manager->getLocalKartInfo(player_id); - m_num_local_players = race_manager->getNumLocalPlayers(); - - allocate(getCharLength() // m_kart_info.getLocalPlayerId()) - +getStringLength(m_kart_info.getKartName()) - +m_kart_info.getPlayerName().size() + 1 // FIXME: encoding issues - +getCharLength()); // m_num_local_players) - addChar(m_kart_info.getLocalPlayerId()); - addString(m_kart_info.getKartName()); - addString(core::stringc(m_kart_info.getPlayerName().c_str()).c_str()); // FIXME: encoding issues - // Piggy backing this information saves sending it as a separate - // message. It is actually only required in the first message - if(host_id>-1) - addChar(host_id); - else - addChar(race_manager->getNumLocalPlayers()); - } // CharacterSelectedMessage - - // ------------------------------------------------------------------------ - /** Unpacks a character selected message. The additional field is either - * the number of local players (when send from client to server), or the - * hostid of the host selected the character. - * \param pkt Received enet packet. - */ - CharacterSelectedMessage(ENetPacket* pkt):Message(pkt, MT_CHARACTER_INFO) - { - m_kart_info.setLocalPlayerId(getChar()); - m_kart_info.setKartName(getString()); - m_kart_info.setPlayerName(core::stringw(getString().c_str())); // FIXME: encoding issues - m_num_local_players = getChar(); - } // CharacterSelectedMessage(EnetPacket) - - // ------------------------------------------------------------------------ - /** Returns the remote kart info structure of the selected kart. */ - const RemoteKartInfo& getKartInfo () const { return m_kart_info; } - - /** Returns the number of local players. */ - int getNumPlayers() const { return m_num_local_players; } - - /** Returns the host id of the host who selected the kart successfully. - * This information is actually stored in m_num_local_players field, which - * is used when a client receives this message. - */ - int getHostId () const { return m_num_local_players; } -}; // CharacterSelectedMessage -#endif diff --git a/src/network/client_network_manager.cpp b/src/network/client_network_manager.cpp new file mode 100644 index 000000000..22a22f186 --- /dev/null +++ b/src/network/client_network_manager.cpp @@ -0,0 +1,135 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/client_network_manager.hpp" + +#include "network/protocols/get_public_address.hpp" +#include "network/protocols/hide_public_address.hpp" +#include "network/protocols/show_public_address.hpp" +#include "network/protocols/get_peer_address.hpp" +#include "network/protocols/connect_to_server.hpp" +#include "network/protocols/client_lobby_room_protocol.hpp" +#include "network/protocols/synchronization_protocol.hpp" + +#include "utils/log.hpp" + +#include +#include +#include + +void* waitInput(void* data) +{ + std::string str = ""; + bool stop = false; + int n = 0; + + while(!stop) + { + getline(std::cin, str); + if (str == "quit") + { + stop = true; + } + else if (str == "disconnect") + { + NetworkManager::getInstance()->getPeers()[0]->disconnect(); + } + else if (str == "connect") + { + ProtocolManager::getInstance()->requestStart(new ConnectToServer()); + } + else if (str == "select") + { + std::string str2; + getline(std::cin, str2); + Protocol* protocol = ProtocolManager::getInstance()->getProtocol(PROTOCOL_LOBBY_ROOM); + ClientLobbyRoomProtocol* clrp = static_cast(protocol); + clrp->requestKartSelection(str2); + } + else if (str == "synchronize") + { + ProtocolManager::getInstance()->requestStart(new SynchronizationProtocol()); + } + else if (NetworkManager::getInstance()->getPeers().size() > 0) + { + NetworkString msg; + msg.ai8(0); + msg += str; + NetworkManager::getInstance()->getPeers()[0]->sendPacket(msg); + } + } + + exit(0); + + return NULL; +} + +ClientNetworkManager::ClientNetworkManager() +{ + m_thread_keyboard = NULL; + m_connected = false; +} + +ClientNetworkManager::~ClientNetworkManager() +{ +} + +void ClientNetworkManager::run() +{ + if (enet_initialize() != 0) + { + Log::error("ClientNetworkManager", "Could not initialize enet.\n"); + return; + } + m_localhost = new STKHost(); + m_localhost->setupClient(1, 2, 0, 0); + m_localhost->startListening(); + + Log::info("ClientNetworkManager", "Host initialized."); + + // listen keyboard console input + m_thread_keyboard = (pthread_t*)(malloc(sizeof(pthread_t))); + pthread_create(m_thread_keyboard, NULL, waitInput, NULL); + + NetworkManager::run(); + + Log::info("ClientNetworkManager", "Ready !"); +} + +void ClientNetworkManager::reset() +{ + NetworkManager::reset(); + + m_connected = false; + m_localhost = new STKHost(); + m_localhost->setupClient(1, 2, 0, 0); + m_localhost->startListening(); + +} + +void ClientNetworkManager::sendPacket(const NetworkString& data, bool reliable) +{ + if (m_peers.size() > 1) + Log::warn("ClientNetworkManager", "Ambiguous send of data.\n"); + m_peers[0]->sendPacket(data, reliable); +} + +STKPeer* ClientNetworkManager::getPeer() +{ + return m_peers[0]; +} diff --git a/src/network/client_network_manager.hpp b/src/network/client_network_manager.hpp new file mode 100644 index 000000000..ba4101f8c --- /dev/null +++ b/src/network/client_network_manager.hpp @@ -0,0 +1,50 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef CLIENT_NETWORK_MANAGER_HPP +#define CLIENT_NETWORK_MANAGER_HPP + +#include "network/network_manager.hpp" + +class ClientNetworkManager : public NetworkManager +{ + friend class Singleton; + public: + static ClientNetworkManager* getInstance() + { + return Singleton::getInstance(); + } + + virtual void run(); + virtual void reset(); + virtual void sendPacket(const NetworkString& data, bool reliable = true); + + STKPeer* getPeer(); + virtual bool isServer() { return false; } + void setConnected(bool value) { m_connected = value; } + bool isConnected() { return m_connected; } + + protected: + ClientNetworkManager(); + virtual ~ClientNetworkManager(); + + bool m_connected; //!< Is the user connected to a server + pthread_t* m_thread_keyboard; +}; + +#endif // CLIENT_NETWORK_MANAGER_HPP diff --git a/src/network/connect_message.cpp b/src/network/connect_message.cpp deleted file mode 100644 index 03660c158..000000000 --- a/src/network/connect_message.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/connect_message.hpp" - -#include -#include -#include -#ifndef WIN32 -# include -#endif - -#include "config/player.hpp" -#include "karts/kart_properties_manager.hpp" -#include "states_screens/state_manager.hpp" -#include "tracks/track_manager.hpp" - -// ---------------------------------------------------------------------------- -/** Creates the connect message. It includes the id of the client (currently - * player name @ hostname), and the list of available tracks. - */ -ConnectMessage::ConnectMessage() : Message(MT_CONNECT) -{ - setId(); - const std::vector &all_tracks = - track_manager->getAllTrackIdentifiers(); - std::vector all_karts = - kart_properties_manager->getAllAvailableKarts(); - allocate(getStringLength(m_id) + getStringVectorLength(all_tracks) - + getStringVectorLength(all_karts)); - addString(m_id); - addStringVector(all_tracks); - addStringVector(all_karts); -} // ConnectMessage - -// ---------------------------------------------------------------------------- -/** Unpacks a connect message. The id of the client is stored in this object, - * and the list of tracks is used to set tracks that are not available on - * the client to be 'unavailable' on the server. - * \param pkt Enet packet. - */ -ConnectMessage::ConnectMessage(ENetPacket* pkt):Message(pkt, MT_CONNECT) -{ - m_id = getString(); - std::vector all_tracks = getStringVector(); - std::vector all_karts = getStringVector(); - track_manager->setUnavailableTracks(all_tracks); - kart_properties_manager->setUnavailableKarts(all_karts); -} // ConnectMessage - -// ---------------------------------------------------------------------------- -/** Sets the id, i.e. player name @ hostname, of this client. - */ -void ConnectMessage::setId() -{ - char hostname[256]; - gethostname(hostname, 255); - const std::string& id = core::stringc(StateManager::get()->getActivePlayerProfile(0)->getName()).c_str(); - std::ostringstream o; - o << id << '@' << hostname; - m_id = o.str(); -} // ConnectMessage diff --git a/src/network/event.cpp b/src/network/event.cpp new file mode 100644 index 000000000..0f4835e6d --- /dev/null +++ b/src/network/event.cpp @@ -0,0 +1,99 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/event.hpp" +#include "network/network_manager.hpp" + +#include "utils/log.hpp" + +#include + +Event::Event(ENetEvent* event) +{ + switch (event->type) + { + case ENET_EVENT_TYPE_CONNECT: + type = EVENT_TYPE_CONNECTED; + break; + case ENET_EVENT_TYPE_DISCONNECT: + type = EVENT_TYPE_DISCONNECTED; + break; + case ENET_EVENT_TYPE_RECEIVE: + type = EVENT_TYPE_MESSAGE; + break; + case ENET_EVENT_TYPE_NONE: + return; + break; + } + if (type == EVENT_TYPE_MESSAGE) + { + data = NetworkString(std::string((char*)(event->packet->data), event->packet->dataLength-1)); + } + else if (event->data) + { + } + + m_packet = NULL; + if (event->packet) + m_packet = event->packet; + + if (m_packet) + enet_packet_destroy(m_packet); // we got all we need, just remove the data. + m_packet = NULL; + + std::vector peers = NetworkManager::getInstance()->getPeers(); + peer = new STKPeer*; + *peer = NULL; + for (unsigned int i = 0; i < peers.size(); i++) + { + if (peers[i]->m_peer == event->peer) + { + *peer = peers[i]; + Log::verbose("Event", "The peer you sought has been found on %lx", (long int)(peer)); + return; + } + } + if (*peer == NULL) // peer does not exist, create him + { + STKPeer* new_peer = new STKPeer(); + new_peer->m_peer = event->peer; + *peer = new_peer; + Log::debug("Event", "Creating a new peer, address are STKPeer:%lx, Peer:%lx", (long int)(new_peer), (long int)(event->peer)); + } +} + +Event::Event(const Event& event) +{ + m_packet = NULL; + data = event.data; + // copy the peer + peer = event.peer; + type = event.type; +} + +Event::~Event() +{ + peer = NULL; + m_packet = NULL; +} + +void Event::removeFront(int size) +{ + data.removeFront(size); +} + diff --git a/src/network/event.hpp b/src/network/event.hpp new file mode 100644 index 000000000..ce3a97629 --- /dev/null +++ b/src/network/event.hpp @@ -0,0 +1,76 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef EVENT_HPP +#define EVENT_HPP + +#include "network/stk_peer.hpp" +#include "network/network_string.hpp" +#include "utils/types.hpp" + +/*! + * \enum EVENT_TYPE + * \brief Represents a network event type. + */ +enum EVENT_TYPE +{ + EVENT_TYPE_CONNECTED, //!< A peer is connected + EVENT_TYPE_DISCONNECTED,//!< A peer is disconnected + EVENT_TYPE_MESSAGE //!< A message between server and client protocols +}; + +/*! + * \class Event + * \brief Class representing an event that need to pass trough the system. + * This is used to remove ENet dependency in the network. + * It interfaces the ENetEvent structure. + * The user has to be extremely careful about the peer. + * Indeed, when packets are logged, the state of the peer cannot be stored at + * all times, and then the user of this class can rely only on the address/port + * of the peer, and not on values that might change over time. + */ +class Event +{ + public: + /*! \brief Constructor + * \param event : The event that needs to be translated. + */ + Event(ENetEvent* event); + /*! \brief Constructor + * \param event : The event to copy. + */ + Event(const Event& event); + /*! \brief Destructor + * frees the memory of the ENetPacket. + */ + ~Event(); + + /*! \brief Remove bytes at the beginning of data. + * \param size : The number of bytes to remove. + */ + void removeFront(int size); + + EVENT_TYPE type; //!< Type of the event. + NetworkString data; //!< Copy of the data passed by the event. + STKPeer** peer; //!< Pointer to the peer that triggered that event. + + private: + ENetPacket* m_packet; //!< A pointer on the ENetPacket to be deleted. +}; + +#endif // EVENT_HPP diff --git a/src/network/flyable_info.hpp b/src/network/flyable_info.hpp deleted file mode 100644 index eedc33585..000000000 --- a/src/network/flyable_info.hpp +++ /dev/null @@ -1,70 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_FLYABLE_INFO_HPP -#define HEADER_FLYABLE_INFO_HPP - -#include "network/message.hpp" - -/** Class used to transfer information about projectiles from server to client. - * It contains only the coordinates, rotation, and explosion state. - */ -class FlyableInfo -{ -public: - Vec3 m_xyz; /** Position of object. */ - btQuaternion m_rotation; /** Orientation of object */ - bool m_exploded; /** If the object exploded in the current frame. */ - - /** Constructor to initialise all fields. - */ - FlyableInfo(const Vec3& xyz, const btQuaternion &rotation, bool exploded) : - m_xyz(xyz), m_rotation(rotation), m_exploded(exploded) - {}; - // ------------------------------------------------------------------------ - /** Allow this object to be stored in std::vector fields. - */ - FlyableInfo() {}; - // ------------------------------------------------------------------------ - /** Construct a FlyableInfo from a message (which is unpacked). - */ - FlyableInfo(Message *m) - { - m_xyz = m->getVec3(); - m_rotation = m->getQuaternion(); - m_exploded = m->getBool(); - } // FlyableInfo(Message) - // ------------------------------------------------------------------------ - /** Returns the length of the serialised message. */ - static int getLength() - { - return Message::getVec3Length() - + Message::getQuaternionLength() - + Message::getBoolLength(); - } // getLength - // ------------------------------------------------------------------------ - void serialise(Message *m) - { - m->addVec3(m_xyz); - m->addQuaternion(m_rotation); - m->addBool(m_exploded); - } // serialise -}; - -#endif - diff --git a/src/network/game_setup.cpp b/src/network/game_setup.cpp new file mode 100644 index 000000000..50f50c9cc --- /dev/null +++ b/src/network/game_setup.cpp @@ -0,0 +1,145 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/game_setup.hpp" + +#include "utils/log.hpp" + +//----------------------------------------------------------------------------- + +GameSetup::GameSetup() +{ +} + +//----------------------------------------------------------------------------- + +GameSetup::~GameSetup() +{ + // remove all players + for (unsigned int i = 0; i < m_players.size(); i++) + { + delete m_players[i]; + }; + m_players.clear(); +} + +//----------------------------------------------------------------------------- + +void GameSetup::addPlayer(NetworkPlayerProfile* profile) +{ + m_players.push_back(profile); + Log::verbose("GameSetup", "New player in the game setup. Global id : %u, " + "Race id : %d.", profile->user_profile->getUserID(), profile->race_id); +} + +//----------------------------------------------------------------------------- + +bool GameSetup::removePlayer(uint32_t id) +{ + for (unsigned int i = 0; i < m_players.size(); i++) + { + if (m_players[i]->user_profile->getUserID() == id) + { + delete m_players[i]; + m_players.erase(m_players.begin()+i, m_players.begin()+i+1); + Log::verbose("GameSetup", "Removed a player from the game setup. " + "Remains %u.", m_players.size()); + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- + +bool GameSetup::removePlayer(uint8_t id) +{ + for (unsigned int i = 0; i < m_players.size(); i++) + { + if (m_players[i]->race_id == id) // check the given id + { + delete m_players[i]; + m_players.erase(m_players.begin()+i, m_players.begin()+i+1); + Log::verbose("GameSetup", "Removed a player from the game setup. " + "Remains %u.", m_players.size()); + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- + +void GameSetup::setPlayerKart(uint8_t id, std::string kart_name) +{ + bool found = false; + for (unsigned int i = 0; i < m_players.size(); i++) + { + if (m_players[i]->race_id == id) + { + m_players[i]->kart_name = kart_name; + Log::info("GameSetup::setPlayerKart", "Player %d took kart %s", + id, kart_name.c_str()); + found = true; + } + } + if (!found) + { + Log::info("GameSetup::setPlayerKart", "The player %d was unknown.", id); + } +} + +//----------------------------------------------------------------------------- + +const NetworkPlayerProfile* GameSetup::getProfile(uint32_t id) +{ + for (unsigned int i = 0; i < m_players.size(); i++) + { + if (m_players[i]->user_profile->getUserID() == id) + { + return m_players[i]; + } + } + return NULL; +} + +//----------------------------------------------------------------------------- + +const NetworkPlayerProfile* GameSetup::getProfile(uint8_t id) +{ + for (unsigned int i = 0; i < m_players.size(); i++) + { + if (m_players[i]->race_id == id) + { + return m_players[i]; + } + } + return NULL; +} + +//----------------------------------------------------------------------------- + +bool GameSetup::isKartAvailable(std::string kart_name) +{ + for (unsigned int i = 0; i < m_players.size(); i++) + { + if (m_players[i]->kart_name == kart_name) + return false; + } + return true; +} diff --git a/src/network/game_setup.hpp b/src/network/game_setup.hpp new file mode 100644 index 000000000..c7ab6cb3e --- /dev/null +++ b/src/network/game_setup.hpp @@ -0,0 +1,72 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/*! \file game_setup.hpp + */ + +#ifndef GAME_SETUP_HPP +#define GAME_SETUP_HPP + +#include "online/user.hpp" + +#include +#include + +/*! \class PlayerProfile + * \brief Contains the profile of a player. + */ +class NetworkPlayerProfile +{ + public: + NetworkPlayerProfile() { race_id = 0; user_profile = NULL; } + ~NetworkPlayerProfile() {} + + uint8_t race_id; //!< The id of the player for the race + std::string kart_name; //!< The selected kart. + Online::User* user_profile; //!< Pointer to the lobby profile +}; + +/*! \class GameSetup + * \brief Used to store the needed data about the players that join a game. + * This class stores all the possible information about players in a lobby. + */ +class GameSetup +{ + public: + GameSetup(); + virtual ~GameSetup(); + + void addPlayer(NetworkPlayerProfile* profile); //!< Add a player. + bool removePlayer(uint32_t id); //!< Remove a player by id. + bool removePlayer(uint8_t id); //!< Remove a player by local id. + void setPlayerKart(uint8_t id, std::string kart_name); //!< Set the kart of a player + + std::vector getPlayers() { return m_players; } + int getPlayerCount() { return m_players.size(); } + const NetworkPlayerProfile* getProfile(uint32_t id); //!< Get a profile by database id + const NetworkPlayerProfile* getProfile(uint8_t id); //!< Get the profile by the lobby id + + bool isKartAvailable(std::string kart_name); + bool isKartAllowed(std::string kart_name) {return true; } + + protected: + std::vector m_players; //!< Information about players + NetworkPlayerProfile m_self_profile; //!< Information about self (client only) +}; + +#endif // GAME_SETUP_HPP diff --git a/src/network/http_functions.cpp b/src/network/http_functions.cpp new file mode 100644 index 000000000..8ff861a05 --- /dev/null +++ b/src/network/http_functions.cpp @@ -0,0 +1,67 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/http_functions.hpp" + +#include "utils/log.hpp" + +#include +#include +#include +#include +#include + +namespace HTTP +{ +CURL *curl; +CURLcode res; + +static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp) +{ + ((std::string*)userp)->append((char*)contents, size * nmemb); + return size * nmemb; +} + +void init() +{ + curl_global_init(CURL_GLOBAL_DEFAULT); + curl = curl_easy_init(); + if(!curl) + Log::error("HTTP", "Error while loading cURL library.\n"); +} + +std::string getPage(std::string url) +{ + std::string readBuffer; + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); + res = curl_easy_perform(curl); + if(res != CURLE_OK) + Log::error("HTTP", "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + return readBuffer; +} + +void shutdown() +{ + curl_easy_cleanup(curl); + curl_global_cleanup(); +} + +} diff --git a/src/network/kart_update_message.hpp b/src/network/http_functions.hpp similarity index 72% rename from src/network/kart_update_message.hpp rename to src/network/http_functions.hpp index 7d3a38fc4..970b9c1dd 100644 --- a/src/network/kart_update_message.hpp +++ b/src/network/http_functions.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,15 +16,20 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_KART_UPDATE_MESSAGE_HPP -#define HEADER_KART_UPDATE_MESSAGE_HPP +#ifndef HTTP_FUNCTIONS_HPP +#define HTTP_FUNCTIONS_HPP -#include "network/message.hpp" +#include -class KartUpdateMessage : public Message +namespace HTTP { -public: - KartUpdateMessage(); - KartUpdateMessage(ENetPacket* pkt); -}; // KartUpdateMessage -#endif + +void init(); +void shutdown(); + +std::string getPage(std::string url); + + +} + +#endif // HTTP_FUNCTIONS_HPP diff --git a/src/network/item_info.hpp b/src/network/item_info.hpp deleted file mode 100644 index 0334e2ee0..000000000 --- a/src/network/item_info.hpp +++ /dev/null @@ -1,66 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_ITEM_INFO_HPP -#define HEADER_ITEM_INFO_HPP -/** Class used to transfer information about collected items from -* server to client. -*/ -class ItemInfo -{ -public: - /** Kart id (in world) of the kart collecting the item. */ - unsigned char m_kart_id; - /** Index of the collected item. This is set to -1 if a kart - * triggers a rescue (i.e. attaches the butterfly).] - */ - short m_item_id; - /** Additional info used, depending on item type. This is usually - * the type of the collected item. - */ - char m_add_info; - - /** Constructor to initialise all fields. */ - ItemInfo(int kart, int item, char add_info) : - m_kart_id(kart), m_item_id(item), - m_add_info(add_info) - {} - // ------------------------------------------------------------- - /** Construct ItemInfo from a message (which is unpacked). */ - ItemInfo(Message *m) - { - m_kart_id = m->getChar(); - m_item_id = m->getShort(); - m_add_info = m->getChar(); - } - // ------------------------------------------------------------- - /*** Returns size in bytes necessary to store ItemInfo. */ - static int getLength() {return 2*Message::getCharLength() - + Message::getShortLength();} - // ------------------------------------------------------------- - /** Serialises this object into the message object */ - void serialise(Message *m) - { - m->addChar(m_kart_id); - m->addShort(m_item_id); - m->addChar(m_add_info); - } // serialise -}; // ItemInfo - -#endif - diff --git a/src/network/kart_control_message.cpp b/src/network/kart_control_message.cpp deleted file mode 100644 index db6cea020..000000000 --- a/src/network/kart_control_message.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/kart_control_message.hpp" - -#include "karts/controller/controller.hpp" -#include "modes/world.hpp" -#include "network/network_kart.hpp" - -KartControlMessage::KartControlMessage() - : Message(Message::MT_KART_CONTROL) -{ - World *world=World::getWorld(); - unsigned int num_local_players = race_manager->getNumLocalPlayers(); - unsigned int control_size = KartControl::getLength(); - allocate(control_size*num_local_players); - for(unsigned int i=0; igetLocalPlayerKart(i); - const KartControl& controls = kart->getControls(); - controls.serialise(this); - } -} // KartControlMessage -// ---------------------------------------------------------------------------- -/** Receives a kart control message. - * \param kart_id_offset is the global id of the first kart on the host from - * which this packet was received. - */ -KartControlMessage::KartControlMessage(ENetPacket* pkt, int kart_id_offset, - int num_local_players) - : Message(pkt, MT_KART_CONTROL) -{ - // FIXME: This probably does not work anymore - it assume that - // num_local_Players is the number of all local karts, while it might - // only be the number of all network karts. - for(int i=kart_id_offset; igetKart(i); - if(kart->getController()->isNetworkController()) - { - ((NetworkKart*)kart)->setControl(kc); - } - } -}; // KartControlMessage - diff --git a/src/network/kart_update_message.cpp b/src/network/kart_update_message.cpp deleted file mode 100644 index 2b16cc0e9..000000000 --- a/src/network/kart_update_message.cpp +++ /dev/null @@ -1,62 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/kart_update_message.hpp" - -#include "karts/abstract_kart.hpp" -#include "modes/world.hpp" - -KartUpdateMessage::KartUpdateMessage() - : Message(Message::MT_KART_INFO) -{ - World *world = World::getWorld(); - unsigned int num_karts = world->getNumKarts(); - - // Send the number of karts and for each kart the compressed - // control structure (3 ints) and xyz,hpr (4 floats: quaternion: - allocate(getCharLength()+ - num_karts*(KartControl::getLength() + getVec3Length() - +getQuaternionLength()) ); - addChar(num_karts); - for(unsigned int i=0; igetKart(i); - const KartControl& kc=kart->getControls(); - kc.serialise(this); - addVec3(kart->getXYZ()); - addQuaternion(kart->getRotation()); - } // for i -} // KartUpdateMessage -// ---------------------------------------------------------------------------- -KartUpdateMessage::KartUpdateMessage(ENetPacket* pkt) - : Message(pkt, MT_KART_INFO) -{ - World *world = World::getWorld(); - unsigned int num_karts = getInt(); - for(unsigned int i=0; igetKart(i); - kart->setXYZ(xyz); - kart->setRotation(q); - } // for i -}; // KartUpdateMessage - diff --git a/src/network/message.cpp b/src/network/message.cpp deleted file mode 100644 index b1f754c02..000000000 --- a/src/network/message.cpp +++ /dev/null @@ -1,225 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs, Stephen Leak -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/message.hpp" - -#include -#include -#include -#include - -/** Creates a message to be sent. - * This only initialised the data structures, it does not reserve any memory. - * A call to allocate() is therefore necessary. - * \param type The type of the message - */ -Message::Message(MessageType type) -{ - assert(sizeof(int)==4); - m_type = type; - m_pos = 0; - m_data_size = -1; - m_data = NULL; - m_needs_destroy = 0; // enet destroys message after send -} // Message - -// ---------------------------------------------------------------------------- -/** Handles a received message. - * The message in pkt is received, and the message type is checked. - * \param pkt The ENetPacket - * \param m The type of the message. The type is checked via an assert! - */ - -Message::Message(ENetPacket* pkt, MessageType m) -{ - receive(pkt, m); -} - -// ---------------------------------------------------------------------------- -/** Loads the message in pkt, and checks for the correct message type. - * Paramaters: - * \param pkt The ENetPacket - * \param m The type of the message. The type is checked via an assert! - */ -void Message::receive(ENetPacket* pkt, MessageType m) -{ - assert(sizeof(int)==4); - m_pkt = pkt; - m_data_size = pkt->dataLength; - m_data = (char*)pkt->data; - m_type = (MessageType)m_data[0]; - if(m_type!=m) - printf("type %d %d\n",m_type,m); - assert(m_type==m); - m_pos = 1; - m_needs_destroy = true; -} // Message - -// ---------------------------------------------------------------------------- -/** Frees the memory allocated for this message. */ -Message::~Message() -{ - clear(); -} // ~Message - -// ---------------------------------------------------------------------------- -/** Frees the memory for a received message. - * Calls enet_packet_destroy if necessary (i.e. if the message was received). - * The memory for a message created to be sent does not need to be freed, it - * is handled by enet. */ -void Message::clear() -{ - if(m_needs_destroy) - enet_packet_destroy(m_pkt); -} // clear - -// ---------------------------------------------------------------------------- -/** Reserves the memory for a message. - * \param size Number of bytes to reserve. - */ -void Message::allocate(int size) -{ - m_data_size = size+1; - m_pkt = enet_packet_create (NULL, m_data_size, ENET_PACKET_FLAG_RELIABLE); - m_data = (char*)m_pkt->data; - m_data[0] = m_type; - m_pos = 1; -} // allocate - -// ---------------------------------------------------------------------------- -/** Adds an integer value to the message. - * \param data The integer value to add. - */ -void Message::addInt(int data) -{ - assert((int)(m_pos + sizeof(int)) <= m_data_size); - int l=htonl(data); - memcpy(m_data+m_pos, &l, sizeof(int)); - m_pos+=sizeof(int); -} // addInt - -// ---------------------------------------------------------------------------- -/** Extracts an integer value from a message. - * \return The extracted integer. - */ -int Message::getInt() -{ - m_pos+=sizeof(int); - return ntohl(*(int*)(&m_data[m_pos-sizeof(int)])); -} // getInt - -// ---------------------------------------------------------------------------- -/** Adds a short value to the message. - * \param data The integer value to add. - */ -void Message::addShort(short data) -{ - assert((int)(m_pos + sizeof(short)) <= m_data_size); - int l=htons(data); - memcpy(m_data+m_pos, &l, sizeof(short)); - m_pos+=sizeof(short); -} // addShort - -// ---------------------------------------------------------------------------- -/** Extracts a short value from a message. - * \return The short value. - */ -short Message::getShort() -{ - m_pos+=sizeof(short); - return ntohs(*(short*)(&m_data[m_pos-sizeof(short)])); -} // getShort - -// ---------------------------------------------------------------------------- -/** Adds a floating point value to the message. - * \param data Floating point value to add. - */ -void Message::addFloat(const float data) -{ - // The simple approach of using addInt(*(int*)&data) - // does not work (at least with optimisation on certain g++ versions, - // see getFloat for more details) - int n; - memcpy(&n, &data, sizeof(float)); - addInt(n); -} // addFloat -// ---------------------------------------------------------------------------- -float Message::getFloat() -{ - int i = getInt(); - float f; - memcpy(&f, &i, sizeof(int)); - return f; - // The 'obvious' way: - // float *f = (float*) &i; - // return *f; - // does NOT work, see http://www.velocityreviews.com/forums/showthread.php?t=537336 - // for details -} // getFloat - -// ---------------------------------------------------------------------------- -void Message::addString(const std::string &data) -{ - int len = data.size()+1; // copy 0 end byte - assert((int)(m_pos+len) <=m_data_size); - memcpy (&(m_data[m_pos]), data.c_str(), len); - m_pos += len; -} // addString - -// ---------------------------------------------------------------------------- -std::string Message::getString() -{ - char *str = (char*) &(m_data[m_pos]); - int len = strlen(str)+1; - m_pos += len; - return std::string(str); -} // getString - -// ---------------------------------------------------------------------------- -/** Returns the number of bytes necessary to store a string vector. - * \param vs std::vector - */ -int Message::getStringVectorLength(const std::vector& vs) -{ - int len=getShortLength(); - for(unsigned int i=0; i to the message. - */ -void Message::addStringVector(const std::vector& vs) -{ - assert(vs.size()<32767); - addShort(vs.size()); - for(unsigned short i=0; i Message::getStringVector() -{ - std::vector vs; - vs.resize(getShort()); - for(unsigned int i=0; i -#include -#include -#include -#include "btBulletDynamicsCommon.h" - -using std::memcpy; - -#include "enet/enet.h" - -#include "utils/vec3.hpp" - -// sjl: when a message is received, need to work out what kind of message it -// is and therefore what to do with it - -/** Base class to serialises/deserialises messages. - * This is the base class for all messages being exchange between client - * and server. It handles the interface to enet, and adds a message type - * (which is checked via an assert to help finding bugs by receiving a - * message of an incorrect type). It also takes care of endianess (though - * floats are converted via a byte swap, too - so it must be guaranteed - * that the float representation between all machines is identical). - */ -class Message -{ -public: - /** Contains all tags used in identifying a message. */ - enum MessageType {MT_CONNECT=1, MT_CHARACTER_INFO, MT_CHARACTER_CONFIRM, - MT_RACE_INFO, MT_RACE_START, MT_WORLD_LOADED, - MT_KART_INFO, MT_KART_CONTROL, MT_RACE_STATE, - MT_RACE_RESULT, MT_RACE_RESULT_ACK - }; -private: - ENetPacket *m_pkt; - char *m_data; - MessageType m_type; - int m_data_size; - unsigned int m_pos; // simple stack counter for constructing packet data - bool m_needs_destroy; // only received messages need to be destroyed - -public: - void addInt(int data); - void addShort(short data); - void addString(const std::string &data); - void addStringVector(const std::vector& vs); - void addUInt(unsigned int data) { addInt(*(int*)&data); } - void addFloat(const float data); - void addBool(bool data) { addChar(data?1:0); } - void addChar(char data) { addCharArray((char*)&data,1);} - void addCharArray(char *c, unsigned int n=1) - { assert((int)(m_pos+n)<=m_data_size); - memcpy(m_data+m_pos,c,n); - m_pos+=n; } -#ifndef WIN32 // on windows size_t is unsigned int - void addSizeT(size_t data) { addInt((int)data); } -#endif - void addIntArray(int *d, unsigned int n) - { for(unsigned int i=0; - i - getStringVector(); - char getChar() {char c;getCharArray(&c,1); - return c; } - void getCharArray(char *c, int n=1) {memcpy(c,m_data+m_pos,n); - m_pos+=n; - return; } - Vec3 getVec3() { Vec3 v; - v.setX(getFloat()); - v.setY(getFloat()); - v.setZ(getFloat()); - return v; } - btQuaternion getQuaternion() { btQuaternion q; - q.setX(getFloat()); - q.setY(getFloat()); - q.setZ(getFloat()); - q.setW(getFloat()); - return q; } - static int getIntLength() { return sizeof(int); } - static int getUIntLength() { return sizeof(int); } - static int getShortLength() { return sizeof(short); } - static int getCharLength() { return sizeof(char); } - static int getBoolLength() { return sizeof(char); } - static int getFloatLength() { return sizeof(float); } - static int getStringLength(const std::string& s) { return s.size()+1;} - static int getVec3Length() { return 3*sizeof(float); } - static int getQuaternionLength() { return 4*sizeof(float); } - static int getStringVectorLength(const std::vector& vs); -#ifndef WIN32 - static int getSizeTLength(size_t n) { return sizeof(int); } -#endif - -public: - Message(MessageType m); - Message(ENetPacket *pkt, MessageType m); - void receive(ENetPacket *pkt, MessageType m); - ~Message(); - void clear(); - void allocate(int size); - MessageType getType() const { return m_type; } - ENetPacket* getPacket() const { assert(m_data_size>-1); return m_pkt; } - /** Return the type of a message without unserialising the message */ - static MessageType peekType(ENetPacket *pkt) - { return (MessageType)pkt->data[0];} - -}; // Message - - -#endif - diff --git a/src/network/kart_control_message.hpp b/src/network/network_interface.cpp similarity index 68% rename from src/network/kart_control_message.hpp rename to src/network/network_interface.cpp index 6127f62b9..262083330 100644 --- a/src/network/kart_control_message.hpp +++ b/src/network/network_interface.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,16 +16,13 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_KART_CONTROL_MESSAGE_HPP -#define HEADER_KART_CONTROL_MESSAGE_HPP +#include "network/network_interface.hpp" -#include "network/message.hpp" -class KartControlMessage : public Message +NetworkInterface::NetworkInterface() { -public: - KartControlMessage(); - KartControlMessage(ENetPacket* pkt, int kart_id_offset, - int num_local_players); -}; // KartUpdateMessage -#endif +} + +NetworkInterface::~NetworkInterface() +{ +} diff --git a/src/network/connect_message.hpp b/src/network/network_interface.hpp similarity index 59% rename from src/network/connect_message.hpp rename to src/network/network_interface.hpp index 1875b5718..65e5a4f4b 100644 --- a/src/network/connect_message.hpp +++ b/src/network/network_interface.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,22 +16,29 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_CONNECT_MESSAGE_HPP -#define HEADER_CONNECT_MESSAGE_HPP +#ifndef NETWORK_INTERFACE_H +#define NETWORK_INTERFACE_H +#include "network/singleton.hpp" +#include "network/types.hpp" +#include "network/network_manager.hpp" + +#include #include -#include "network/message.hpp" -class ConnectMessage : public Message +class NetworkInterface : public Singleton { -private: - std::string m_id; - void setId(); -public: - ConnectMessage(); - ConnectMessage(ENetPacket* pkt); - const std::string& - getId() { return m_id; } -}; // ConnectMessage -#endif + friend class Singleton; + public: + + void initNetwork(bool server); + + protected: + // protected functions + NetworkInterface(); + virtual ~NetworkInterface(); + +}; + +#endif // NETWORK_INTERFACE_H diff --git a/src/network/network_kart.cpp b/src/network/network_kart.cpp deleted file mode 100644 index 42cf8a3cc..000000000 --- a/src/network/network_kart.cpp +++ /dev/null @@ -1,43 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2004-2005 Steve Baker -// Copyright (C) 2006 SuperTuxKart-Team, Joerg Henrichs, Steve Baker -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/network_manager.hpp" -#include "network/network_kart.hpp" - -/** A network kart. On the server, it receives its control information (steering etc) - from the network manager. - */ -NetworkKart::NetworkKart(const std::string &kart_name, - unsigned int world_kart_id, int position, - const btTransform &init_transform, - int global_player_id, - RaceManager::KartType type) - : Kart(kart_name, world_kart_id, position, - init_transform) -{ - m_global_player_id = global_player_id; -} // NetworkKart - -// ---------------------------------------------------------------------------- -void NetworkKart::setControl(const KartControl& kc) -{ - assert(network_manager->getMode()==NetworkManager::NW_SERVER); - m_controls = kc; -} // setControl - diff --git a/src/network/network_manager.cpp b/src/network/network_manager.cpp index 2554e070d..5e44bac61 100644 --- a/src/network/network_manager.cpp +++ b/src/network/network_manager.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs, Stephen Leak +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -18,723 +18,221 @@ #include "network/network_manager.hpp" -#include "config/stk_config.hpp" -#include "config/user_config.hpp" -#include "karts/kart_properties_manager.hpp" -#include "modes/world.hpp" -#include "network/connect_message.hpp" -#include "network/character_info_message.hpp" -#include "network/character_selected_message.hpp" -#include "network/race_info_message.hpp" -#include "network/race_start_message.hpp" -#include "network/world_loaded_message.hpp" -#include "network/race_state.hpp" -#include "network/kart_control_message.hpp" -#include "network/character_confirm_message.hpp" -#include "network/race_result_message.hpp" -#include "network/race_result_ack_message.hpp" -#include "race/race_manager.hpp" +#include "network/protocols/hide_public_address.hpp" +#include "network/protocols/show_public_address.hpp" +#include "network/protocols/get_public_address.hpp" -NetworkManager* network_manager = 0; +#include "network/protocol_manager.hpp" +#include "network/client_network_manager.hpp" +#include "network/server_network_manager.hpp" + +#include "utils/log.hpp" + +#include +#include + +//----------------------------------------------------------------------------- NetworkManager::NetworkManager() { - m_mode = NW_NONE; - m_state = NS_ACCEPT_CONNECTIONS; - m_host = NULL; + m_public_address.ip = 0; + m_public_address.port = 0; + m_localhost = NULL; + m_game_setup = NULL; +} - m_num_clients = 0; - m_host_id = 0; +//----------------------------------------------------------------------------- - if (enet_initialize () != 0) - { - fprintf (stderr, "An error occurred while initializing ENet.\n"); - exit(-1); - } -} // NetworkManager - -// ----------------------------------------------------------------------------- -bool NetworkManager::initialiseConnections() -{ - switch(m_mode) - { - case NW_NONE: return true; - case NW_CLIENT: return initClient(); - case NW_SERVER: return initServer(); - } - return true; -} // NetworkManager - -// ----------------------------------------------------------------------------- NetworkManager::~NetworkManager() { - if(m_mode==NW_SERVER || m_mode==NW_CLIENT) enet_host_destroy(m_host); - enet_deinitialize(); -} // ~NetworkManager + ProtocolManager::kill(); -// ----------------------------------------------------------------------------- -bool NetworkManager::initServer() -{ - ENetAddress address; - address.host = ENET_HOST_ANY; - address.port = UserConfigParams::m_server_port; - - m_host = enet_host_create (& address /* the address to bind the server host to */, - stk_config->m_max_karts /* number of connections */, - 0 /* channel limit */, - 0 /* incoming bandwidth */, - 0 /* outgoing bandwidth */ ); - if (m_host == NULL) + if (m_localhost) + delete m_localhost; + while(!m_peers.empty()) { - fprintf (stderr, - "An error occurred while trying to create an ENet server host.\n" - "Progressing in non-network mode\n"); - m_mode = NW_NONE; - return false; + delete m_peers.back(); + m_peers.pop_back(); } +} - m_server = NULL; - m_clients.push_back(NULL); // server has host_id=0, so put a dummy entry at 0 in client array +//----------------------------------------------------------------------------- - m_client_names.push_back("server"); - return true; -} // initServer - -// ----------------------------------------------------------------------------- -/** Initialises the client. This function tries to connect to the server. - */ -bool NetworkManager::initClient() +void NetworkManager::run() { - m_host = enet_host_create (NULL /* create a client host */, - 1 /* only allow 1 outgoing connection */, - 0 /* channel limit */, - 0 /* downstream bandwidth unlimited */, - 0 /* upstream bandwidth unlimited */ ); + // create the protocol manager + ProtocolManager::getInstance(); +} - if (m_host == NULL) +void NetworkManager::reset() +{ + if (m_localhost) + delete m_localhost; + m_localhost = NULL; + while(!m_peers.empty()) { - fprintf (stderr, - "An error occurred while trying to create an ENet client host.\n"); - return false; + delete m_peers.back(); + m_peers.pop_back(); } +} - ENetAddress address; - ENetEvent event; - ENetPeer *peer; +//----------------------------------------------------------------------------- - enet_address_set_host (& address, UserConfigParams::m_server_address.c_str()); - address.port = UserConfigParams::m_server_port; - - /* Initiate the connection, allocating the two channels 0 and 1. */ - peer = enet_host_connect (m_host, &address, 2, 0); - - if (peer == NULL) - { - fprintf(stderr, - "No available peers for initiating an ENet connection.\n"); - return false; - } - - /* Wait up to 5 seconds for the connection attempt to succeed. */ - if (enet_host_service (m_host, & event, 5000) <= 0 || - event.type != ENET_EVENT_TYPE_CONNECT) - { - /* Either the 5 seconds are up or a disconnect event was */ - /* received. Reset the peer in the event the 5 seconds */ - /* had run out without any significant event. */ - enet_peer_reset (peer); - - fprintf(stderr, "Connection to '%s:%d' failed.\n", - UserConfigParams::m_server_address.c_str(), (int)UserConfigParams::m_server_port); - return false; - } - m_server = peer; - return true; -} // initClient - -// ---------------------------------------------------------------------------- -/** Switches the network manager to client mode. This function sets the state - * to waiting_for_chars (so that the message from the server containing all - * available characters can be received). - */ -void NetworkManager::becomeClient() +bool NetworkManager::connect(TransportAddress peer) { - m_mode = NW_CLIENT; - m_state = NS_WAIT_FOR_AVAILABLE_CHARACTERS; -} // becomeClient + if (peerExists(peer)) + return isConnectedTo(peer); -// ---------------------------------------------------------------------------- -/** Switches the network manager to server mode. This function sets the state - * to accepting connections. - */ -void NetworkManager::becomeServer() + return STKPeer::connectToHost(m_localhost, peer, 2, 0); +} + +//----------------------------------------------------------------------------- + +void NetworkManager::setManualSocketsMode(bool manual) { - m_mode = NW_SERVER; - m_state = NS_ACCEPT_CONNECTIONS; -} // becomeServer - -// ---------------------------------------------------------------------------- -/** Called in case of an error, to switch back to non-networking mode. -*/ -void NetworkManager::disableNetworking() -{ - m_mode=NW_NONE; - if (m_host != NULL) - { - enet_host_destroy(m_host); - m_host = NULL; - } - // FIXME: what other enet data structures do we have to free/reset??? - -} // disableNetworking - -// ---------------------------------------------------------------------------- -void NetworkManager::handleNewConnection(ENetEvent *event) -{ - // Only accept while waiting for connections - if(m_state!=NS_ACCEPT_CONNECTIONS) return; - - // The logical connection (from STK point of view) happens when - // the connection message is received. But for now reserve the - // space in the data structures (e.g. in case that two connects - // happen before a connect message is received - m_client_names.push_back("NOT SET YET"); - m_clients.push_back(event->peer); - event->peer->data = (void*)int(m_clients.size()-1); // save hostid in peer data - -} // handleNewConnection - -// ---------------------------------------------------------------------------- -void NetworkManager::handleDisconnection(ENetEvent *event) -{ - if(m_state!=NS_ACCEPT_CONNECTIONS) - { - fprintf(stderr, "Disconnect while in race - close your eyes and hope for the best.\n"); - return; - } - fprintf(stderr, "%x:%d disconnected (host id %d).\n", event->peer->address.host, - event->peer->address.port, (int)(long)event->peer->data ); - m_num_clients--; -} // handleDisconnection - -// ---------------------------------------------------------------------------- -void NetworkManager::handleMessageAtServer(ENetEvent *event) -{ // handle message at server (from client) - - switch(m_state) - { - case NS_ACCEPT_CONNECTIONS: - { - ConnectMessage m(event->packet); - m_client_names[(int)(long)event->peer->data] = m.getId(); - m_num_clients++; - return; - } - case NS_KART_CONFIRMED: // Fall through - case NS_CHARACTER_SELECT: - { - CharacterSelectedMessage m(event->packet); - unsigned int hostid=(unsigned int)(long)event->peer->data; - assert(hostid>=1 && hostid<=m_num_clients); - if(m_num_local_players[hostid]==-1) // first package from that host - { - m_num_local_players[hostid] = m.getNumPlayers(); - m_num_all_players += m.getNumPlayers(); - // count how many hosts have sent (at least) one message - m_barrier_count ++; - } - RemoteKartInfo ki=m.getKartInfo(); - ki.setHostId(hostid); - m_kart_info.push_back(ki); - - int kart_id = kart_properties_manager->getKartId(ki.getKartName()); - kart_properties_manager->testAndSetKart(kart_id); - // TODO - character selection screen in networking - /* - CharSel *menu = dynamic_cast(menu_manager->getCurrentMenu()); - if(menu) - menu->updateAvailableCharacters(); - */ - - // Broadcast the information about a selected kart to all clients - CharacterConfirmMessage ccm(ki.getKartName(), hostid); - broadcastToClients(ccm); - // See if this was the last message, i.e. we have received at least - // one message from each client, and the size of the kart_info - // array is the same as the number of all players (which does not - // yet include the number of players on the host). - if(m_barrier_count == (int)m_num_clients && - m_num_all_players==(int)m_kart_info.size()) - { - // we can't send the race info yet, since the server might - // not yet have selected all characters! - m_state = NS_ALL_REMOTE_CHARACTERS_DONE; - } - break; - } - case NS_READY_SET_GO_BARRIER: - { - m_barrier_count ++; - if(m_barrier_count==(int)m_num_clients) - { - m_state = NS_RACING; - RaceStartMessage m; - broadcastToClients(m); - } - break; - } - case NS_RACE_RESULT_BARRIER: - { - // Other message, esp. kart control, are silently ignored. - // FIXME: we might want to make sure that no such message actually arrives - if(Message::peekType(event->packet)!=Message::MT_RACE_RESULT_ACK) - { - enet_packet_destroy(event->packet); - return; - } - m_barrier_count++; - if(m_barrier_count==(int)m_num_clients) - { - m_state = NS_MAIN_MENU; - } - break; - } - default: assert(0); // should not happen - } // switch m_state -} // handleMessageAtServer - -// ---------------------------------------------------------------------------- -void NetworkManager::handleMessageAtClient(ENetEvent *event) -{ // handle message at client (from server) - switch(m_state) - { - case NS_WAIT_FOR_AVAILABLE_CHARACTERS: - { - CharacterInfoMessage m(event->packet); - // FIXME: handle list of available characters - m_state = NS_CHARACTER_SELECT; - break; - } - case NS_CHARACTER_SELECT: - { - CharacterConfirmMessage m(event->packet); - kart_properties_manager->selectKartName(m.getKartName()); - // TODO - karts selection screen in networking - /* - CharSel *menu = dynamic_cast(menu_manager->getCurrentMenu()); - if(menu) - menu->updateAvailableCharacters(); - */ - break; - } - case NS_WAIT_FOR_KART_CONFIRMATION: - { - CharacterConfirmMessage m(event->packet); - kart_properties_manager->selectKartName(m.getKartName()); - - // If the current menu is the character selection menu, - // update the menu so that the newly taken character is removed. - // TODO - kart selection screen and networking - /* - CharSel *menu = dynamic_cast(menu_manager->getCurrentMenu()); - if(menu) - menu->updateAvailableCharacters();*/ - // Check if we received a message about the kart we just selected. - // If so, the menu needs to progress, otherwise a different kart - // must be selected by the current player. - if(m.getKartName()==m_kart_to_confirm) - { - int host_id = m.getHostId(); - m_state = (host_id == getMyHostId()) ? NS_KART_CONFIRMED - : NS_CHARACTER_SELECT; - } // m.getkartName()==m_kart_to_confirm - break; - } // wait for kart confirmation - case NS_WAIT_FOR_RACE_DATA: - { - // It is possible that character confirm messages arrive at the - // client when it has already left the character selection screen. - // In this case the messages can simply be ignored. - if(Message::peekType(event->packet)==Message::MT_CHARACTER_CONFIRM) - { - // Receiving it will automatically free the memory. - CharacterConfirmMessage m(event->packet); - return; - } - RaceInfoMessage m(event->packet); - // The constructor actually sets the information in the race manager - m_state = NS_LOADING_WORLD; - break; - } - case NS_READY_SET_GO_BARRIER: - { - // Not actually needed, but the destructor of RaceStartMessage - // will free the memory of the event. - RaceStartMessage m(event->packet); - m_state = NS_RACING; - break; - } - case NS_RACING: - { - assert(false); // should never be here while racing - break; - } - case NS_RACE_RESULT_BARRIER: - { - RaceResultAckMessage message(event->packet); - // TODO - race results menu in networking - /* - RaceResultsGUI *menu = dynamic_cast(menu_manager->getCurrentMenu()); - if(menu) - menu->setSelectedWidget(message.getSelectedMenu());*/ - m_state = NS_RACE_RESULT_BARRIER_OVER; - break; - } - default: - { - printf("received unknown message: type %d\n", - Message::peekType(event->packet)); - // assert(0); // should not happen - } - } // switch m_state -} // handleMessageAtClient - -// ---------------------------------------------------------------------------- -void NetworkManager::update(float dt) -{ - if(m_mode==NW_NONE) return; - // Messages during racing are handled in the sendUpdates/receiveUpdate - // calls, so don't do anything in this case. - if(m_state==NS_RACING) return; - - ENetEvent event; - int result = enet_host_service (m_host, &event, 0); - if(result==0) return; - if(result<0) - { - fprintf(stderr, "Error while receiving messages -> ignored.\n"); - return; - } - switch (event.type) - { - case ENET_EVENT_TYPE_CONNECT: handleNewConnection(&event); break; - case ENET_EVENT_TYPE_RECEIVE: - if(m_mode==NW_SERVER) - handleMessageAtServer(&event); - else - handleMessageAtClient(&event); - break; - case ENET_EVENT_TYPE_DISCONNECT: handleDisconnection(&event); break; - case ENET_EVENT_TYPE_NONE: break; - } -} // update - -// ---------------------------------------------------------------------------- -void NetworkManager::broadcastToClients(Message &m) -{ - enet_host_broadcast(m_host, 0, m.getPacket()); - enet_host_flush(m_host); -} // broadcastToClients - -// ---------------------------------------------------------------------------- -void NetworkManager::sendToServer(Message &m) -{ - enet_peer_send(m_server, 0, m.getPacket()); - enet_host_flush(m_host); -} // sendToServer - -// ---------------------------------------------------------------------------- -/** Cleans up character related data structures. Must be called before any - * character related data is set. - */ -void NetworkManager::initCharacterDataStructures() -{ - // This is called the first time the character selection menu is displayed - - if(m_mode==NW_CLIENT) - { - // Change state to wait for list of characters from server - m_state = NS_WAIT_FOR_AVAILABLE_CHARACTERS; - } - else // Server or no network - { - if(m_mode==NW_SERVER) - { - // server: create message with all valid characters - // ================================================ - for(unsigned int i=1; i<=m_num_clients; i++) - { - CharacterInfoMessage m(i); - enet_peer_send(m_clients[i], 0, m.getPacket()); - } - enet_host_flush(m_host); - } - // For server and no network: - // ========================== - // Prepare the data structures to receive and - // store information from all clients. - m_num_local_players.clear(); - // Server (hostid 0) is not included in the num_clients count. So to - // be able to use the hostid as index, we have to allocate one - // additional element. - m_num_local_players.resize(m_num_clients+1, -1); - m_num_local_players[0] = race_manager->getNumLocalPlayers(); - m_kart_info.clear(); - m_num_all_players = 0; - // use barrier count to see if we had at least one message from each host - m_barrier_count = 0; - m_state = NS_CHARACTER_SELECT; - } - -} // switchTocharacterSelection - -// ---------------------------------------------------------------------------- -/** Called on the client to send the data about the selected kart to the - * server and wait for confirmation. - * \param player_id Local id of the player which selected the kart. - * \param kart_id Identifier of the selected kart. this is used to wait till - * a message about this kart is received back from the server. - */ -void NetworkManager::sendCharacterSelected(int player_id, - const std::string &kart_id) -{ - if(m_mode==NW_SERVER) - { - CharacterConfirmMessage ccm(kart_id, getMyHostId()); - broadcastToClients(ccm); - } - else if(m_mode==NW_CLIENT) - { - CharacterSelectedMessage m(player_id); - sendToServer(m); - // Wait till we receive confirmation about the selected character. - m_state = NS_WAIT_FOR_KART_CONFIRMATION; - m_kart_to_confirm = kart_id; - } -} // sendCharacterSelected - -// ---------------------------------------------------------------------------- -void NetworkManager::waitForRaceInformation() -{ - m_state = NS_WAIT_FOR_RACE_DATA; -} // waitForRaceInformation - -// ---------------------------------------------------------------------------- -void NetworkManager::worldLoaded() -{ - if(m_mode==NW_CLIENT) - { - WorldLoadedMessage m; - sendToServer(m); - m_state = NS_READY_SET_GO_BARRIER; - } -} // worldLoaded - -// ---------------------------------------------------------------------------- -/** Receive and store the information from sendKartsInformation() -*/ -void NetworkManager::setupPlayerKartInfo() -{ - // Not sure if this should be here, but without it extra uncontrolled - // human players accumulate after each race. - m_kart_info.clear(); - - // Get the local kart info - for(unsigned int i=0; igetNumLocalPlayers(); i++) - m_kart_info.push_back(race_manager->getLocalKartInfo(i)); - - // Now sort by (hostid, playerid) - std::sort(m_kart_info.begin(), m_kart_info.end()); - - // Set the player kart information - race_manager->setNumPlayers(m_kart_info.size()); - - // Set the global player ID for each player - for(unsigned int i=0; isetPlayerKart(i, m_kart_info[i]); - } - - // Compute the id of the first kart from each host - m_kart_id_offset.resize(m_num_clients+1); - m_kart_id_offset[0]=0; - for(unsigned int i=1; i<=m_num_clients; i++) - m_kart_id_offset[i]=m_kart_id_offset[i-1]+m_num_local_players[i-1]; - - race_manager->computeRandomKartList(); -} // setupPlayerKartInfo - -// ---------------------------------------------------------------------------- -/** Sends the information from the race_manager to all clients. -*/ -void NetworkManager::sendRaceInformationToClients() -{ - if(m_mode==NW_SERVER) - { - setupPlayerKartInfo(); - RaceInfoMessage m(m_kart_info); - broadcastToClients(m); - } - beginReadySetGoBarrier(); -} // sendRaceInformationToClients - -// ---------------------------------------------------------------------------- -void NetworkManager::beginReadySetGoBarrier() -{ - m_state = NS_READY_SET_GO_BARRIER; - m_barrier_count = 0; - if(m_num_clients==0) m_state = NS_RACING; -} // beginReadySetGoBarrier -// ---------------------------------------------------------------------------- -void NetworkManager::sendConnectMessage() -{ - ConnectMessage msg; - sendToServer(msg); -} // sendConnectMessage -// ---------------------------------------------------------------------------- -/*** Send all kart controls and kart positions to all clients -*/ -void NetworkManager::sendUpdates() -{ - if(m_mode==NW_SERVER) - { - race_state->serialise(); - broadcastToClients(*race_state); - } - else if(m_mode==NW_CLIENT) - { - KartControlMessage m; - sendToServer(m); - } -} // sendUpdates - -// ---------------------------------------------------------------------------- -void NetworkManager::receiveUpdates() -{ - if(m_mode==NW_NONE) return; // do nothing if not networking - // The server receives m_num_clients messages, each client one message - int num_messages = m_mode==NW_SERVER ? m_num_clients : 1; - ENetEvent event; - bool correct=true; - - for(int i=0; ienterRaceOverState(); - return; - } - race_state->receive(event.packet); - } - } // for istopListening(); else + m_localhost->startListening(); +} + +//----------------------------------------------------------------------------- + +void NetworkManager::notifyEvent(Event* event) +{ + Log::verbose("NetworkManager", "EVENT received of type %d", (int)(event->type)); + STKPeer* peer = *event->peer; + if (event->type == EVENT_TYPE_CONNECTED) { - broadcastToClients(m); + Log::info("NetworkManager", "A client has just connected. There are now %lu peers.", m_peers.size() + 1); + Log::debug("NetworkManager", "Addresses are : %lx, %lx, %lx", event->peer, *event->peer, peer); + // create the new peer: + m_peers.push_back(peer); } -} // sendRaceResultAck + if (event->type == EVENT_TYPE_MESSAGE) + { + uint32_t addr = peer->getAddress(); + Log::verbose("NetworkManager", "Message, Sender : %i.%i.%i.%i, message = \"%s\"", + ((addr>>24)&0xff), + ((addr>>16)&0xff), + ((addr>>8)&0xff), + (addr&0xff), event->data.c_str()); + + } + + // notify for the event now. + ProtocolManager::getInstance()->notifyEvent(event); +} + +//----------------------------------------------------------------------------- + +void NetworkManager::sendPacket(STKPeer* peer, const NetworkString& data, bool reliable) +{ + if (peer) + peer->sendPacket(data, reliable); +} + +//----------------------------------------------------------------------------- + +void NetworkManager::sendPacketExcept(STKPeer* peer, const NetworkString& data, bool reliable) +{ + for (unsigned int i = 0; i < m_peers.size(); i++) + { + STKPeer* p = m_peers[i]; + if (!p->isSamePeer(peer)) + { + p->sendPacket(data, reliable); + } + } +} + +//----------------------------------------------------------------------------- + +GameSetup* NetworkManager::setupNewGame() +{ + if (m_game_setup) + delete m_game_setup; + m_game_setup = NULL; + m_game_setup = new GameSetup(); + return m_game_setup; +} + +//----------------------------------------------------------------------------- + +void NetworkManager::disconnected() +{ + // delete the game setup + if (m_game_setup) + delete m_game_setup; + m_game_setup = NULL; + + // remove all peers + for (unsigned int i = 0; i < m_peers.size(); i++) + { + delete m_peers[i]; + m_peers[i] = NULL; + } + m_peers.clear(); +} + +//----------------------------------------------------------------------------- + +void NetworkManager::setLogin(std::string username, std::string password) +{ + m_player_login.username = username; + m_player_login.password = password; +} + +//----------------------------------------------------------------------------- + +void NetworkManager::setPublicAddress(TransportAddress addr) +{ + m_public_address = addr; +} +//----------------------------------------------------------------------------- + +void NetworkManager::removePeer(STKPeer* peer) +{ + if (!peer || !peer->exists()) // peer does not exist (already removed) + return; + Log::debug("NetworkManager", "Disconnected host: %i.%i.%i.%i:%i", + peer->getAddress()>>24&0xff, + peer->getAddress()>>16&0xff, + peer->getAddress()>>8&0xff, + peer->getAddress()&0xff, + peer->getPort()); + // remove the peer: + bool removed = false; + for (unsigned int i = 0; i < m_peers.size(); i++) + { + if (m_peers[i]->isSamePeer(peer) && !removed) // remove only one + { + delete m_peers[i]; + m_peers.erase(m_peers.begin()+i, m_peers.begin()+i+1); + Log::verbose("NetworkManager", "The peer has been removed from the Network Manager."); + removed = true; + } + else if (m_peers[i]->isSamePeer(peer)) + { + Log::fatal("NetworkManager", "Multiple peers match the disconnected one."); + } + } + if (!removed) + Log::warn("NetworkManager", "The peer that has been disconnected was not registered by the Network Manager."); + + Log::info("NetworkManager", "Somebody is now disconnected. There are now %lu peers.", m_peers.size()); +} + +//----------------------------------------------------------------------------- + +bool NetworkManager::peerExists(TransportAddress peer) +{ + return m_localhost->peerExists(peer); +} + +//----------------------------------------------------------------------------- + +bool NetworkManager::isConnectedTo(TransportAddress peer) +{ + return m_localhost->isConnectedTo(peer); +} diff --git a/src/network/network_manager.hpp b/src/network/network_manager.hpp index 83d55f556..6d859151c 100644 --- a/src/network/network_manager.hpp +++ b/src/network/network_manager.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs, Stephen Leak +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,107 +16,71 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_NETWORK_MANAGER_HPP -#define HEADER_NETWORK_MANAGER_HPP +#ifndef NETWORKMANAGER_HPP +#define NETWORKMANAGER_HPP + +#include "network/stk_peer.hpp" +#include "network/stk_host.hpp" + +#include "network/protocol_manager.hpp" +#include "network/singleton.hpp" +#include "network/types.hpp" +#include "network/event.hpp" +#include "network/game_setup.hpp" -#include #include -#include "enet/enet.h" - -#include "network/remote_kart_info.hpp" - - -class Message; - -class NetworkManager +class NetworkManager : public Singleton { -public: - // The mode the network manager is operating in - enum NetworkMode {NW_SERVER, NW_CLIENT, NW_NONE}; + friend class Singleton; + public: + virtual void run(); + virtual void reset(); - // States for the finite state machine. First for server: - enum NetworkState {NS_MAIN_MENU, // before char sel gui - NS_ACCEPT_CONNECTIONS, // server: accept connections - NS_WAIT_FOR_AVAILABLE_CHARACTERS, // client: wait for list - NS_ALL_REMOTE_CHARACTERS_DONE, // server: all client data received - NS_WAIT_FOR_KART_CONFIRMATION, // client: wait for confirmation - // if character selection was ok - NS_KART_CONFIRMED, // Character was confirmed - NS_WAIT_FOR_RACE_DATA, // client: wait for race info - NS_READY_SET_GO_BARRIER, // c&s: barrier before r.s.g. - NS_CHARACTER_SELECT, // c&s: character select in progress - NS_LOADING_WORLD, // client: loading world - NS_RACING, - NS_WAIT_FOR_RACE_RESULT, // clients: waiting for race results - NS_RACE_RESULT_BARRIER , // Wait till all ack results - NS_RACE_RESULT_BARRIER_OVER // Barrier is over, goto next state - }; -private: + // network management functions + virtual bool connect(TransportAddress peer); + virtual void setManualSocketsMode(bool manual); - NetworkMode m_mode; - NetworkState m_state; - unsigned int m_num_clients; - std::vector m_kart_info; - int m_host_id; - std::vector m_client_names; - std::vector m_num_local_players; - std::vector m_kart_id_offset; // kart id of first kart on host i - int m_num_all_players; - int m_barrier_count; + // message/packets related functions + virtual void notifyEvent(Event* event); + virtual void sendPacket(const NetworkString& data, bool reliable = true) = 0; + virtual void sendPacket(STKPeer* peer, const NetworkString& data, bool reliable = true); + virtual void sendPacketExcept(STKPeer* peer, const NetworkString& data, bool reliable = true); - ENetHost *m_host; // me - ENetPeer *m_server; // (clients only) - std::vector m_clients; // (server only) pos in vector is client host_id - /** Name of the kart that a client is waiting for confirmation for. */ - std::string m_kart_to_confirm; + // Game related functions + virtual GameSetup* setupNewGame(); //!< Creates a new game setup and returns it + virtual void disconnected(); //!< Called when you leave a server - bool initServer(); - bool initClient(); - void handleNewConnection(ENetEvent *event); - void handleMessageAtServer(ENetEvent *event); - void handleMessageAtClient(ENetEvent *event); - void handleDisconnection(ENetEvent *event); - // the first cast to long avoid compiler errors on 64 bit systems - // about lost precision, then cast long to int to get the right type - unsigned int getHostId(ENetPeer *p) const {return (int)(long)p->data; } + // raw data management + void setLogin(std::string username, std::string password); + void setPublicAddress(TransportAddress addr); + void removePeer(STKPeer* peer); - void sendToServer(Message &m); - void broadcastToClients(Message &m); -public: - NetworkManager(); - ~NetworkManager(); - void setMode(NetworkMode m) {m_mode = m; } - NetworkMode getMode() const {return m_mode; } - void becomeServer(); - void becomeClient(); - void setState(NetworkState s) {m_state = s; } - NetworkState getState() const {return m_state; } - int getMyHostId() const {return m_host_id; } - void setHostId(int host_id) {m_host_id = host_id; } - unsigned int getNumClients() const {return m_num_clients; } - const std::string& - getClientName(int i) const {return m_client_names[i];} - bool initialiseConnections(); - void update(float dt); + // getters + virtual bool peerExists(TransportAddress peer); + virtual bool isConnectedTo(TransportAddress peer); - void disableNetworking(); - void sendConnectMessage(); // client send initial info to server - void initCharacterDataStructures(); - void sendCharacterSelected(int player_id, const std::string &kartid); - void waitForRaceInformation(); - void worldLoaded(); - void setupPlayerKartInfo(); - void beginReadySetGoBarrier(); - void sendRaceInformationToClients(); - void sendUpdates(); - void receiveUpdates(); - void waitForClientData(); - void sendRaceResults(); - void beginRaceResultBarrier(); - void sendRaceResultAck(char menu_selection=-1); + virtual bool isServer() = 0; + inline bool isClient() { return !isServer(); } + bool isPlayingOnline() { return m_playing_online; } + STKHost* getHost() { return m_localhost; } + std::vector getPeers() { return m_peers; } + unsigned int getPeerCount() { return m_peers.size(); } + TransportAddress getPublicAddress() { return m_public_address; } + GameSetup* getGameSetup() { return m_game_setup; } + + protected: + NetworkManager(); + virtual ~NetworkManager(); + + // protected members + std::vector m_peers; + STKHost* m_localhost; + bool m_playing_online; + GameSetup* m_game_setup; + + TransportAddress m_public_address; + PlayerLogin m_player_login; }; -extern NetworkManager *network_manager; - -#endif +#endif // NETWORKMANAGER_HPP diff --git a/src/network/network_string.cpp b/src/network/network_string.cpp new file mode 100644 index 000000000..251c98fb5 --- /dev/null +++ b/src/network/network_string.cpp @@ -0,0 +1,8 @@ +#include "network/network_string.hpp" + +NetworkString operator+(NetworkString const& a, NetworkString const& b) +{ + NetworkString ns(a); + ns += b; + return ns; +} diff --git a/src/network/network_string.hpp b/src/network/network_string.hpp new file mode 100644 index 000000000..83e3eb59a --- /dev/null +++ b/src/network/network_string.hpp @@ -0,0 +1,230 @@ +#ifndef NETWORK_STRING_HPP +#define NETWORK_STRING_HPP + +#include "utils/types.hpp" + +#include +#include +#include +#include + +typedef unsigned char uchar; + +class NetworkString +{ + union { + float f; + uint8_t i[4]; + } f_as_i; // float as integer + union { + double d; + uint8_t i[8]; + } d_as_i; // double as integer + public: + NetworkString() { } + NetworkString(const uint8_t& value) { m_string.push_back(value); } + NetworkString(NetworkString const& copy) { m_string = copy.m_string; } + NetworkString(const std::string & value) { m_string = std::vector(value.begin(), value.end()); } + + NetworkString& removeFront(int size) + { + m_string.erase(m_string.begin(), m_string.begin()+size); + return *this; + } + NetworkString& remove(int pos, int size) + { + m_string.erase(m_string.begin()+pos, m_string.begin()+pos+size); + return *this; + } + + uint8_t operator[](const int& pos) const + { + return getUInt8(pos); + } + + NetworkString& addUInt8(const uint8_t& value) + { + m_string.push_back(value); + return *this; + } + inline NetworkString& ai8(const uint8_t& value) { return addUInt8(value); } + NetworkString& addUInt16(const uint16_t& value) + { + m_string.push_back((value>>8)&0xff); + m_string.push_back(value&0xff); + return *this; + } + inline NetworkString& ai16(const uint16_t& value) { return addUInt16(value); } + NetworkString& addUInt32(const uint32_t& value) + { + m_string.push_back((value>>24)&0xff); + m_string.push_back((value>>16)&0xff); + m_string.push_back((value>>8)&0xff); + m_string.push_back(value&0xff); + return *this; + } + inline NetworkString& ai32(const uint32_t& value) { return addUInt32(value); } + NetworkString& addInt(const int& value) + { + m_string.push_back((value>>24)&0xff); + m_string.push_back((value>>16)&0xff); + m_string.push_back((value>>8)&0xff); + m_string.push_back(value&0xff); + return *this; + } + inline NetworkString& ai(const int& value) { return addInt(value); } + NetworkString& addFloat(const float& value) //!< BEWARE OF PRECISION + { + assert(sizeof(float)==4); + f_as_i.f = value; + m_string.push_back(f_as_i.i[0]); + m_string.push_back(f_as_i.i[1]); + m_string.push_back(f_as_i.i[2]); + m_string.push_back(f_as_i.i[3]); + return *this; + } + inline NetworkString& af(const float& value) { return addFloat(value); } + NetworkString& addDouble(const double& value) //!< BEWARE OF PRECISION + { + assert(sizeof(double)==8); + d_as_i.d = value; + m_string.push_back(d_as_i.i[0]); + m_string.push_back(d_as_i.i[1]); + m_string.push_back(d_as_i.i[2]); + m_string.push_back(d_as_i.i[3]); + m_string.push_back(d_as_i.i[4]); + m_string.push_back(d_as_i.i[5]); + m_string.push_back(d_as_i.i[6]); + m_string.push_back(d_as_i.i[7]); + return *this; + } + inline NetworkString& ad(const double& value) { return addDouble(value); } + NetworkString& addChar(const char& value) + { + m_string.push_back((uint8_t)(value)); + return *this; + } + inline NetworkString& ac(const char& value) { return addChar(value); } + + NetworkString& addString(const std::string& value) + { + for (unsigned int i = 0; i < value.size(); i++) + m_string.push_back((uint8_t)(value[i])); + return *this; + } + inline NetworkString& as(const std::string& value) { return addString(value); } + + NetworkString& operator+=(NetworkString const& value) + { + m_string.insert( m_string.end(), value.m_string.begin(), value.m_string.end() ); + return *this; + } + + const char* c_str() const + { + std::string str(m_string.begin(), m_string.end()); + return str.c_str(); + } + int size() const + { + return m_string.size(); + } + + template + T get(int pos) const + { + int a = n; + T result = 0; + while(a--) + { + result <<= 8; // offset one byte + result += ((uint8_t)(m_string[pos+n-1-a]) & 0xff); // add the data to result + } + return result; + } + + inline int getInt(int pos = 0) const { return get(pos); } + inline uint32_t getUInt(int pos = 0) const { return get(pos); } + inline uint32_t getUInt32(int pos = 0) const { return get(pos); } + inline uint16_t getUInt16(int pos = 0) const { return get(pos); } + inline uint8_t getUInt8(int pos = 0) const { return get(pos); } + inline char getChar(int pos = 0) const { return get(pos); } + inline unsigned char getUChar(int pos = 0) const { return get(pos); } + std::string getString(int pos, int len) const { return std::string(m_string.begin()+pos, m_string.begin()+pos+len); } + + inline int gi(int pos = 0) const { return get(pos); } + inline uint32_t gui(int pos = 0) const { return get(pos); } + inline uint32_t gui32(int pos = 0) const { return get(pos); } + inline uint16_t gui16(int pos = 0) const { return get(pos); } + inline uint8_t gui8(int pos = 0) const { return get(pos); } + inline char gc(int pos = 0) const { return get(pos); } + inline unsigned char guc(int pos = 0) const { return get(pos); } + std::string gs(int pos, int len) const { return std::string(m_string.begin()+pos, m_string.begin()+pos+len); } + + double getDouble(int pos = 0) //!< BEWARE OF PRECISION + { + for (int i = 0; i < 8; i++) + d_as_i.i[i] = m_string[pos+i]; + return d_as_i.d; + } + float getFloat(int pos = 0) //!< BEWARE OF PRECISION + { + for (int i = 0; i < 4; i++) + f_as_i.i[i] = m_string[pos+i]; + return f_as_i.f; + } + + //! Functions to get while removing + template + T getAndRemove(int pos) + { + int a = n; + T result = 0; + while(a--) + { + result <<= 8; // offset one byte + result += ((uint8_t)(m_string[pos+n-1-a]) & 0xff); // add the data + } + remove(pos,n); + return result; + } + + inline int getAndRemoveInt(int pos = 0) { return getAndRemove(pos); } + inline uint32_t getAndRemoveUInt(int pos = 0) { return getAndRemove(pos); } + inline uint32_t getAndRemoveUInt32(int pos = 0) { return getAndRemove(pos); } + inline uint16_t getAndRemoveUInt16(int pos = 0) { return getAndRemove(pos); } + inline uint8_t getAndRemoveUInt8(int pos = 0) { return getAndRemove(pos); } + inline char getAndRemoveChar(int pos = 0) { return getAndRemove(pos); } + inline unsigned char getAndRemoveUChar(int pos = 0) { return getAndRemove(pos); } + double getAndRemoveDouble(int pos = 0) //!< BEWARE OF PRECISION + { + for (int i = 0; i < 8; i++) + d_as_i.i[i] = m_string[pos+i]; + return d_as_i.d; + remove(pos, 8); + } + float getAndRemoveFloat(int pos = 0) //!< BEWARE OF PRECISION + { + for (int i = 0; i < 4; i++) + f_as_i.i[i] = m_string[pos+i]; + return f_as_i.f; + remove(pos, 4); + } + + inline NetworkString& gui8(uint8_t* dst) { *dst = getAndRemoveUInt8(0); return *this; } + inline NetworkString& gui16(uint16_t* dst) { *dst = getAndRemoveUInt16(0); return *this; } + inline NetworkString& gui32(uint32_t* dst) { *dst = getAndRemoveUInt32(0); return *this; } + inline NetworkString& gui(uint32_t* dst) { *dst = getAndRemoveUInt32(0); return *this; } + inline NetworkString& gi(int* dst) { *dst = getAndRemoveInt(0); return *this; } + inline NetworkString& gc(char* dst) { *dst = getAndRemoveChar(0); return *this; } + inline NetworkString& guc(uchar* dst) { *dst = getAndRemoveUChar(0); return *this; } + inline NetworkString& gd(double* dst) { *dst = getAndRemoveDouble(0); return *this; } + inline NetworkString& gf(float* dst) { *dst = getAndRemoveFloat(0); return *this; } + + protected: + std::vector m_string; +}; + +NetworkString operator+(NetworkString const& a, NetworkString const& b); + +#endif // NETWORK_STRING_HPP diff --git a/src/network/network_world.cpp b/src/network/network_world.cpp new file mode 100644 index 000000000..ad7407eda --- /dev/null +++ b/src/network/network_world.cpp @@ -0,0 +1,40 @@ +#include "network/network_world.hpp" + +#include "network/protocol_manager.hpp" +#include "network/protocols/synchronization_protocol.hpp" +#include "network/protocols/controller_events_protocol.hpp" +#include "modes/world.hpp" + +#include "karts/controller/controller.hpp" + +NetworkWorld::NetworkWorld() +{ + m_running = false; +} + +NetworkWorld::~NetworkWorld() +{ +} + +void NetworkWorld::update(float dt) +{ + SynchronizationProtocol* protocol = static_cast( + ProtocolManager::getInstance()->getProtocol(PROTOCOL_SYNCHRONIZATION)); + if (protocol) // if this protocol exists, that's that we play online + { + Log::debug("NetworkWorld", "Coutdown value is %f", protocol->getCountdown()); + if (protocol->getCountdown() > 0.0) + { + return; + } + } + World::getWorld()->updateWorld(dt); +} + +void NetworkWorld::controllerAction(Controller* controller, PlayerAction action, int value) +{ + ControllerEventsProtocol* protocol = static_cast( + ProtocolManager::getInstance()->getProtocol(PROTOCOL_CONTROLLER_EVENTS)); + if (protocol) + protocol->controllerAction(controller, action, value); +} diff --git a/src/network/network_world.hpp b/src/network/network_world.hpp new file mode 100644 index 000000000..bce5666f6 --- /dev/null +++ b/src/network/network_world.hpp @@ -0,0 +1,37 @@ +#ifndef NETWORK_WORLD_HPP +#define NETWORK_WORLD_HPP + +#include "network/singleton.hpp" +#include "input/input.hpp" +#include + +class Controller; +class KartUpdateProtocol; +class AbstractKart; + +/*! \brief Manages the world updates during an online game + * This function's update is to be called instead of the normal World update +*/ +class NetworkWorld : public Singleton +{ + friend class Singleton; + public: + void update(float dt); + + void start() { m_running = true; } + void stop() { m_running = false; } + bool isRunning() { return m_running; } + + void controllerAction(Controller* controller, PlayerAction action, int value); + + std::string m_self_kart; + protected: + bool m_running; + float m_race_time; + + private: + NetworkWorld(); + virtual ~NetworkWorld(); +}; + +#endif // NETWORK_WORLD_HPP diff --git a/src/network/race_start_message.hpp b/src/network/protocol.cpp similarity index 57% rename from src/network/race_start_message.hpp rename to src/network/protocol.cpp index eac2d7594..e4a34f310 100644 --- a/src/network/race_start_message.hpp +++ b/src/network/protocol.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,25 +16,39 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_RACE_START_MESSAGE_HPP -#define HEADER_RACE_START_MESSAGE_HPP +#include "network/protocol.hpp" -#include "network/message.hpp" -#include "network/remote_kart_info.hpp" -#include "race/race_manager.hpp" +#include "network/protocol_manager.hpp" -class RaceStartMessage : public Message +Protocol::Protocol(CallbackObject* callback_object, PROTOCOL_TYPE type) { -private: -// For now this is an empty message -public: - RaceStartMessage() : Message(Message::MT_RACE_START) - { - allocate(0); - } // RaceStartMessage + m_callback_object = callback_object; + m_type = type; +} - RaceStartMessage(ENetPacket* pkt):Message(pkt, MT_RACE_START) - { - } -}; // RaceStartMessage -#endif +Protocol::~Protocol() +{ +} + +void Protocol::pause() +{ + m_listener->requestPause(this); +} +void Protocol::unpause() +{ + m_listener->requestUnpause(this); +} + +void Protocol::kill() +{ +} + +void Protocol::setListener(ProtocolManager* listener) +{ + m_listener = listener; +} + +PROTOCOL_TYPE Protocol::getProtocolType() +{ + return m_type; +} diff --git a/src/network/protocol.hpp b/src/network/protocol.hpp new file mode 100644 index 000000000..2d5494f7f --- /dev/null +++ b/src/network/protocol.hpp @@ -0,0 +1,119 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef PROTOCOL_HPP +#define PROTOCOL_HPP + +#include "network/event.hpp" +#include "network/types.hpp" +#include "utils/types.hpp" + +class ProtocolManager; + +/** \enum PROTOCOL_TYPE + * \brief The types that protocols can have. This is used to select which protocol receives which event. + * \ingroup network + */ +enum PROTOCOL_TYPE +{ + PROTOCOL_NONE = 0, //!< No protocol type assigned. + PROTOCOL_CONNECTION = 1, //!< Protocol that deals with client-server connection. + PROTOCOL_LOBBY_ROOM = 2, //!< Protocol that is used during the lobby room phase. + PROTOCOL_START_GAME = 3, //!< Protocol used when starting the game. + PROTOCOL_SYNCHRONIZATION = 4,//! +#include +#include +#include + +void* protocolManagerUpdate(void* data) +{ + ProtocolManager* manager = static_cast(data); + while(!manager->exit()) + { + manager->update(); + irr_driver->getDevice()->sleep(20); + } + return NULL; +} +void* protocolManagerAsynchronousUpdate(void* data) +{ + ProtocolManager* manager = static_cast(data); + while(!manager->exit()) + { + manager->asynchronousUpdate(); + irr_driver->getDevice()->sleep(20); + } + return NULL; +} + +ProtocolManager::ProtocolManager() +{ + pthread_mutex_init(&m_events_mutex, NULL); + pthread_mutex_init(&m_protocols_mutex, NULL); + pthread_mutex_init(&m_asynchronous_protocols_mutex, NULL); + pthread_mutex_init(&m_requests_mutex, NULL); + pthread_mutex_init(&m_id_mutex, NULL); + pthread_mutex_init(&m_exit_mutex, NULL); + m_next_protocol_id = 0; + + + pthread_mutex_lock(&m_exit_mutex); // will let the update function run + /// FIXME used on server because mainloop never running + /*if (NetworkManager::getInstance()->isServer()) + { + m_update_thread = (pthread_t*)(malloc(sizeof(pthread_t))); + pthread_create(m_update_thread, NULL, protocolManagerUpdate, this); + }*/ + // always run this one + m_asynchronous_update_thread = (pthread_t*)(malloc(sizeof(pthread_t))); + pthread_create(m_asynchronous_update_thread, NULL, protocolManagerAsynchronousUpdate, this); +} + +ProtocolManager::~ProtocolManager() +{ + pthread_mutex_unlock(&m_exit_mutex); // will stop the update function + pthread_mutex_lock(&m_events_mutex); + pthread_mutex_lock(&m_protocols_mutex); + pthread_mutex_lock(&m_asynchronous_protocols_mutex); + pthread_mutex_lock(&m_requests_mutex); + pthread_mutex_lock(&m_id_mutex); + for (unsigned int i = 0; i < m_protocols.size() ; i++) + delete m_protocols[i].protocol; + for (unsigned int i = 0; i < m_events_to_process.size() ; i++) + delete m_events_to_process[i]; + m_protocols.clear(); + m_requests.clear(); + m_events_to_process.clear(); + pthread_mutex_unlock(&m_events_mutex); + pthread_mutex_unlock(&m_protocols_mutex); + pthread_mutex_unlock(&m_asynchronous_protocols_mutex); + pthread_mutex_unlock(&m_requests_mutex); + pthread_mutex_unlock(&m_id_mutex); + + pthread_mutex_destroy(&m_events_mutex); + pthread_mutex_destroy(&m_protocols_mutex); + pthread_mutex_destroy(&m_asynchronous_protocols_mutex); + pthread_mutex_destroy(&m_requests_mutex); + pthread_mutex_destroy(&m_id_mutex); + pthread_mutex_destroy(&m_exit_mutex); +} + +void ProtocolManager::notifyEvent(Event* event) +{ + pthread_mutex_lock(&m_events_mutex); + Event* event2 = new Event(*event); + m_events_to_process.push_back(event2); // add the event to the queue + pthread_mutex_unlock(&m_events_mutex); +} + +void ProtocolManager::sendMessage(Protocol* sender, const NetworkString& message, bool reliable) +{ + NetworkString newMessage; + newMessage.ai8(sender->getProtocolType()); // add one byte to add protocol type + newMessage += message; + NetworkManager::getInstance()->sendPacket(newMessage, reliable); +} + +void ProtocolManager::sendMessage(Protocol* sender, STKPeer* peer, const NetworkString& message, bool reliable) +{ + NetworkString newMessage; + newMessage.ai8(sender->getProtocolType()); // add one byte to add protocol type + newMessage += message; + NetworkManager::getInstance()->sendPacket(peer, newMessage, reliable); +} +void ProtocolManager::sendMessageExcept(Protocol* sender, STKPeer* peer, const NetworkString& message, bool reliable) +{ + NetworkString newMessage; + newMessage.ai8(sender->getProtocolType()); // add one byte to add protocol type + newMessage += message; + NetworkManager::getInstance()->sendPacketExcept(peer, newMessage, reliable); +} + +uint32_t ProtocolManager::requestStart(Protocol* protocol) +{ + // create the request + ProtocolRequest req; + ProtocolInfo info; + info.protocol = protocol; + info.state = PROTOCOL_STATE_RUNNING; + assignProtocolId(&info); // assign a unique id to the protocol. + req.protocol_info = info; + req.type = PROTOCOL_REQUEST_START; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); + + return info.id; +} + +void ProtocolManager::requestStop(Protocol* protocol) +{ + if (!protocol) + return; + // create the request + ProtocolRequest req; + req.protocol_info.protocol = protocol; + req.type = PROTOCOL_REQUEST_STOP; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); +} + +void ProtocolManager::requestPause(Protocol* protocol) +{ + if (!protocol) + return; + // create the request + ProtocolRequest req; + req.protocol_info.protocol = protocol; + req.type = PROTOCOL_REQUEST_PAUSE; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); +} + +void ProtocolManager::requestUnpause(Protocol* protocol) +{ + if (!protocol) + return; + // create the request + ProtocolRequest req; + req.protocol_info.protocol = protocol; + req.type = PROTOCOL_REQUEST_UNPAUSE; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); +} + +void ProtocolManager::requestTerminate(Protocol* protocol) +{ + if (!protocol) + return; + // create the request + ProtocolRequest req; + req.protocol_info.protocol = protocol; + req.type = PROTOCOL_REQUEST_TERMINATE; + // add it to the request stack + pthread_mutex_lock(&m_requests_mutex); + // check that the request does not already exist : + for (unsigned int i = 0; i < m_requests.size(); i++) + { + if (m_requests[i].protocol_info.protocol == protocol) + { + pthread_mutex_unlock(&m_requests_mutex); + return; + } + } + m_requests.push_back(req); + pthread_mutex_unlock(&m_requests_mutex); +} + +void ProtocolManager::startProtocol(ProtocolInfo protocol) +{ + // add the protocol to the protocol vector so that it's updated + pthread_mutex_lock(&m_protocols_mutex); + pthread_mutex_lock(&m_asynchronous_protocols_mutex); + Log::info("ProtocolManager", "A %s protocol with id=%u has been started. There are %ld protocols running.", typeid(*protocol.protocol).name(), protocol.id, m_protocols.size()+1); + m_protocols.push_back(protocol); + // setup the protocol and notify it that it's started + protocol.protocol->setListener(this); + protocol.protocol->setup(); + pthread_mutex_unlock(&m_protocols_mutex); + pthread_mutex_unlock(&m_asynchronous_protocols_mutex); +} +void ProtocolManager::stopProtocol(ProtocolInfo protocol) +{ + +} +void ProtocolManager::pauseProtocol(ProtocolInfo protocol) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol == protocol.protocol && m_protocols[i].state == PROTOCOL_STATE_RUNNING) + { + m_protocols[i].state = PROTOCOL_STATE_PAUSED; + m_protocols[i].protocol->pause(); + } + } +} +void ProtocolManager::unpauseProtocol(ProtocolInfo protocol) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol == protocol.protocol && m_protocols[i].state == PROTOCOL_STATE_PAUSED) + { + m_protocols[i].state = PROTOCOL_STATE_RUNNING; + m_protocols[i].protocol->unpause(); + } + } +} +void ProtocolManager::protocolTerminated(ProtocolInfo protocol) +{ + pthread_mutex_lock(&m_protocols_mutex); // be sure that noone accesses the protocols vector while we erase a protocol + pthread_mutex_lock(&m_asynchronous_protocols_mutex); + int offset = 0; + std::string protocol_type = typeid(*protocol.protocol).name(); + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i-offset].protocol == protocol.protocol) + { + delete m_protocols[i].protocol; + m_protocols.erase(m_protocols.begin()+(i-offset), m_protocols.begin()+(i-offset)+1); + offset++; + } + } + Log::info("ProtocolManager", "A %s protocol has been terminated. There are %ld protocols running.", protocol_type.c_str(), m_protocols.size()); + pthread_mutex_unlock(&m_asynchronous_protocols_mutex); + pthread_mutex_unlock(&m_protocols_mutex); +} + +void ProtocolManager::propagateEvent(Event* event) +{ + PROTOCOL_TYPE searchedProtocol = PROTOCOL_NONE; + if (event->type == EVENT_TYPE_MESSAGE) + { + if (event->data.size() > 0) + searchedProtocol = (PROTOCOL_TYPE)(event->data.getAndRemoveUInt8()); + } + if (event->type == EVENT_TYPE_CONNECTED) + { + searchedProtocol = PROTOCOL_CONNECTION; + } + Log::verbose("ProtocolManager", "Received event for protocols of type %d", searchedProtocol); + for (unsigned int i = 0; i < m_protocols.size() ; i++) + { + if (m_protocols[i].protocol->getProtocolType() == searchedProtocol || event->type == EVENT_TYPE_DISCONNECTED) // pass data to protocols even when paused + m_protocols[i].protocol->notifyEvent(event); + } + if (searchedProtocol == PROTOCOL_NONE) // no protocol was aimed, show the msg to debug + { + Log::debug("ProtocolManager", "NO PROTOCOL : Message is \"%s\"", event->data.c_str()); + } + + // because we made a copy of the event + delete event->peer; // no more need of that + delete event; +} + +void ProtocolManager::update() +{ + // now update all protocols + pthread_mutex_lock(&m_protocols_mutex); + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].state == PROTOCOL_STATE_RUNNING) + m_protocols[i].protocol->update(); + } + pthread_mutex_unlock(&m_protocols_mutex); +} + +void ProtocolManager::asynchronousUpdate() +{ + // before updating, notice protocols that they have received information + int size = m_events_to_process.size(); + for (int i = 0; i < size; i++) + { + pthread_mutex_lock(&m_events_mutex); // secure threads + Event* event = m_events_to_process.back(); + m_events_to_process.pop_back(); + pthread_mutex_unlock(&m_events_mutex); // release the mutex + + propagateEvent(event); + } + + // now update all protocols that need to be updated in asynchronous mode + pthread_mutex_lock(&m_asynchronous_protocols_mutex); + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].state == PROTOCOL_STATE_RUNNING) + m_protocols[i].protocol->asynchronousUpdate(); + } + pthread_mutex_unlock(&m_asynchronous_protocols_mutex); + + // process queued events for protocols + // these requests are asynchronous + pthread_mutex_lock(&m_requests_mutex); + for (unsigned int i = 0; i < m_requests.size(); i++) + { + switch (m_requests[i].type) + { + case PROTOCOL_REQUEST_START: + startProtocol(m_requests[i].protocol_info); + break; + case PROTOCOL_REQUEST_STOP: + stopProtocol(m_requests[i].protocol_info); + break; + case PROTOCOL_REQUEST_PAUSE: + pauseProtocol(m_requests[i].protocol_info); + break; + case PROTOCOL_REQUEST_UNPAUSE: + unpauseProtocol(m_requests[i].protocol_info); + break; + case PROTOCOL_REQUEST_TERMINATE: + protocolTerminated(m_requests[i].protocol_info); + break; + } + } + m_requests.clear(); + pthread_mutex_unlock(&m_requests_mutex); +} + +int ProtocolManager::runningProtocolsCount() +{ + return m_protocols.size(); +} + +PROTOCOL_STATE ProtocolManager::getProtocolState(uint32_t id) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].id == id) // we know a protocol with that id + return m_protocols[i].state; // return its state + } + // the protocol isn't running right now + for (unsigned int i = 0; i < m_requests.size(); i++) + { + if (m_requests[i].protocol_info.id == id) // the protocol is going to be started + return PROTOCOL_STATE_RUNNING; // we can say it's running + } + return PROTOCOL_STATE_TERMINATED; // else, it's already finished +} + +PROTOCOL_STATE ProtocolManager::getProtocolState(Protocol* protocol) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol == protocol) // the protocol is known + return m_protocols[i].state; // return its state + } + for (unsigned int i = 0; i < m_requests.size(); i++) + { + if (m_requests[i].protocol_info.protocol == protocol) // the protocol is going to be started + return PROTOCOL_STATE_RUNNING; // we can say it's running + } + return PROTOCOL_STATE_TERMINATED; // we don't know this protocol at all, it's finished +} + +uint32_t ProtocolManager::getProtocolID(Protocol* protocol) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol == protocol) + return m_protocols[i].id; + } + return 0; +} + +Protocol* ProtocolManager::getProtocol(uint32_t id) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].id == id) + return m_protocols[i].protocol; + } + return NULL; +} + +Protocol* ProtocolManager::getProtocol(PROTOCOL_TYPE type) +{ + for (unsigned int i = 0; i < m_protocols.size(); i++) + { + if (m_protocols[i].protocol->getProtocolType() == type) + return m_protocols[i].protocol; + } + return NULL; +} + +bool ProtocolManager::isServer() +{ + return NetworkManager::getInstance()->isServer(); +} + +int ProtocolManager::exit() +{ + switch(pthread_mutex_trylock(&m_exit_mutex)) { + case 0: /* if we got the lock, unlock and return 1 (true) */ + pthread_mutex_unlock(&m_exit_mutex); + return 1; + case EBUSY: /* return 0 (false) if the mutex was locked */ + return 0; + } + return 1; +} + +void ProtocolManager::assignProtocolId(ProtocolInfo* protocol_info) +{ + pthread_mutex_lock(&m_id_mutex); + protocol_info->id = m_next_protocol_id; + m_next_protocol_id++; + pthread_mutex_unlock(&m_id_mutex); +} + + diff --git a/src/network/protocol_manager.hpp b/src/network/protocol_manager.hpp new file mode 100644 index 000000000..ceb88ba45 --- /dev/null +++ b/src/network/protocol_manager.hpp @@ -0,0 +1,317 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/*! \file protocol_manager.hpp + * \brief Contains structures and enumerations related to protocol management. + */ + +#ifndef PROTOCOL_MANAGER_HPP +#define PROTOCOL_MANAGER_HPP + +#include "network/singleton.hpp" +#include "network/event.hpp" +#include "network/network_string.hpp" +#include "network/protocol.hpp" +#include "utils/types.hpp" + +#include + +/*! + * \enum PROTOCOL_STATE + * \brief Defines the three states that a protocol can have. + */ +enum PROTOCOL_STATE +{ + PROTOCOL_STATE_RUNNING, //!< The protocol is being updated everytime. + PROTOCOL_STATE_PAUSED, //!< The protocol is paused. + PROTOCOL_STATE_TERMINATED //!< The protocol is terminated/does not exist. +}; + +/*! + * \enum PROTOCOL_REQUEST_TYPE + * \brief Defines actions that can be done about protocols. + * This enum is used essentially to keep the manager thread-safe and + * to avoid protocols modifying directly their state. + */ +enum PROTOCOL_REQUEST_TYPE +{ + PROTOCOL_REQUEST_START, //!< Start a protocol + PROTOCOL_REQUEST_STOP, //!< Stop a protocol + PROTOCOL_REQUEST_PAUSE, //!< Pause a protocol + PROTOCOL_REQUEST_UNPAUSE, //!< Unpause a protocol + PROTOCOL_REQUEST_TERMINATE //!< Terminate a protocol +}; + +/*! +* \struct ProtocolInfo +* \brief Stores the information needed to manage protocols +*/ +typedef struct ProtocolInfo +{ + PROTOCOL_STATE state; //!< The state of the protocol + Protocol* protocol; //!< A pointer to the protocol + uint32_t id; //!< The unique id of the protocol +} ProtocolInfo; + +/*! +* \struct ProtocolRequest +* \brief Represents a request to do an action about a protocol. +*/ +typedef struct ProtocolRequest +{ + PROTOCOL_REQUEST_TYPE type; //!< The type of request + ProtocolInfo protocol_info; //!< The concerned protocol information +} ProtocolRequest; + +/*! + * \class ProtocolManager + * \brief Manages the protocols at runtime. + * + * This class is in charge of storing and managing protocols. + * It is a singleton as there can be only one protocol manager per game + * instance. Any game object that wants to start a protocol must create a + * protocol and give it to this singleton. The protocols are updated in a + * special thread, to ensure that they are processed independently from the + * frames per second. Then, the management of protocols is thread-safe: any + * object can start/pause/stop protocols whithout problems. + */ +class ProtocolManager : public Singleton +{ + friend class Singleton; + + public: + /*! + * \brief Function that processes incoming events. + * This function is called by the network manager each time there is an + * incoming packet. + */ + virtual void notifyEvent(Event* event); + /*! + * \brief WILL BE COMMENTED LATER + */ + virtual void sendMessage(Protocol* sender, const NetworkString& message, bool reliable = true); + /*! + * \brief WILL BE COMMENTED LATER + */ + virtual void sendMessage(Protocol* sender, STKPeer* peer, const NetworkString& message, bool reliable = true); + /*! + * \brief WILL BE COMMENTED LATER + */ + virtual void sendMessageExcept(Protocol* sender, STKPeer* peer, const NetworkString& message, bool reliable = true); + + /*! + * \brief Asks the manager to start a protocol. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol to start + * \return The unique id of the protocol that is being started. + */ + virtual uint32_t requestStart(Protocol* protocol); + /*! + * \brief Asks the manager to stop a protocol. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol to stop + */ + virtual void requestStop(Protocol* protocol); + /*! + * \brief Asks the manager to pause a protocol. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol to pause + */ + virtual void requestPause(Protocol* protocol); + /*! + * \brief Asks the manager to unpause a protocol. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol to unpause + */ + virtual void requestUnpause(Protocol* protocol); + /*! + * \brief Notifies the manager that a protocol is terminated. + * This function will store the request, and process it at a time it is + * thread-safe. + * \param protocol : A pointer to the protocol that is finished + */ + virtual void requestTerminate(Protocol* protocol); + + /*! + * \brief Updates the manager. + * + * This function processes the events queue, notifies the concerned + * protocols that they have events to process. Then ask all protocols + * to update themselves. Finally processes stored requests about + * starting, stoping, pausing etc... protocols. + * This function is called by the main loop. + * This function IS FPS-dependant. + */ + virtual void update(); + /*! + * \brief Updates the manager. + * + * This function processes the events queue, notifies the concerned + * protocols that they have events to process. Then ask all protocols + * to update themselves. Finally processes stored requests about + * starting, stoping, pausing etc... protocols. + * This function is called in a thread. + * This function IS NOT FPS-dependant. + */ + virtual void asynchronousUpdate(); + + /*! + * \brief Get the number of protocols running. + * \return The number of protocols that are actually running. + */ + virtual int runningProtocolsCount(); + /*! + * \brief Get the state of a protocol using its id. + * \param id : The id of the protocol you seek the state. + * \return The state of the protocol. + */ + virtual PROTOCOL_STATE getProtocolState(uint32_t id); + /*! + * \brief Get the state of a protocol using a pointer on it. + * \param protocol : A pointer to the protocol you seek the state. + * \return The state of the protocol. + */ + virtual PROTOCOL_STATE getProtocolState(Protocol* protocol); + /*! + * \brief Get the id of a protocol. + * \param protocol : A pointer to the protocol you seek the id. + * \return The id of the protocol pointed by the protocol parameter. + */ + virtual uint32_t getProtocolID(Protocol* protocol); + + /*! + * \brief Get a protocol using its id. + * \param id : Unique ID of the seek protocol. + * \return The protocol that has the ID id. + */ + virtual Protocol* getProtocol(uint32_t id); + /*! + * \brief Get a protocol using its type. + * \param type : The type of the protocol. + * \return The protocol that matches the given type. + */ + virtual Protocol* getProtocol(PROTOCOL_TYPE type); + + /*! \brief Know whether the app is a server. + * \return True if this application is in server mode, false elseway. + */ + bool isServer(); + + /*! \brief Tells if we need to stop the update thread. */ + int exit(); + + protected: + // protected functions + /*! + * \brief Constructor + */ + ProtocolManager(); + /*! + * \brief Destructor + */ + virtual ~ProtocolManager(); + /*! + * \brief Assign an id to a protocol. + * This function will assign m_next_protocol_id as the protocol id. + * This id starts at 0 at the beginning and is increased by 1 each time + * a protocol starts. + * \param protocol_info : The protocol info that needs an id. + */ + void assignProtocolId(ProtocolInfo* protocol_info); + + /*! + * \brief Starts a protocol. + * Add the protocol info to the m_protocols vector. + * \param protocol : ProtocolInfo to start. + */ + virtual void startProtocol(ProtocolInfo protocol); + /*! + * \brief Stops a protocol. + * Coes nothing. Noone can stop running protocols for now. + * \param protocol : ProtocolInfo to stop. + */ + virtual void stopProtocol(ProtocolInfo protocol); + /*! + * \brief Pauses a protocol. + * Pauses a protocol and tells it that it's being paused. + * \param protocol : ProtocolInfo to pause. + */ + virtual void pauseProtocol(ProtocolInfo protocol); + /*! + * \brief Unpauses a protocol. + * Unpauses a protocol and notifies it. + * \param protocol : ProtocolInfo to unpause. + */ + virtual void unpauseProtocol(ProtocolInfo protocol); + /*! + * \brief Notes that a protocol is terminated. + * Remove a protocol from the protocols vector. + * \param protocol : ProtocolInfo concerned. + */ + virtual void protocolTerminated(ProtocolInfo protocol); + + void propagateEvent(Event* event); + + // protected members + /*! + * \brief Contains the running protocols. + * This stores the protocols that are either running or paused, their + * state and their unique id. + */ + std::vector m_protocols; + /*! + * \brief Contains the network events to pass to protocols. + */ + std::vector m_events_to_process; + /*! + * \brief Contains the requests to start/stop etc... protocols. + */ + std::vector m_requests; + /*! \brief The next id to assign to a protocol. + * This value is incremented by 1 each time a protocol is started. + * If a protocol has an id lower than this value, it means that it have + * been formerly started. + */ + uint32_t m_next_protocol_id; + + // mutexes: + /*! Used to ensure that the event queue is used thread-safely. */ + pthread_mutex_t m_events_mutex; + /*! Used to ensure that the protocol vector is used thread-safely. */ + pthread_mutex_t m_protocols_mutex; + /*! Used to ensure that the protocol vector is used thread-safely. */ + pthread_mutex_t m_asynchronous_protocols_mutex; + /*! Used to ensure that the request vector is used thread-safely. */ + pthread_mutex_t m_requests_mutex; + /*! Used to ensure that the protocol id is used in a thread-safe way.*/ + pthread_mutex_t m_id_mutex; + /*! Used when need to quit.*/ + pthread_mutex_t m_exit_mutex; + + /*! Update thread.*/ + pthread_t* m_update_thread; + /*! Asynchronous update thread.*/ + pthread_t* m_asynchronous_update_thread; + +}; + +#endif // PROTOCOL_MANAGER_HPP diff --git a/src/network/protocols/client_lobby_room_protocol.cpp b/src/network/protocols/client_lobby_room_protocol.cpp new file mode 100644 index 000000000..0768739ac --- /dev/null +++ b/src/network/protocols/client_lobby_room_protocol.cpp @@ -0,0 +1,457 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/protocols/client_lobby_room_protocol.hpp" + +#include "network/network_manager.hpp" +#include "network/protocols/start_game_protocol.hpp" +#include "online/current_user.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/network_kart_selection.hpp" +#include "utils/log.hpp" + +ClientLobbyRoomProtocol::ClientLobbyRoomProtocol(const TransportAddress& server_address) + : LobbyRoomProtocol(NULL) +{ + m_server_address = server_address; + m_server = NULL; +} + +//----------------------------------------------------------------------------- + +ClientLobbyRoomProtocol::~ClientLobbyRoomProtocol() +{ +} + +//----------------------------------------------------------------------------- + +void ClientLobbyRoomProtocol::setup() +{ + m_setup = NetworkManager::getInstance()->setupNewGame(); // create a new setup + m_state = NONE; +} + +//----------------------------------------------------------------------------- + +void ClientLobbyRoomProtocol::requestKartSelection(std::string kart_name) +{ + NetworkString request; + // 0x02 : kart selection request, size_token (4), token, size kart name, kart name + request.ai8(0x02).ai8(4).ai32(m_server->getClientServerToken()).ai8(kart_name.size()).as(kart_name); + m_listener->sendMessage(this, request); +} + +//----------------------------------------------------------------------------- + +void ClientLobbyRoomProtocol::leave() +{ + m_server->disconnect(); + m_server_address.ip = 0; + m_server_address.port = 0; +} + +//----------------------------------------------------------------------------- + +void ClientLobbyRoomProtocol::notifyEvent(Event* event) +{ + assert(m_setup); // assert that the setup exists + if (event->type == EVENT_TYPE_MESSAGE) + { + assert(event->data.size()); // assert that data isn't empty + uint8_t message_type = event->data.getAndRemoveUInt8(); + + Log::info("ClientLobbyRoomProtocol", "Message of type %d", message_type); + if (message_type == 0x01) // new player connected + newPlayer(event); + else if (message_type == 0x02) // player disconnected + disconnectedPlayer(event); + else if (message_type == 0x03) // kart selection update + kartSelectionUpdate(event); + else if (message_type == 0x04) // start race + startGame(event); + else if (message_type == 0x05) // start selection phase + startSelection(event); + else if (message_type == 0x80) // connection refused + connectionRefused(event); + else if (message_type == 0x81) // connection accepted + connectionAccepted(event); + else if (message_type == 0x82) // kart selection refused + kartSelectionRefused(event); + + } // message + else if (event->type == EVENT_TYPE_CONNECTED) + { + } // connection + else if (event->type == EVENT_TYPE_DISCONNECTED) // means we left essentially + { + NetworkManager::getInstance()->removePeer(m_server); + m_server = NULL; + NetworkManager::getInstance()->disconnected(); + m_listener->requestTerminate(this); + NetworkManager::getInstance()->reset(); + NetworkManager::getInstance()->removePeer(*event->peer); // prolly the same as m_server + } // disconnection +} + +//----------------------------------------------------------------------------- + +void ClientLobbyRoomProtocol::update() +{ + switch (m_state) + { + case NONE: + if (NetworkManager::getInstance()->isConnectedTo(m_server_address)) + { + m_state = LINKED; + } + break; + case LINKED: + { + NetworkString ns; + // 1 (connection request), 4 (size of id), global id + ns.ai8(1).ai8(4).ai32(Online::CurrentUser::get()->getUserID()); + m_listener->sendMessage(this, ns); + m_state = REQUESTING_CONNECTION; + } break; + case REQUESTING_CONNECTION: + break; + case CONNECTED: + break; + case KART_SELECTION: + { + NetworkKartSelectionScreen* screen = NetworkKartSelectionScreen::getInstance(); + StateManager::get()->pushScreen(screen); + m_state = SELECTING_KARTS; + } break; + case SELECTING_KARTS: + break; + case PLAYING: + break; + case DONE: + m_state = EXITING; + m_listener->requestTerminate(this); + break; + case EXITING: + break; + } +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when a new player is connected to the server + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 6 7 + * ------------------------------------------------ + * Size | 1 | 4 | 1 | 1 | + * Data | 4 | player global id | 1 | 0 <= race id < 16 | + * ------------------------------------------------ + */ +void ClientLobbyRoomProtocol::newPlayer(Event* event) +{ + if (event->data.size() != 7 || event->data[0] != 4 || event->data[5] != 1) // 7 bytes remains now + { + Log::error("ClientLobbyRoomProtocol", "A message notifying a new player wasn't formated as expected."); + return; + } + + uint32_t global_id = event->data.gui32(1); + uint8_t race_id = event->data.gui8(6); + + if (global_id == Online::CurrentUser::get()->getUserID()) + { + Log::error("ClientLobbyRoomProtocol", "The server notified me that i'm a new player in the room (not normal)."); + } + else if (m_setup->getProfile(race_id) == NULL || m_setup->getProfile(global_id) == NULL) + { + Log::verbose("ClientLobbyRoomProtocol", "New player connected."); + NetworkPlayerProfile* profile = new NetworkPlayerProfile(); + profile->kart_name = ""; + profile->race_id = race_id; + profile->user_profile = new Online::User("", global_id); + m_setup->addPlayer(profile); + } + else + { + Log::error("ClientLobbyRoomProtocol", "One of the player notified in the list is myself."); + } +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when a new player is disconnected + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 2 + * ------------------------- + * Size | 1 | 1 | + * Data | 1 | 0 <= race id < 16 | + * ------------------------- + */ +void ClientLobbyRoomProtocol::disconnectedPlayer(Event* event) +{ + if (event->data.size() != 2 || event->data[0] != 1) + { + Log::error("ClientLobbyRoomProtocol", "A message notifying a new player wasn't formated as expected."); + return; + } + uint8_t id = event->data[1]; + if (m_setup->removePlayer(id)) + { + Log::info("ClientLobbyRoomProtocol", "Peer removed successfully."); + } + else + { + Log::error("ClientLobbyRoomProtocol", "The disconnected peer wasn't known."); + } +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when the server accepts the connection. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 2 3 7 8 12 + * ---------------------------------------------------------- + * Size | 1 | 1 | 1 | 4 | 1 | 4 | + * Data | 1 | 0 <= race id < 16 | 4 | priv token | 4 | global id | + * ---------------------------------------------------------- + */ +void ClientLobbyRoomProtocol::connectionAccepted(Event* event) +{ + if (event->data.size() < 12 || event->data[0] != 1 || event->data[2] != 4 || event->data[7] != 4) // 12 bytes remains now + { + Log::error("ClientLobbyRoomProtocol", "A message notifying an accepted connection wasn't formated as expected."); + return; + } + STKPeer* peer = *(event->peer); + + uint32_t global_id = event->data.gui32(8); + if (global_id == Online::CurrentUser::get()->getUserID()) + { + Log::info("ClientLobbyRoomProtocol", "The server accepted the connection."); + + // self profile + NetworkPlayerProfile* profile = new NetworkPlayerProfile(); + profile->kart_name = ""; + profile->race_id = event->data.gui8(1); + profile->user_profile = Online::CurrentUser::get(); + m_setup->addPlayer(profile); + // connection token + uint32_t token = event->data.gui32(3); + peer->setClientServerToken(token); + // add all players + event->data.removeFront(12); // remove the 12 first bytes + int remaining = event->data.size(); + if (remaining%7 != 0) + { + Log::error("ClientLobbyRoomProtocol", "ConnectionAccepted : Error in the server list"); + } + remaining /= 7; + for (int i = 0; i < remaining; i++) + { + if (event->data[0] != 1 || event->data[2] != 4) + Log::error("ClientLobbyRoomProtocol", "Bad format in players list."); + uint8_t race_id = event->data[1]; + uint32_t global_id = event->data.gui32(3); + Online::User* new_user = new Online::User("", global_id); + NetworkPlayerProfile* profile2 = new NetworkPlayerProfile(); + profile2->race_id = race_id; + profile2->user_profile = new_user; + profile2->kart_name = ""; + m_setup->addPlayer(profile2); + event->data.removeFront(7); + } + + // add self + m_server = *(event->peer); + m_state = CONNECTED; + } + else + Log::info("ClientLobbyRoomProtocol", "Failure during the connection acceptation process."); +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when the server refuses the connection. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 2 + * -------------------- + * Size | 1 | 1 | + * Data | 1 | refusal code | + * -------------------- + */ +void ClientLobbyRoomProtocol::connectionRefused(Event* event) +{ + if (event->data.size() != 2 || event->data[0] != 1) // 2 bytes remains now + { + Log::error("ClientLobbyRoomProtocol", "A message notifying a refused connection wasn't formated as expected."); + return; + } + + switch (event->data[1]) // the second byte + { + case 0: + Log::info("ClientLobbyRoomProtocol", "Connection refused : too many players."); + break; + case 1: + Log::info("ClientLobbyRoomProtocol", "Connection refused : banned."); + break; + default: + Log::info("ClientLobbyRoomProtocol", "Connection refused."); + break; + } +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when the server refuses the kart selection request. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 2 + * -------------------- + * Size | 1 | 1 | + * Data | 1 | refusal code | + * -------------------- + */ +void ClientLobbyRoomProtocol::kartSelectionRefused(Event* event) +{ + if (event->data.size() != 2 || event->data[0] != 1) + { + Log::error("ClientLobbyRoomProtocol", "A message notifying a refused kart selection wasn't formated as expected."); + return; + } + + switch (event->data[1]) // the error code + { + case 0: + Log::info("ClientLobbyRoomProtocol", "Kart selection refused : already taken."); + break; + case 1: + Log::info("ClientLobbyRoomProtocol", "Kart selection refused : not available."); + break; + default: + Log::info("ClientLobbyRoomProtocol", "Kart selection refused."); + break; + } +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when the server tells to update a player's kart. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 2 3 N+3 + * ------------------------------------------------ + * Size | 1 | 1 | 1 | N | + * Data | 1 | race id | N (kart name size) | kart name | + * ------------------------------------------------ + */ +void ClientLobbyRoomProtocol::kartSelectionUpdate(Event* event) +{ + if (event->data.size() < 3 || event->data[0] != 1) + { + Log::error("ClientLobbyRoomProtocol", "A message notifying a kart selection update wasn't formated as expected."); + return; + } + uint8_t player_id = event->data[1]; + uint8_t kart_name_length = event->data[2]; + std::string data = event->data.getString(3, kart_name_length); + if (data.size() != kart_name_length) + { + Log::error("ClientLobbyRoomProtocol", "Kart names sizes differ: told: %d, real: %d.", kart_name_length, data.size()); + return; + } + if (!m_setup->isKartAvailable(data)) + { + Log::error("ClientLobbyRoomProtocol", "The updated kart is taken already."); + } + m_setup->setPlayerKart(player_id, data); +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when the race needs to be started. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 + * ------------- + * Size | 1 | 4 | + * Data | 4 | token | + * ------------- + */ +void ClientLobbyRoomProtocol::startGame(Event* event) +{ + if (event->data.size() < 5 || event->data[0] != 4) + { + Log::error("ClientLobbyRoomProtocol", "A message notifying a kart " + "selection update wasn't formated as expected."); + return; + } + uint8_t token = event->data.gui32(1); + if (token == NetworkManager::getInstance()->getPeers()[0]->getClientServerToken()) + { + m_state = PLAYING; + m_listener->requestStart(new StartGameProtocol(m_setup)); + Log::error("ClientLobbyRoomProtocol", "Starting new game"); + } + else + Log::error("ClientLobbyRoomProtocol", "Bad token when starting game"); + +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when the kart selection starts. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 + * ------------- + * Size | 1 | 4 | + * Data | 4 | token | + * ------------- + */ +void ClientLobbyRoomProtocol::startSelection(Event* event) +{ + if (event->data.size() < 5 || event->data[0] != 4) + { + Log::error("ClientLobbyRoomProtocol", "A message notifying a kart " + "selection update wasn't formated as expected."); + return; + } + uint8_t token = event->data.gui32(1); + if (token == NetworkManager::getInstance()->getPeers()[0]->getClientServerToken()) + { + m_state = KART_SELECTION; + Log::info("ClientLobbyRoomProtocol", "Kart selection starts now"); + } + else + Log::error("ClientLobbyRoomProtocol", "Bad token"); + +} + +//----------------------------------------------------------------------------- diff --git a/src/network/protocols/client_lobby_room_protocol.hpp b/src/network/protocols/client_lobby_room_protocol.hpp new file mode 100644 index 000000000..5866e4ff0 --- /dev/null +++ b/src/network/protocols/client_lobby_room_protocol.hpp @@ -0,0 +1,49 @@ +#ifndef CLIENT_LOBBY_ROOM_PROTOCOL_HPP +#define CLIENT_LOBBY_ROOM_PROTOCOL_HPP + +#include "network/protocols/lobby_room_protocol.hpp" + +class ClientLobbyRoomProtocol : public LobbyRoomProtocol +{ + public: + ClientLobbyRoomProtocol(const TransportAddress& server_address); + virtual ~ClientLobbyRoomProtocol(); + + void requestKartSelection(std::string kart_name); + void sendMessage(std::string message); + void leave(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update(); + virtual void asynchronousUpdate() {} + + protected: + void newPlayer(Event* event); + void disconnectedPlayer(Event* event); + void connectionAccepted(Event* event); //!< Callback function on connection acceptation + void connectionRefused(Event* event); //!< Callback function on connection refusal + void kartSelectionRefused(Event* event); + void kartSelectionUpdate(Event* event); + void startGame(Event* event); + void startSelection(Event* event); + + TransportAddress m_server_address; + STKPeer* m_server; + + enum STATE + { + NONE, + LINKED, + REQUESTING_CONNECTION, + CONNECTED, // means in the lobby room + KART_SELECTION, + SELECTING_KARTS, // in the network kart selection screen + PLAYING, + DONE, + EXITING + }; + STATE m_state; +}; + +#endif // CLIENT_LOBBY_ROOM_PROTOCOL_HPP diff --git a/src/network/protocols/connect_to_peer.cpp b/src/network/protocols/connect_to_peer.cpp new file mode 100644 index 000000000..d810234af --- /dev/null +++ b/src/network/protocols/connect_to_peer.cpp @@ -0,0 +1,115 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/protocols/connect_to_peer.hpp" + +#include "network/client_network_manager.hpp" +#include "network/protocols/get_public_address.hpp" +#include "network/protocols/get_peer_address.hpp" +#include "network/protocols/show_public_address.hpp" +#include "network/protocols/hide_public_address.hpp" +#include "network/protocols/request_connection.hpp" +#include "network/protocols/ping_protocol.hpp" +#include "online/current_user.hpp" +#include "utils/time.hpp" +#include "utils/log.hpp" + +// ---------------------------------------------------------------------------- + +ConnectToPeer::ConnectToPeer(uint32_t peer_id) : + Protocol(NULL, PROTOCOL_CONNECTION) +{ + m_peer_id = peer_id; + m_state = NONE; +} + +// ---------------------------------------------------------------------------- + +ConnectToPeer::~ConnectToPeer() +{ +} + +// ---------------------------------------------------------------------------- + +void ConnectToPeer::notifyEvent(Event* event) +{ + if (event->type == EVENT_TYPE_CONNECTED) + { + Log::debug("ConnectToPeer", "Received event notifying peer connection."); + m_state = CONNECTED; // we received a message, we are connected + } +} + +// ---------------------------------------------------------------------------- + +void ConnectToPeer::setup() +{ + m_state = NONE; + m_public_address.ip = 0; + m_public_address.port = 0; + m_peer_address.ip = 0; + m_peer_address.port = 0; + m_current_protocol_id = 0; +} + +// ---------------------------------------------------------------------------- + +void ConnectToPeer::asynchronousUpdate() +{ + switch(m_state) + { + case NONE: + { + m_current_protocol_id = m_listener->requestStart(new GetPeerAddress(m_peer_id, &m_peer_address)); + m_state = WAITING_PEER_ADDRESS; + break; + } + case WAITING_PEER_ADDRESS: + if (m_listener->getProtocolState(m_current_protocol_id) + == PROTOCOL_STATE_TERMINATED) // we know the peer address + { + if (m_peer_address.ip != 0 && m_peer_address.port != 0) + { + m_state = CONNECTING; + m_current_protocol_id = m_listener->requestStart(new PingProtocol(m_peer_address, 2.0)); + } + else + { + Log::error("ConnectToPeer", "The peer you want to connect to has hidden his address."); + m_state = DONE; + } + } + break; + case CONNECTING: // waiting the peer to connect + case CONNECTED: + { + m_listener->requestTerminate( m_listener->getProtocol(m_current_protocol_id)); // kill the ping protocol because we're connected + m_state = DONE; + break; + } + case DONE: + m_state = EXITING; + m_listener->requestTerminate(this); + break; + case EXITING: + break; + } +} + +// ---------------------------------------------------------------------------- + diff --git a/src/network/protocols/connect_to_peer.hpp b/src/network/protocols/connect_to_peer.hpp new file mode 100644 index 000000000..03bbab88d --- /dev/null +++ b/src/network/protocols/connect_to_peer.hpp @@ -0,0 +1,55 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef CONNECT_TO_SERVER_HPP +#define CONNECT_TO_SERVER_HPP + +#include "network/protocol.hpp" +#include "network/types.hpp" +#include + +class ConnectToPeer : public Protocol, public CallbackObject +{ + public: + ConnectToPeer(uint32_t peer_id); + virtual ~ConnectToPeer(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + TransportAddress m_peer_address; + TransportAddress m_public_address; + uint32_t m_peer_id; + uint32_t m_current_protocol_id; + + enum STATE + { + NONE, + WAITING_PEER_ADDRESS, + CONNECTING, + CONNECTED, + DONE, + EXITING + }; + STATE m_state; +}; + +#endif // CONNECT_TO_SERVER_HPP diff --git a/src/network/protocols/connect_to_server.cpp b/src/network/protocols/connect_to_server.cpp new file mode 100644 index 000000000..72c6445b8 --- /dev/null +++ b/src/network/protocols/connect_to_server.cpp @@ -0,0 +1,192 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/protocols/connect_to_server.hpp" + +#include "network/client_network_manager.hpp" +#include "network/protocols/get_public_address.hpp" +#include "network/protocols/get_peer_address.hpp" +#include "network/protocols/show_public_address.hpp" +#include "network/protocols/hide_public_address.hpp" +#include "network/protocols/request_connection.hpp" +#include "network/protocols/ping_protocol.hpp" +#include "network/protocols/quick_join_protocol.hpp" +#include "network/protocols/client_lobby_room_protocol.hpp" +#include "online/current_user.hpp" +#include "utils/time.hpp" +#include "utils/log.hpp" + +// ---------------------------------------------------------------------------- + +ConnectToServer::ConnectToServer() : + Protocol(NULL, PROTOCOL_CONNECTION) +{ + m_server_id = 0; + m_quick_join = true; + m_state = NONE; +} + +// ---------------------------------------------------------------------------- + +ConnectToServer::ConnectToServer(uint32_t server_id, uint32_t host_id) : + Protocol(NULL, PROTOCOL_CONNECTION) +{ + m_server_id = server_id; + m_host_id = host_id; + m_quick_join = false; + m_state = NONE; +} + +// ---------------------------------------------------------------------------- + +ConnectToServer::~ConnectToServer() +{ +} + +// ---------------------------------------------------------------------------- + +void ConnectToServer::notifyEvent(Event* event) +{ + if (event->type == EVENT_TYPE_CONNECTED) + { + Log::info("ConnectToServer", "The Connect To Server protocol has " + "received an event notifying that he's connected to the peer."); + m_state = CONNECTED; // we received a message, we are connected + } +} + +// ---------------------------------------------------------------------------- + +void ConnectToServer::setup() +{ + Log::info("ConnectToServer", "SETUPP"); + m_state = NONE; + m_public_address.ip = 0; + m_public_address.port = 0; + m_server_address.ip = 0; + m_server_address.port = 0; + m_current_protocol_id = 0; +} + +// ---------------------------------------------------------------------------- + +void ConnectToServer::asynchronousUpdate() +{ + switch(m_state) + { + case NONE: + { + Log::info("ConnectToServer", "Protocol starting"); + m_current_protocol_id = m_listener->requestStart(new GetPublicAddress(&m_public_address)); + m_state = GETTING_SELF_ADDRESS; + break; + } + case GETTING_SELF_ADDRESS: + if (m_listener->getProtocolState(m_current_protocol_id) + == PROTOCOL_STATE_TERMINATED) // now we know the public addr + { + m_state = SHOWING_SELF_ADDRESS; + NetworkManager::getInstance()->setPublicAddress(m_public_address); // set our public address + m_current_protocol_id = m_listener->requestStart(new ShowPublicAddress()); + Log::info("ConnectToServer", "Public address known"); + /* + if (m_quick_join) + m_current_protocol_id = m_listener->requestStart(new QuickJoinProtocol(&m_server_address, &m_server_id)); + else + m_current_protocol_id = m_listener->requestStart(new GetPeerAddress(m_server_id, &m_server_address));*/ + } + break; + case SHOWING_SELF_ADDRESS: + if (m_listener->getProtocolState(m_current_protocol_id) + == PROTOCOL_STATE_TERMINATED) // now our public address is in the database + { + Log::info("ConnectToServer", "Public address shown"); + if (m_quick_join) + { + m_current_protocol_id = m_listener->requestStart(new QuickJoinProtocol(&m_server_address, &m_server_id)); + m_state = REQUESTING_CONNECTION; + } + else + { + m_current_protocol_id = m_listener->requestStart(new GetPeerAddress(m_host_id, &m_server_address)); + m_state = GETTING_SERVER_ADDRESS; + } + } + break; + case GETTING_SERVER_ADDRESS: + if (m_listener->getProtocolState(m_current_protocol_id) + == PROTOCOL_STATE_TERMINATED) // we know the server address + { + m_state = REQUESTING_CONNECTION; + m_current_protocol_id = m_listener->requestStart(new RequestConnection(m_server_id)); + Log::info("ConnectToServer", "Server's address known"); + } + break; + case REQUESTING_CONNECTION: + if (m_listener->getProtocolState(m_current_protocol_id) + == PROTOCOL_STATE_TERMINATED) // server knows we wanna connect + { + Log::info("ConnectToServer", "Connection request made"); + if (m_server_address.ip == 0 || m_server_address.port == 0) + { // server data not correct, hide address and stop + m_state = HIDING_ADDRESS; + m_current_protocol_id = m_listener->requestStart(new HidePublicAddress()); + return; + } + m_state = CONNECTING; + m_current_protocol_id = m_listener->requestStart(new PingProtocol(m_server_address, 2.0)); + } + break; + case CONNECTING: // waiting the server to answer our connection + { + static double timer = 0; + if (Time::getRealTime() > timer+5.0) // every 5 seconds + { + timer = Time::getRealTime(); + NetworkManager::getInstance()->connect(m_server_address); + Log::info("ConnectToServer", "Trying to connect"); + } + break; + } + case CONNECTED: + { + Log::info("ConnectToServer", "Connected"); + m_listener->requestTerminate( m_listener->getProtocol(m_current_protocol_id)); // kill the ping protocol because we're connected + m_current_protocol_id = m_listener->requestStart(new HidePublicAddress()); + ClientNetworkManager::getInstance()->setConnected(true); + m_state = HIDING_ADDRESS; + break; + } + case HIDING_ADDRESS: + if (m_listener->getProtocolState(m_current_protocol_id) + == PROTOCOL_STATE_TERMINATED) // we have hidden our address + { + Log::info("ConnectToServer", "Address hidden"); + m_state = DONE; + if (ClientNetworkManager::getInstance()->isConnected()) // lobby room protocol if we're connected only + m_listener->requestStart(new ClientLobbyRoomProtocol(m_server_address)); + } + break; + case DONE: + m_listener->requestTerminate(this); + break; + } +} + +// ---------------------------------------------------------------------------- + diff --git a/src/network/protocols/connect_to_server.hpp b/src/network/protocols/connect_to_server.hpp new file mode 100644 index 000000000..c49523e48 --- /dev/null +++ b/src/network/protocols/connect_to_server.hpp @@ -0,0 +1,61 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef CONNECT_TO_SERVER_HPP +#define CONNECT_TO_SERVER_HPP + +#include "network/protocol.hpp" +#include "network/types.hpp" +#include + +class ConnectToServer : public Protocol, public CallbackObject +{ + public: + ConnectToServer(); //!< Quick join + ConnectToServer(uint32_t server_id, uint32_t host_id); //!< Specify server id + virtual ~ConnectToServer(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + TransportAddress m_server_address; + TransportAddress m_public_address; + uint32_t m_server_id; + uint32_t m_host_id; + uint32_t m_current_protocol_id; + bool m_quick_join; + + enum STATE + { + NONE, + GETTING_SELF_ADDRESS, + SHOWING_SELF_ADDRESS, + GETTING_SERVER_ADDRESS, + REQUESTING_CONNECTION, + CONNECTING, + CONNECTED, + HIDING_ADDRESS, + DONE + }; + STATE m_state; +}; + +#endif // CONNECT_TO_SERVER_HPP diff --git a/src/network/protocols/controller_events_protocol.cpp b/src/network/protocols/controller_events_protocol.cpp new file mode 100644 index 000000000..ed91740a3 --- /dev/null +++ b/src/network/protocols/controller_events_protocol.cpp @@ -0,0 +1,168 @@ +#include "network/protocols/controller_events_protocol.hpp" + +#include "modes/world.hpp" +#include "karts/abstract_kart.hpp" +#include "network/network_manager.hpp" +#include "network/network_world.hpp" +#include "utils/log.hpp" + +//----------------------------------------------------------------------------- + +ControllerEventsProtocol::ControllerEventsProtocol() : + Protocol(NULL, PROTOCOL_CONTROLLER_EVENTS) +{ +} + +//----------------------------------------------------------------------------- + +ControllerEventsProtocol::~ControllerEventsProtocol() +{ +} + +//----------------------------------------------------------------------------- + +void ControllerEventsProtocol::setup() +{ + m_self_controller_index = 0; + std::vector karts = World::getWorld()->getKarts(); + std::vector peers = NetworkManager::getInstance()->getPeers(); + for (unsigned int i = 0; i < karts.size(); i++) + { + if (karts[i]->getIdent() == NetworkWorld::getInstance()->m_self_kart) + { + Log::info("ControllerEventsProtocol", "My id is %d", i); + m_self_controller_index = i; + } + STKPeer* peer = NULL; + if (m_listener->isServer()) + { + for (unsigned int j = 0; j < peers.size(); j++) + { + if (peers[j]->getPlayerProfile()->kart_name == karts[i]->getIdent()) + { + peer = peers[j]; + } + Log::info("ControllerEventsProtocol", "Compared %s and %s", + peers[j]->getPlayerProfile()->kart_name.c_str(), karts[i]->getIdent().c_str()); + } + } + else + { + if (peers.size() > 0) + peer = peers[0]; + } + if (peer == NULL) + { + Log::error("ControllerEventsProtocol", "Couldn't find the peer corresponding to the kart."); + } + m_controllers.push_back(std::pair(karts[i]->getController(), peer)); + } +} + +//----------------------------------------------------------------------------- + +void ControllerEventsProtocol::notifyEvent(Event* event) +{ + if (event->data.size() < 17) + { + Log::error("ControllerEventsProtocol", "The data supplied was not complete. Size was %d.", event->data.size()); + return; + } + uint32_t token = event->data.gui32(); + NetworkString pure_message = event->data; + pure_message.removeFront(4); + if (token != (*event->peer)->getClientServerToken()) + { + Log::error("ControllerEventsProtocol", "Bad token from peer."); + return; + } + NetworkString ns = pure_message; + float event_timestamp = ns.getFloat(); + ns.removeFront(4); + uint8_t client_index = -1; + while (ns.size() >= 9) + { + uint8_t controller_index = ns.gui8(); + client_index = controller_index; + uint8_t serialized_1 = ns.gui8(1), serialized_2 = ns.gui8(2), serialized_3 = ns.gui8(3); + PlayerAction action = (PlayerAction)(ns.gui8(4)); + int action_value = ns.gui32(5); + + KartControl* controls = m_controllers[controller_index].first->getControls(); + controls->m_brake = (serialized_1 & 0x40)!=0; + controls->m_nitro = (serialized_1 & 0x20)!=0; + controls->m_rescue = (serialized_1 & 0x10)!=0; + controls->m_fire = (serialized_1 & 0x08)!=0; + controls->m_look_back = (serialized_1 & 0x04)!=0; + controls->m_skid = KartControl::SkidControl(serialized_1 & 0x03); + + m_controllers[controller_index].first->action(action, action_value); + ns.removeFront(9); + //Log::info("ControllerEventProtocol", "Registered one action."); + } + if (ns.size() > 0 && ns.size() != 9) + { + Log::warn("ControllerEventProtocol", "The data seems corrupted. Remains %d", ns.size()); + return; + } + if (client_index < 0) + { + Log::warn("ControllerEventProtocol", "Couldn't have a client id."); + return; + } + if (m_listener->isServer()) + { + // notify everybody of the event : + for (unsigned int i = 0; i < m_controllers.size(); i++) + { + if (i == client_index) // don't send that message to the sender + continue; + NetworkString ns2; + ns2.ai32(m_controllers[i].second->getClientServerToken()); + ns2 += pure_message; + m_listener->sendMessage(this, m_controllers[i].second, ns2, false); + //Log::info("ControllerEventsProtocol", "Sizes are %d and %d", ns2.size(), pure_message.size()); + } + } +} + +//----------------------------------------------------------------------------- + +void ControllerEventsProtocol::update() +{ +} + +//----------------------------------------------------------------------------- + +void ControllerEventsProtocol::controllerAction(Controller* controller, + PlayerAction action, int value) +{ + assert(!m_listener->isServer()); + + KartControl* controls = controller->getControls(); + uint8_t serialized_1 = 0; + serialized_1 |= (controls->m_brake==true); + serialized_1 <<= 1; + serialized_1 |= (controls->m_nitro==true); + serialized_1 <<= 1; + serialized_1 |= (controls->m_rescue==true); + serialized_1 <<= 1; + serialized_1 |= (controls->m_fire==true); + serialized_1 <<= 1; + serialized_1 |= (controls->m_look_back==true); + serialized_1 <<= 2; + serialized_1 += controls->m_skid; + uint8_t serialized_2 = (uint8_t)(controls->m_accel*255.0); + uint8_t serialized_3 = (uint8_t)(controls->m_steer*127.0); + + NetworkString ns; + ns.ai32(m_controllers[m_self_controller_index].second->getClientServerToken()); + ns.af(World::getWorld()->getTime()); + ns.ai8(m_self_controller_index); + ns.ai8(serialized_1).ai8(serialized_2).ai8(serialized_3); + ns.ai8((uint8_t)(action)).ai32(value); + + m_listener->sendMessage(this, ns, false); // send message to server +} + + diff --git a/src/network/protocols/controller_events_protocol.hpp b/src/network/protocols/controller_events_protocol.hpp new file mode 100644 index 000000000..3500765bd --- /dev/null +++ b/src/network/protocols/controller_events_protocol.hpp @@ -0,0 +1,28 @@ +#ifndef CONTROLLER_EVENTS_PROTOCOL_HPP +#define CONTROLLER_EVENTS_PROTOCOL_HPP + +#include "network/protocol.hpp" + +#include "input/input.hpp" +#include "karts/controller/controller.hpp" + +class ControllerEventsProtocol : public Protocol +{ + protected: + std::vector > m_controllers; + uint32_t m_self_controller_index; + + public: + ControllerEventsProtocol(); + virtual ~ControllerEventsProtocol(); + + virtual void setup(); + virtual void notifyEvent(Event* event); + virtual void update(); + virtual void asynchronousUpdate() {} + + void controllerAction(Controller* controller, PlayerAction action, int value); + +}; + +#endif // CONTROLLER_EVENTS_PROTOCOL_HPP diff --git a/src/network/protocols/game_events_protocol.cpp b/src/network/protocols/game_events_protocol.cpp new file mode 100644 index 000000000..2b9ded2fd --- /dev/null +++ b/src/network/protocols/game_events_protocol.cpp @@ -0,0 +1,21 @@ +#include "network/protocols/game_events_protocol.hpp" + +GameEventsProtocol::GameEventsProtocol() : Protocol(NULL, PROTOCOL_GAME_EVENTS) +{ +} + +GameEventsProtocol::~GameEventsProtocol() +{ +} + +void GameEventsProtocol::notifyEvent(Event* event) +{ +} + +void GameEventsProtocol::setup() +{ +} + +void GameEventsProtocol::update() +{ +} diff --git a/src/network/protocols/game_events_protocol.hpp b/src/network/protocols/game_events_protocol.hpp new file mode 100644 index 000000000..0a3d6107c --- /dev/null +++ b/src/network/protocols/game_events_protocol.hpp @@ -0,0 +1,21 @@ +#ifndef GAME_EVENTS_PROTOCOL_HPP +#define GAME_EVENTS_PROTOCOL_HPP + +#include "network/protocol.hpp" + + +class GameEventsProtocol : public Protocol +{ + public: + GameEventsProtocol(); + virtual ~GameEventsProtocol(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update(); + virtual void asynchronousUpdate() {} + + protected: +}; + +#endif // GAME_EVENTS_PROTOCOL_HPP diff --git a/src/network/protocols/get_peer_address.cpp b/src/network/protocols/get_peer_address.cpp new file mode 100644 index 000000000..4b71feb13 --- /dev/null +++ b/src/network/protocols/get_peer_address.cpp @@ -0,0 +1,99 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/protocols/get_peer_address.hpp" + +#include "network/protocol_manager.hpp" +#include "network/http_functions.hpp" +#include "online/http_manager.hpp" +#include "online/current_user.hpp" +#include "config/user_config.hpp" +#include "utils/log.hpp" + +GetPeerAddress::GetPeerAddress(uint32_t peer_id, CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_SILENT) +{ + m_peer_id = peer_id; +} + +GetPeerAddress::~GetPeerAddress() +{ +} + +void GetPeerAddress::notifyEvent(Event* event) +{ + // nothing there. If we receive events, they must be ignored +} + +void GetPeerAddress::setup() +{ + m_state = NONE; + m_request = NULL; +} + +void GetPeerAddress::asynchronousUpdate() +{ + if (m_state == NONE) + { + m_request = new Online::XMLRequest(); + m_request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); + m_request->setParameter("id",Online::CurrentUser::get()->getUserID()); + m_request->setParameter("token",Online::CurrentUser::get()->getToken()); + m_request->setParameter("peer_id",m_peer_id); + m_request->setParameter("action","get"); + + Online::HTTPManager::get()->addRequest(m_request); + m_state = REQUEST_PENDING; + } + else if (m_state == REQUEST_PENDING && m_request->isDone()) + { + const XMLNode * result = m_request->getResult(); + std::string rec_success; + + if(result->get("success", &rec_success)) + { + if (rec_success == "yes") + { + TransportAddress* addr = static_cast(m_callback_object); + result->get("ip", &addr->ip); + result->get("port", &addr->port); + Log::debug("GetPeerAddress", "Address gotten successfully."); + } + else + { + Log::error("GetPeerAddress", "Fail to get address."); + } + } + else + { + Log::error("GetPeerAddress", "Fail to get address."); + } + m_state = DONE; + } + else if (m_state == DONE) + { + m_state = EXITING; + delete m_request; + m_request = NULL; + m_listener->requestTerminate(this); + } +} + +void GetPeerAddress::setPeerID(uint32_t peer_id) +{ + m_peer_id = peer_id; +} diff --git a/src/network/protocols/get_peer_address.hpp b/src/network/protocols/get_peer_address.hpp new file mode 100644 index 000000000..4007387fd --- /dev/null +++ b/src/network/protocols/get_peer_address.hpp @@ -0,0 +1,51 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef GET_PEER_ADDRESS_HPP +#define GET_PEER_ADDRESS_HPP + +#include "network/protocol.hpp" +#include "online/request.hpp" + +class GetPeerAddress : public Protocol +{ + public: + GetPeerAddress(uint32_t peer_id, CallbackObject* callback_object); + virtual ~GetPeerAddress(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + void setPeerID(uint32_t m_peer_id); + protected: + uint32_t m_peer_id; + Online::XMLRequest* m_request; + enum STATE + { + NONE, + REQUEST_PENDING, + DONE, + EXITING + }; + STATE m_state; + +}; + +#endif // GET_PEER_ADDRESS_HPP diff --git a/src/network/protocols/get_public_address.cpp b/src/network/protocols/get_public_address.cpp new file mode 100644 index 000000000..1483ea1ef --- /dev/null +++ b/src/network/protocols/get_public_address.cpp @@ -0,0 +1,210 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/protocols/get_public_address.hpp" + +#include "network/network_manager.hpp" +#include "network/client_network_manager.hpp" +#include "network/protocols/connect_to_server.hpp" +#include "network/network_interface.hpp" + +#include "utils/log.hpp" + +#include + +int stunRand() +{ + static bool init = false; + if (!init) + { + srand((unsigned int)time(NULL)); + init = true; + } + return rand(); +} + +GetPublicAddress::GetPublicAddress(CallbackObject* callback_object) : Protocol(callback_object, PROTOCOL_SILENT) +{ +} + +GetPublicAddress::~GetPublicAddress() +{ +} + +void GetPublicAddress::notifyEvent(Event* event) +{ + +} + +void GetPublicAddress::setup() +{ + m_state = NOTHING_DONE; +} + +void GetPublicAddress::asynchronousUpdate() +{ + if (m_state == NOTHING_DONE) + { + // format : 00MMMMMCMMMCMMMM (cf rfc 5389) + uint16_t message_type = 0x0001; // binding request + m_stun_tansaction_id[0] = stunRand(); + m_stun_tansaction_id[1] = stunRand(); + m_stun_tansaction_id[2] = stunRand(); + uint16_t message_length = 0x0000; + + uint8_t bytes[21]; // the message to be sent + // bytes 0-1 : the type of the message, + bytes[0] = (uint8_t)(message_type>>8); + bytes[1] = (uint8_t)(message_type); + + // bytes 2-3 : message length added to header (attributes) + bytes[2] = (uint8_t)(message_length>>8); + bytes[3] = (uint8_t)(message_length); + + // bytes 4-7 : magic cookie to recognize the stun protocol + bytes[4] = (uint8_t)(m_stun_magic_cookie>>24); + bytes[5] = (uint8_t)(m_stun_magic_cookie>>16); + bytes[6] = (uint8_t)(m_stun_magic_cookie>>8); + bytes[7] = (uint8_t)(m_stun_magic_cookie); + + // bytes 8-19 : the transaction id + bytes[8] = (uint8_t)(m_stun_tansaction_id[0]>>24); + bytes[9] = (uint8_t)(m_stun_tansaction_id[0]>>16); + bytes[10] = (uint8_t)(m_stun_tansaction_id[0]>>8); + bytes[11] = (uint8_t)(m_stun_tansaction_id[0]); + bytes[12] = (uint8_t)(m_stun_tansaction_id[1]>>24); + bytes[13] = (uint8_t)(m_stun_tansaction_id[1]>>16); + bytes[14] = (uint8_t)(m_stun_tansaction_id[1]>>8); + bytes[15] = (uint8_t)(m_stun_tansaction_id[1]); + bytes[16] = (uint8_t)(m_stun_tansaction_id[2]>>24); + bytes[17] = (uint8_t)(m_stun_tansaction_id[2]>>16); + bytes[18] = (uint8_t)(m_stun_tansaction_id[2]>>8); + bytes[19] = (uint8_t)(m_stun_tansaction_id[2]); + bytes[20] = '\0'; + + Log::verbose("GetPublicAddress", "Querrying STUN server 132.177.123.6"); + unsigned int dst = (132<<24)+(177<<16)+(123<<8)+6; + NetworkManager::getInstance()->setManualSocketsMode(true); + NetworkManager::getInstance()->getHost()->sendRawPacket(bytes, 20, TransportAddress(dst, 3478)); + m_state = TEST_SENT; + } + if (m_state == TEST_SENT) + { + unsigned int dst = (132<<24)+(177<<16)+(123<<8)+6; + uint8_t* data = NetworkManager::getInstance()->getHost()->receiveRawPacket(TransportAddress(dst, 3478)); + assert(data); + + // check that the stun response is a response, contains the magic cookie and the transaction ID + if ( data[0] == 0x01 && + data[1] == 0x01 && + data[4] == (uint8_t)(m_stun_magic_cookie>>24) && + data[5] == (uint8_t)(m_stun_magic_cookie>>16) && + data[6] == (uint8_t)(m_stun_magic_cookie>>8) && + data[7] == (uint8_t)(m_stun_magic_cookie) ) + { + if( + data[8] == (uint8_t)(m_stun_tansaction_id[0]>>24) && + data[9] == (uint8_t)(m_stun_tansaction_id[0]>>16) && + data[10] == (uint8_t)(m_stun_tansaction_id[0]>>8 ) && + data[11] == (uint8_t)(m_stun_tansaction_id[0] ) && + data[12] == (uint8_t)(m_stun_tansaction_id[1]>>24) && + data[13] == (uint8_t)(m_stun_tansaction_id[1]>>16) && + data[14] == (uint8_t)(m_stun_tansaction_id[1]>>8 ) && + data[15] == (uint8_t)(m_stun_tansaction_id[1] ) && + data[16] == (uint8_t)(m_stun_tansaction_id[2]>>24) && + data[17] == (uint8_t)(m_stun_tansaction_id[2]>>16) && + data[18] == (uint8_t)(m_stun_tansaction_id[2]>>8 ) && + data[19] == (uint8_t)(m_stun_tansaction_id[2] )) + { + Log::verbose("GetPublicAddress", "The STUN server responded with a valid answer"); + int message_size = data[2]*256+data[3]; + + // parse the stun message now: + bool finish = false; + uint8_t* attributes = data+20; + if (message_size == 0) + { + Log::error("GetPublicAddress", "STUN answer does not contain any information."); + finish = true; + } + if (message_size < 4) // cannot even read the size + { + Log::error("GetPublicAddress", "STUN message is not valid."); + finish = true; + } + uint16_t port; + uint32_t address; + bool valid = false; + while(!finish) + { + int type = attributes[0]*256+attributes[1]; + int size = attributes[2]*256+attributes[3]; + switch(type) + { + case 0: + case 1: + assert(size == 8); + assert(attributes[5] = 0x01); // IPv4 only + port = attributes[6]*256+attributes[7]; + address = (attributes[8]<<24 & 0xFF000000)+(attributes[9]<<16 & 0x00FF0000)+(attributes[10]<<8 & 0x0000FF00)+(attributes[11] & 0x000000FF); + finish = true; + valid = true; + continue; + break; + default: + break; + } + attributes = attributes + 4 + size; + message_size -= 4 + size; + if (message_size == 0) + finish = true; + if (message_size < 4) // cannot even read the size + { + Log::error("GetPublicAddress", "STUN message is not valid."); + finish = true; + } + } + // finished parsing, we know our public transport address + if (valid) + { + Log::debug("GetPublicAddress", "The public address has been found : %i.%i.%i.%i:%i", address>>24&0xff, address>>16&0xff, address>>8&0xff, address&0xff, port); + m_state = ADDRESS_KNOWN; + NetworkManager::getInstance()->setManualSocketsMode(false); + TransportAddress* addr = static_cast(m_callback_object); + addr->ip = address; + addr->port = port; + } + else + m_state = NOTHING_DONE; // need to re-send the stun request + } + else + { + m_state = NOTHING_DONE; // need to re-send the stun request + } + } + } + if (m_state == ADDRESS_KNOWN) + { + m_state = EXITING; + // terminate the protocol + m_listener->requestTerminate(this); + } + if (m_state == EXITING) + { + } +} diff --git a/src/network/race_result_message.hpp b/src/network/protocols/get_public_address.hpp similarity index 51% rename from src/network/race_result_message.hpp rename to src/network/protocols/get_public_address.hpp index 95d3a6152..b564207b7 100644 --- a/src/network/race_result_message.hpp +++ b/src/network/protocols/get_public_address.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,30 +16,34 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_RACE_RESULT_MESSAGE_HPP -#define HEADER_RACE_RESULT_MESSAGE_HPP +#ifndef GET_PUBLIC_ADDRESS_HPP +#define GET_PUBLIC_ADDRESS_HPP -#include +#include "network/protocol.hpp" -#include "network/message.hpp" - - -/** This message is from the server to all clients to inform them about the - * result of a race. The clients wait for this message before they finish - * a race. - */ -class RaceResultMessage : public Message +class GetPublicAddress : public Protocol { - struct RaceResult { - float m_time; - int m_score; - }; // RaceResult -private: - std::vector m_all_results; -public: - RaceResultMessage(); - RaceResultMessage(ENetPacket* pkt); - void addRaceResult(int kart_id, float time, int points); - void getRaceResult(int kart_id, float &time, int &points); -}; // RaceResultMessage -#endif + public: + GetPublicAddress(CallbackObject* callback_object); + virtual ~GetPublicAddress(); + + virtual void notifyEvent(Event* event); + + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + enum STATE + { + NOTHING_DONE, + TEST_SENT, + ADDRESS_KNOWN, + EXITING + }; + STATE m_state; + uint32_t m_stun_tansaction_id[3]; + static const uint32_t m_stun_magic_cookie = 0x2112A442; +}; + +#endif // GET_PUBLIC_ADDRESS_HPP diff --git a/src/network/protocols/hide_public_address.cpp b/src/network/protocols/hide_public_address.cpp new file mode 100644 index 000000000..8bebc6c5a --- /dev/null +++ b/src/network/protocols/hide_public_address.cpp @@ -0,0 +1,86 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/protocols/hide_public_address.hpp" + +#include "network/protocol_manager.hpp" +#include "online/http_manager.hpp" +#include "online/current_user.hpp" +#include "config/user_config.hpp" +#include "utils/log.hpp" + +HidePublicAddress::HidePublicAddress() : Protocol(NULL, PROTOCOL_SILENT) +{ +} + +HidePublicAddress::~HidePublicAddress() +{ +} + +void HidePublicAddress::notifyEvent(Event* event) +{ +} + +void HidePublicAddress::setup() +{ + m_state = NONE; +} + +void HidePublicAddress::asynchronousUpdate() +{ + if (m_state == NONE) + { + m_request = new Online::XMLRequest(); + m_request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); + m_request->setParameter("id",Online::CurrentUser::get()->getUserID()); + m_request->setParameter("token",Online::CurrentUser::get()->getToken()); + m_request->setParameter("action","unset"); + + Online::HTTPManager::get()->addRequest(m_request); + m_state = REQUEST_PENDING; + } + else if (m_state == REQUEST_PENDING && m_request->isDone()) + { + const XMLNode * result = m_request->getResult(); + std::string rec_success; + + if(result->get("success", &rec_success)) + { + if(rec_success == "yes") + { + Log::debug("ShowPublicAddress", "Address hidden successfully."); + } + else + { + Log::error("ShowPublicAddress", "Fail to hide address."); + } + } + else + { + Log::error("ShowPublicAddress", "Fail to hide address."); + } + m_state = DONE; + } + else if (m_state == DONE) + { + m_state = EXITING; + delete m_request; + m_request = NULL; + m_listener->requestTerminate(this); + } +} diff --git a/src/network/num_players_message.hpp b/src/network/protocols/hide_public_address.hpp similarity index 54% rename from src/network/num_players_message.hpp rename to src/network/protocols/hide_public_address.hpp index 4279f9eb9..ed58fbdd1 100644 --- a/src/network/num_players_message.hpp +++ b/src/network/protocols/hide_public_address.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,27 +16,34 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_NUM_PLAYERS_MESSAGE_HPP -#define HEADER_NUM_PLAYERS_MESSAGE_HPP +#ifndef HIDE_PUBLIC_ADDRESS_HPP +#define HIDE_PUBLIC_ADDRESS_HPP +#include "network/protocol.hpp" +#include "online/request.hpp" #include -#include -#ifndef WIN32 -# include -#endif -#include "network/message.hpp" -#include "race/race_manager.hpp" - -class NumPlayersMessage : public Message +class HidePublicAddress : public Protocol { -private: - int m_num_players -public: - NumPlayersMessage():Message(Message::MT_CONNECT) { m_num_players=race } - NumPlayersMessage(ENetPacket* pkt):Message(pkt) - { m_id=getString(); } - const std::string& - getNumPlayers() { return m_num_players; } -}; // ConnectMessage -#endif + public: + HidePublicAddress(); + virtual ~HidePublicAddress(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + Online::XMLRequest* m_request; + enum STATE + { + NONE, + REQUEST_PENDING, + DONE, + EXITING + }; + STATE m_state; +}; + +#endif // HIDE_PUBLIC_ADDRESS_HPP diff --git a/src/network/protocols/kart_update_protocol.cpp b/src/network/protocols/kart_update_protocol.cpp new file mode 100644 index 000000000..0652c22c5 --- /dev/null +++ b/src/network/protocols/kart_update_protocol.cpp @@ -0,0 +1,135 @@ +#include "network/protocols/kart_update_protocol.hpp" + +#include "karts/abstract_kart.hpp" +#include "modes/world.hpp" +#include "network/protocol_manager.hpp" +#include "network/network_world.hpp" + +KartUpdateProtocol::KartUpdateProtocol() + : Protocol(NULL, PROTOCOL_KART_UPDATE) +{ + m_karts = World::getWorld()->getKarts(); + for (unsigned int i = 0; i < m_karts.size(); i++) + { + //if (m_karts[i]->getWorldKartId()) + { + Log::info("KartUpdateProtocol", "Kart %d has id %d and name %s", i, m_karts[i]->getWorldKartId(), m_karts[i]->getIdent().c_str()); + } + if (m_karts[i]->getIdent() == NetworkWorld::getInstance()->m_self_kart) + { + Log::info("KartUpdateProtocol", "My id is %d", i); + m_self_kart_index = i; + } + } + pthread_mutex_init(&m_positions_updates_mutex, NULL); +} + +KartUpdateProtocol::~KartUpdateProtocol() +{ +} + +void KartUpdateProtocol::notifyEvent(Event* event) +{ + if (event->type != EVENT_TYPE_MESSAGE) + return; + NetworkString ns = event->data; + if (ns.size() < 36) + { + Log::info("KartUpdateProtocol", "Message too short."); + return; + } + float game_time = ns.getFloat(0); + ns.removeFront(4); + while(ns.size() >= 16) + { + uint32_t kart_id = ns.getUInt32(0); + + float a,b,c; + a = ns.getFloat(4); + b = ns.getFloat(8); + c = ns.getFloat(12); + float d,e,f,g; + d = ns.getFloat(16); + e = ns.getFloat(20); + f = ns.getFloat(24); + g = ns.getFloat(28); + pthread_mutex_trylock(&m_positions_updates_mutex); + m_next_positions.push_back(Vec3(a,b,c)); + m_next_quaternions.push_back(btQuaternion(d,e,f,g)); + m_karts_ids.push_back(kart_id); + pthread_mutex_unlock(&m_positions_updates_mutex); + ns.removeFront(32); + } +} + +void KartUpdateProtocol::setup() +{ +} + +void KartUpdateProtocol::update() +{ + static double time = 0; + double current_time = Time::getRealTime(); + if (current_time > time + 0.1) // 10 updates per second + { + time = current_time; + if (m_listener->isServer()) + { + NetworkString ns; + ns.af( World::getWorld()->getTime()); + for (unsigned int i = 0; i < m_karts.size(); i++) + { + AbstractKart* kart = m_karts[i]; + Vec3 v = kart->getXYZ(); + btQuaternion quat = kart->getRotation(); + ns.ai32( kart->getWorldKartId()); + ns.af(v[0]).af(v[1]).af(v[2]); // add position + ns.af(quat.x()).af(quat.y()).af(quat.z()).af(quat.w()); // add rotation + Log::verbose("KartUpdateProtocol", "Sending %d's positions %f %f %f", kart->getWorldKartId(), v[0], v[1], v[2]); + } + m_listener->sendMessage(this, ns, false); + } + else + { + AbstractKart* kart = m_karts[m_self_kart_index]; + Vec3 v = kart->getXYZ(); + btQuaternion quat = kart->getRotation(); + NetworkString ns; + ns.af( World::getWorld()->getTime()); + ns.ai32( kart->getWorldKartId()); + ns.af(v[0]).af(v[1]).af(v[2]); // add position + ns.af(quat.x()).af(quat.y()).af(quat.z()).af(quat.w()); // add rotation + Log::verbose("KartUpdateProtocol", "Sending %d's positions %f %f %f", kart->getWorldKartId(), v[0], v[1], v[2]); + m_listener->sendMessage(this, ns, false); + } + } + switch(pthread_mutex_trylock(&m_positions_updates_mutex)) + { + case 0: /* if we got the lock */ + while (!m_next_positions.empty()) + { + uint32_t id = m_karts_ids.back(); + if (id != m_self_kart_index || m_listener->isServer()) // server takes all updates + { + Vec3 pos = m_next_positions.back(); + btTransform transform = m_karts[id]->getBody()->getInterpolationWorldTransform(); + transform.setOrigin(pos); + transform.setRotation(m_next_quaternions.back()); + m_karts[id]->getBody()->setCenterOfMassTransform(transform); + //m_karts[id]->getBody()->setLinearVelocity(Vec3(0,0,0)); + Log::verbose("KartUpdateProtocol", "Update kart %i pos to %f %f %f", id, pos[0], pos[1], pos[2]); + } + m_next_positions.pop_back(); + m_next_quaternions.pop_back(); + m_karts_ids.pop_back(); + } + pthread_mutex_unlock(&m_positions_updates_mutex); + break; + default: + break; + } +} + + + + diff --git a/src/network/protocols/kart_update_protocol.hpp b/src/network/protocols/kart_update_protocol.hpp new file mode 100644 index 000000000..dd24f45a0 --- /dev/null +++ b/src/network/protocols/kart_update_protocol.hpp @@ -0,0 +1,33 @@ +#ifndef KART_UPDATE_PROTOCOL_HPP +#define KART_UPDATE_PROTOCOL_HPP + +#include "network/protocol.hpp" +#include "utils/vec3.hpp" +#include "LinearMath/btQuaternion.h" +#include + +class AbstractKart; + +class KartUpdateProtocol : public Protocol +{ + public: + KartUpdateProtocol(); + virtual ~KartUpdateProtocol(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update(); + virtual void asynchronousUpdate() {}; + + protected: + std::vector m_karts; + uint32_t m_self_kart_index; + + std::list m_next_positions; + std::list m_next_quaternions; + std::list m_karts_ids; + + pthread_mutex_t m_positions_updates_mutex; +}; + +#endif // KART_UPDATE_PROTOCOL_HPP diff --git a/src/network/race_info_message.hpp b/src/network/protocols/lobby_room_protocol.cpp similarity index 67% rename from src/network/race_info_message.hpp rename to src/network/protocols/lobby_room_protocol.cpp index 52404b3f5..46be6df03 100644 --- a/src/network/race_info_message.hpp +++ b/src/network/protocols/lobby_room_protocol.cpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,18 +16,16 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_RACE_INFO_MESSAGE_HPP -#define HEADER_RACE_INFO_MESSAGE_HPP +#include "network/protocols/lobby_room_protocol.hpp" -#include - -#include "network/message.hpp" -#include "network/remote_kart_info.hpp" - -class RaceInfoMessage : public Message +LobbyRoomProtocol::LobbyRoomProtocol(CallbackObject* callback_object) : + Protocol(callback_object, PROTOCOL_LOBBY_ROOM) { -public: - RaceInfoMessage(const std::vector& kart_info); - RaceInfoMessage(ENetPacket* pkt); -}; // RaceInfoMessage -#endif + m_setup = NULL; +} + +//----------------------------------------------------------------------------- + +LobbyRoomProtocol::~LobbyRoomProtocol() +{ +} diff --git a/src/network/protocols/lobby_room_protocol.hpp b/src/network/protocols/lobby_room_protocol.hpp new file mode 100644 index 000000000..f5d10b897 --- /dev/null +++ b/src/network/protocols/lobby_room_protocol.hpp @@ -0,0 +1,47 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef LOBBY_ROOM_PROTOCOL_HPP +#define LOBBY_ROOM_PROTOCOL_HPP + +#include "network/protocol.hpp" + +#include "network/game_setup.hpp" +#include "network/network_string.hpp" + +/*! + * \class LobbyRoomProtocol + * \brief Class used while the game is being prepared. + * This protocol starts when a server opens a game, or when a client joins a game. + * It is used to exchange data about the race settings, like kart selection. + */ +class LobbyRoomProtocol : public Protocol +{ + public: + LobbyRoomProtocol(CallbackObject* callback_object); + virtual ~LobbyRoomProtocol(); + + virtual void notifyEvent(Event* event) = 0; + virtual void setup() = 0; + virtual void update() = 0; + + protected: + GameSetup* m_setup; //!< The game setup. +}; + +#endif // LOBBY_ROOM_PROTOCOL_HPP diff --git a/src/network/protocols/ping_protocol.cpp b/src/network/protocols/ping_protocol.cpp new file mode 100644 index 000000000..4a4c29332 --- /dev/null +++ b/src/network/protocols/ping_protocol.cpp @@ -0,0 +1,52 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/protocols/ping_protocol.hpp" + +#include "network/network_manager.hpp" +#include "utils/time.hpp" + +PingProtocol::PingProtocol(const TransportAddress& ping_dst, double delay_between_pings) : Protocol(NULL, PROTOCOL_SILENT) +{ + m_ping_dst = ping_dst; + m_delay_between_pings = delay_between_pings; +} + +PingProtocol::~PingProtocol() +{ +} + +void PingProtocol::notifyEvent(Event* event) +{ +} + +void PingProtocol::setup() +{ + m_last_ping_time = 0; +} + +void PingProtocol::asynchronousUpdate() +{ + if (Time::getRealTime() > m_last_ping_time+m_delay_between_pings) + { + m_last_ping_time = Time::getRealTime(); + uint8_t data = 0; + NetworkManager::getInstance()->getHost()->sendRawPacket(&data, 1, m_ping_dst); + Log::info("PingProtocol", "Ping message sent"); + } +} diff --git a/src/network/protocols/ping_protocol.hpp b/src/network/protocols/ping_protocol.hpp new file mode 100644 index 000000000..61f35b978 --- /dev/null +++ b/src/network/protocols/ping_protocol.hpp @@ -0,0 +1,24 @@ +#ifndef PING_PROTOCOL_HPP +#define PING_PROTOCOL_HPP + +#include "network/protocol.hpp" + + +class PingProtocol : public Protocol +{ + public: + PingProtocol(const TransportAddress& ping_dst, double delay_between_pings); + virtual ~PingProtocol(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + TransportAddress m_ping_dst; + double m_delay_between_pings; + double m_last_ping_time; +}; + +#endif // PING_PROTOCOL_HPP diff --git a/src/network/protocols/quick_join_protocol.cpp b/src/network/protocols/quick_join_protocol.cpp new file mode 100644 index 000000000..80504b824 --- /dev/null +++ b/src/network/protocols/quick_join_protocol.cpp @@ -0,0 +1,92 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "quick_join_protocol.hpp" + +#include "network/network_manager.hpp" +#include "online/current_user.hpp" +#include "online/http_manager.hpp" +#include "config/user_config.hpp" +#include "utils/log.hpp" + +QuickJoinProtocol::QuickJoinProtocol(CallbackObject* callback_object, uint32_t* server_id) : Protocol(callback_object, PROTOCOL_SILENT) +{ + m_server_id = server_id; +} + +QuickJoinProtocol::~QuickJoinProtocol() +{ +} + +void QuickJoinProtocol::notifyEvent(Event* event) +{ +} + +void QuickJoinProtocol::setup() +{ + m_state = NONE; +} + +void QuickJoinProtocol::asynchronousUpdate() +{ + if (m_state == NONE) + { + TransportAddress addr = NetworkManager::getInstance()->getPublicAddress(); + m_request = new Online::XMLRequest(); + m_request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); + m_request->setParameter("id",Online::CurrentUser::get()->getUserID()); + m_request->setParameter("token",Online::CurrentUser::get()->getToken()); + m_request->setParameter("action","quick-join"); + + Online::HTTPManager::get()->addRequest(m_request); + m_state = REQUEST_PENDING; + } + else if (m_state == REQUEST_PENDING && m_request->isDone()) + { + const XMLNode * result = m_request->getResult(); + std::string rec_success; + TransportAddress* res = static_cast(m_callback_object); + + if(result->get("success", &rec_success)) + { + if(rec_success == "yes") + { + result->get("ip", &res->ip); + result->get("port", &res->port); + result->get("hostid", m_server_id); + Log::info("QuickJoinProtocol", "Quick joining %d:%d (server#%d).", res->ip, res->port, *m_server_id); + } + else + { + Log::error("QuickJoinProtocol", "Fail to quick join."); + } + } + else + { + Log::error("QuickJoinProtocol", "Fail to quick join."); + } + m_state = DONE; + } + else if (m_state == DONE) + { + m_state = EXITING; + delete m_request; + m_request = NULL; + m_listener->requestTerminate(this); + } +} diff --git a/src/network/protocols/quick_join_protocol.hpp b/src/network/protocols/quick_join_protocol.hpp new file mode 100644 index 000000000..9cefedf32 --- /dev/null +++ b/src/network/protocols/quick_join_protocol.hpp @@ -0,0 +1,31 @@ +#ifndef QUICK_JOIN_PROTOCOL_HPP +#define QUICK_JOIN_PROTOCOL_HPP + +#include "network/protocol.hpp" +#include "online/request.hpp" + +class QuickJoinProtocol : public Protocol +{ + public: + QuickJoinProtocol(CallbackObject* callback_object, uint32_t* server_id); + virtual ~QuickJoinProtocol(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + uint32_t* m_server_id; + Online::XMLRequest* m_request; + enum STATE + { + NONE, + REQUEST_PENDING, + DONE, + EXITING + }; + STATE m_state; +}; + +#endif // QUICK_JOIN_PROTOCOL_HPP diff --git a/src/network/protocols/request_connection.cpp b/src/network/protocols/request_connection.cpp new file mode 100644 index 000000000..16a7f8b88 --- /dev/null +++ b/src/network/protocols/request_connection.cpp @@ -0,0 +1,97 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/protocols/request_connection.hpp" + +#include "network/protocol_manager.hpp" +#include "online/http_manager.hpp" +#include "online/current_user.hpp" +#include "config/user_config.hpp" + +RequestConnection::RequestConnection(uint32_t server_id) : Protocol(NULL, PROTOCOL_SILENT) +{ + m_server_id = server_id; +} + +RequestConnection::~RequestConnection() +{ +} + +void RequestConnection::notifyEvent(Event* event) +{ +} + +void RequestConnection::setup() +{ + m_state = NONE; +} + +void RequestConnection::asynchronousUpdate() +{ + switch (m_state) + { + case NONE: + { + m_request = new Online::CurrentUser::ServerJoinRequest(); + m_request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); + m_request->setParameter("id",Online::CurrentUser::get()->getUserID()); + m_request->setParameter("token",Online::CurrentUser::get()->getToken()); + m_request->setParameter("server_id",m_server_id); + m_request->setParameter("action","request-connection"); + + Online::HTTPManager::get()->addRequest(m_request); + m_state = REQUEST_PENDING; + break; + } + case REQUEST_PENDING: + { + if (!m_request->isDone()) + return; + const XMLNode * result = m_request->getResult(); + std::string rec_success; + + if(result->get("success", &rec_success)) + { + if (rec_success == "yes") + { + Log::debug("RequestConnection", "Connection Request made successfully."); + } + else + { + Log::error("RequestConnection", "Fail to make a request to connecto to server %d", m_server_id); + } + } + else + { + Log::error("RequestConnection", "Fail to make a request."); + } + m_state = DONE; + + break; + } + case DONE: + m_state = EXITING; + delete m_request; + m_request = NULL; + m_listener->requestTerminate(this); + break; + case EXITING: + break; + } +} + diff --git a/src/network/protocols/request_connection.hpp b/src/network/protocols/request_connection.hpp new file mode 100644 index 000000000..05527731b --- /dev/null +++ b/src/network/protocols/request_connection.hpp @@ -0,0 +1,32 @@ +#ifndef REQUEST_CONNECTION_HPP +#define REQUEST_CONNECTION_HPP + +#include "network/protocol.hpp" +#include "online/current_user.hpp" + +class RequestConnection : public Protocol +{ + public: + RequestConnection(uint32_t server_id); + virtual ~RequestConnection(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + uint32_t m_server_id; + Online::CurrentUser::ServerJoinRequest* m_request; + enum STATE + { + NONE, + REQUEST_PENDING, + DONE, + EXITING + }; + STATE m_state; + +}; + +#endif // REQUEST_CONNECTION_HPP diff --git a/src/network/protocols/server_lobby_room_protocol.cpp b/src/network/protocols/server_lobby_room_protocol.cpp new file mode 100644 index 000000000..0cc325d44 --- /dev/null +++ b/src/network/protocols/server_lobby_room_protocol.cpp @@ -0,0 +1,369 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/protocols/server_lobby_room_protocol.hpp" + +#include "network/server_network_manager.hpp" +#include "network/protocols/get_public_address.hpp" +#include "network/protocols/show_public_address.hpp" +#include "network/protocols/connect_to_peer.hpp" +#include "network/protocols/start_server.hpp" +#include "network/protocols/start_game_protocol.hpp" + +#include "online/current_user.hpp" +#include "online/http_manager.hpp" +#include "config/user_config.hpp" +#include "utils/log.hpp" +#include "utils/time.hpp" +#include "utils/random_generator.hpp" + +ServerLobbyRoomProtocol::ServerLobbyRoomProtocol() : LobbyRoomProtocol(NULL) +{ +} + +//----------------------------------------------------------------------------- + +ServerLobbyRoomProtocol::~ServerLobbyRoomProtocol() +{ +} + +//----------------------------------------------------------------------------- + +void ServerLobbyRoomProtocol::setup() +{ + m_setup = NetworkManager::getInstance()->setupNewGame(); // create a new setup + m_next_id = 0; + m_state = NONE; + m_public_address.ip = 0; + m_public_address.port = 0; + m_selection_enabled = false; + Log::info("ServerLobbyRoomProtocol", "Starting the protocol."); +} + +//----------------------------------------------------------------------------- + +void ServerLobbyRoomProtocol::notifyEvent(Event* event) +{ + assert(m_setup); // assert that the setup exists + if (event->type == EVENT_TYPE_MESSAGE) + { + assert(event->data.size()); // message not empty + uint8_t message_type; + message_type = event->data.getAndRemoveUInt8(); + Log::info("ServerLobbyRoomProtocol", "Message received with type %d.", message_type); + if (message_type == 0x01) // player requesting connection + connectionRequested(event); + if (message_type == 0x02) // player requesting kart selection + kartSelectionRequested(event); + } // if (event->type == EVENT_TYPE_MESSAGE) + else if (event->type == EVENT_TYPE_CONNECTED) + { + } // if (event->type == EVENT_TYPE_CONNECTED) + else if (event->type == EVENT_TYPE_DISCONNECTED) + { + kartDisconnected(event); + } // if (event->type == EVENT_TYPE_DISCONNECTED) +} + +//----------------------------------------------------------------------------- + +void ServerLobbyRoomProtocol::update() +{ + switch (m_state) + { + case NONE: + m_current_protocol_id = m_listener->requestStart(new GetPublicAddress(&m_public_address)); + m_state = GETTING_PUBLIC_ADDRESS; + break; + case GETTING_PUBLIC_ADDRESS: + if (m_listener->getProtocolState(m_current_protocol_id) == PROTOCOL_STATE_TERMINATED) + { + NetworkManager::getInstance()->setPublicAddress(m_public_address); + m_current_protocol_id = m_listener->requestStart(new StartServer()); + m_state = LAUNCHING_SERVER; + Log::debug("ServerLobbyRoomProtocol", "Public address known."); + } + break; + case LAUNCHING_SERVER: + if (m_listener->getProtocolState(m_current_protocol_id) == PROTOCOL_STATE_TERMINATED) + { + m_state = WORKING; + Log::info("ServerLobbyRoomProtocol", "Server setup"); + } + break; + case WORKING: + { + // first poll every 5 seconds + static double last_poll_time = 0; + if (Time::getRealTime() > last_poll_time+10.0) + { + last_poll_time = Time::getRealTime(); + TransportAddress addr = NetworkManager::getInstance()->getPublicAddress(); + Online::XMLRequest* request = new Online::XMLRequest(); + request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); + request->setParameter("id",Online::CurrentUser::get()->getUserID()); + request->setParameter("token",Online::CurrentUser::get()->getToken()); + request->setParameter("address",addr.ip); + request->setParameter("port",addr.port); + request->setParameter("action","poll-connection-requests"); + + Online::HTTPManager::get()->synchronousRequest(request); + assert(request->isDone()); + + const XMLNode * result = request->getResult(); + std::string rec_success; + if(result->get("success", &rec_success)) + { + if(rec_success == "yes") + { + const XMLNode * users_xml = result->getNode("users"); + uint32_t id = 0; + for (unsigned int i = 0; i < users_xml->getNumNodes(); i++) + { + users_xml->getNode(i)->get("id", &id); + Log::debug("ServerLobbyRoomProtocol", "User with id %d wants to connect.", id); + m_incoming_peers_ids.push_back(id); + } + } + else + { + Log::error("ServerLobbyRoomProtocol", "Error while reading the list."); + } + } + else + { + Log::error("ServerLobbyRoomProtocol", "Cannot retrieve the list."); + } + delete request; + } + + // now + for (unsigned int i = 0; i < m_incoming_peers_ids.size(); i++) + { + m_listener->requestStart(new ConnectToPeer(m_incoming_peers_ids[i])); + } + m_incoming_peers_ids.clear(); + + break; + } + case DONE: + m_state = EXITING; + m_listener->requestTerminate(this); + break; + case EXITING: + break; + } +} + +//----------------------------------------------------------------------------- + +void ServerLobbyRoomProtocol::startGame() +{ + std::vector peers = NetworkManager::getInstance()->getPeers(); + for (unsigned int i = 0; i < peers.size(); i++) + { + NetworkString ns; + ns.ai8(0x04).ai8(4).ai32(peers[i]->getClientServerToken()); // start game + m_listener->sendMessage(this, peers[i], ns, true); // reliably + } + m_listener->requestStart(new StartGameProtocol(m_setup)); +} + +//----------------------------------------------------------------------------- + +void ServerLobbyRoomProtocol::startSelection() +{ + std::vector peers = NetworkManager::getInstance()->getPeers(); + for (unsigned int i = 0; i < peers.size(); i++) + { + NetworkString ns; + ns.ai8(0x05).ai8(4).ai32(peers[i]->getClientServerToken()); // start selection + m_listener->sendMessage(this, peers[i], ns, true); // reliably + } + m_selection_enabled = true; +} + +//----------------------------------------------------------------------------- + +void ServerLobbyRoomProtocol::kartDisconnected(Event* event) +{ + STKPeer* peer = *(event->peer); + if (peer->getPlayerProfile() != NULL) // others knew him + { + NetworkString msg; + msg.ai8(0x02).ai8(1).ai8(peer->getPlayerProfile()->race_id); + m_listener->sendMessage(this, msg); + Log::info("ServerLobbyRoomProtocol", "Player disconnected : id %d", + peer->getPlayerProfile()->race_id); + m_setup->removePlayer(peer->getPlayerProfile()->race_id); + NetworkManager::getInstance()->removePeer(peer); + } + else + Log::info("ServerLobbyRoomProtocol", "The DC peer wasn't registered."); +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when a player asks for a connection. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 + * ------------------------ + * Size | 1 | 4 | + * Data | 4 | global player id | + * ------------------------ + */ +void ServerLobbyRoomProtocol::connectionRequested(Event* event) +{ + STKPeer* peer = *(event->peer); + if (event->data.size() != 5 || event->data[0] != 4) + { + Log::warn("ServerLobbyRoomProtocol", "The server is sending a badly formated message. Size is %d and first byte %d", event->data.size(), event->data[0]); + return; + } + uint32_t player_id = 0; + player_id = event->data.getUInt32(1); + // can we add the player ? + if (m_setup->getPlayerCount() < 16) // accept player + { + // add the player to the game setup + while(m_setup->getProfile(m_next_id)!=NULL) + m_next_id++; + // notify everybody that there is a new player + NetworkString message; + // new player (1) -- size of id -- id -- size of local id -- local id; + message.ai8(1).ai8(4).ai32(player_id).ai8(1).ai8(m_next_id); + m_listener->sendMessageExcept(this, peer, message); + + /// now answer to the peer that just connected + RandomGenerator token_generator; + // use 4 random numbers because rand_max is probably 2^15-1. + uint32_t token = (uint32_t)(((token_generator.get(RAND_MAX)<<24) & 0xff) + + ((token_generator.get(RAND_MAX)<<16) & 0xff) + + ((token_generator.get(RAND_MAX)<<8) & 0xff) + + ((token_generator.get(RAND_MAX) & 0xff))); + + // send a message to the one that asked to connect + NetworkString message_ack; + // connection success (129) -- size of token -- token + message_ack.ai8(0x81).ai8(1).ai8(m_next_id).ai8(4).ai32(token).ai8(4).ai32(player_id); + // add all players so that this user knows + std::vector players = m_setup->getPlayers(); + for (unsigned int i = 0; i < players.size(); i++) + { + // do not make a duplicate of the player + if (players[i]->race_id != m_next_id && players[i]->user_profile->getUserID() != player_id) + message_ack.ai8(1).ai8(players[i]->race_id).ai8(4).ai32(players[i]->user_profile->getUserID()); + } + m_listener->sendMessage(this, peer, message_ack); + + peer->setClientServerToken(token); + + NetworkPlayerProfile* profile = new NetworkPlayerProfile(); + profile->race_id = m_next_id; + profile->kart_name = ""; + profile->user_profile = new Online::User("", player_id); + m_setup->addPlayer(profile); + peer->setPlayerProfile(profile); + Log::verbose("ServerLobbyRoomProtocol", "New player."); + } // accept player + else // refuse the connection with code 0 (too much players) + { + NetworkString message; + message.ai8(0x80); // 128 means connection refused + message.ai8(1); // 1 bytes for the error code + message.ai8(0); // 0 = too much players + // send only to the peer that made the request + m_listener->sendMessage(this, peer, message); + Log::verbose("ServerLobbyRoomProtocol", "Player refused"); + } +} + +//----------------------------------------------------------------------------- + +/*! \brief Called when a player asks to select a kart. + * \param event : Event providing the information. + * + * Format of the data : + * Byte 0 1 5 6 N+6 + * --------------------------------------------------- + * Size | 1 | 4 | 1 | N | + * Data | 4 | priv token | N (kart name size) | kart name | + * --------------------------------------------------- + */ +void ServerLobbyRoomProtocol::kartSelectionRequested(Event* event) +{ + if (event->data.size() < 6 || event->data[0] != 4) + { + Log::warn("ServerLobbyRoomProtocol", "The server is sending a badly " + "formated message. Size is %d and first byte %d", + event->data.size(), event->data[0]); + return; + } + STKPeer* peer = *(event->peer); + uint32_t token = event->data.gui32(1); + if (token != peer->getClientServerToken()) + { + Log::warn("ServerLobbyRoomProtocol", "Peer sending bad token. Request " + "aborted."); + return; + } + uint8_t kart_name_size = event->data.gui8(5); + std::string kart_name = event->data.gs(6, kart_name_size); + if (kart_name.size() != kart_name_size) + { + Log::error("ServerLobbyRoomProtocol", "Kart names sizes differ: told:" + "%d, real: %d.", kart_name_size, kart_name.size()); + return; + } + // check if selection is possible + if (!m_selection_enabled) + { + NetworkString answer; + answer.ai8(0x82).ai8(1).ai8(2); // selection still not started + m_listener->sendMessage(this, peer, answer); + return; + } + // check if somebody picked that kart + if (!m_setup->isKartAvailable(kart_name)) + { + NetworkString answer; + answer.ai8(0x82).ai8(1).ai8(0); // kart is already taken + m_listener->sendMessage(this, peer, answer); + return; + } + // check if this kart is authorized + if (!m_setup->isKartAllowed(kart_name)) + { + NetworkString answer; + answer.ai8(0x82).ai8(1).ai8(1); // kart is not authorized + m_listener->sendMessage(this, peer, answer); + return; + } + // send a kart update to everyone + NetworkString answer; + // kart update (3), 1, race id + answer.ai8(0x03).ai8(1).ai8(peer->getPlayerProfile()->race_id); + // kart name size, kart name + answer.ai8(kart_name.size()).as(kart_name); + m_listener->sendMessage(this, answer); + m_setup->setPlayerKart(peer->getPlayerProfile()->race_id, kart_name); +} + +//----------------------------------------------------------------------------- diff --git a/src/network/protocols/server_lobby_room_protocol.hpp b/src/network/protocols/server_lobby_room_protocol.hpp new file mode 100644 index 000000000..d4a2516ab --- /dev/null +++ b/src/network/protocols/server_lobby_room_protocol.hpp @@ -0,0 +1,43 @@ +#ifndef SERVER_LOBBY_ROOM_PROTOCOL_HPP +#define SERVER_LOBBY_ROOM_PROTOCOL_HPP + +#include "network/protocols/lobby_room_protocol.hpp" + +class ServerLobbyRoomProtocol : public LobbyRoomProtocol +{ + public: + ServerLobbyRoomProtocol(); + virtual ~ServerLobbyRoomProtocol(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update(); + virtual void asynchronousUpdate() {}; + + void startGame(); + void startSelection(); + + protected: + void kartDisconnected(Event* event); + void connectionRequested(Event* event); + void kartSelectionRequested(Event* event); + + uint8_t m_next_id; //!< Next id to assign to a peer. + std::vector m_peers; + std::vector m_incoming_peers_ids; + uint32_t m_current_protocol_id; + TransportAddress m_public_address; + bool m_selection_enabled; + + enum STATE + { + NONE, + GETTING_PUBLIC_ADDRESS, + LAUNCHING_SERVER, + WORKING, + DONE, + EXITING + }; + STATE m_state; +}; +#endif // SERVER_LOBBY_ROOM_PROTOCOL_HPP diff --git a/src/network/protocols/show_public_address.cpp b/src/network/protocols/show_public_address.cpp new file mode 100644 index 000000000..f9ce19071 --- /dev/null +++ b/src/network/protocols/show_public_address.cpp @@ -0,0 +1,89 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/protocols/show_public_address.hpp" + +#include "network/network_manager.hpp" +#include "online/http_manager.hpp" +#include "online/current_user.hpp" +#include "config/user_config.hpp" +#include "utils/log.hpp" + +ShowPublicAddress::ShowPublicAddress() : Protocol(NULL, PROTOCOL_SILENT) +{ +} + +ShowPublicAddress::~ShowPublicAddress() +{ +} + +void ShowPublicAddress::notifyEvent(Event* event) +{ +} + +void ShowPublicAddress::setup() +{ + m_state = NONE; +} + +void ShowPublicAddress::asynchronousUpdate() +{ + if (m_state == NONE) + { + TransportAddress addr = NetworkManager::getInstance()->getPublicAddress(); + m_request = new Online::XMLRequest(); + m_request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); + m_request->setParameter("id",Online::CurrentUser::get()->getUserID()); + m_request->setParameter("token",Online::CurrentUser::get()->getToken()); + m_request->setParameter("address",addr.ip); + m_request->setParameter("port",addr.port); + m_request->setParameter("action","set"); + + Online::HTTPManager::get()->addRequest(m_request); + m_state = REQUEST_PENDING; + } + else if (m_state == REQUEST_PENDING && m_request->isDone()) + { + const XMLNode * result = m_request->getResult(); + std::string rec_success; + + if(result->get("success", &rec_success)) + { + if(rec_success == "yes") + { + Log::debug("ShowPublicAddress", "Address shown successfully."); + } + else + { + Log::error("ShowPublicAddress", "Fail to show address."); + } + } + else + { + Log::error("ShowPublicAddress", "Fail to show address."); + } + m_state = DONE; + } + else if (m_state == DONE) + { + m_state = EXITING; + delete m_request; + m_request = NULL; + m_listener->requestTerminate(this); + } +} diff --git a/src/network/network_kart.hpp b/src/network/protocols/show_public_address.hpp similarity index 52% rename from src/network/network_kart.hpp rename to src/network/protocols/show_public_address.hpp index 892439a24..1e069f79f 100644 --- a/src/network/network_kart.hpp +++ b/src/network/protocols/show_public_address.hpp @@ -1,6 +1,6 @@ // // SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs +// Copyright (C) 2013 SuperTuxKart-Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -16,22 +16,34 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -#ifndef HEADER_NETWORK_KART_HPP -#define HEADER_NETWORK_KART_HPP +#ifndef SHOW_PUBLIC_ADDRESS_HPP +#define SHOW_PUBLIC_ADDRESS_HPP -#include "karts/kart.hpp" +#include "network/protocol.hpp" +#include "online/request.hpp" +#include -class Track; - -class NetworkKart : public Kart +class ShowPublicAddress : public Protocol { -private: - int m_global_player_id; // to identify this kart to the network manager -public: - NetworkKart(const std::string& kart_name, unsigned int world_kart_id, - int position, const btTransform& init_transform, - int global_player_id, RaceManager::KartType type); - void setControl(const KartControl& kc); - virtual bool isNetworkKart() const { return true; } -}; // NetworkKart -#endif + public: + ShowPublicAddress(); + virtual ~ShowPublicAddress(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + Online::XMLRequest* m_request; + enum STATE + { + NONE, + REQUEST_PENDING, + DONE, + EXITING + }; + STATE m_state; +}; + +#endif // HIDE_PUBLIC_ADDRESS_HPP diff --git a/src/network/protocols/start_game_protocol.cpp b/src/network/protocols/start_game_protocol.cpp new file mode 100644 index 000000000..ecd275c9b --- /dev/null +++ b/src/network/protocols/start_game_protocol.cpp @@ -0,0 +1,199 @@ +#include "network/protocols/start_game_protocol.hpp" + +#include "network/network_manager.hpp" +#include "network/protocol_manager.hpp" +#include "network/game_setup.hpp" +#include "network/network_world.hpp" +#include "network/protocols/synchronization_protocol.hpp" +#include "race/race_manager.hpp" +#include "utils/log.hpp" +#include "utils/time.hpp" + +#include "input/device_manager.hpp" +#include "input/input_manager.hpp" +#include "challenges/unlock_manager.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/kart_selection.hpp" +#include "online/current_user.hpp" + +StartGameProtocol::StartGameProtocol(GameSetup* game_setup) : + Protocol(NULL, PROTOCOL_START_GAME) +{ + m_game_setup = game_setup; + std::vector players = m_game_setup->getPlayers(); + for (unsigned int i = 0; i < players.size(); i++) + { + m_player_states.insert(std::pair(players[i], LOADING)); + } + m_ready_count = 0; +} + +StartGameProtocol::~StartGameProtocol() +{ +} + +void StartGameProtocol::notifyEvent(Event* event) +{ + if (event->data.size() < 5) + { + Log::error("StartGameProtocol", "Too short message."); + return; + } + uint32_t token = event->data.gui32(); + uint8_t ready = event->data.gui8(4); + STKPeer* peer = (*(event->peer)); + if (peer->getClientServerToken() != token) + { + Log::error("StartGameProtocol", "Bad token received."); + return; + } + if (m_listener->isServer() && ready) // on server, player is ready + { + Log::info("StartGameProtocol", "One of the players is ready."); + m_player_states[peer->getPlayerProfile()] = READY; + m_ready_count++; + if (m_ready_count == m_game_setup->getPlayerCount()) + { + // everybody ready, synchronize + SynchronizationProtocol* protocol = static_cast(m_listener->getProtocol(PROTOCOL_SYNCHRONIZATION)); + if (protocol) + { + protocol->startCountdown(5000); // 5 seconds countdown + Log::info("StartGameProtocol", "All players ready, starting countdown."); + m_ready = true; + return; + } + else + Log::error("StartGameProtocol", "The Synchronization protocol hasn't been started."); + } + } + else + { + Log::error("StartGameProtocol", "Received a message with bad format."); + } + // on the client, we shouldn't even receive messages. +} + +void StartGameProtocol::setup() +{ + m_state = NONE; + m_ready_count = 0; + m_ready = false; + Log::info("SynchronizationProtocol", "Ready !"); +} + +bool sort_karts (NetworkPlayerProfile* a, NetworkPlayerProfile* b) +{ return (a->race_id < b->race_id); } + +void StartGameProtocol::update() +{ + if (m_state == NONE) + { + // if no synchronization protocol exists, create one + m_listener->requestStart(new SynchronizationProtocol()); + Log::info("StartGameProtocol", "SynchronizationProtocol started."); + // race startup sequence + + NetworkWorld::getInstance()->start(); // builds it and starts + race_manager->setNumKarts(m_game_setup->getPlayerCount()); + race_manager->setNumPlayers(m_game_setup->getPlayerCount()); + race_manager->setNumLocalPlayers(1); + std::vector players = m_game_setup->getPlayers(); + std::sort(players.begin(), players.end(), sort_karts); + // have to add self first + for (unsigned int i = 0; i < players.size(); i++) + { + bool is_me = (players[i]->user_profile == Online::CurrentUser::get()); + if (is_me) + { + NetworkPlayerProfile* profile = players[i]; + RemoteKartInfo rki(profile->race_id, profile->kart_name, + profile->user_profile->getUserName(), profile->race_id, !is_me); + rki.setGlobalPlayerId(profile->race_id); + rki.setLocalPlayerId(is_me?0:1); + rki.setHostId(profile->race_id); + PlayerProfile* profileToUse = unlock_manager->getCurrentPlayer(); + assert(profileToUse); + InputDevice* device = input_manager->getDeviceList()->getLatestUsedDevice(); + int new_player_id = StateManager::get()->createActivePlayer( profileToUse, device , players[i]->user_profile); + device->setPlayer(StateManager::get()->getActivePlayer(new_player_id)); + input_manager->getDeviceList()->setSinglePlayer(StateManager::get()->getActivePlayer(new_player_id)); + + race_manager->setPlayerKart(i, rki); + race_manager->setLocalKartInfo(new_player_id, profile->kart_name); + Log::info("StartGameProtocol", "Self player device added."); // self config + NetworkWorld::getInstance()->m_self_kart = profile->kart_name; + } + } + for (unsigned int i = 0; i < players.size(); i++) + { + bool is_me = (players[i]->user_profile == Online::CurrentUser::get()); + NetworkPlayerProfile* profile = players[i]; + RemoteKartInfo rki(profile->race_id, profile->kart_name, + profile->user_profile->getUserName(), profile->race_id, !is_me); + rki.setGlobalPlayerId(profile->race_id); + // on the server, the race id must be the local one. + rki.setLocalPlayerId(m_listener->isServer()?profile->race_id:(is_me?0:1)); + rki.setHostId(profile->race_id); + Log::info("StartGameProtocol", "Creating kart %s for Player#%d with race_id %d", profile->kart_name.c_str(), i, profile->race_id); + + if (!is_me) + { + StateManager::get()->createActivePlayer( NULL, NULL , players[i]->user_profile); + + race_manager->setPlayerKart(i, rki); + } + } + race_manager->computeRandomKartList(); + Log::info("StartGameProtocol", "Player configuration ready."); + m_state = SYNCHRONIZATION_WAIT; +/* + KartSelectionScreen* s = KartSelectionScreen::getInstance(); + s->setMultiplayer(false); + s->setFromOverworld(false); + StateManager::get()->pushScreen( s );*/ + } + else if (m_state == SYNCHRONIZATION_WAIT) + { + SynchronizationProtocol* protocol = static_cast + (m_listener->getProtocol(PROTOCOL_SYNCHRONIZATION)); + if (protocol) + { + // now the synchronization protocol exists. + Log::info("StartGameProtocol", "Starting the race loading."); + race_manager->startSingleRace("jungle", 1, false); + m_state = LOADING; + } + } + else if (m_state == LOADING) + { + if (m_ready) + { + m_state = READY; + } + } + else if (m_state == READY) + { + m_state = EXITING; + m_listener->requestTerminate(this); + } +} + +void StartGameProtocol::ready() // on clients, means the loading is finished +{ + if (!m_listener->isServer()) // if we're a client + { + assert(NetworkManager::getInstance()->getPeerCount() == 1); + NetworkString ns; + ns.ai32(NetworkManager::getInstance()->getPeers()[0]->getClientServerToken()).ai8(1); + Log::info("StartGameProtocol", "Player ready, notifying server."); + m_listener->sendMessage(this, ns, true); + m_state = READY; + m_ready = true; + return; + } + else // on the server + { + } +} + diff --git a/src/network/protocols/start_game_protocol.hpp b/src/network/protocols/start_game_protocol.hpp new file mode 100644 index 000000000..23d7fcbc1 --- /dev/null +++ b/src/network/protocols/start_game_protocol.hpp @@ -0,0 +1,36 @@ +#ifndef START_GAME_PROTOCOL_HPP +#define START_GAME_PROTOCOL_HPP + +#include "network/protocol.hpp" +#include + +class GameSetup; +class NetworkPlayerProfile; + +class StartGameProtocol : public Protocol +{ + protected: + enum STATE { NONE, SYNCHRONIZATION_WAIT, LOADING, READY, EXITING }; + std::map m_player_states; + + GameSetup* m_game_setup; + int m_ready_count; + double m_sending_time; + + STATE m_state; + bool m_ready; + + public: + StartGameProtocol(GameSetup* game_setup); + virtual ~StartGameProtocol(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update(); + virtual void asynchronousUpdate() {} + + void ready(); + +}; + +#endif // START_GAME_PROTOCOL_HPP diff --git a/src/network/protocols/start_server.cpp b/src/network/protocols/start_server.cpp new file mode 100644 index 000000000..8afec9a6d --- /dev/null +++ b/src/network/protocols/start_server.cpp @@ -0,0 +1,89 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/protocols/start_server.hpp" + +#include "network/network_manager.hpp" +#include "online/current_user.hpp" +#include "online/http_manager.hpp" +#include "config/user_config.hpp" + +StartServer::StartServer() : Protocol(NULL, PROTOCOL_SILENT) +{ +} + +StartServer::~StartServer() +{ +} + +void StartServer::notifyEvent(Event* event) +{ +} + +void StartServer::setup() +{ + m_state = NONE; +} + +void StartServer::asynchronousUpdate() +{ + if (m_state == NONE) + { + TransportAddress addr = NetworkManager::getInstance()->getPublicAddress(); + m_request = new Online::XMLRequest(); + m_request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); + m_request->setParameter("id",Online::CurrentUser::get()->getUserID()); + m_request->setParameter("token",Online::CurrentUser::get()->getToken()); + m_request->setParameter("address",addr.ip); + m_request->setParameter("port",addr.port); + m_request->setParameter("max_players",UserConfigParams::m_server_max_players); + m_request->setParameter("action","start-server"); + + Online::HTTPManager::get()->addRequest(m_request); + m_state = REQUEST_PENDING; + } + else if (m_state == REQUEST_PENDING && m_request->isDone()) + { + const XMLNode * result = m_request->getResult(); + std::string rec_success; + + if(result->get("success", &rec_success)) + { + if(rec_success == "yes") + { + Log::info("StartServer", "Server is now online."); + } + else + { + Log::error("StartServer", "Fail to start server."); + } + } + else + { + Log::error("StartServer", "Fail to start server."); + } + m_state = DONE; + } + else if (m_state == DONE) + { + m_state = EXITING; + delete m_request; + m_request = NULL; + m_listener->requestTerminate(this); + } +} diff --git a/src/network/protocols/start_server.hpp b/src/network/protocols/start_server.hpp new file mode 100644 index 000000000..c56d8352c --- /dev/null +++ b/src/network/protocols/start_server.hpp @@ -0,0 +1,34 @@ +#ifndef START_SERVER_HPP +#define START_SERVER_HPP + +#include "network/protocol.hpp" +#include "online/request.hpp" + +/*! + * This protocol tells to the database that the server is up and running, + * and shows online the public IP:port that stores the NetworkManager. + */ +class StartServer : public Protocol +{ + public: + StartServer(); + virtual ~StartServer(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + Online::XMLRequest* m_request; + enum STATE + { + NONE, + REQUEST_PENDING, + DONE, + EXITING + }; + STATE m_state; +}; + +#endif // START_SERVER_HPP diff --git a/src/network/protocols/stop_server.cpp b/src/network/protocols/stop_server.cpp new file mode 100644 index 000000000..187104d8b --- /dev/null +++ b/src/network/protocols/stop_server.cpp @@ -0,0 +1,88 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/protocols/stop_server.hpp" + +#include "network/network_manager.hpp" +#include "online/current_user.hpp" +#include "online/http_manager.hpp" +#include "config/user_config.hpp" + +StopServer::StopServer() : Protocol(NULL, PROTOCOL_SILENT) +{ +} + +StopServer::~StopServer() +{ +} + +void StopServer::notifyEvent(Event* event) +{ +} + +void StopServer::setup() +{ + m_state = NONE; +} + +void StopServer::asynchronousUpdate() +{ + if (m_state == NONE) + { + TransportAddress addr = NetworkManager::getInstance()->getPublicAddress(); + m_request = new Online::XMLRequest(); + m_request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); + m_request->setParameter("id",Online::CurrentUser::get()->getUserID()); + m_request->setParameter("token",Online::CurrentUser::get()->getToken()); + m_request->setParameter("address",addr.ip); + m_request->setParameter("port",addr.port); + m_request->setParameter("action","stop-server"); + + Online::HTTPManager::get()->addRequest(m_request); + m_state = REQUEST_PENDING; + } + else if (m_state == REQUEST_PENDING && m_request->isDone()) + { + const XMLNode * result = m_request->getResult(); + std::string rec_success; + + if(result->get("success", &rec_success)) + { + if(rec_success == "yes") + { + Log::info("StopServer", "Server is now offline."); + } + else + { + Log::error("StopServer", "Fail to stop server."); + } + } + else + { + Log::error("StopServer", "Fail to stop server."); + } + m_state = DONE; + } + else if (m_state == DONE) + { + m_state = EXITING; + delete m_request; + m_request = NULL; + m_listener->requestTerminate(this); + } +} diff --git a/src/network/protocols/stop_server.hpp b/src/network/protocols/stop_server.hpp new file mode 100644 index 000000000..7ad35723c --- /dev/null +++ b/src/network/protocols/stop_server.hpp @@ -0,0 +1,33 @@ +#ifndef STOP_SERVER_HPP +#define STOP_SERVER_HPP + +#include "network/protocol.hpp" +#include "online/request.hpp" + +/*! \brief Removes the server info from the database + */ + +class StopServer : public Protocol +{ + public: + StopServer(); + virtual ~StopServer(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + protected: + Online::XMLRequest* m_request; + enum STATE + { + NONE, + REQUEST_PENDING, + DONE, + EXITING + }; + STATE m_state; +}; + +#endif // STOP_SERVER_HPP diff --git a/src/network/protocols/synchronization_protocol.cpp b/src/network/protocols/synchronization_protocol.cpp new file mode 100644 index 000000000..f9f4c626c --- /dev/null +++ b/src/network/protocols/synchronization_protocol.cpp @@ -0,0 +1,179 @@ +#include "network/protocols/synchronization_protocol.hpp" + +#include "network/network_manager.hpp" +#include "network/protocols/kart_update_protocol.hpp" +#include "network/protocols/controller_events_protocol.hpp" +#include "utils/time.hpp" + +//----------------------------------------------------------------------------- + +SynchronizationProtocol::SynchronizationProtocol() : Protocol(NULL, PROTOCOL_SYNCHRONIZATION) +{ + unsigned int size = NetworkManager::getInstance()->getPeerCount(); + m_pings.resize(size, std::map()); + m_pings_count.resize(size); + for (unsigned int i = 0; i < size; i++) + { + m_pings_count[i] = 0; + } + m_successed_pings.resize(size); + m_total_diff.resize(size); + m_average_ping.resize(size); + m_countdown_activated = false; +} + +//----------------------------------------------------------------------------- + +SynchronizationProtocol::~SynchronizationProtocol() +{ +} + +//----------------------------------------------------------------------------- + +void SynchronizationProtocol::notifyEvent(Event* event) +{ + if (event->type != EVENT_TYPE_MESSAGE) + return; + if (event->data.size() < 10) + { + Log::warn("SynchronizationProtocol", "Received a message too short."); + return; + } + uint8_t talk_id = event->data.gui8(); + uint32_t token = event->data.gui32(1); + uint32_t request = event->data.gui8(5); + uint32_t sequence = event->data.gui32(6); + + std::vector peers = NetworkManager::getInstance()->getPeers(); + + if (m_listener->isServer()) + { + if (talk_id > peers.size()) + { + Log::warn("SynchronizationProtocol", "The ID isn't known."); + return; + } + } + + uint8_t peer_id; + for (unsigned int i = 0; i < peers.size(); i++) + { + if (peers[i]->isSamePeer(*event->peer)) + { + peer_id = i; + } + } + if (peers[peer_id]->getClientServerToken() != token) + { + Log::warn("SynchronizationProtocol", "Bad token from peer %d", talk_id); + return; + } + + if (request) + { + NetworkString response; + response.ai8(event->data.gui8(talk_id)).ai32(token).ai8(0).ai32(sequence); + m_listener->sendMessage(this, peers[peer_id], response, false); + Log::verbose("SynchronizationProtocol", "Answering sequence %u", sequence); + if (event->data.size() == 14 && !m_listener->isServer()) // countdown time in the message + { + uint32_t time_to_start = event->data.gui32(10); + Log::debug("SynchronizationProtocol", "Request to start game in %d.", time_to_start); + if (!m_countdown_activated) + startCountdown(time_to_start); + else + m_countdown = (double)(time_to_start/1000.0); + } + else + Log::verbose("SynchronizationProtocol", "No countdown for now."); + } + else // response + { + if (sequence >= m_pings[peer_id].size()) + { + Log::warn("SynchronizationProtocol", "The sequence# %u isn't known.", sequence); + return; + } + double current_time = Time::getRealTime(); + m_total_diff[peer_id] += current_time - m_pings[peer_id][sequence]; + Log::verbose("SynchronizationProtocol", "InstantPing is %u", + (unsigned int)((current_time - m_pings[peer_id][sequence])*1000)); + m_successed_pings[peer_id]++; + m_average_ping[peer_id] = (int)((m_total_diff[peer_id]/m_successed_pings[peer_id])*1000.0); + + Log::debug("SynchronizationProtocol", "Ping is %u", m_average_ping[peer_id]); + } +} + +//----------------------------------------------------------------------------- + +void SynchronizationProtocol::setup() +{ + Log::info("SynchronizationProtocol", "Ready !"); + m_countdown = 5.0; // init the countdown to 5s + m_has_quit = false; +} + +//----------------------------------------------------------------------------- + +void SynchronizationProtocol::asynchronousUpdate() +{ + static double timer = Time::getRealTime(); + double current_time = Time::getRealTime(); + if (m_countdown_activated) + { + m_countdown -= (current_time - m_last_countdown_update); + m_last_countdown_update = current_time; + Log::debug("SynchronizationProtocol", "Update! Countdown remaining : %f", m_countdown); + if (m_countdown < 0.0 && !m_has_quit) + { + m_has_quit = true; + Log::info("SynchronizationProtocol", "Countdown finished. Starting now."); + m_listener->requestStart(new KartUpdateProtocol()); + m_listener->requestStart(new ControllerEventsProtocol()); + m_listener->requestTerminate(this); + return; + } + static int seconds = -1; + if (seconds == -1) + { + seconds = (int)(ceil(m_countdown)); + } + else if (seconds != (int)(ceil(m_countdown))) + { + seconds = (int)(ceil(m_countdown)); + Log::info("SynchronizationProtocol", "Starting in %d seconds.", seconds); + } + } + if (current_time > timer+0.1) + { + std::vector peers = NetworkManager::getInstance()->getPeers(); + for (unsigned int i = 0; i < peers.size(); i++) + { + NetworkString ns; + ns.ai8(i).addUInt32(peers[i]->getClientServerToken()).addUInt8(1).addUInt32(m_pings[i].size()); + // now add the countdown if necessary + if (m_countdown_activated && m_listener->isServer()) + { + ns.addUInt32((int)(m_countdown*1000.0)); + Log::debug("SynchronizationProtocol", "CNTActivated: Countdown value : %f", m_countdown); + } + Log::verbose("SynchronizationProtocol", "Added sequence number %u for peer %d", m_pings[i].size(), i); + timer = current_time; + m_pings[i].insert(std::pair(m_pings_count[i], timer)); + m_listener->sendMessage(this, peers[i], ns, false); + m_pings_count[i]++; + } + } + +} + +//----------------------------------------------------------------------------- + +void SynchronizationProtocol::startCountdown(int ms_countdown) +{ + m_countdown_activated = true; + m_countdown = (double)(ms_countdown)/1000.0; + m_last_countdown_update = Time::getRealTime(); + Log::info("SynchronizationProtocol", "Countdown started with value %f", m_countdown); +} diff --git a/src/network/protocols/synchronization_protocol.hpp b/src/network/protocols/synchronization_protocol.hpp new file mode 100644 index 000000000..53fe36ca5 --- /dev/null +++ b/src/network/protocols/synchronization_protocol.hpp @@ -0,0 +1,35 @@ +#ifndef SYNCHRONIZATION_PROTOCOL_HPP +#define SYNCHRONIZATION_PROTOCOL_HPP + +#include "network/protocol.hpp" +#include +#include + +class SynchronizationProtocol : public Protocol +{ + public: + SynchronizationProtocol(); + virtual ~SynchronizationProtocol(); + + virtual void notifyEvent(Event* event); + virtual void setup(); + virtual void update() {} + virtual void asynchronousUpdate(); + + void startCountdown(int ms_countdown); + + int getCountdown() { return (int)(m_countdown*1000.0); } + + protected: + std::vector > m_pings; + std::vector m_average_ping; + std::vector m_pings_count; + std::vector m_successed_pings; + std::vector m_total_diff; + bool m_countdown_activated; + double m_countdown; + double m_last_countdown_update; + bool m_has_quit; +}; + +#endif // SYNCHRONIZATION_PROTOCOL_HPP diff --git a/src/network/race_info_message.cpp b/src/network/race_info_message.cpp deleted file mode 100644 index 4d2b6aa0b..000000000 --- a/src/network/race_info_message.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/race_info_message.hpp" - -#include "race/grand_prix_manager.hpp" -#include "race/race_manager.hpp" - -RaceInfoMessage::RaceInfoMessage(const std::vector& kart_info) - : Message(Message::MT_RACE_INFO) -{ - const GrandPrixData *cup=NULL; - int len = 2*getCharLength() // major, difficulty - + getIntLength() // minor - which is too big for a char/short! - + getCharLength(); // num karts - if(race_manager->getMajorMode()==RaceManager::MAJOR_MODE_GRAND_PRIX) - { - cup = race_manager->getGrandPrix(); - len += getStringLength(cup->getId()); - } - else - { - len += getStringLength(race_manager->getTrackName()); - len += getCharLength(); // num laps - } - len += getCharLength(); // kart_info.size() - for(unsigned int i=0; i& rkl=race_manager->getAIKartList(); - len += getStringVectorLength(rkl); - - allocate(len); - addChar(race_manager->getMajorMode() ); - addInt (race_manager->getMinorMode() ); - addChar(race_manager->getDifficulty() ); - addChar(race_manager->getNumberOfKarts()); - if(race_manager->getMajorMode()==RaceManager::MAJOR_MODE_GRAND_PRIX) - addString(cup->getId()); - else - { - addString(race_manager->getTrackName()); - addChar(race_manager->getNumLaps()); - } - - addChar(kart_info.size()); - for(unsigned int i=0; isetMajorMode ( RaceManager::MajorRaceModeType(getChar()) ); - race_manager->setMinorMode ( RaceManager::MinorRaceModeType(getInt()) ); - race_manager->setDifficulty( RaceManager::Difficulty (getChar()) ); - race_manager->setNumKarts ( getChar() ); - if(race_manager->getMajorMode()==RaceManager::MAJOR_MODE_GRAND_PRIX) - { - const GrandPrixData *cup = grand_prix_manager->getGrandPrix(getString()); - race_manager->setGrandPrix(*cup); - } - else - { - race_manager->setTrack(getString()); - race_manager->setNumLaps(getChar()); - } - - std::vector kart_info; - kart_info.resize(getChar()); - - for(unsigned int i=0; isetNumPlayers(kart_info.size()); - for(unsigned int i=0; isetPlayerKart(i, kart_info[i]); - } - std::vector rkl=getStringVector(); - race_manager->setAIKartList(rkl); -} // RaceInfoMessage diff --git a/src/network/race_result_ack_message.hpp b/src/network/race_result_ack_message.hpp deleted file mode 100644 index eda990208..000000000 --- a/src/network/race_result_ack_message.hpp +++ /dev/null @@ -1,58 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_RACE_RESULT_ACK_MESSAGE_HPP -#define HEADER_RACE_RESULT_ACK_MESSAGE_HPP - -#include - -#include "network/message.hpp" - - -/** This message is sent from the clients to the server when the race result - * screen was acknowledged, and then from the server to all clients to - * finish synchronisation. - */ -class RaceResultAckMessage : public Message -{ -private: - char m_menu_selected; -public: - /** Constructor, creates an empty message - */ - RaceResultAckMessage(char menu_choice) : Message(Message::MT_RACE_RESULT_ACK) - { - allocate(getCharLength()); - addChar(menu_choice); - } // RaceResultAckMessage - - // ------------------------------------------------------------------------ - /** Receives the ack message. - * \param pkt Received enet packet. - */ - RaceResultAckMessage(ENetPacket* pkt):Message(pkt, MT_RACE_RESULT_ACK) - { - m_menu_selected = getChar(); - } // RaceResultAckMessage(EnetPacket) - // ------------------------------------------------------------------------ - /** Returns the menu selected on the server after this message is received - * on a client. */ - char getSelectedMenu() const {return m_menu_selected; } - -}; // RaceResultAckMessageMessage -#endif diff --git a/src/network/race_result_message.cpp b/src/network/race_result_message.cpp deleted file mode 100644 index eeaacd656..000000000 --- a/src/network/race_result_message.cpp +++ /dev/null @@ -1,60 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/race_result_message.hpp" - -#include "karts/abstract_kart.hpp" -#include "modes/world.hpp" -#include "race/race_manager.hpp" - -/** Creates a message containing the finishing time and rank of each kart. - * This message is serialised so that it can be sent. - */ -RaceResultMessage::RaceResultMessage() : Message(MT_RACE_RESULT) -{ - World *world = World::getWorld(); - const unsigned int num_karts = world->getNumKarts(); - allocate(num_karts * (getFloatLength()+getCharLength())); - for(unsigned int i=0; igetKart(i); - addFloat(kart->getFinishTime()); - addChar(kart->getPosition()); - } // for i in karts -} // RaceResultMessage - -// ---------------------------------------------------------------------------- -/** De-serialises a race result message and sets the appropriate results in - * the kart and the race manager. - * \param pkt The enet message paket. - */ -RaceResultMessage::RaceResultMessage(ENetPacket* pkt) - : Message(pkt, MT_RACE_RESULT) -{ - World *world = World::getWorld(); - const unsigned int num_karts = world->getNumKarts(); - for(unsigned int i=0; igetKart(i); - float time = getFloat(); - char position = getChar(); - kart->setPosition(position); - kart->finishedRace(time); - } -} // RaceResultMessage - diff --git a/src/network/race_state.cpp b/src/network/race_state.cpp deleted file mode 100644 index db5c66c83..000000000 --- a/src/network/race_state.cpp +++ /dev/null @@ -1,195 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#include "network/race_state.hpp" - -#include "items/item_manager.hpp" -#include "items/powerup.hpp" -#include "items/projectile_manager.hpp" -#include "karts/rescue_animation.hpp" -#include "modes/world.hpp" -#include "network/network_manager.hpp" -#include "physics/physics.hpp" - -RaceState *race_state=NULL; - -// ---------------------------------------------------------------------------- -void RaceState::serialise() -{ - // First compute the overall size needed - // ===================================== - int len = 0; - - // 1. Add all kart information - // --------------------------- - unsigned int num_karts = World::getWorld()->getCurrentNumKarts(); - KartControl c; - // Send the number of karts and for each kart the compressed - // control structure, xyz,hpr, and speed (which is necessary to - // display the speed, and e.g. to determine when a parachute is detached) - len += 1 + num_karts*(KartControl::getLength() - + getVec3Length()+getQuaternionLength() - + getFloatLength()) ; - - // 2. Add information about collected items - // --------------------------------------- - len += 1 + m_item_info.size()* ItemInfo::getLength(); - - // 3. Add rocket positions - // ----------------------- - len += 2 + m_flyable_info.size()*FlyableInfo::getLength(); - - // 4. Add collisions - // ================= - len += 1 + m_collision_info.size()*getCharLength(); - - // Now add the data - // ================ - allocate(len); - - // 1. Kart positions - // ----------------- - addChar(num_karts); - World *world = World::getWorld(); - for(unsigned int i=0; igetKart(i); - m_kart_controls[i].serialise(this); - addVec3(kart->getXYZ()); - addQuaternion(kart->getRotation()); - addFloat(kart->getSpeed()); - } // for i - - // 2. Collected items - // ----------------- - addChar(m_item_info.size()); - for(unsigned int i=0; igetKart(i); - // Firing needs to be done from here to guarantee that any potential - // new rockets are created before the update for the rockets is handled - if(kc.m_fire) - kart->getPowerup()->use(); - kart->setXYZ(xyz); - kart->setRotation(q); - kart->setSpeed(getFloat()); - } // for i - - // 2. Collected Items - // ----------------- - unsigned short num_items=getChar(); - for(unsigned int i=0; igetKart(hi.m_kart_id)); - else - { - Item *item = ItemManager::get()->getItem(hi.m_item_id); - ItemManager::get()->collectedItem(item, - world->getKart(hi.m_kart_id), - hi.m_add_info); - } - } - - // 3. Projectiles - // -------------- - unsigned short num_flyables = getShort(); - m_flyable_info.clear(); - m_flyable_info.resize(num_flyables); - for(unsigned short i=0; igetKart(kart_id1)->crashed(NULL, normal); - } - else - { - // FIXME: KartKartCollision now takes information about the - // collision points. This either needs to be added as the third - // parameter, or perhaps the outcome of the collision (the - // impulse) could be added. - world->getPhysics()->KartKartCollision( - world->getKart(kart_id1), Vec3(0,0,0), - world->getKart(kart_id2), Vec3(0,0,0)); - } - } // for(i=0; i - -#include "items/flyable.hpp" -#include "items/item.hpp" -#include "karts/abstract_kart.hpp" -#include "karts/controller/kart_control.hpp" -#include "modes/world.hpp" -#include "network/flyable_info.hpp" -#include "network/item_info.hpp" -#include "network/message.hpp" -#include "utils/aligned_array.hpp" - -/** This class stores the state information of a (single) race, e.g. the - position and orientation of karts, collisions that have happened etc. - It is used for the network version to update the clients with the - 'official' state information from the server. - */ -class RaceState : public Message -{ -private: - - /** Updates about collected items. */ - std::vector m_item_info; - /** Updates about existing flyables. */ - AlignedArray m_flyable_info; - /** Stores the controls of each kart at the beginning of its update(). */ - std::vector m_kart_controls; - /** Collision information. This vector stores information about which - * kart collided with which kart or track (kartid=-1) */ - std::vector m_collision_info; - - public: - /** Initialise the global race state. */ - RaceState() : Message(MT_RACE_STATE) - { - m_kart_controls.resize(World::getWorld()->getNumKarts()); - } // RaceState() - // -------------------------------------------------------------------- - void itemCollected(int kartid, int item_id, char add_info=-1) - { - m_item_info.push_back(ItemInfo(kartid, item_id, add_info)); - } // itemCollected - // -------------------------------------------------------------------- - /** Collects information about collision in which at least one kart was - * involved. Other collision (e.g. projectiles, moving physics) are - * not needed on the client, so it's not stored at all. If a kart - * track collision happens, the second kart id is -1 (necessary to - * play back sound effects). A simple int vector is used to store the - * pair of collision, so the first collision is using the index 0 and - * 1; the second one 2 and 3 etc. - * \param kartId1 World id of the kart involved in the collision. - * \param kartId2 World id of the 2nd kart involved in the collision, - * or -1 if it's the track (which is the default). - */ - void addCollision(signed char kartId1, signed char kartId2=-1) - { - m_collision_info.push_back(kartId1); - m_collision_info.push_back(kartId2); - } // addCollision - // -------------------------------------------------------------------- - void setNumFlyables(int n) { m_flyable_info.resize(n); } - // -------------------------------------------------------------------- - void setFlyableInfo(int n, const FlyableInfo& fi) - { - m_flyable_info[n] = fi; - } - // -------------------------------------------------------------------- - /** Stores the current kart control (at the time kart->update() is - * called. This allows modifications of kart->m_control during the - * update (e.g. see in kart::update() how firing is handled). - */ - void storeKartControls(const AbstractKart& kart) - { - m_kart_controls[kart.getWorldKartId()] = kart.getControls(); - } // storeKartControls - // -------------------------------------------------------------------- - void serialise(); - void receive(ENetPacket *pkt); - void clear(); // Removes all currently stored information - unsigned int getNumFlyables() const {return m_flyable_info.size(); } - const FlyableInfo - &getFlyable(unsigned int i) const {return m_flyable_info[i];} - }; // RaceState - -extern RaceState *race_state; - -#endif - diff --git a/src/network/remote_kart_info.hpp b/src/network/remote_kart_info.hpp index 507458d08..2a3da9b7e 100644 --- a/src/network/remote_kart_info.hpp +++ b/src/network/remote_kart_info.hpp @@ -38,12 +38,15 @@ class RemoteKartInfo int m_global_player_id; int m_host_id; SoccerTeam m_soccer_team; + bool m_network_player; public: RemoteKartInfo(int player_id, const std::string& kart_name, - const irr::core::stringw& user_name, int host_id) + const irr::core::stringw& user_name, int host_id, bool network) : m_kart_name(kart_name), m_user_name(user_name), - m_local_player_id(player_id), m_host_id(host_id), m_soccer_team(SOCCER_TEAM_NONE) + m_local_player_id(player_id), m_host_id(host_id), + m_soccer_team(SOCCER_TEAM_NONE), m_network_player(network) + {}; RemoteKartInfo(const std::string& kart_name) {m_kart_name=kart_name; m_user_name=""; @@ -56,10 +59,12 @@ public: void setLocalPlayerId(int id) { m_local_player_id = id; } void setGlobalPlayerId(int id) { m_global_player_id = id; } void setSoccerTeam(SoccerTeam team) { m_soccer_team = team; } + void setNetworkPlayer(bool value) { m_network_player = value; } int getHostId() const { return m_host_id; } int getLocalPlayerId() const { return m_local_player_id; } int getGlobalPlayerId() const { return m_global_player_id; } + bool isNetworkPlayer() const { return m_network_player; } const std::string& getKartName() const { return m_kart_name; } const irr::core::stringw& getPlayerName() const { return m_user_name; } const SoccerTeam getSoccerTeam() const {return m_soccer_team; } diff --git a/src/network/server_network_manager.cpp b/src/network/server_network_manager.cpp new file mode 100644 index 000000000..3b7618ca0 --- /dev/null +++ b/src/network/server_network_manager.cpp @@ -0,0 +1,122 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/server_network_manager.hpp" + +#include "network/protocols/get_public_address.hpp" +#include "network/protocols/hide_public_address.hpp" +#include "network/protocols/show_public_address.hpp" +#include "network/protocols/get_peer_address.hpp" +#include "network/protocols/connect_to_server.hpp" +#include "network/protocols/stop_server.hpp" +#include "network/protocols/server_lobby_room_protocol.hpp" + +#include "main_loop.hpp" +#include "utils/log.hpp" + +#include +#include +#include +#include +#include + +void* waitInput2(void* data) +{ + std::string str = ""; + bool stop = false; + while(!stop) + { + getline(std::cin, str); + if (str == "quit") + { + stop = true; + } + else if (str == "kickall") + { + ServerNetworkManager::getInstance()->kickAllPlayers(); + } + else if (str == "start") + { + ServerLobbyRoomProtocol* protocol = static_cast(ProtocolManager::getInstance()->getProtocol(PROTOCOL_LOBBY_ROOM)); + assert(protocol); + protocol->startGame(); + } + else if (str == "selection") + { + ServerLobbyRoomProtocol* protocol = static_cast(ProtocolManager::getInstance()->getProtocol(PROTOCOL_LOBBY_ROOM)); + assert(protocol); + protocol->startSelection(); + } + } + + uint32_t id = ProtocolManager::getInstance()->requestStart(new StopServer()); + while(ProtocolManager::getInstance()->getProtocolState(id) != PROTOCOL_STATE_TERMINATED) + { + } + + main_loop->abort(); + exit(0); + + return NULL; +} + +ServerNetworkManager::ServerNetworkManager() +{ + m_localhost = NULL; + m_thread_keyboard = NULL; +} + +ServerNetworkManager::~ServerNetworkManager() +{ + if (m_thread_keyboard) + pthread_cancel(*m_thread_keyboard);//, SIGKILL); +} + +void ServerNetworkManager::run() +{ + if (enet_initialize() != 0) + { + Log::error("ServerNetworkManager", "Could not initialize enet."); + return; + } + m_localhost = new STKHost(); + m_localhost->setupServer(STKHost::HOST_ANY, 7321, 16, 2, 0, 0); + m_localhost->startListening(); + + Log::info("ServerNetworkManager", "Host initialized."); + + // listen keyboard console input + m_thread_keyboard = (pthread_t*)(malloc(sizeof(pthread_t))); + pthread_create(m_thread_keyboard, NULL, waitInput2, NULL); + + NetworkManager::run(); + Log::info("ServerNetworkManager", "Ready."); +} + +void ServerNetworkManager::kickAllPlayers() +{ + for (unsigned int i = 0; i < m_peers.size(); i++) + { + m_peers[i]->disconnect(); + } +} + +void ServerNetworkManager::sendPacket(const NetworkString& data, bool reliable) +{ + m_localhost->broadcastPacket(data, reliable); +} diff --git a/src/network/server_network_manager.hpp b/src/network/server_network_manager.hpp new file mode 100644 index 000000000..2c2dc4990 --- /dev/null +++ b/src/network/server_network_manager.hpp @@ -0,0 +1,50 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SERVER_NETWORK_MANAGER_HPP +#define SERVER_NETWORK_MANAGER_HPP + +#include "network/network_manager.hpp" + + +class ServerNetworkManager : public NetworkManager +{ + friend class Singleton; + public: + static ServerNetworkManager* getInstance() + { + return Singleton::getInstance(); + } + + virtual void run(); + + void kickAllPlayers(); + + virtual void sendPacket(const NetworkString& data, bool reliable = true); + + virtual bool isServer() { return true; } + + protected: + ServerNetworkManager(); + virtual ~ServerNetworkManager(); + + pthread_t* m_thread_keyboard; + +}; + +#endif // SERVER_NETWORK_MANAGER_HPP diff --git a/src/network/singleton.hpp b/src/network/singleton.hpp new file mode 100644 index 000000000..616b0fac6 --- /dev/null +++ b/src/network/singleton.hpp @@ -0,0 +1,65 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef SINGLETON_HPP +#define SINGLETON_HPP + +#include "utils/log.hpp" + +template +class Singleton +{ + protected: + Singleton () { m_singleton = NULL; } + virtual ~Singleton () + { + Log::info("Singleton", "Destroyed singleton."); + } + + public: + template + static S *getInstance () + { + if (m_singleton == NULL) + m_singleton = new S; + + S* result = (dynamic_cast (m_singleton)); + if (result == NULL) + Log::debug("Singleton", "THE SINGLETON HAS NOT BEEN REALOCATED, IT IS NOT OF THE REQUESTED TYPE."); + return result; + } + static T *getInstance() + { + return (dynamic_cast (m_singleton)); + } + + static void kill () + { + if (m_singleton) + { + delete m_singleton; + } + } + + private: + static T *m_singleton; +}; + +template T *Singleton::m_singleton = NULL; + +#endif // SINGLETON_HPP diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp new file mode 100644 index 000000000..6012881eb --- /dev/null +++ b/src/network/stk_host.cpp @@ -0,0 +1,242 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/stk_host.hpp" + +#include "graphics/irr_driver.hpp" // get access to irrlicht sleep function +#include "network/network_manager.hpp" +#include "utils/log.hpp" + +#include +#ifdef WIN32 +# include "Ws2tcpip.h" +# define inet_ntop InetNtop +#else +# include +#endif +#include +#include + +// ---------------------------------------------------------------------------- + +void* STKHost::receive_data(void* self) +{ + ENetEvent event; + ENetHost* host = (((STKHost*)(self))->m_host); + while (1) + { + while (enet_host_service(host, &event, 20) != 0) { + Event* evt = new Event(&event); + if (event.type != ENET_EVENT_TYPE_NONE) + NetworkManager::getInstance()->notifyEvent(evt); + delete evt; + } + } + return NULL; +} + +// ---------------------------------------------------------------------------- + +STKHost::STKHost() +{ + m_host = NULL; + m_listening_thread = NULL; +} + +// ---------------------------------------------------------------------------- + +STKHost::~STKHost() +{ + if (m_listening_thread) + { + pthread_cancel(*m_listening_thread);//, SIGKILL); with kill + delete m_listening_thread; + m_listening_thread = NULL; + } + if (m_host) + { + enet_host_destroy(m_host); + } +} + +// ---------------------------------------------------------------------------- + +void STKHost::setupServer(uint32_t address, uint16_t port, int peer_count, + int channel_limit, uint32_t max_incoming_bandwidth, + uint32_t max_outgoing_bandwidth) +{ + ENetAddress* addr = (ENetAddress*)(malloc(sizeof(ENetAddress))); + addr->host = address; + addr->port = port; + + m_host = enet_host_create(addr, peer_count, channel_limit, + max_incoming_bandwidth, max_outgoing_bandwidth); + if (m_host == NULL) + { + Log::error("STKHost", "An error occurred while trying to create an ENet" + " server host."); + exit (EXIT_FAILURE); + } +} + +// ---------------------------------------------------------------------------- + +void STKHost::setupClient(int peer_count, int channel_limit, + uint32_t max_incoming_bandwidth, + uint32_t max_outgoing_bandwidth) +{ + m_host = enet_host_create(NULL, peer_count, channel_limit, + max_incoming_bandwidth, max_outgoing_bandwidth); + if (m_host == NULL) + { + Log::error("STKHost", "An error occurred while trying to create an ENet" + " client host."); + exit (EXIT_FAILURE); + } +} + +// ---------------------------------------------------------------------------- + +void STKHost::startListening() +{ + m_listening_thread = (pthread_t*)(malloc(sizeof(pthread_t))); + pthread_create(m_listening_thread, NULL, &STKHost::receive_data, this); +} + +// ---------------------------------------------------------------------------- + +void STKHost::stopListening() +{ + if(m_listening_thread) + { + pthread_cancel(*m_listening_thread); + m_listening_thread = NULL; + } +} + +// ---------------------------------------------------------------------------- + +void STKHost::sendRawPacket(uint8_t* data, int length, TransportAddress dst) +{ + struct sockaddr_in to; + int to_len = sizeof(to); + memset(&to,0,to_len); + + to.sin_family = AF_INET; + to.sin_port = htons(dst.port); + to.sin_addr.s_addr = htonl(dst.ip); + + sendto(m_host->socket, (char*)data, length, 0,(sockaddr*)&to, to_len); + Log::verbose("STKHost", "Raw packet sent to %i.%i.%i.%i:%u", ((dst.ip>>24)&0xff) + , ((dst.ip>>16)&0xff), ((dst.ip>>8)&0xff), ((dst.ip>>0)&0xff), dst.port); +} + +// ---------------------------------------------------------------------------- + +uint8_t* STKHost::receiveRawPacket() +{ + uint8_t* buffer; // max size needed normally (only used for stun) + buffer = (uint8_t*)(malloc(sizeof(uint8_t)*2048)); + memset(buffer, 0, 2048); + + int len = recv(m_host->socket,(char*)buffer,2048, 0); + int i = 0; + // wait to receive the message because enet sockets are non-blocking + while(len < 0) + { + i++; + len = recv(m_host->socket,(char*)buffer,2048, 0); + irr_driver->getDevice()->sleep(1); + } + return buffer; +} + +// ---------------------------------------------------------------------------- + +uint8_t* STKHost::receiveRawPacket(TransportAddress sender) +{ + uint8_t* buffer; // max size needed normally (only used for stun) + buffer = (uint8_t*)(malloc(sizeof(uint8_t)*2048)); + memset(buffer, 0, 2048); + + socklen_t from_len; + struct sockaddr addr; + + from_len = sizeof(addr); + int len = recvfrom(m_host->socket, (char*)buffer, 2048, 0, &addr, &from_len); + + int i = 0; + // wait to receive the message because enet sockets are non-blocking + while(len < 0 || ( + (uint8_t)(addr.sa_data[2]) != (sender.ip>>24&0xff) + && (uint8_t)(addr.sa_data[3]) != (sender.ip>>16&0xff) + && (uint8_t)(addr.sa_data[4]) != (sender.ip>>8&0xff) + && (uint8_t)(addr.sa_data[5]) != (sender.ip&0xff))) + { + i++; + len = recvfrom(m_host->socket, (char*)buffer, 2048, 0, &addr, &from_len); + irr_driver->getDevice()->sleep(1); // wait 1 millisecond between two checks + } + if (addr.sa_family == AF_INET) + { + char s[20]; + inet_ntop(AF_INET, &(((struct sockaddr_in *)&addr)->sin_addr), s, 20); + Log::info("STKHost", "IPv4 Address of the sender was %s", s); + } + return buffer; +} + +// ---------------------------------------------------------------------------- + +void STKHost::broadcastPacket(const NetworkString& data, bool reliable) +{ + ENetPacket* packet = enet_packet_create(data.c_str(), data.size()+1, + (reliable ? ENET_PACKET_FLAG_RELIABLE : ENET_PACKET_FLAG_UNSEQUENCED)); + enet_host_broadcast(m_host, 0, packet); +} + +// ---------------------------------------------------------------------------- + +bool STKHost::peerExists(TransportAddress peer) +{ + for (unsigned int i = 0; i < m_host->peerCount; i++) + { + if (m_host->peers[i].address.host == turnEndianness(peer.ip) && + m_host->peers[i].address.port == peer.port) + { + return true; + } + } + return false; +} + +// ---------------------------------------------------------------------------- + +bool STKHost::isConnectedTo(TransportAddress peer) +{ + for (unsigned int i = 0; i < m_host->peerCount; i++) + { + if (m_host->peers[i].address.host == turnEndianness(peer.ip) && + m_host->peers[i].address.port == peer.port && + m_host->peers[i].state == ENET_PEER_STATE_CONNECTED) + { + return true; + } + } + return false; +} diff --git a/src/network/stk_host.hpp b/src/network/stk_host.hpp new file mode 100644 index 000000000..5cf3eac7f --- /dev/null +++ b/src/network/stk_host.hpp @@ -0,0 +1,145 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/*! \file stk_host.hpp + * \brief Defines an interface to use network low-level functions easily. + */ +#ifndef STK_HOST_HPP +#define STK_HOST_HPP + +#include "network/types.hpp" +#include "network/network_string.hpp" + +#include + +#include + +/*! \class STKHost + * \brief Represents the local host. + * This host is either a server host or a client host. A client host is in + * charge of connecting to a server. A server opens a socket for incoming + * connections. + * By default, this host will use ENet to exchange packets. It also defines an + * interface for ENet use. Nevertheless, this class can be used to send and/or + * receive packets whithout ENet adding its headers. + * This class is used by the Network Manager to send packets. + */ +class STKHost +{ + friend class STKPeer; // allow direct enet modifications in implementations + public: + /*! \enum HOST_TYPE + * \brief Defines three host types for the server. + * These values tells the host where he will accept connections from. + */ + enum HOST_TYPE + { + HOST_ANY = 0, //!< Any host. + HOST_BROADCAST = 0xFFFFFFFF, //!< Defines the broadcast address. + PORT_ANY = 0 //!< Any port. + }; + + /*! \brief Constructor */ + STKHost(); + /*! \brief Destructor */ + virtual ~STKHost(); + + /*! \brief Thread function checking if data is received. + * This function tries to get data from network low-level functions as + * often as possible. When something is received, it generates an + * event and passes it to the Network Manager. + * \param self : used to pass the ENet host to the function. + */ + static void* receive_data(void* self); + + /*! \brief Setups the host as a server. + * \param address : The IPv4 address of incoming connections. + * \param port : The port on which the server listens. + * \param peer_count : The maximum number of peers. + * \param channel_limit : The maximum number of channels per peer. + * \param max_incoming_bandwidth : The maximum incoming bandwidth. + * \param max_outgoing_bandwidth : The maximum outgoing bandwidth. + */ + void setupServer(uint32_t address, uint16_t port, + int peer_count, int channel_limit, + uint32_t max_incoming_bandwidth, + uint32_t max_outgoing_bandwidth); + /*! \brief Setups the host as a client. + * In fact there is only one peer connected to this host. + * \param peer_count : The maximum number of peers. + * \param channel_limit : The maximum number of channels per peer. + * \param max_incoming_bandwidth : The maximum incoming bandwidth. + * \param max_outgoing_bandwidth : The maximum outgoing bandwidth. + */ + void setupClient(int peer_count, int channel_limit, + uint32_t max_incoming_bandwidth, + uint32_t max_outgoing_bandwidth); + + /*! \brief Starts the listening of events from ENet. + * Starts a thread that updates it as often as possible. + */ + void startListening(); + /*! \brief Stops the listening of events from ENet. + * Stops the thread that was receiving events. + */ + void stopListening(); + + /*! \brief Sends a packet whithout ENet adding its headers. + * This function is used in particular to achieve the STUN protocol. + * \param data : Data to send. + * \param length : Length of the sent data. + * \param dst : Destination of the packet. + */ + void sendRawPacket(uint8_t* data, int length, + TransportAddress dst); + /*! \brief Receives a packet directly from the network interface. + * Receive a packet whithout ENet processing it. + * \return A string containing the data of the received packet. + */ + uint8_t* receiveRawPacket(); + /*! \brief Receives a packet directly from the network interface and + * filter its address. + * Receive a packet whithout ENet processing it. Checks that the + * sender of the packet is the one that corresponds to the sender + * parameter. Does not check the port right now. + * \param sender : Transport address of the original sender of the + * wanted packet. + * \return A string containing the data of the received packet + * matching the sender's ip address. + */ + uint8_t* receiveRawPacket(TransportAddress sender); + /*! \brief Broadcasts a packet to all peers. + * \param data : Data to send. + */ + void broadcastPacket(const NetworkString& data, bool reliable = true); + + /*! \brief Tells if a peer is known. + * \return True if the peer is known, false elseway. + */ + bool peerExists(TransportAddress peer_address); + /*! \brief Tells if a peer is known and connected. + * \return True if the peer is known and connected, false elseway. + */ + bool isConnectedTo(TransportAddress peer_address); + protected: + ENetHost* m_host; //!< ENet host interfacing sockets. + pthread_t* m_listening_thread; //!< Thread listening network events. + +}; + +#endif // STK_HOST_HPP diff --git a/src/network/stk_peer.cpp b/src/network/stk_peer.cpp new file mode 100644 index 000000000..e96eda48d --- /dev/null +++ b/src/network/stk_peer.cpp @@ -0,0 +1,148 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#include "network/stk_peer.hpp" +#include "network/network_manager.hpp" +#include "utils/log.hpp" + +#include + +STKPeer::STKPeer() +{ + m_peer = NULL; + m_player_profile = new NetworkPlayerProfile*; + *m_player_profile = NULL; + m_client_server_token = new uint32_t; + *m_client_server_token = 0; + m_token_set = new bool; + *m_token_set = false; +} + +//----------------------------------------------------------------------------- + +STKPeer::STKPeer(const STKPeer& peer) +{ + m_peer = peer.m_peer; + m_player_profile = peer.m_player_profile; + m_client_server_token = peer.m_client_server_token; + m_token_set = peer.m_token_set; +} + +//----------------------------------------------------------------------------- + +STKPeer::~STKPeer() +{ + if (m_peer) + m_peer = NULL; + if (m_player_profile) + delete m_player_profile; + m_player_profile = NULL; +} + +//----------------------------------------------------------------------------- + +bool STKPeer::connectToHost(STKHost* localhost, TransportAddress host, + uint32_t channel_count, uint32_t data) +{ + ENetAddress address; + address.host = + ((host.ip & 0xff000000) >> 24) + + ((host.ip & 0x00ff0000) >> 8) + + ((host.ip & 0x0000ff00) << 8) + + ((host.ip & 0x000000ff) << 24); // because ENet wants little endian + address.port = host.port; + + ENetPeer* peer = enet_host_connect(localhost->m_host, &address, 2, 0); + if (peer == NULL) + { + Log::error("STKPeer", "Could not try to connect to server.\n"); + return false; + } + Log::verbose("STKPeer", "Connecting to %i.%i.%i.%i:%i.\nENetPeer address " + "is %ld", (peer->address.host>>0)&0xff, + (peer->address.host>>8)&0xff,(peer->address.host>>16)&0xff, + (peer->address.host>>24)&0xff,peer->address.port, (long int)(peer)); + return true; +} + +//----------------------------------------------------------------------------- + +void STKPeer::disconnect() +{ + enet_peer_disconnect(m_peer, 0); +} + +//----------------------------------------------------------------------------- + +void STKPeer::sendPacket(NetworkString const& data, bool reliable) +{ + Log::verbose("STKPeer", "sending packet of size %d to %i.%i.%i.%i:%i", + data.size(), (m_peer->address.host>>0)&0xff, + (m_peer->address.host>>8)&0xff,(m_peer->address.host>>16)&0xff, + (m_peer->address.host>>24)&0xff,m_peer->address.port); + ENetPacket* packet = enet_packet_create(data.c_str(), data.size()+1, + (reliable ? ENET_PACKET_FLAG_RELIABLE : ENET_PACKET_FLAG_UNSEQUENCED)); + /* to debug the packet output + printf("STKPeer: "); + for (unsigned int i = 0; i < data.size(); i++) + { + printf("%d ", (uint8_t)(data[i])); + } + printf("\n"); + */ + enet_peer_send(m_peer, 0, packet); +} + +//----------------------------------------------------------------------------- + +uint32_t STKPeer::getAddress() const +{ + return turnEndianness(m_peer->address.host); +} + +//----------------------------------------------------------------------------- + +uint16_t STKPeer::getPort() const +{ + return m_peer->address.port; +} + +//----------------------------------------------------------------------------- + +bool STKPeer::isConnected() const +{ + Log::info("STKPeer", "The peer state is %i\n", m_peer->state); + return (m_peer->state == ENET_PEER_STATE_CONNECTED); +} + +//----------------------------------------------------------------------------- + +bool STKPeer::exists() const +{ + return (m_peer != NULL); // assert that the peer exists +} + +//----------------------------------------------------------------------------- + +bool STKPeer::isSamePeer(const STKPeer* peer) const +{ + return peer->m_peer==m_peer; +} + +//----------------------------------------------------------------------------- + diff --git a/src/network/stk_peer.hpp b/src/network/stk_peer.hpp new file mode 100644 index 000000000..d7f10ba8f --- /dev/null +++ b/src/network/stk_peer.hpp @@ -0,0 +1,61 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef STK_PEER_HPP +#define STK_PEER_HPP + +#include "network/stk_host.hpp" +#include "network/network_string.hpp" +#include "network/game_setup.hpp" +#include + +class STKPeer +{ + friend class Event; + public: + STKPeer(); + STKPeer(const STKPeer& peer); + virtual ~STKPeer(); + + virtual void sendPacket(const NetworkString& data, bool reliable = true); + static bool connectToHost(STKHost* localhost, TransportAddress host, uint32_t channel_count, uint32_t data); + void disconnect(); + + void setClientServerToken(const uint32_t& token) { *m_client_server_token = token; *m_token_set = true; } + void unsetClientServerToken() { *m_token_set = false; } + void setPlayerProfile(NetworkPlayerProfile* profile) { *m_player_profile = profile; } + void setPlayerProfilePtr(NetworkPlayerProfile** profile) { m_player_profile = profile; } + + bool isConnected() const; + bool exists() const; + uint32_t getAddress() const; + uint16_t getPort() const; + NetworkPlayerProfile* getPlayerProfile() { return *m_player_profile; } + uint32_t getClientServerToken() const { return *m_client_server_token; } + bool isClientServerTokenSet() const { return *m_token_set; } + + bool isSamePeer(const STKPeer* peer) const; + + protected: + ENetPeer* m_peer; + NetworkPlayerProfile** m_player_profile; + uint32_t *m_client_server_token; + bool *m_token_set; +}; + +#endif // STK_PEER_HPP diff --git a/src/network/types.cpp b/src/network/types.cpp new file mode 100644 index 000000000..deb244c32 --- /dev/null +++ b/src/network/types.cpp @@ -0,0 +1,9 @@ +#include "network/types.hpp" + +uint32_t turnEndianness(uint32_t val) +{ + return ((val&0xff000000)>>24) + +((val&0x00ff0000)>>8) + +((val&0x0000ff00)<<8) + +((val&0x000000ff)<<24); +} diff --git a/src/network/types.hpp b/src/network/types.hpp new file mode 100644 index 000000000..81d8f2bc0 --- /dev/null +++ b/src/network/types.hpp @@ -0,0 +1,71 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 SuperTuxKart-Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/*! \file types.hpp + * \brief Declares the general types that are used by the network. + */ +#ifndef TYPES_HPP +#define TYPES_HPP + +#include "utils/types.hpp" + +#include + +/*! \class CallbackObject + * \brief Class that must be inherited to pass objects to protocols. + */ +class CallbackObject +{ + public: + CallbackObject() {} + ~CallbackObject() {} + +}; + +/*! \class TransportAddress + * \brief Describes a transport-layer address. + * For IP networks, a transport address is the couple ip:port. + */ +class TransportAddress : public CallbackObject +{ + public: + TransportAddress(uint32_t p_ip = 0, uint16_t p_port = 0) + { ip = p_ip; port = p_port; } + ~TransportAddress() {} + + uint32_t ip; //!< The IPv4 address + uint16_t port; //!< The port number +}; + +/*! \class PlayerLogin + * \brief Contains the information needed to authenticate a user. + */ +class PlayerLogin : public CallbackObject +{ + public: + PlayerLogin() {} + ~PlayerLogin() { username.clear(); password.clear(); } + + std::string username; //!< Username of the player + std::string password; //!< Password of the player +}; + +uint32_t turnEndianness(uint32_t val); + + +#endif // TYPES_HPP diff --git a/src/network/world_loaded_message.hpp b/src/network/world_loaded_message.hpp deleted file mode 100644 index 682e0fa05..000000000 --- a/src/network/world_loaded_message.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2008 Joerg Henrichs -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU 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 General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -#ifndef HEADER_WORLD_LOADED_HPP -#define HEADER_WORLD_LOADED_HPP - -#include "network/message.hpp" - -class WorldLoadedMessage : public Message -{ -// For now this is an empty message -public: - WorldLoadedMessage() :Message(MT_WORLD_LOADED) {allocate(0);} - WorldLoadedMessage(ENetPacket* pkt):Message(pkt, MT_WORLD_LOADED) {} -}; // WorldLoadedMessage -#endif diff --git a/src/online/current_user.cpp b/src/online/current_user.cpp index d16c68f4c..fd53934d1 100644 --- a/src/online/current_user.cpp +++ b/src/online/current_user.cpp @@ -19,13 +19,14 @@ #include "online/current_user.hpp" +#include "config/user_config.hpp" +#include "online/servers_manager.hpp" +#include "utils/log.hpp" +#include "utils/translation.hpp" + #include #include #include -#include "config/user_config.hpp" -#include "utils/translation.hpp" -#include "utils/log.hpp" -#include "online/servers_manager.hpp" using namespace Online; @@ -105,9 +106,9 @@ namespace Online{ return request; } - const CurrentUser::SignInRequest * CurrentUser::requestSignIn( const irr::core::stringw &username, - const irr::core::stringw &password, - bool save_session) + CurrentUser::SignInRequest * CurrentUser::requestSignIn( const irr::core::stringw &username, + const irr::core::stringw &password, + bool save_session, bool request_now) { assert(getUserState() == US_SIGNED_OUT); setSaveSession(save_session); @@ -116,8 +117,11 @@ namespace Online{ request->setParameter("action",std::string("connect")); request->setParameter("username",username); request->setParameter("password",password); - HTTPManager::get()->addRequest(request); - setUserState (US_SIGNING_IN); + if (request_now) + { + HTTPManager::get()->addRequest(request); + setUserState (US_SIGNING_IN); + } return request; } @@ -214,7 +218,9 @@ namespace Online{ // ============================================================================ - const CurrentUser::ServerJoinRequest * CurrentUser::requestServerJoin(uint32_t server_id){ + CurrentUser::ServerJoinRequest * CurrentUser::requestServerJoin(uint32_t server_id, + bool request_now) + { assert(getUserState() == US_SIGNED_IN || getUserState() == US_GUEST); ServerJoinRequest * request = new ServerJoinRequest(); request->setURL((std::string)UserConfigParams::m_server_multiplayer + "address-management.php"); @@ -222,7 +228,8 @@ namespace Online{ request->setParameter("token", getToken()); request->setParameter("id", getUserID()); request->setParameter("server_id", server_id); - HTTPManager::get()->addRequest(request); + if (request_now) + HTTPManager::get()->addRequest(request); return request; } diff --git a/src/online/current_user.hpp b/src/online/current_user.hpp index dfa548f6a..3a1b2ee63 100644 --- a/src/online/current_user.hpp +++ b/src/online/current_user.hpp @@ -19,14 +19,15 @@ #ifndef HEADER_CURRENT_ONLINE_USER_HPP #define HEADER_CURRENT_ONLINE_USER_HPP -#include "online/user.hpp" -#include -#include -#include "utils/types.hpp" -#include "online/server.hpp" #include "http_manager.hpp" +#include "online/server.hpp" +#include "online/user.hpp" +#include "utils/types.hpp" #include "utils/synchronised.hpp" +#include + +#include namespace Online{ @@ -92,7 +93,6 @@ namespace Online{ Synchronised m_state; bool getSaveSession() const { return m_save_session.getAtomic(); } - const std::string getToken() const { return m_token.getAtomic(); } void setUserState (UserState user_state) { m_state.setAtomic(user_state); } void setSaveSession (bool save_session) { m_save_session.setAtomic(save_session); } @@ -110,27 +110,29 @@ namespace Online{ static void deallocate(); const SignInRequest * requestSavedSession(); - const SignInRequest * requestSignIn( const irr::core::stringw &username, - const irr::core::stringw &password, - bool save_session); + SignInRequest * requestSignIn( const irr::core::stringw &username, + const irr::core::stringw &password, + bool save_session, + bool request_now = true); const SignOutRequest * requestSignOut(); - const ServerCreationRequest * requestServerCreation( const irr::core::stringw &name, int max_players); - const ServerJoinRequest * requestServerJoin( uint32_t server_id); + const ServerCreationRequest * requestServerCreation(const irr::core::stringw &name, int max_players); + ServerJoinRequest * requestServerJoin(uint32_t server_id, bool request_now = true); /** Register */ - const XMLRequest * requestSignUp( const irr::core::stringw &username, - const irr::core::stringw &password, - const irr::core::stringw &password_ver, - const irr::core::stringw &email, - bool terms); + const XMLRequest * requestSignUp( const irr::core::stringw &username, + const irr::core::stringw &password, + const irr::core::stringw &password_ver, + const irr::core::stringw &email, + bool terms); - const XMLRequest * requestRecovery( const irr::core::stringw &username, - const irr::core::stringw &email); + const XMLRequest * requestRecovery(const irr::core::stringw &username, + const irr::core::stringw &email); /** Returns the username if signed in. */ - const irr::core::stringw getUserName() const; - const UserState getUserState() const { return m_state.getAtomic(); } + const irr::core::stringw getUserName() const; + const UserState getUserState() const { return m_state.getAtomic(); } + const std::string getToken() const { return m_token.getAtomic(); } }; // class CurrentUser diff --git a/src/online/request.hpp b/src/online/request.hpp index c621a014a..8a00b9579 100644 --- a/src/online/request.hpp +++ b/src/online/request.hpp @@ -19,14 +19,17 @@ #ifndef HEADER_ONLINE_REQUEST_HPP #define HEADER_ONLINE_REQUEST_HPP -#include -#include - +#include "io/file_manager.hpp" #include "utils/cpp2011.h" #include "utils/string_utils.hpp" #include "utils/synchronised.hpp" -#include "io/file_manager.hpp" +#ifdef WIN32 +# include +#endif +#include + +#include namespace Online{ diff --git a/src/online/server.hpp b/src/online/server.hpp index 6e0e49dc6..779e6a8ce 100644 --- a/src/online/server.hpp +++ b/src/online/server.hpp @@ -65,7 +65,7 @@ namespace Online{ Server() {}; public: - + /** Initialises the object from an XML node. */ Server(const XMLNode & xml); // ------------------------------------------------------------------------ @@ -81,6 +81,7 @@ namespace Online{ // ------------------------------------------------------------------------ /** Returns the ID of this server. */ const uint32_t getServerId() const { return m_server_id; } + const uint32_t getHostId() const { return m_host_id; } const int getMaxPlayers() const { return m_max_players; } const int getCurrentPlayers() const { return m_current_players; } // ------------------------------------------------------------------------ diff --git a/src/online/servers_manager.cpp b/src/online/servers_manager.cpp index 59f7e820e..479cd559f 100644 --- a/src/online/servers_manager.cpp +++ b/src/online/servers_manager.cpp @@ -69,7 +69,7 @@ namespace Online{ } // ============================================================================ - const ServersManager::RefreshRequest * ServersManager::refreshRequest() const + ServersManager::RefreshRequest * ServersManager::refreshRequest(bool request_now) const { RefreshRequest * request = NULL; if(Time::getRealTime() - m_last_load_time.getAtomic() > SERVER_REFRESH_INTERVAL) @@ -77,7 +77,8 @@ namespace Online{ request = new RefreshRequest(); request->setURL((std::string)UserConfigParams::m_server_multiplayer + "client-user.php"); request->setParameter("action",std::string("get_server_list")); - HTTPManager::get()->addRequest(request); + if (request_now) + HTTPManager::get()->addRequest(request); } return request; } @@ -105,9 +106,9 @@ namespace Online{ // ============================================================================ const Server * ServersManager::getQuickPlay() const { - /*if(m_sorted_servers.size() > 0) + if(m_sorted_servers.getData().size() > 0) return getServerBySort(0); - */ + return NULL; } diff --git a/src/online/servers_manager.hpp b/src/online/servers_manager.hpp index 90b4b95c1..94864435e 100644 --- a/src/online/servers_manager.hpp +++ b/src/online/servers_manager.hpp @@ -68,7 +68,7 @@ namespace Online { static ServersManager* get(); static void deallocate(); - const RefreshRequest * refreshRequest() const; + RefreshRequest * refreshRequest(bool request_now = true) const; void setJoinedServer(uint32_t server_id); void unsetJoinedServer(); void addServer(Server * server); diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index eaaeee67d..5c528ff57 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -19,9 +19,11 @@ #include "physics/physics.hpp" #include "animations/three_d_animation.hpp" +#include "karts/abstract_kart.hpp" #include "karts/kart_properties.hpp" #include "karts/rescue_animation.hpp" -#include "network/race_state.hpp" +#include "items/flyable.hpp" +#include "modes/world.hpp" #include "graphics/stars.hpp" #include "karts/explosion_animation.hpp" #include "physics/btKart.hpp" @@ -156,8 +158,6 @@ void Physics::update(float dt) { AbstractKart *a=p->getUserPointer(0)->getPointerKart(); AbstractKart *b=p->getUserPointer(1)->getPointerKart(); - race_state->addCollision(a->getWorldKartId(), - b->getWorldKartId()); KartKartCollision(p->getUserPointer(0)->getPointerKart(), p->getContactPointCS(0), p->getUserPointer(1)->getPointerKart(), @@ -443,7 +443,6 @@ btScalar Physics::solveGroup(btCollisionObject** bodies, int numBodies, else if(upB->is(UserPointer::UP_KART)) { AbstractKart *kart=upB->getPointerKart(); - race_state->addCollision(kart->getWorldKartId()); int n = contact_manifold->getContactPoint(0).m_index0; const Material *m = n>=0 ? upA->getPointerTriangleMesh()->getMaterial(n) @@ -463,7 +462,6 @@ btScalar Physics::solveGroup(btCollisionObject** bodies, int numBodies, if(upB->is(UserPointer::UP_TRACK)) { AbstractKart *kart = upA->getPointerKart(); - race_state->addCollision(kart->getWorldKartId()); int n = contact_manifold->getContactPoint(0).m_index1; const Material *m = n>=0 ? upB->getPointerTriangleMesh()->getMaterial(n) diff --git a/src/race/history.hpp b/src/race/history.hpp index 22aa430ed..71d38a034 100644 --- a/src/race/history.hpp +++ b/src/race/history.hpp @@ -20,6 +20,7 @@ #define HEADER_HISTORY_HPP #include +#include #include "LinearMath/btQuaternion.h" diff --git a/src/race/race_manager.cpp b/src/race/race_manager.cpp index b7247bedf..4f6516bb9 100644 --- a/src/race/race_manager.cpp +++ b/src/race/race_manager.cpp @@ -42,7 +42,9 @@ #include "modes/world.hpp" #include "modes/three_strikes_battle.hpp" #include "modes/soccer_world.hpp" -#include "network/network_manager.hpp" +#include "network/protocol_manager.hpp" +#include "network/network_world.hpp" +#include "network/protocols/start_game_protocol.hpp" #include "states_screens/grand_prix_lose.hpp" #include "states_screens/grand_prix_win.hpp" #include "states_screens/kart_selection.hpp" @@ -143,9 +145,10 @@ void RaceManager::setLocalKartInfo(unsigned int player_id, assert(0<=player_id && player_id getKart(kart) != NULL); + const PlayerProfile* profile = StateManager::get()->getActivePlayerProfile(player_id); m_local_player_karts[player_id] = RemoteKartInfo(player_id, kart, - StateManager::get()->getActivePlayerProfile(player_id)->getName(), - network_manager->getMyHostId()); + profile->getName(), + 0, false); } // setLocalKartInfo //----------------------------------------------------------------------------- @@ -293,7 +296,7 @@ void RaceManager::startNew(bool from_overworld) // Create the kart status data structure to keep track of scores, times, ... // ========================================================================== m_kart_status.clear(); - + Log::verbose("RaceManager", "Nb of karts=%u, ai:%lu players:%lu\n", (unsigned int)m_num_karts, m_ai_kart_list.size(), m_player_karts.size()); assert((unsigned int)m_num_karts == m_ai_kart_list.size()+m_player_karts.size()); // First add the AI karts (randomly chosen) @@ -321,8 +324,7 @@ void RaceManager::startNew(bool from_overworld) // ------------------------------------------------- for(int i=m_player_karts.size()-1; i>=0; i--) { - KartType kt=(m_player_karts[i].getHostId()==network_manager->getMyHostId()) - ? KT_PLAYER : KT_NETWORK_PLAYER; + KartType kt= m_player_karts[i].isNetworkPlayer() ? KT_NETWORK_PLAYER : KT_PLAYER; m_kart_status.push_back(KartStatus(m_player_karts[i].getKartName(), i, m_player_karts[i].getLocalPlayerId(), m_player_karts[i].getGlobalPlayerId(), @@ -462,6 +464,10 @@ void RaceManager::startNextRace() m_kart_status[i].m_last_time = 0; } + StartGameProtocol* protocol = static_cast( + ProtocolManager::getInstance()->getProtocol(PROTOCOL_START_GAME)); + if (protocol) + protocol->ready(); } // startNextRace //----------------------------------------------------------------------------- @@ -509,21 +515,10 @@ void RaceManager::next() } user_config->saveConfig(); } - - if(network_manager->getMode()==NetworkManager::NW_SERVER) - network_manager->beginReadySetGoBarrier(); - else - network_manager->setState(NetworkManager::NS_WAIT_FOR_RACE_DATA); startNextRace(); } else { - // Back to main menu. Change the state of the state of the - // network manager. - if(network_manager->getMode()==NetworkManager::NW_SERVER) - network_manager->setState(NetworkManager::NS_MAIN_MENU); - else - network_manager->setState(NetworkManager::NS_WAIT_FOR_AVAILABLE_CHARACTERS); exitRace(); } } // next @@ -753,7 +748,7 @@ void RaceManager::startGP(const GrandPrixData* gp, bool from_overworld) StateManager::get()->enterGameState(); setGrandPrix(*gp); setCoinTarget( 0 ); // Might still be set from a previous challenge - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); setMajorMode(RaceManager::MAJOR_MODE_GRAND_PRIX); startNew(from_overworld); @@ -778,9 +773,38 @@ void RaceManager::startSingleRace(const std::string &track_ident, setMajorMode(RaceManager::MAJOR_MODE_SINGLE); setCoinTarget( 0 ); // Might still be set from a previous challenge - network_manager->setupPlayerKartInfo(); + if (!NetworkWorld::getInstance()->isRunning()) // if not in a network world + race_manager->setupPlayerKartInfo(); // do this setup player kart startNew(from_overworld); } +//----------------------------------------------------------------------------- +/** Receive and store the information from sendKartsInformation() +*/ +void RaceManager::setupPlayerKartInfo() +{ + + std::vector kart_info; + + // Get the local kart info + for(unsigned int i=0; isetState(NetworkManager::NS_MAIN_MENU); +// network_manager->setState(NetworkManager::NS_MAIN_MENU); World::getWorld()->scheduleUnpause(); race_manager->rerunRace(); return GUIEngine::EVENT_BLOCK; diff --git a/src/states_screens/dialogs/select_challenge.cpp b/src/states_screens/dialogs/select_challenge.cpp index 174d67358..c8267be1b 100644 --- a/src/states_screens/dialogs/select_challenge.cpp +++ b/src/states_screens/dialogs/select_challenge.cpp @@ -201,7 +201,7 @@ GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::strin // Initialise global data - necessary even in local games to avoid // many if tests in other places (e.g. if network_game call // network_manager else call race_manager). - network_manager->initCharacterDataStructures(); +// network_manager->initCharacterDataStructures(); // Launch challenge if (eventSource == "novice") @@ -228,7 +228,7 @@ GUIEngine::EventPropagation SelectChallengeDialog::processEvent(const std::strin } // Sets up kart info, including random list of kart for AI - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startNew(true); irr_driver->hidePointer(); diff --git a/src/states_screens/dialogs/server_info_dialog.cpp b/src/states_screens/dialogs/server_info_dialog.cpp index 53a7b924d..3abe48b20 100644 --- a/src/states_screens/dialogs/server_info_dialog.cpp +++ b/src/states_screens/dialogs/server_info_dialog.cpp @@ -25,6 +25,8 @@ #include "states_screens/state_manager.hpp" #include "utils/translation.hpp" #include "utils/string_utils.hpp" +#include "network/protocol_manager.hpp" +#include "network/protocols/connect_to_server.hpp" #include "online/current_user.hpp" #include "online/servers_manager.hpp" #include "online/messages.hpp" @@ -40,8 +42,8 @@ using namespace Online; // ----------------------------------------------------------------------------- -ServerInfoDialog::ServerInfoDialog(const uint32_t server_id, bool from_server_creation) - : ModalDialog(0.8f,0.8f), m_server_id(server_id) +ServerInfoDialog::ServerInfoDialog(uint32_t server_id, uint32_t host_id, bool from_server_creation) + : ModalDialog(0.8f,0.8f), m_server_id(server_id), m_host_id(host_id) { m_self_destroy = false; m_enter_lobby = false; @@ -71,12 +73,19 @@ ServerInfoDialog::ServerInfoDialog(const uint32_t server_id, bool from_server_cr // ----------------------------------------------------------------------------- ServerInfoDialog::~ServerInfoDialog() { - delete m_server_join_request; + if (m_server_join_request) + delete m_server_join_request; + m_server_join_request = NULL; } // ----------------------------------------------------------------------------- void ServerInfoDialog::requestJoin() { - m_server_join_request = Online::CurrentUser::get()->requestServerJoin(m_server_id); + //m_server_join_request = Online::CurrentUser::get()->requestServerJoin(m_server_id); + Online::ServersManager::get()->setJoinedServer(m_server_id); + ProtocolManager::getInstance()->requestStart(new ConnectToServer(m_server_id, m_host_id)); + ModalDialog::dismiss(); + StateManager::get()->pushScreen(NetworkingLobby::getInstance()); + //Online::CurrentUser::release(); } // ----------------------------------------------------------------------------- diff --git a/src/states_screens/dialogs/server_info_dialog.hpp b/src/states_screens/dialogs/server_info_dialog.hpp index a71c184a6..9aa522989 100644 --- a/src/states_screens/dialogs/server_info_dialog.hpp +++ b/src/states_screens/dialogs/server_info_dialog.hpp @@ -38,7 +38,7 @@ class ServerInfoDialog : public GUIEngine::ModalDialog { private: - + bool m_self_destroy; bool m_enter_lobby; bool m_from_server_creation; @@ -47,6 +47,7 @@ private: float m_load_timer; const uint32_t m_server_id; + uint32_t m_host_id; GUIEngine::LabelWidget * m_name_widget; GUIEngine::LabelWidget * m_info_widget; @@ -54,16 +55,16 @@ private: GUIEngine::RibbonWidget * m_options_widget; GUIEngine::IconButtonWidget * m_join_widget; GUIEngine::IconButtonWidget * m_cancel_widget; - + void requestJoin(); public: - ServerInfoDialog(const uint32_t server_id, bool just_created = false); + ServerInfoDialog(uint32_t server_id, uint32_t host_id, bool just_created = false); ~ServerInfoDialog(); void onEnterPressedInternal(); GUIEngine::EventPropagation processEvent(const std::string& eventSource); - + virtual void onUpdate(float dt); }; diff --git a/src/states_screens/dialogs/track_info_dialog.cpp b/src/states_screens/dialogs/track_info_dialog.cpp index a9ea32304..0272be680 100644 --- a/src/states_screens/dialogs/track_info_dialog.cpp +++ b/src/states_screens/dialogs/track_info_dialog.cpp @@ -28,7 +28,6 @@ #include "io/file_manager.hpp" #include "karts/kart_properties.hpp" #include "karts/kart_properties_manager.hpp" -#include "network/network_manager.hpp" #include "race/highscores.hpp" #include "race/highscore_manager.hpp" #include "race/race_manager.hpp" diff --git a/src/states_screens/help_screen_1.cpp b/src/states_screens/help_screen_1.cpp index 20ac321d9..e54da3e39 100644 --- a/src/states_screens/help_screen_1.cpp +++ b/src/states_screens/help_screen_1.cpp @@ -24,7 +24,6 @@ #include "input/device_manager.hpp" #include "input/input_manager.hpp" #include "karts/kart_properties_manager.hpp" -#include "network/network_manager.hpp" #include "race/race_manager.hpp" #include "states_screens/help_screen_2.hpp" #include "states_screens/help_screen_3.hpp" @@ -65,7 +64,7 @@ void HelpScreen1::eventCallback(Widget* widget, const std::string& name, const i // Create player and associate player with keyboard StateManager::get()->createActivePlayer(unlock_manager->getCurrentPlayer(), - device); + device, NULL); if (kart_properties_manager->getKart(UserConfigParams::m_default_kart) == NULL) { @@ -82,7 +81,7 @@ void HelpScreen1::eventCallback(Widget* widget, const std::string& name, const i ->setSinglePlayer( StateManager::get()->getActivePlayer(0) ); StateManager::get()->enterGameState(); - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startNew(false); } else if (name == "category") diff --git a/src/states_screens/kart_selection.cpp b/src/states_screens/kart_selection.cpp index 334fd4046..96b9fb194 100644 --- a/src/states_screens/kart_selection.cpp +++ b/src/states_screens/kart_selection.cpp @@ -58,8 +58,8 @@ static const char ID_DONT_USE[] = "x"; // Use '/' as special character to avoid that someone creates // a kart called 'locked' static const char ID_LOCKED[] = "locked/"; - -DEFINE_SCREEN_SINGLETON( KartSelectionScreen ); +KartSelectionScreen* KartSelectionScreen::m_instance_ptr = NULL; +//DEFINE_SCREEN_SINGLETON( KartSelectionScreen ); class PlayerKartWidget; @@ -71,66 +71,50 @@ class PlayerKartWidget; way?) */ static int g_root_id; -class FocusDispatcher : public Widget +// ------------------------------------------------------------------------ +FocusDispatcher::FocusDispatcher(KartSelectionScreen* parent) : Widget(WTYPE_BUTTON) { - KartSelectionScreen* m_parent; - int m_reserved_id; + m_parent = parent; + m_supports_multiplayer = true; + m_is_initialised = false; - bool m_is_initialised; + m_x = 0; + m_y = 0; + m_w = 1; + m_h = 1; -public: + m_reserved_id = Widget::getNewNoFocusID(); +} // FocusDispatcher +// ------------------------------------------------------------------------ +void FocusDispatcher::setRootID(const int reservedID) +{ + assert(reservedID != -1); - LEAK_CHECK() + m_reserved_id = reservedID; - // ------------------------------------------------------------------------ - FocusDispatcher(KartSelectionScreen* parent) : Widget(WTYPE_BUTTON) + if (m_element != NULL) { - m_parent = parent; - m_supports_multiplayer = true; - m_is_initialised = false; + m_element->setID(m_reserved_id); + } - m_x = 0; - m_y = 0; - m_w = 1; - m_h = 1; + m_is_initialised = true; +} // setRootID - m_reserved_id = Widget::getNewNoFocusID(); - } // FocusDispatcher - // ------------------------------------------------------------------------ - void setRootID(const int reservedID) - { - assert(reservedID != -1); +// ------------------------------------------------------------------------ +void FocusDispatcher::add() +{ + core::rect widget_size(m_x, m_y, m_x + m_w, m_y + m_h); - m_reserved_id = reservedID; + m_element = GUIEngine::getGUIEnv()->addButton(widget_size, NULL, + m_reserved_id, + L"Dispatcher", L""); - if (m_element != NULL) - { - m_element->setID(m_reserved_id); - } - - m_is_initialised = true; - } // setRootID - - // ------------------------------------------------------------------------ - virtual void add() - { - core::rect widget_size(m_x, m_y, m_x + m_w, m_y + m_h); - - m_element = GUIEngine::getGUIEnv()->addButton(widget_size, NULL, - m_reserved_id, - L"Dispatcher", L""); - - m_id = m_element->getID(); - m_element->setTabStop(true); - m_element->setTabGroup(false); - m_element->setTabOrder(m_id); - m_element->setVisible(false); - } // add - - // ------------------------------------------------------------------------ - - virtual EventPropagation focused(const int playerID); -}; // FocusDispatcher + m_id = m_element->getID(); + m_element->setTabStop(true); + m_element->setTabGroup(false); + m_element->setTabOrder(m_id); + m_element->setVisible(false); +} static FocusDispatcher* g_dispatcher = NULL; @@ -138,63 +122,53 @@ static FocusDispatcher* g_dispatcher = NULL; /** A small extension to the spinner widget to add features like player ID * management or badging */ -class PlayerNameSpinner : public SpinnerWidget + +PlayerNameSpinner::PlayerNameSpinner(KartSelectionScreen* parent, const int playerID) { - int m_playerID; - bool m_incorrect; - irr::gui::IGUIImage* m_red_mark_widget; - KartSelectionScreen* m_parent; + m_playerID = playerID; + m_incorrect = false; + m_red_mark_widget = NULL; + m_parent = parent; +} // PlayerNameSpinner +// ------------------------------------------------------------------------ +void PlayerNameSpinner::setID(const int m_playerID) +{ + PlayerNameSpinner::m_playerID = m_playerID; +} // setID +// ------------------------------------------------------------------------ +/** Add a red mark on the spinner to mean "invalid choice" */ +void PlayerNameSpinner::markAsIncorrect() +{ + if (m_incorrect) return; // already flagged as incorrect - //virtual EventPropagation focused(const int m_playerID) ; + m_incorrect = true; -public: - PlayerNameSpinner(KartSelectionScreen* parent, const int playerID) + irr::video::ITexture* texture = irr_driver->getTexture( + file_manager->getTextureFile("red_mark.png") ); + const int mark_size = m_h; + const int mark_x = m_w - mark_size*2; + const int mark_y = 0; + core::recti red_mark_area(mark_x, mark_y, mark_x + mark_size, + mark_y + mark_size); + m_red_mark_widget = GUIEngine::getGUIEnv()->addImage( red_mark_area, + /* parent */ m_element ); + m_red_mark_widget->setImage(texture); + m_red_mark_widget->setScaleImage(true); + m_red_mark_widget->setTabStop(false); + m_red_mark_widget->setUseAlphaChannel(true); +} // markAsIncorrect + +// ------------------------------------------------------------------------ +/** Remove any red mark set with 'markAsIncorrect' */ +void PlayerNameSpinner::markAsCorrect() +{ + if (m_incorrect) { - m_playerID = playerID; - m_incorrect = false; + m_red_mark_widget->remove(); m_red_mark_widget = NULL; - m_parent = parent; - } // PlayerNameSpinner - // ------------------------------------------------------------------------ - void setID(const int m_playerID) - { - PlayerNameSpinner::m_playerID = m_playerID; - } // setID - // ------------------------------------------------------------------------ - /** Add a red mark on the spinner to mean "invalid choice" */ - void markAsIncorrect() - { - if (m_incorrect) return; // already flagged as incorrect - - m_incorrect = true; - - irr::video::ITexture* texture = irr_driver->getTexture( - file_manager->getTextureFile("red_mark.png") ); - const int mark_size = m_h; - const int mark_x = m_w - mark_size*2; - const int mark_y = 0; - core::recti red_mark_area(mark_x, mark_y, mark_x + mark_size, - mark_y + mark_size); - m_red_mark_widget = GUIEngine::getGUIEnv()->addImage( red_mark_area, - /* parent */ m_element ); - m_red_mark_widget->setImage(texture); - m_red_mark_widget->setScaleImage(true); - m_red_mark_widget->setTabStop(false); - m_red_mark_widget->setUseAlphaChannel(true); - } // markAsIncorrect - - // ------------------------------------------------------------------------ - /** Remove any red mark set with 'markAsIncorrect' */ - void markAsCorrect() - { - if (m_incorrect) - { - m_red_mark_widget->remove(); - m_red_mark_widget = NULL; - m_incorrect = false; - } - } // markAsCorrect -}; + m_incorrect = false; + } +} // markAsCorrect // ============================================================================ @@ -205,639 +179,588 @@ public: /** A widget representing the kart selection for a player (i.e. the player's * number, name, the kart view, the kart's name) */ -class PlayerKartWidget : public Widget, - public SpinnerWidget::ISpinnerConfirmListener + +PlayerKartWidget::PlayerKartWidget(KartSelectionScreen* parent, + StateManager::ActivePlayer* associatedPlayer, + core::recti area, const int playerID, + std::string kartGroup, + const int irrlichtWidgetID) : Widget(WTYPE_DIV) { - /** Whether this player confirmed their selection */ - bool m_ready; - - /** widget coordinates */ - int player_id_x, player_id_y, player_id_w, player_id_h; - int player_name_x, player_name_y, player_name_w, player_name_h; - int model_x, model_y, model_w, model_h; - int kart_name_x, kart_name_y, kart_name_w, kart_name_h; - - /** A reserved ID for this widget if any, -1 otherwise. (If no ID is - * reserved, widget will not be in the regular tabbing order */ - int m_irrlicht_widget_ID; - - /** For animation purposes (see method 'move') */ - int target_x, target_y, target_w, target_h; - float x_speed, y_speed, w_speed, h_speed; - - /** Object representing this player */ - StateManager::ActivePlayer* m_associatedPlayer; - int m_playerID; - - /** Internal name of the spinner; useful to interpret spinner events, - * which contain the name of the activated object */ - std::string spinnerID; - #ifdef DEBUG - long m_magic_number; + assert(associatedPlayer->ok()); + m_magic_number = 0x33445566; #endif + m_ready_text = NULL; + m_parent_screen = parent; -public: + m_associatedPlayer = associatedPlayer; + x_speed = 1.0f; + y_speed = 1.0f; + w_speed = 1.0f; + h_speed = 1.0f; + m_ready = false; + m_not_updated_yet = true; - LEAK_CHECK() + m_irrlicht_widget_ID = irrlichtWidgetID; - /** Sub-widgets created by this widget */ - //LabelWidget* m_player_ID_label; - PlayerNameSpinner* m_player_ident_spinner; - ModelViewWidget* m_model_view; - LabelWidget* m_kart_name; + m_playerID = playerID; + m_properties[PROP_ID] = StringUtils::insertValues("@p%i", m_playerID); - KartSelectionScreen* m_parent_screen; + setSize(area.UpperLeftCorner.X, area.UpperLeftCorner.Y, + area.getWidth(), area.getHeight() ); + target_x = m_x; + target_y = m_y; + target_w = m_w; + target_h = m_h; - gui::IGUIStaticText* m_ready_text; + // ---- Player identity spinner + m_player_ident_spinner = new PlayerNameSpinner(parent, m_playerID); + m_player_ident_spinner->m_x = player_name_x; + m_player_ident_spinner->m_y = player_name_y; + m_player_ident_spinner->m_w = player_name_w; + m_player_ident_spinner->m_h = player_name_h; - //LabelWidget *getPlayerIDLabel() {return m_player_ID_label;} - core::stringw deviceName; - std::string m_kartInternalName; - - bool m_not_updated_yet; - - PlayerKartWidget(KartSelectionScreen* parent, - StateManager::ActivePlayer* associatedPlayer, - core::recti area, const int playerID, - std::string kartGroup, - const int irrlichtWidgetID=-1) : Widget(WTYPE_DIV) + if (parent->m_multiplayer) { -#ifdef DEBUG - assert(associatedPlayer->ok()); - m_magic_number = 0x33445566; -#endif - m_ready_text = NULL; - m_parent_screen = parent; - - m_associatedPlayer = associatedPlayer; - x_speed = 1.0f; - y_speed = 1.0f; - w_speed = 1.0f; - h_speed = 1.0f; - m_ready = false; - m_not_updated_yet = true; - - m_irrlicht_widget_ID = irrlichtWidgetID; - - m_playerID = playerID; - m_properties[PROP_ID] = StringUtils::insertValues("@p%i", m_playerID); - - setSize(area.UpperLeftCorner.X, area.UpperLeftCorner.Y, - area.getWidth(), area.getHeight() ); - target_x = m_x; - target_y = m_y; - target_w = m_w; - target_h = m_h; - - // ---- Player identity spinner - m_player_ident_spinner = new PlayerNameSpinner(parent, m_playerID); - m_player_ident_spinner->m_x = player_name_x; - m_player_ident_spinner->m_y = player_name_y; - m_player_ident_spinner->m_w = player_name_w; - m_player_ident_spinner->m_h = player_name_h; - - if (parent->m_multiplayer) + if (associatedPlayer->getDevice()->getType() == DT_KEYBOARD) { - if (associatedPlayer->getDevice()->getType() == DT_KEYBOARD) - { - m_player_ident_spinner->setBadge(KEYBOARD_BADGE); - } - else if (associatedPlayer->getDevice()->getType() == DT_GAMEPAD) - { - m_player_ident_spinner->setBadge(GAMEPAD_BADGE); - } + m_player_ident_spinner->setBadge(KEYBOARD_BADGE); } - - if (irrlichtWidgetID == -1) + else if (associatedPlayer->getDevice()->getType() == DT_GAMEPAD) { - m_player_ident_spinner->m_tab_down_root = g_root_id; + m_player_ident_spinner->setBadge(GAMEPAD_BADGE); } + } - spinnerID = StringUtils::insertValues("@p%i_spinner", m_playerID); + if (irrlichtWidgetID == -1) + { + m_player_ident_spinner->m_tab_down_root = g_root_id; + } - m_player_ident_spinner->m_properties[PROP_ID] = spinnerID; - if (parent->m_multiplayer) + spinnerID = StringUtils::insertValues("@p%i_spinner", m_playerID); + + m_player_ident_spinner->m_properties[PROP_ID] = spinnerID; + if (parent->m_multiplayer) + { + const int playerAmount = UserConfigParams::m_all_players.size(); + m_player_ident_spinner->m_properties[PROP_MIN_VALUE] = "0"; + m_player_ident_spinner->m_properties[PROP_MAX_VALUE] = + StringUtils::toString(playerAmount-1); + m_player_ident_spinner->m_properties[PROP_WRAP_AROUND] = "true"; + } + else + { + m_player_ident_spinner->m_properties[PROP_MIN_VALUE] = "0"; + m_player_ident_spinner->m_properties[PROP_MAX_VALUE] = "0"; + } + + //m_player_ident_spinner->m_event_handler = this; + m_children.push_back(m_player_ident_spinner); + + + // ----- Kart model view + m_model_view = new ModelViewWidget(); + + m_model_view->m_x = model_x; + m_model_view->m_y = model_y; + m_model_view->m_w = model_w; + m_model_view->m_h = model_h; + m_model_view->m_properties[PROP_ID] = + StringUtils::insertValues("@p%i_model", m_playerID); + //m_model_view->setParent(this); + m_children.push_back(m_model_view); + + // Init kart model + const std::string default_kart = UserConfigParams::m_default_kart; + const KartProperties* props = + kart_properties_manager->getKart(default_kart); + + if(!props) + { + // If the default kart can't be found (e.g. previously a addon + // kart was used, but the addon package was removed), use the + // first kart as a default. This way we don't have to hardcode + // any kart names. + int id = kart_properties_manager->getKartByGroup(kartGroup, 0); + if (id == -1) { - const int playerAmount = UserConfigParams::m_all_players.size(); - m_player_ident_spinner->m_properties[PROP_MIN_VALUE] = "0"; - m_player_ident_spinner->m_properties[PROP_MAX_VALUE] = - StringUtils::toString(playerAmount-1); - m_player_ident_spinner->m_properties[PROP_WRAP_AROUND] = "true"; + props = kart_properties_manager->getKartById(0); } else { - m_player_ident_spinner->m_properties[PROP_MIN_VALUE] = "0"; - m_player_ident_spinner->m_properties[PROP_MAX_VALUE] = "0"; + props = kart_properties_manager->getKartById(id); } - //m_player_ident_spinner->m_event_handler = this; - m_children.push_back(m_player_ident_spinner); - - - // ----- Kart model view - m_model_view = new ModelViewWidget(); - - m_model_view->m_x = model_x; - m_model_view->m_y = model_y; - m_model_view->m_w = model_w; - m_model_view->m_h = model_h; - m_model_view->m_properties[PROP_ID] = - StringUtils::insertValues("@p%i_model", m_playerID); - //m_model_view->setParent(this); - m_children.push_back(m_model_view); - - // Init kart model - const std::string default_kart = UserConfigParams::m_default_kart; - const KartProperties* props = - kart_properties_manager->getKart(default_kart); - if(!props) { - // If the default kart can't be found (e.g. previously a addon - // kart was used, but the addon package was removed), use the - // first kart as a default. This way we don't have to hardcode - // any kart names. - int id = kart_properties_manager->getKartByGroup(kartGroup, 0); - if (id == -1) - { - props = kart_properties_manager->getKartById(0); - } - else - { - props = kart_properties_manager->getKartById(id); - } - - if(!props) - { - fprintf(stderr, - "[KartSelectionScreen] WARNING: Can't find default " - "kart '%s' nor any other kart.\n", - default_kart.c_str()); - exit(-1); - } - } - m_kartInternalName = props->getIdent(); - - const KartModel &kart_model = props->getMasterKartModel(); - - m_model_view->addModel( kart_model.getModel(), Vec3(0,0,0), - Vec3(35.0f, 35.0f, 35.0f), - kart_model.getBaseFrame() ); - m_model_view->addModel( kart_model.getWheelModel(0), - kart_model.getWheelGraphicsPosition(0) ); - m_model_view->addModel( kart_model.getWheelModel(1), - kart_model.getWheelGraphicsPosition(1) ); - m_model_view->addModel( kart_model.getWheelModel(2), - kart_model.getWheelGraphicsPosition(2) ); - m_model_view->addModel( kart_model.getWheelModel(3), - kart_model.getWheelGraphicsPosition(3) ); - m_model_view->setRotateContinuously( 35.0f ); - - // ---- Kart name label - m_kart_name = new LabelWidget(); - m_kart_name->setText(props->getName(), false); - m_kart_name->m_properties[PROP_TEXT_ALIGN] = "center"; - m_kart_name->m_properties[PROP_ID] = - StringUtils::insertValues("@p%i_kartname", m_playerID); - m_kart_name->m_x = kart_name_x; - m_kart_name->m_y = kart_name_y; - m_kart_name->m_w = kart_name_w; - m_kart_name->m_h = kart_name_h; - //m_kart_name->setParent(this); - m_children.push_back(m_kart_name); - } // PlayerKartWidget - // ------------------------------------------------------------------------ - - ~PlayerKartWidget() - { - if (GUIEngine::getFocusForPlayer(m_playerID) == this) - { - GUIEngine::focusNothingForPlayer(m_playerID); - } - - //if (m_player_ID_label->getIrrlichtElement() != NULL) - // m_player_ID_label->getIrrlichtElement()->remove(); - - if (m_player_ident_spinner != NULL) - { - m_player_ident_spinner->setListener(NULL); - - if (m_player_ident_spinner->getIrrlichtElement() != NULL) - { - m_player_ident_spinner->getIrrlichtElement()->remove(); - } - } - - if (m_model_view->getIrrlichtElement() != NULL) - m_model_view->getIrrlichtElement()->remove(); - - if (m_kart_name->getIrrlichtElement() != NULL) - m_kart_name->getIrrlichtElement()->remove(); - - getCurrentScreen()->manualRemoveWidget(this); - -#ifdef DEBUG - m_magic_number = 0xDEADBEEF; -#endif - } // ~PlayerKartWidget - - // ------------------------------------------------------------------------ - /** Called when players are renumbered (changes the player ID) */ - void setPlayerID(const int newPlayerID) - { - assert(m_magic_number == 0x33445566); - - if (StateManager::get()->getActivePlayer(newPlayerID) - != m_associatedPlayer) - { - std::cerr << "[KartSelectionScreen] WARNING: Internal " - "inconsistency, PlayerKartWidget has IDs and " - "pointers that do not correspond to one player\n"; fprintf(stderr, - " Player: %p - Index: %d - m_associatedPlayer: %p\n", - StateManager::get()->getActivePlayer(newPlayerID), - newPlayerID, m_associatedPlayer); - assert(false); + "[KartSelectionScreen] WARNING: Can't find default " + "kart '%s' nor any other kart.\n", + default_kart.c_str()); + exit(-1); } + } + m_kartInternalName = props->getIdent(); - // Remove current focus, but rembmer it - Widget* focus = GUIEngine::getFocusForPlayer(m_playerID); + const KartModel &kart_model = props->getMasterKartModel(); + + m_model_view->addModel( kart_model.getModel(), Vec3(0,0,0), + Vec3(35.0f, 35.0f, 35.0f), + kart_model.getBaseFrame() ); + m_model_view->addModel( kart_model.getWheelModel(0), + kart_model.getWheelGraphicsPosition(0) ); + m_model_view->addModel( kart_model.getWheelModel(1), + kart_model.getWheelGraphicsPosition(1) ); + m_model_view->addModel( kart_model.getWheelModel(2), + kart_model.getWheelGraphicsPosition(2) ); + m_model_view->addModel( kart_model.getWheelModel(3), + kart_model.getWheelGraphicsPosition(3) ); + m_model_view->setRotateContinuously( 35.0f ); + + // ---- Kart name label + m_kart_name = new LabelWidget(); + m_kart_name->add(); // add the widget + m_kart_name->setText(props->getName(), false); + m_kart_name->m_properties[PROP_TEXT_ALIGN] = "center"; + m_kart_name->m_properties[PROP_ID] = + StringUtils::insertValues("@p%i_kartname", m_playerID); + m_kart_name->m_x = kart_name_x; + m_kart_name->m_y = kart_name_y; + m_kart_name->m_w = kart_name_w; + m_kart_name->m_h = kart_name_h; + //m_kart_name->setParent(this); + m_children.push_back(m_kart_name); +} // PlayerKartWidget +// ------------------------------------------------------------------------ + +PlayerKartWidget::~PlayerKartWidget() +{ + if (GUIEngine::getFocusForPlayer(m_playerID) == this) + { GUIEngine::focusNothingForPlayer(m_playerID); + } - // Change the player ID - m_playerID = newPlayerID; + //if (m_player_ID_label->getIrrlichtElement() != NULL) + // m_player_ID_label->getIrrlichtElement()->remove(); - // restore previous focus, but with new player ID - if (focus != NULL) focus->setFocusForPlayer(m_playerID); - - if (m_player_ident_spinner != NULL) - m_player_ident_spinner->setID(m_playerID); - } // setPlayerID - - // ------------------------------------------------------------------------ - /** Returns the ID of this player */ - int getPlayerID() const + if (m_player_ident_spinner != NULL) { - assert(m_magic_number == 0x33445566); - return m_playerID; - } // getPlayerID - - // ------------------------------------------------------------------------ - /** Add the widgets to the current screen */ - virtual void add() - { - assert(m_magic_number == 0x33445566); - - assert(KartSelectionScreen::getInstance() - ->m_kart_widgets.contains(this)); - bool mineInList = false; - for (int p=0; pactivePlayerCount(); p++) - { -#ifdef DEBUG - assert(StateManager::get()->getActivePlayer(p)->ok()); -#endif - if (StateManager::get()->getActivePlayer(p) == m_associatedPlayer) - { - mineInList = true; - } - } - assert(mineInList); - - //m_player_ID_label->add(); - - // the first player will have an ID of its own to allow for keyboard - // navigation despite this widget being added last - if (m_irrlicht_widget_ID != -1) - m_player_ident_spinner->m_reserved_id = m_irrlicht_widget_ID; - else - m_player_ident_spinner->m_reserved_id = Widget::getNewNoFocusID(); - - m_player_ident_spinner->add(); - m_player_ident_spinner->getIrrlichtElement()->setTabStop(false); - m_player_ident_spinner->setListener(this); - - m_model_view->add(); - m_kart_name->add(); - - m_model_view->update(0); - - m_player_ident_spinner->clearLabels(); - if (m_parent_screen->m_multiplayer) - { - const int playerAmount = UserConfigParams::m_all_players.size(); - for (int n=0; naddLabel( translations->fribidize(name) ); - } - - // select the right player profile in the spinner - m_player_ident_spinner->setValue(m_associatedPlayer->getProfile() - ->getName() ); - } - else - { - m_player_ident_spinner->addLabel( m_associatedPlayer->getProfile()->getName() ); - m_player_ident_spinner->setVisible(false); - } - - assert(m_player_ident_spinner->getStringValue() == - m_associatedPlayer->getProfile()->getName()); - } // add - - // ------------------------------------------------------------------------ - /** Get the associated ActivePlayer object*/ - StateManager::ActivePlayer* getAssociatedPlayer() - { - assert(m_magic_number == 0x33445566); - return m_associatedPlayer; - } // getAssociatedPlayer - - // ------------------------------------------------------------------------ - /** Starts a 'move/resize' animation, by simply passing destination coords. - * The animation will then occur on each call to 'onUpdate'. */ - void move(const int x, const int y, const int w, const int h) - { - assert(m_magic_number == 0x33445566); - target_x = x; - target_y = y; - target_w = w; - target_h = h; - - x_speed = abs( m_x - x ) / 300.0f; - y_speed = abs( m_y - y ) / 300.0f; - w_speed = abs( m_w - w ) / 300.0f; - h_speed = abs( m_h - h ) / 300.0f; - } // move - - // ------------------------------------------------------------------------ - /** Call when player confirmed his identity and kart */ - void markAsReady() - { - assert(m_magic_number == 0x33445566); - if (m_ready) return; // already ready - - m_ready = true; - - stringw playerNameString = m_player_ident_spinner->getStringValue(); - core::rect rect(core::position2di(m_player_ident_spinner->m_x, - m_player_ident_spinner->m_y), - core::dimension2di(m_player_ident_spinner->m_w, - m_player_ident_spinner->m_h)); - // 'playerNameString' is already fribidize, so we need to use - // 'insertValues' and not _("...", a) so it's not flipped again - m_ready_text = - GUIEngine::getGUIEnv()->addStaticText( - StringUtils::insertValues(_("%s is ready"), - playerNameString).c_str(), - rect ); - m_ready_text->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER ); - - m_children.remove(m_player_ident_spinner); m_player_ident_spinner->setListener(NULL); - m_player_ident_spinner->getIrrlichtElement()->remove(); - m_player_ident_spinner->elementRemoved(); - delete m_player_ident_spinner; - m_player_ident_spinner = NULL; - sfx_manager->quickSound( "wee" ); + if (m_player_ident_spinner->getIrrlichtElement() != NULL) + { + m_player_ident_spinner->getIrrlichtElement()->remove(); + } + } - m_model_view->setRotateTo(30.0f, 1.0f); + if (m_model_view->getIrrlichtElement() != NULL) + m_model_view->getIrrlichtElement()->remove(); - player_id_w *= 2; - player_name_w = 0; + if (m_kart_name->getIrrlichtElement() != NULL) + m_kart_name->getIrrlichtElement()->remove(); - m_model_view->setBadge(OK_BADGE); - } // markAsReady + getCurrentScreen()->manualRemoveWidget(this); - // ------------------------------------------------------------------------ - /** \return Whether this player confirmed his kart and indent selection */ - bool isReady() +#ifdef DEBUG + m_magic_number = 0xDEADBEEF; +#endif +} // ~PlayerKartWidget + +// ------------------------------------------------------------------------ +/** Called when players are renumbered (changes the player ID) */ +void PlayerKartWidget::setPlayerID(const int newPlayerID) +{ + assert(m_magic_number == 0x33445566); + + if (StateManager::get()->getActivePlayer(newPlayerID) + != m_associatedPlayer) { - assert(m_magic_number == 0x33445566); - return m_ready; - } // isReady + std::cerr << "[KartSelectionScreen] WARNING: Internal " + "inconsistency, PlayerKartWidget has IDs and " + "pointers that do not correspond to one player\n"; + fprintf(stderr, + " Player: %p - Index: %d - m_associatedPlayer: %p\n", + StateManager::get()->getActivePlayer(newPlayerID), + newPlayerID, m_associatedPlayer); + assert(false); + } - // ------------------------------------------------------------------------- - /** Updates the animation (moving/shrinking/etc.) */ - void onUpdate(float delta) + // Remove current focus, but rembmer it + Widget* focus = GUIEngine::getFocusForPlayer(m_playerID); + GUIEngine::focusNothingForPlayer(m_playerID); + + // Change the player ID + m_playerID = newPlayerID; + + // restore previous focus, but with new player ID + if (focus != NULL) focus->setFocusForPlayer(m_playerID); + + if (m_player_ident_spinner != NULL) + m_player_ident_spinner->setID(m_playerID); +} // setPlayerID + +// ------------------------------------------------------------------------ +/** Returns the ID of this player */ +int PlayerKartWidget::getPlayerID() const +{ + assert(m_magic_number == 0x33445566); + return m_playerID; +} // getPlayerID + +// ------------------------------------------------------------------------ +/** Add the widgets to the current screen */ +void PlayerKartWidget::add() +{ + assert(m_magic_number == 0x33445566); + + assert(KartSelectionScreen::getRunningInstance() + ->m_kart_widgets.contains(this)); + bool mineInList = false; + for (int p=0; pactivePlayerCount(); p++) { - assert(m_magic_number == 0x33445566); - if (target_x == m_x && target_y == m_y && +#ifdef DEBUG + assert(StateManager::get()->getActivePlayer(p)->ok()); +#endif + if (StateManager::get()->getActivePlayer(p) == m_associatedPlayer) + { + mineInList = true; + } + } + assert(mineInList); + + //m_player_ID_label->add(); + + // the first player will have an ID of its own to allow for keyboard + // navigation despite this widget being added last + if (m_irrlicht_widget_ID != -1) + m_player_ident_spinner->m_reserved_id = m_irrlicht_widget_ID; + else + m_player_ident_spinner->m_reserved_id = Widget::getNewNoFocusID(); + + m_player_ident_spinner->add(); + m_player_ident_spinner->getIrrlichtElement()->setTabStop(false); + m_player_ident_spinner->setListener(this); + + m_model_view->add(); + m_kart_name->add(); + + m_model_view->update(0); + + m_player_ident_spinner->clearLabels(); + if (m_parent_screen->m_multiplayer) + { + const int playerAmount = UserConfigParams::m_all_players.size(); + for (int n=0; naddLabel( translations->fribidize(name) ); + } + + // select the right player profile in the spinner + m_player_ident_spinner->setValue(m_associatedPlayer->getProfile() + ->getName() ); + } + else + { + m_player_ident_spinner->addLabel( m_associatedPlayer->getProfile()->getName() ); + m_player_ident_spinner->setVisible(false); + } + + assert(m_player_ident_spinner->getStringValue() == + m_associatedPlayer->getProfile()->getName()); +} // add + +// ------------------------------------------------------------------------ +/** Get the associated ActivePlayer object*/ +StateManager::ActivePlayer* PlayerKartWidget::getAssociatedPlayer() +{ + assert(m_magic_number == 0x33445566); + return m_associatedPlayer; +} // getAssociatedPlayer + +// ------------------------------------------------------------------------ +/** Starts a 'move/resize' animation, by simply passing destination coords. + * The animation will then occur on each call to 'onUpdate'. */ +void PlayerKartWidget::move(const int x, const int y, const int w, const int h) +{ + assert(m_magic_number == 0x33445566); + target_x = x; + target_y = y; + target_w = w; + target_h = h; + + x_speed = abs( m_x - x ) / 300.0f; + y_speed = abs( m_y - y ) / 300.0f; + w_speed = abs( m_w - w ) / 300.0f; + h_speed = abs( m_h - h ) / 300.0f; +} // move + +// ------------------------------------------------------------------------ +/** Call when player confirmed his identity and kart */ +void PlayerKartWidget::markAsReady() +{ + assert(m_magic_number == 0x33445566); + if (m_ready) return; // already ready + + m_ready = true; + + stringw playerNameString = m_player_ident_spinner->getStringValue(); + core::rect rect(core::position2di(m_player_ident_spinner->m_x, + m_player_ident_spinner->m_y), + core::dimension2di(m_player_ident_spinner->m_w, + m_player_ident_spinner->m_h)); + // 'playerNameString' is already fribidize, so we need to use + // 'insertValues' and not _("...", a) so it's not flipped again + m_ready_text = + GUIEngine::getGUIEnv()->addStaticText( + StringUtils::insertValues(_("%s is ready"), + playerNameString).c_str(), + rect ); + m_ready_text->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER ); + + m_children.remove(m_player_ident_spinner); + m_player_ident_spinner->setListener(NULL); + m_player_ident_spinner->getIrrlichtElement()->remove(); + m_player_ident_spinner->elementRemoved(); + delete m_player_ident_spinner; + m_player_ident_spinner = NULL; + + sfx_manager->quickSound( "wee" ); + + m_model_view->setRotateTo(30.0f, 1.0f); + + player_id_w *= 2; + player_name_w = 0; + + m_model_view->setBadge(OK_BADGE); +} // markAsReady + +// ------------------------------------------------------------------------ +/** \return Whether this player confirmed his kart and indent selection */ +bool PlayerKartWidget::isReady() +{ + assert(m_magic_number == 0x33445566); + return m_ready; +} // isReady + +// ------------------------------------------------------------------------- +/** Updates the animation (moving/shrinking/etc.) */ +void PlayerKartWidget::onUpdate(float delta) +{ + assert(m_magic_number == 0x33445566); + if (target_x == m_x && target_y == m_y && target_w == m_w && target_h == m_h) return; - int move_step = (int)(delta*1000.0f); + int move_step = (int)(delta*1000.0f); - // move x towards target - if (m_x < target_x) - { - m_x += (int)(move_step*x_speed); - // don't move to the other side of the target - if (m_x > target_x) m_x = target_x; - } - else if (m_x > target_x) - { - m_x -= (int)(move_step*x_speed); - // don't move to the other side of the target - if (m_x < target_x) m_x = target_x; - } - - // move y towards target - if (m_y < target_y) - { - m_y += (int)(move_step*y_speed); - // don't move to the other side of the target - if (m_y > target_y) m_y = target_y; - } - else if (m_y > target_y) - { - m_y -= (int)(move_step*y_speed); - // don't move to the other side of the target - if (m_y < target_y) m_y = target_y; - } - - // move w towards target - if (m_w < target_w) - { - m_w += (int)(move_step*w_speed); - // don't move to the other side of the target - if (m_w > target_w) m_w = target_w; - } - else if (m_w > target_w) - { - m_w -= (int)(move_step*w_speed); - // don't move to the other side of the target - if (m_w < target_w) m_w = target_w; - } - // move h towards target - if (m_h < target_h) - { - m_h += (int)(move_step*h_speed); - // don't move to the other side of the target - if (m_h > target_h) m_h = target_h; - } - else if (m_h > target_h) - { - m_h -= (int)(move_step*h_speed); - // don't move to the other side of the target - if (m_h < target_h) m_h = target_h; - } - - setSize(m_x, m_y, m_w, m_h); - - if (m_player_ident_spinner != NULL) - { - m_player_ident_spinner->move(player_name_x, - player_name_y, - player_name_w, - player_name_h ); - } - if (m_ready_text != NULL) - { - m_ready_text->setRelativePosition( - core::recti(core::position2di(player_name_x, player_name_y), - core::dimension2di(player_name_w, player_name_h)) ); - } - - m_model_view->move(model_x, - model_y, - model_w, - model_h); - m_kart_name->move(kart_name_x, - kart_name_y, - kart_name_w, - kart_name_h); - - // When coming from the overworld, we must rebuild the preview scene at - // least once, since the scene is being cleared by leaving the overworld - if (m_not_updated_yet) - { - m_model_view->clearRttProvider(); - m_not_updated_yet = false; - } - } // onUpdate - - // ------------------------------------------------------------------------- - /** Event callback */ - virtual GUIEngine::EventPropagation transmitEvent( - Widget* w, - const std::string& originator, - const int m_playerID) + // move x towards target + if (m_x < target_x) { - assert(m_magic_number == 0x33445566); - // if it's declared ready, there is really nothing to process - if (m_ready) return EVENT_LET; + m_x += (int)(move_step*x_speed); + // don't move to the other side of the target + if (m_x > target_x) m_x = target_x; + } + else if (m_x > target_x) + { + m_x -= (int)(move_step*x_speed); + // don't move to the other side of the target + if (m_x < target_x) m_x = target_x; + } - //std::cout << "= kart selection :: transmitEvent " - // << originator << " =\n"; + // move y towards target + if (m_y < target_y) + { + m_y += (int)(move_step*y_speed); + // don't move to the other side of the target + if (m_y > target_y) m_y = target_y; + } + else if (m_y > target_y) + { + m_y -= (int)(move_step*y_speed); + // don't move to the other side of the target + if (m_y < target_y) m_y = target_y; + } - std::string name = w->m_properties[PROP_ID]; + // move w towards target + if (m_w < target_w) + { + m_w += (int)(move_step*w_speed); + // don't move to the other side of the target + if (m_w > target_w) m_w = target_w; + } + else if (m_w > target_w) + { + m_w -= (int)(move_step*w_speed); + // don't move to the other side of the target + if (m_w < target_w) m_w = target_w; + } + // move h towards target + if (m_h < target_h) + { + m_h += (int)(move_step*h_speed); + // don't move to the other side of the target + if (m_h > target_h) m_h = target_h; + } + else if (m_h > target_h) + { + m_h -= (int)(move_step*h_speed); + // don't move to the other side of the target + if (m_h < target_h) m_h = target_h; + } - //std::cout << " (checking if that's me: I am " - // << spinnerID << ")\n"; + setSize(m_x, m_y, m_w, m_h); - // update player profile when spinner changed - if (originator == spinnerID) + if (m_player_ident_spinner != NULL) + { + m_player_ident_spinner->move(player_name_x, + player_name_y, + player_name_w, + player_name_h ); + } + if (m_ready_text != NULL) + { + m_ready_text->setRelativePosition( + core::recti(core::position2di(player_name_x, player_name_y), + core::dimension2di(player_name_w, player_name_h)) ); + } + + m_model_view->move(model_x, + model_y, + model_w, + model_h); + m_kart_name->move(kart_name_x, + kart_name_y, + kart_name_w, + kart_name_h); + + // When coming from the overworld, we must rebuild the preview scene at + // least once, since the scene is being cleared by leaving the overworld + if (m_not_updated_yet) + { + m_model_view->clearRttProvider(); + m_not_updated_yet = false; + } +} // onUpdate + +// ------------------------------------------------------------------------- +/** Event callback */ +GUIEngine::EventPropagation PlayerKartWidget::transmitEvent( + Widget* w, + const std::string& originator, + const int m_playerID) +{ + assert(m_magic_number == 0x33445566); + // if it's declared ready, there is really nothing to process + if (m_ready) return EVENT_LET; + + //std::cout << "= kart selection :: transmitEvent " + // << originator << " =\n"; + + std::string name = w->m_properties[PROP_ID]; + + //std::cout << " (checking if that's me: I am " + // << spinnerID << ")\n"; + + // update player profile when spinner changed + if (originator == spinnerID) + { + if(UserConfigParams::logGUI()) { - if(UserConfigParams::logGUI()) - { - std::cout << "[KartSelectionScreen] Identity changed " - "for player " << m_playerID - << " : " << irr::core::stringc( - m_player_ident_spinner->getStringValue() - .c_str()).c_str() - << std::endl; - } - - if (m_parent_screen->m_multiplayer) - { - m_associatedPlayer->setPlayerProfile( - UserConfigParams::m_all_players.get(m_player_ident_spinner - ->getValue()) ); - } + std::cout << "[KartSelectionScreen] Identity changed " + "for player " << m_playerID + << " : " << irr::core::stringc( + m_player_ident_spinner->getStringValue() + .c_str()).c_str() + << std::endl; } - return EVENT_LET; // continue propagating the event - } // transmitEvent - - // ------------------------------------------------------------------------- - /** Sets the size of the widget as a whole, and placed children widgets - * inside itself */ - void setSize(const int x, const int y, const int w, const int h) - { - assert(m_magic_number == 0x33445566); - m_x = x; - m_y = y; - m_w = w; - m_h = h; - - // -- sizes - player_id_w = w; - player_id_h = GUIEngine::getFontHeight(); - - player_name_h = 40; - player_name_w = std::min(400, w); - - kart_name_w = w; - kart_name_h = 25; - - // for shrinking effect - if (h < 175) + if (m_parent_screen->m_multiplayer) { - const float factor = h / 175.0f; - kart_name_h = (int)(kart_name_h*factor); - player_name_h = (int)(player_name_h*factor); - player_id_h = (int)(player_id_h*factor); + m_associatedPlayer->setPlayerProfile( + UserConfigParams::m_all_players.get(m_player_ident_spinner + ->getValue()) ); } + } - // --- layout - player_id_x = x; - player_id_y = y; + return EVENT_LET; // continue propagating the event +} // transmitEvent - player_name_x = x + w/2 - player_name_w/2; - player_name_y = y + player_id_h; +// ------------------------------------------------------------------------- +/** Sets the size of the widget as a whole, and placed children widgets + * inside itself */ +void PlayerKartWidget::setSize(const int x, const int y, const int w, const int h) +{ + assert(m_magic_number == 0x33445566); + m_x = x; + m_y = y; + m_w = w; + m_h = h; - const int modelMaxHeight = h - kart_name_h - player_name_h - - player_id_h; - const int modelMaxWidth = w; - const int bestSize = std::min(modelMaxWidth, modelMaxHeight); - const int modelY = y + player_name_h + player_id_h; - model_x = x + w/2 - (int)(bestSize/2); - model_y = modelY + modelMaxHeight/2 - bestSize/2; - model_w = (int)(bestSize); - model_h = bestSize; + // -- sizes + player_id_w = w; + player_id_h = GUIEngine::getFontHeight(); - kart_name_x = x; - kart_name_y = y + h - kart_name_h; - } // setSize + player_name_h = 40; + player_name_w = std::min(400, w); - // ------------------------------------------------------------------------- + kart_name_w = w; + kart_name_h = 25; - /** Sets which kart was selected for this player */ - void setKartInternalName(const std::string& whichKart) + // for shrinking effect + if (h < 175) { - assert(m_magic_number == 0x33445566); - m_kartInternalName = whichKart; - } // setKartInternalName + const float factor = h / 175.0f; + kart_name_h = (int)(kart_name_h*factor); + player_name_h = (int)(player_name_h*factor); + player_id_h = (int)(player_id_h*factor); + } - // ------------------------------------------------------------------------- + // --- layout + player_id_x = x; + player_id_y = y; - const std::string& getKartInternalName() const - { - assert(m_magic_number == 0x33445566); - return m_kartInternalName; - } // getKartInternalName + player_name_x = x + w/2 - player_name_w/2; + player_name_y = y + player_id_h; - // ------------------------------------------------------------------------- + const int modelMaxHeight = h - kart_name_h - player_name_h + - player_id_h; + const int modelMaxWidth = w; + const int bestSize = std::min(modelMaxWidth, modelMaxHeight); + const int modelY = y + player_name_h + player_id_h; + model_x = x + w/2 - (int)(bestSize/2); + model_y = modelY + modelMaxHeight/2 - bestSize/2; + model_w = (int)(bestSize); + model_h = bestSize; - /** \brief Event callback from ISpinnerConfirmListener */ - virtual EventPropagation onSpinnerConfirmed() - { - KartSelectionScreen::getInstance()->playerConfirm(m_playerID); - return EVENT_BLOCK; - } // onSpinnerConfirmed -}; // PlayerKartWidget + kart_name_x = x; + kart_name_y = y + h - kart_name_h; +} // setSize + +// ------------------------------------------------------------------------- + +/** Sets which kart was selected for this player */ +void PlayerKartWidget::setKartInternalName(const std::string& whichKart) +{ + assert(m_magic_number == 0x33445566); + m_kartInternalName = whichKart; +} // setKartInternalName + +// ------------------------------------------------------------------------- + +const std::string& PlayerKartWidget::getKartInternalName() const +{ + assert(m_magic_number == 0x33445566); + return m_kartInternalName; +} // getKartInternalName + +// ------------------------------------------------------------------------- + +/** \brief Event callback from ISpinnerConfirmListener */ +EventPropagation PlayerKartWidget::onSpinnerConfirmed() +{ + KartSelectionScreen::getRunningInstance()->playerConfirm(m_playerID); + return EVENT_BLOCK; +} // onSpinnerConfirmed /** Small utility function that returns whether the two given players chose * the same kart. The advantage of this function is that it can handle @@ -845,7 +768,7 @@ public: bool sameKart(const PlayerKartWidget& player1, const PlayerKartWidget& player2) { return player1.getKartInternalName() == player2.getKartInternalName() && - player1.getKartInternalName() != RANDOM_KART_ID; + player1.getKartInternalName() != RANDOM_KART_ID; } #if 0 @@ -854,123 +777,116 @@ bool sameKart(const PlayerKartWidget& player1, const PlayerKartWidget& player2) #endif // ============================================================================ -class KartHoverListener : public DynamicRibbonHoverListener +KartHoverListener::KartHoverListener(KartSelectionScreen* parent) { - KartSelectionScreen* m_parent; -public: - unsigned int m_magic_number; + m_magic_number = 0xCAFEC001; + m_parent = parent; +} // KartHoverListener - KartHoverListener(KartSelectionScreen* parent) +// ------------------------------------------------------------------------ +KartHoverListener::~KartHoverListener() +{ + assert(m_magic_number == 0xCAFEC001); + m_magic_number = 0xDEADBEEF; +} // ~KartHoverListener + +// ------------------------------------------------------------------------ +void KartHoverListener::onSelectionChanged(DynamicRibbonWidget* theWidget, + const std::string& selectionID, + const irr::core::stringw& selectionText, + const int playerID) +{ + assert(m_magic_number == 0xCAFEC001); + + // Don't allow changing the selection after confirming it + if (m_parent->m_kart_widgets[playerID].isReady()) { - m_magic_number = 0xCAFEC001; - m_parent = parent; - } // KartHoverListener - - // ------------------------------------------------------------------------ - virtual ~KartHoverListener() - { - assert(m_magic_number == 0xCAFEC001); - m_magic_number = 0xDEADBEEF; - } // ~KartHoverListener - - // ------------------------------------------------------------------------ - void onSelectionChanged(DynamicRibbonWidget* theWidget, - const std::string& selectionID, - const irr::core::stringw& selectionText, - const int playerID) - { - assert(m_magic_number == 0xCAFEC001); - - // Don't allow changing the selection after confirming it - if (m_parent->m_kart_widgets[playerID].isReady()) - { - // discard events sent when putting back to the right kart - if (selectionID == + // discard events sent when putting back to the right kart + if (selectionID == m_parent->m_kart_widgets[playerID].m_kartInternalName) return; - DynamicRibbonWidget* w = - m_parent->getWidget("karts"); - assert(w != NULL); + DynamicRibbonWidget* w = + m_parent->getWidget("karts"); + assert(w != NULL); - w->setSelection(m_parent->m_kart_widgets[playerID] - .m_kartInternalName, playerID, true); - return; - } + w->setSelection(m_parent->m_kart_widgets[playerID] + .m_kartInternalName, playerID, true); + return; + } - // Update the displayed model - ModelViewWidget* w3 = m_parent->m_kart_widgets[playerID].m_model_view; - assert( w3 != NULL ); + // Update the displayed model + ModelViewWidget* w3 = m_parent->m_kart_widgets[playerID].m_model_view; + assert( w3 != NULL ); - if (selectionID == RANDOM_KART_ID) + if (selectionID == RANDOM_KART_ID) + { + // Random kart + scene::IMesh* model = + ItemManager::getItemModel(Item::ITEM_BONUS_BOX); + w3->clearModels(); + w3->addModel( model, Vec3(0.0f, -12.0f, 0.0f), + Vec3(35.0f, 35.0f, 35.0f) ); + w3->update(0); + m_parent->m_kart_widgets[playerID].m_kart_name + ->setText( _("Random Kart"), false ); + } + // selectionID contains the name of the kart, so check only for substr + else if (StringUtils::startsWith(selectionID, ID_LOCKED)) + { + w3->clearModels(); + w3->addModel(irr_driver->getAnimatedMesh( + file_manager->getDataDir() + "/models/chest.b3d" )->getMesh(20), + Vec3(0,0,0), Vec3(15.0f, 15.0f, 15.0f) ); + w3->update(0); + + if (m_parent->m_multiplayer) { - // Random kart - scene::IMesh* model = - ItemManager::getItemModel(Item::ITEM_BONUS_BOX); - w3->clearModels(); - w3->addModel( model, Vec3(0.0f, -12.0f, 0.0f), - Vec3(35.0f, 35.0f, 35.0f) ); - w3->update(0); m_parent->m_kart_widgets[playerID].m_kart_name - ->setText( _("Random Kart"), false ); - } - // selectionID contains the name of the kart, so check only for substr - else if (StringUtils::startsWith(selectionID, ID_LOCKED)) - { - w3->clearModels(); - w3->addModel(irr_driver->getAnimatedMesh( - file_manager->getDataDir() + "/models/chest.b3d" )->getMesh(20), - Vec3(0,0,0), Vec3(15.0f, 15.0f, 15.0f) ); - w3->update(0); - - if (m_parent->m_multiplayer) - { - m_parent->m_kart_widgets[playerID].m_kart_name - ->setText(_("Locked"), false ); - } - else - { - m_parent->m_kart_widgets[playerID].m_kart_name - ->setText(_("Locked : solve active challenges to gain " - "access to more!"), false ); - } + ->setText(_("Locked"), false ); } else { - const KartProperties *kp = - kart_properties_manager->getKart(selectionID); - if (kp != NULL) - { - const KartModel &kart_model = kp->getMasterKartModel(); - - w3->clearModels(); - w3->addModel( kart_model.getModel(), Vec3(0,0,0), - Vec3(35.0f, 35.0f, 35.0f), - kart_model.getBaseFrame() ); - w3->addModel( kart_model.getWheelModel(0), - kart_model.getWheelGraphicsPosition(0) ); - w3->addModel( kart_model.getWheelModel(1), - kart_model.getWheelGraphicsPosition(1) ); - w3->addModel( kart_model.getWheelModel(2), - kart_model.getWheelGraphicsPosition(2) ); - w3->addModel( kart_model.getWheelModel(3), - kart_model.getWheelGraphicsPosition(3) ); - w3->update(0); - - m_parent->m_kart_widgets[playerID].m_kart_name - ->setText( selectionText.c_str(), false ); - } - else - { - fprintf(stderr, "[KartSelectionScreen] WARNING: could not " - "find a kart named '%s'\n", - selectionID.c_str()); - } + m_parent->m_kart_widgets[playerID].m_kart_name + ->setText(_("Locked : solve active challenges to gain " + "access to more!"), false ); } + } + else + { + const KartProperties *kp = + kart_properties_manager->getKart(selectionID); + if (kp != NULL) + { + const KartModel &kart_model = kp->getMasterKartModel(); - m_parent->m_kart_widgets[playerID].setKartInternalName(selectionID); - m_parent->validateKartChoices(); - } // onSelectionChanged -}; // KartHoverListener + w3->clearModels(); + w3->addModel( kart_model.getModel(), Vec3(0,0,0), + Vec3(35.0f, 35.0f, 35.0f), + kart_model.getBaseFrame() ); + w3->addModel( kart_model.getWheelModel(0), + kart_model.getWheelGraphicsPosition(0) ); + w3->addModel( kart_model.getWheelModel(1), + kart_model.getWheelGraphicsPosition(1) ); + w3->addModel( kart_model.getWheelModel(2), + kart_model.getWheelGraphicsPosition(2) ); + w3->addModel( kart_model.getWheelModel(3), + kart_model.getWheelGraphicsPosition(3) ); + w3->update(0); + + m_parent->m_kart_widgets[playerID].m_kart_name + ->setText( selectionText.c_str(), false ); + } + else + { + fprintf(stderr, "[KartSelectionScreen] WARNING: could not " + "find a kart named '%s'\n", + selectionID.c_str()); + } + } + + m_parent->m_kart_widgets[playerID].setKartInternalName(selectionID); + m_parent->validateKartChoices(); +} // onSelectionChanged #if 0 #pragma mark - @@ -987,6 +903,13 @@ KartSelectionScreen::KartSelectionScreen() : Screen("karts.stkgui") m_go_to_overworld_next = false; } // KartSelectionScreen +// ============================================================================ + +KartSelectionScreen* KartSelectionScreen::getRunningInstance() +{ + return m_instance_ptr; +} + // ---------------------------------------------------------------------------- void KartSelectionScreen::loadedFromFile() @@ -1051,6 +974,7 @@ void KartSelectionScreen::beforeAddingWidget() void KartSelectionScreen::init() { Screen::init(); + m_must_delete_on_back = false; RibbonWidget* tabs = getWidget("kartgroups"); assert( tabs != NULL ); @@ -1160,6 +1084,9 @@ void KartSelectionScreen::tearDown() Screen::tearDown(); m_kart_widgets.clearAndDeleteAll(); + + if (m_must_delete_on_back) + GUIEngine::removeScreen(this->getName().c_str()); } // tearDown // ---------------------------------------------------------------------------- @@ -1184,20 +1111,20 @@ bool KartSelectionScreen::playerJoin(InputDevice* device, bool firstPlayer) if (w == NULL) { std::cerr << "[KartSelectionScreen] playerJoin(): Called outside of " - "kart selection screen.\n"; + "kart selection screen.\n"; return false; } else if (device == NULL) { std::cerr << "[KartSelectionScreen] playerJoin(): Received null " - "device pointer\n"; + "device pointer\n"; return false; } if (StateManager::get()->activePlayerCount() >= MAX_PLAYER_COUNT) { std::cerr << "[KartSelectionScreen] Maximum number of players " - "reached\n"; + "reached\n"; sfx_manager->quickSound( "anvil" ); return false; } @@ -1240,7 +1167,7 @@ bool KartSelectionScreen::playerJoin(InputDevice* device, bool firstPlayer) } const int new_player_id = - StateManager::get()->createActivePlayer( profileToUse, device ); + StateManager::get()->createActivePlayer( profileToUse, device, NULL ); StateManager::ActivePlayer* aplayer = StateManager::get()->getActivePlayer(new_player_id); @@ -1305,13 +1232,13 @@ bool KartSelectionScreen::playerJoin(InputDevice* device, bool firstPlayer) w->setSelection(new_player_id, new_player_id, true); newPlayerWidget->m_player_ident_spinner - ->setFocusForPlayer(new_player_id); + ->setFocusForPlayer(new_player_id); } if (!m_multiplayer) { input_manager->getDeviceList()->setSinglePlayer( StateManager::get() - ->getActivePlayer(0)); + ->getActivePlayer(0)); } return true; @@ -1327,9 +1254,9 @@ bool KartSelectionScreen::playerQuit(StateManager::ActivePlayer* player) if (w == NULL) { std::cerr << "[KartSelectionScreen] ERROR: playerQuit() called " - "outside of kart selection screen, " + "outside of kart selection screen, " << "or the XML file for this screen was changed without " - "adapting the code accordingly\n"; + "adapting the code accordingly\n"; return false; } @@ -1362,7 +1289,7 @@ bool KartSelectionScreen::playerQuit(StateManager::ActivePlayer* player) if (playerID == -1) { std::cerr << "[KartSelectionScreen] WARNING: playerQuit cannot find " - "passed player\n"; + "passed player\n"; return false; } if(UserConfigParams::logGUI()) @@ -1505,14 +1432,14 @@ void KartSelectionScreen::playerConfirm(const int playerID) const bool player_ready = m_kart_widgets[n].isReady(); const bool ident_conflict = !m_kart_widgets[n].getAssociatedPlayer()->getProfile() - ->isGuestAccount() && + ->isGuestAccount() && m_kart_widgets[n].getAssociatedPlayer()->getProfile() == m_kart_widgets[playerID].getAssociatedPlayer()->getProfile(); const bool kart_conflict = sameKart(m_kart_widgets[n], - m_kart_widgets[playerID]); + m_kart_widgets[playerID]); if (player_ready && (ident_conflict || kart_conflict) && - !willNeedDuplicates) + !willNeedDuplicates) { if (UserConfigParams::logGUI()) printf("[KartSelectionScreen] You can't select this identity " @@ -1525,7 +1452,7 @@ void KartSelectionScreen::playerConfirm(const int playerID) // If two PlayerKart entries are associated to the same ActivePlayer, // something went wrong assert(m_kart_widgets[n].getAssociatedPlayer() != - m_kart_widgets[playerID].getAssociatedPlayer()); + m_kart_widgets[playerID].getAssociatedPlayer()); } // Mark this player as ready to start @@ -1607,7 +1534,7 @@ void KartSelectionScreen::eventCallback(Widget* widget, // the tab switch if (UserConfigParams::logGUI()) std::cout << "[KartSelectionScreen] Player " << n - << " lost their selection when switching tabs!!!\n"; + << " lost their selection when switching tabs!!!\n"; // Select a random kart in this case const int count = w->getItems().size(); @@ -1625,13 +1552,13 @@ void KartSelectionScreen::eventCallback(Widget* widget, n != PLAYER_ID_GAME_MASTER ); if (!success) std::cerr << "[KartSelectionScreen] WARNING: " - "setting kart of player " << n + "setting kart of player " << n << " failed :(\n"; } else { std::cerr << "[KartSelectionScreen] WARNING : 0 items " - "in the ribbon\n"; + "in the ribbon\n"; } } } @@ -1644,7 +1571,7 @@ void KartSelectionScreen::eventCallback(Widget* widget, else if (name == "back") { m_go_to_overworld_next = false; // valid once - + m_must_delete_on_back = true; if (m_from_overworld) { m_from_overworld = false; // valid once @@ -1683,7 +1610,7 @@ void KartSelectionScreen::setMultiplayer(bool multiplayer) bool KartSelectionScreen::onEscapePressed() { m_go_to_overworld_next = false; // valid once - + m_must_delete_on_back = true; // delete the screen if (m_from_overworld) { m_from_overworld = false; // valid once @@ -1729,16 +1656,16 @@ void KartSelectionScreen::allPlayersDone() for (int n=0; ngetName().c_str()).c_str() - << " on " << players[n].getDevice()->m_name << std::endl; + << core::stringc( + players[n].getConstProfile()->getName().c_str()).c_str() + << " on " << players[n].getDevice()->m_name << std::endl; } } for (int n=0; ngetActivePlayer(n)->getProfile() - ->incrementUseFrequency(); + ->incrementUseFrequency(); } // ---- Give player info to race manager race_manager->setNumLocalPlayers( players.size() ); @@ -1779,8 +1706,8 @@ void KartSelectionScreen::allPlayersDone() { randomID = random.get(item_count); if (items[randomID].m_code_name != ID_DONT_USE && - !StringUtils::startsWith(items[randomID].m_code_name, - ID_LOCKED)) + !StringUtils::startsWith(items[randomID].m_code_name, + ID_LOCKED)) { selected_kart = items[randomID].m_code_name; done = true; @@ -1788,7 +1715,8 @@ void KartSelectionScreen::allPlayersDone() items[randomID].m_code_name = ID_DONT_USE; count++; if (count > 100) return; - } while (!done); + } + while (!done); } else { @@ -1814,7 +1742,7 @@ void KartSelectionScreen::allPlayersDone() if (!m_multiplayer) { input_manager->getDeviceList() - ->setSinglePlayer( StateManager::get()->getActivePlayer(0) ); + ->setSinglePlayer( StateManager::get()->getActivePlayer(0) ); } else { @@ -1855,8 +1783,8 @@ bool KartSelectionScreen::validateIdentChoices() if (m_multiplayer) { assert( m_kart_widgets[n].getAssociatedPlayer()->getProfile() == - UserConfigParams::m_all_players.get(m_kart_widgets[n] - .m_player_ident_spinner->getValue()) ); + UserConfigParams::m_all_players.get(m_kart_widgets[n] + .m_player_ident_spinner->getValue()) ); } } } @@ -1867,7 +1795,7 @@ bool KartSelectionScreen::validateIdentChoices() // skip players that took a guest account, they can be many on the // same identity in this case if (m_kart_widgets[n].getAssociatedPlayer()->getProfile() - ->isGuestAccount()) + ->isGuestAccount()) { continue; } @@ -1878,24 +1806,24 @@ bool KartSelectionScreen::validateIdentChoices() // check if 2 players took the same name if (m_kart_widgets[n].getAssociatedPlayer()->getProfile() == - m_kart_widgets[m].getAssociatedPlayer()->getProfile()) + m_kart_widgets[m].getAssociatedPlayer()->getProfile()) { // two players took the same name. check if one is ready if (!m_kart_widgets[n].isReady() && - m_kart_widgets[m].isReady()) + m_kart_widgets[m].isReady()) { // player m is ready, so player n should not choose // this name m_kart_widgets[n].m_player_ident_spinner - ->markAsIncorrect(); + ->markAsIncorrect(); } else if (m_kart_widgets[n].isReady() && - !m_kart_widgets[m].isReady()) + !m_kart_widgets[m].isReady()) { // player n is ready, so player m should not // choose this name m_kart_widgets[m].m_player_ident_spinner - ->markAsIncorrect(); + ->markAsIncorrect(); } else if (m_kart_widgets[n].isReady() && m_kart_widgets[m].isReady()) @@ -1955,7 +1883,7 @@ bool KartSelectionScreen::validateKartChoices() // two players took the same kart. check if one is ready if (!m_kart_widgets[n].isReady() && - m_kart_widgets[m].isReady()) + m_kart_widgets[m].isReady()) { if (UserConfigParams::logGUI()) std::cout << " --> Setting red badge on player " @@ -1966,7 +1894,7 @@ bool KartSelectionScreen::validateKartChoices() m_kart_widgets[n].m_model_view->setBadge(BAD_BADGE); } else if (m_kart_widgets[n].isReady() && - !m_kart_widgets[m].isReady()) + !m_kart_widgets[m].isReady()) { if (UserConfigParams::logGUI()) std::cout << " --> Setting red badge on player " @@ -2030,7 +1958,7 @@ void KartSelectionScreen::setKartsFromCurrentGroup() // selected kart group is removed. In this case, select the // 'standard' group if (selected_kart_group != ALL_KART_GROUPS_ID && - !kart_properties_manager->getKartsInGroup(selected_kart_group).size()) + !kart_properties_manager->getKartsInGroup(selected_kart_group).size()) { selected_kart_group = DEFAULT_GROUP_NAME; } @@ -2055,7 +1983,7 @@ void KartSelectionScreen::setKartsFromCurrentGroup() "to more!"), ID_LOCKED+prop->getIdent(), prop->getAbsoluteIconFile(), LOCKED_BADGE, - IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE); + IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE); } else { @@ -2091,7 +2019,7 @@ void KartSelectionScreen::setKartsFromCurrentGroup() else { w->addItem(translations->fribidize(prop->getName()), - prop->getIdent(), + prop->getIdent(), icon_path, 0, IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE); usableKartCount++; @@ -2144,7 +2072,7 @@ EventPropagation FocusDispatcher::focused(const int playerID) // ")" << std::endl; m_parent->m_kart_widgets[n].m_player_ident_spinner - ->setFocusForPlayer(playerID); + ->setFocusForPlayer(playerID); return GUIEngine::EVENT_BLOCK; diff --git a/src/states_screens/kart_selection.hpp b/src/states_screens/kart_selection.hpp index fc6f1ee00..a7ed47198 100644 --- a/src/states_screens/kart_selection.hpp +++ b/src/states_screens/kart_selection.hpp @@ -16,14 +16,23 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#ifndef KART_SELECTION_INCLUDED +#define KART_SELECTION_INCLUDED + #include #include "guiengine/screen.hpp" +#include "guiengine/widgets/dynamic_ribbon_widget.hpp" +#include "guiengine/widgets/label_widget.hpp" +#include "guiengine/widgets/model_view_widget.hpp" +#include "guiengine/widgets/spinner_widget.hpp" #include "states_screens/state_manager.hpp" +#include namespace GUIEngine { class Widget; class BubbleWidget; + enum EventPropagation; } class InputDevice; class PlayerKartWidget; @@ -33,13 +42,12 @@ class KartHoverListener; * \brief screen where players can choose their kart * \ingroup states_screens */ -class KartSelectionScreen : public GUIEngine::Screen, - public GUIEngine::ScreenSingleton +class KartSelectionScreen : public GUIEngine::Screen { friend class KartHoverListener; friend class PlayerNameSpinner; friend class FocusDispatcher; - +protected: /** Contains the custom widget shown for every player. (ref only since * we're adding them to a Screen, and the Screen will take ownership * of these widgets) @@ -56,6 +64,7 @@ class KartSelectionScreen : public GUIEngine::Screen, bool m_go_to_overworld_next; + bool m_must_delete_on_back; //!< To delete the screen if back is pressed KartSelectionScreen(); @@ -92,7 +101,11 @@ class KartSelectionScreen : public GUIEngine::Screen, void playerConfirm(const int playerID); + /** Stores a pointer to the current selection screen */ + static KartSelectionScreen* m_instance_ptr; public: + /** Returns the current instance */ + static KartSelectionScreen* getRunningInstance(); /** \brief implement callback from parent class GUIEngine::Screen */ virtual void loadedFromFile() OVERRIDE; @@ -139,3 +152,207 @@ public: virtual bool onEscapePressed() OVERRIDE; }; // KartSelectionScreen + +//!---------------------------------------------------------------------------- +//! FocusDispatcher : +/** Currently, navigation for multiple players at the same time is implemented + in a somewhat clunky way. An invisible "dispatcher" widget is added above + kart icons. When a player moves up, he focuses the dispatcher, which in + turn moves the selection to the appropriate spinner. "tabbing roots" are + used to make navigation back down possible. (FIXME: maybe find a cleaner + way?) */ +class FocusDispatcher : public GUIEngine::Widget +{ + KartSelectionScreen* m_parent; + int m_reserved_id; + + bool m_is_initialised; + +public: + + LEAK_CHECK() + + // ------------------------------------------------------------------------ + FocusDispatcher(KartSelectionScreen* parent); + // ------------------------------------------------------------------------ + void setRootID(const int reservedID); + + // ------------------------------------------------------------------------ + virtual void add(); + + // ------------------------------------------------------------------------ + + virtual GUIEngine::EventPropagation focused(const int playerID); +}; // FocusDispatcher + +//!---------------------------------------------------------------------------- +//! PlayerNameSpinner : +/** A small extension to the spinner widget to add features like player ID + * management or badging */ +class PlayerNameSpinner : public GUIEngine::SpinnerWidget +{ + int m_playerID; + bool m_incorrect; + irr::gui::IGUIImage* m_red_mark_widget; + KartSelectionScreen* m_parent; + + //virtual EventPropagation focused(const int m_playerID) ; + +public: + PlayerNameSpinner(KartSelectionScreen* parent, const int playerID); + // ------------------------------------------------------------------------ + void setID(const int m_playerID); + // ------------------------------------------------------------------------ + /** Add a red mark on the spinner to mean "invalid choice" */ + void markAsIncorrect(); + + // ------------------------------------------------------------------------ + /** Remove any red mark set with 'markAsIncorrect' */ + void markAsCorrect(); +}; + +/** A widget representing the kart selection for a player (i.e. the player's + * number, name, the kart view, the kart's name) */ +class PlayerKartWidget : public GUIEngine::Widget, + public GUIEngine::SpinnerWidget::ISpinnerConfirmListener +{ + /** Whether this player confirmed their selection */ + bool m_ready; + + /** widget coordinates */ + int player_id_x, player_id_y, player_id_w, player_id_h; + int player_name_x, player_name_y, player_name_w, player_name_h; + int model_x, model_y, model_w, model_h; + int kart_name_x, kart_name_y, kart_name_w, kart_name_h; + + /** A reserved ID for this widget if any, -1 otherwise. (If no ID is + * reserved, widget will not be in the regular tabbing order */ + int m_irrlicht_widget_ID; + + /** For animation purposes (see method 'move') */ + int target_x, target_y, target_w, target_h; + float x_speed, y_speed, w_speed, h_speed; + + /** Object representing this player */ + StateManager::ActivePlayer* m_associatedPlayer; + int m_playerID; + + /** Internal name of the spinner; useful to interpret spinner events, + * which contain the name of the activated object */ + std::string spinnerID; + +#ifdef DEBUG + long m_magic_number; +#endif + +public: + + LEAK_CHECK() + + /** Sub-widgets created by this widget */ + //LabelWidget* m_player_ID_label; + PlayerNameSpinner* m_player_ident_spinner; + GUIEngine::ModelViewWidget* m_model_view; + GUIEngine::LabelWidget* m_kart_name; + + KartSelectionScreen* m_parent_screen; + + irr::gui::IGUIStaticText* m_ready_text; + + //LabelWidget *getPlayerIDLabel() {return m_player_ID_label;} + core::stringw deviceName; + std::string m_kartInternalName; + + bool m_not_updated_yet; + + PlayerKartWidget(KartSelectionScreen* parent, + StateManager::ActivePlayer* associatedPlayer, + core::recti area, const int playerID, + std::string kartGroup, + const int irrlichtWidgetID=-1); + // ------------------------------------------------------------------------ + + ~PlayerKartWidget(); + + // ------------------------------------------------------------------------ + /** Called when players are renumbered (changes the player ID) */ + void setPlayerID(const int newPlayerID); + + // ------------------------------------------------------------------------ + /** Returns the ID of this player */ + int getPlayerID() const; + + // ------------------------------------------------------------------------ + /** Add the widgets to the current screen */ + virtual void add(); + + // ------------------------------------------------------------------------ + /** Get the associated ActivePlayer object*/ + StateManager::ActivePlayer* getAssociatedPlayer(); + + // ------------------------------------------------------------------------ + /** Starts a 'move/resize' animation, by simply passing destination coords. + * The animation will then occur on each call to 'onUpdate'. */ + void move(const int x, const int y, const int w, const int h); + + // ------------------------------------------------------------------------ + /** Call when player confirmed his identity and kart */ + void markAsReady(); + + // ------------------------------------------------------------------------ + /** \return Whether this player confirmed his kart and indent selection */ + bool isReady(); + + // ------------------------------------------------------------------------- + /** Updates the animation (moving/shrinking/etc.) */ + void onUpdate(float delta); + + // ------------------------------------------------------------------------- + /** Event callback */ + virtual GUIEngine::EventPropagation transmitEvent( + GUIEngine::Widget* w, + const std::string& originator, + const int m_playerID); + + // ------------------------------------------------------------------------- + /** Sets the size of the widget as a whole, and placed children widgets + * inside itself */ + void setSize(const int x, const int y, const int w, const int h); + + // ------------------------------------------------------------------------- + + /** Sets which kart was selected for this player */ + void setKartInternalName(const std::string& whichKart); + + // ------------------------------------------------------------------------- + + const std::string& getKartInternalName() const; + + // ------------------------------------------------------------------------- + + /** \brief Event callback from ISpinnerConfirmListener */ + virtual GUIEngine::EventPropagation onSpinnerConfirmed(); +}; // PlayerKartWidget + +//!---------------------------------------------------------------------------- +//! KartHoverListener : + +class KartHoverListener : public GUIEngine::DynamicRibbonHoverListener +{ + KartSelectionScreen* m_parent; +public: + unsigned int m_magic_number; + + KartHoverListener(KartSelectionScreen* parent); + + // ------------------------------------------------------------------------ + virtual ~KartHoverListener(); + + // ------------------------------------------------------------------------ + void onSelectionChanged(GUIEngine::DynamicRibbonWidget* theWidget, + const std::string& selectionID, + const irr::core::stringw& selectionText, + const int playerID); +}; // KartHoverListener + +#endif diff --git a/src/states_screens/main_menu_screen.cpp b/src/states_screens/main_menu_screen.cpp index 06b632b00..1e934d1b0 100644 --- a/src/states_screens/main_menu_screen.cpp +++ b/src/states_screens/main_menu_screen.cpp @@ -37,12 +37,12 @@ #include "modes/cutscene_world.hpp" #include "modes/overworld.hpp" #include "modes/demo_world.hpp" -#include "network/network_manager.hpp" #include "states_screens/online_screen.hpp" #include "states_screens/addons_screen.hpp" #include "states_screens/credits.hpp" #include "states_screens/help_screen_1.hpp" -#include "states_screens/kart_selection.hpp" +#include "states_screens/offline_kart_selection.hpp" +#include "states_screens/network_kart_selection.hpp" // FIXME : remove when not testing #include "states_screens/options_screen_video.hpp" #include "states_screens/state_manager.hpp" @@ -272,14 +272,14 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, #endif if (selection == "new") { - KartSelectionScreen* s = KartSelectionScreen::getInstance(); + KartSelectionScreen* s = OfflineKartSelectionScreen::getInstance(); //FIXME : that was for tests s->setMultiplayer(false); s->setFromOverworld(false); StateManager::get()->pushScreen( s ); } else if (selection == "multiplayer") { - KartSelectionScreen* s = KartSelectionScreen::getInstance(); + KartSelectionScreen* s = NetworkKartSelectionScreen::getInstance(); s->setMultiplayer(true); s->setFromOverworld(false); StateManager::get()->pushScreen( s ); @@ -315,7 +315,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, // Create player and associate player with keyboard StateManager::get()->createActivePlayer(unlock_manager->getCurrentPlayer(), - device); + device, NULL); if (kart_properties_manager->getKart(UserConfigParams::m_default_kart) == NULL) { @@ -332,7 +332,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, ->setSinglePlayer( StateManager::get()->getActivePlayer(0) ); StateManager::get()->enterGameState(); - network_manager->setupPlayerKartInfo(); + race_manager->setupPlayerKartInfo(); race_manager->startNew(false); } else if (selection == "story") @@ -359,7 +359,7 @@ void MainMenuScreen::eventCallback(Widget* widget, const std::string& name, const std::string default_kart = UserConfigParams::m_default_kart; if (slot->isLocked(default_kart)) { - KartSelectionScreen *next = KartSelectionScreen::getInstance(); + KartSelectionScreen *next = OfflineKartSelectionScreen::getInstance(); next->setGoToOverworldNext(); next->setMultiplayer(false); StateManager::get()->resetAndGoToScreen(next); diff --git a/src/states_screens/network_kart_selection.cpp b/src/states_screens/network_kart_selection.cpp new file mode 100644 index 000000000..936f1a31c --- /dev/null +++ b/src/states_screens/network_kart_selection.cpp @@ -0,0 +1,68 @@ +#include "states_screens/network_kart_selection.hpp" + +#include "network/protocol_manager.hpp" +#include "network/protocols/client_lobby_room_protocol.hpp" +#include "states_screens/state_manager.hpp" + +using namespace GUIEngine; + +DEFINE_SCREEN_SINGLETON( NetworkKartSelectionScreen ); + +NetworkKartSelectionScreen::NetworkKartSelectionScreen() : KartSelectionScreen() +{ + KartSelectionScreen::m_instance_ptr = this; +} + +NetworkKartSelectionScreen::~NetworkKartSelectionScreen() +{ +} + +void NetworkKartSelectionScreen::init() +{ + m_multiplayer = false; + KartSelectionScreen::init(); + + RibbonWidget* tabs = getWidget("kartgroups"); + assert( tabs != NULL ); + tabs->setVisible(false); + tabs->select( "standard", PLAYER_ID_GAME_MASTER); // select standard kart group + + // change the back button image (because it makes the game quit) + IconButtonWidget* back_button = getWidget("back"); + back_button->setImage("gui/main_quit.png"); + + m_multiplayer = false; +} + +/** + * Callback handling events from the kart selection menu + */ +void NetworkKartSelectionScreen::eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) +{ + if (name == "karts") + { + + } + else if (name == "back") + { + KartSelectionScreen::eventCallback(widget, name, playerID); + } + else // name != karts + { + KartSelectionScreen::eventCallback(widget, name, playerID); + } +} // eventCallback + + +bool NetworkKartSelectionScreen::onEscapePressed() +{ + // then remove the lobby screen (you left the server) + StateManager::get()->popMenu(); + // notify the server that we left + ClientLobbyRoomProtocol* protocol = static_cast( + ProtocolManager::getInstance()->getProtocol(PROTOCOL_LOBBY_ROOM)); + if (protocol) + protocol->leave(); + return true; // remove the screen +} diff --git a/src/states_screens/network_kart_selection.hpp b/src/states_screens/network_kart_selection.hpp new file mode 100644 index 000000000..585a99382 --- /dev/null +++ b/src/states_screens/network_kart_selection.hpp @@ -0,0 +1,22 @@ +#ifndef NETWORK_KART_SELECTION_HPP +#define NETWORK_KART_SELECTION_HPP + +#include "states_screens/kart_selection.hpp" +#include "guiengine/screen.hpp" + +class NetworkKartSelectionScreen : public KartSelectionScreen, public GUIEngine::ScreenSingleton +{ + friend class GUIEngine::ScreenSingleton; +protected: + NetworkKartSelectionScreen(); + virtual ~NetworkKartSelectionScreen(); + +public: + virtual void init() OVERRIDE; + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + virtual bool onEscapePressed() OVERRIDE; +}; + +#endif // NETWORK_KART_SELECTION_HPP diff --git a/src/states_screens/networking_lobby.cpp b/src/states_screens/networking_lobby.cpp index 4c7b32fa5..f2cc525d1 100644 --- a/src/states_screens/networking_lobby.cpp +++ b/src/states_screens/networking_lobby.cpp @@ -33,6 +33,8 @@ #include "states_screens/online_screen.hpp" #include "states_screens/state_manager.hpp" #include "states_screens/dialogs/message_dialog.hpp" +#include "network/protocol_manager.hpp" +#include "network/protocols/client_lobby_room_protocol.hpp" #include "modes/demo_world.hpp" #include "utils/translation.hpp" #include "online/servers_manager.hpp" @@ -120,6 +122,18 @@ void NetworkingLobby::tearDown() { } // tearDown +// ---------------------------------------------------------------------------- + +bool NetworkingLobby::onEscapePressed() +{ + // notify the server that we left + ClientLobbyRoomProtocol* protocol = static_cast( + ProtocolManager::getInstance()->getProtocol(PROTOCOL_LOBBY_ROOM)); + if (protocol) + protocol->leave(); + return true; // close the screen +} + // ---------------------------------------------------------------------------- void NetworkingLobby::onDisabledItemClicked(const std::string& item) { diff --git a/src/states_screens/networking_lobby.hpp b/src/states_screens/networking_lobby.hpp index b33b01d55..96f89b37b 100644 --- a/src/states_screens/networking_lobby.hpp +++ b/src/states_screens/networking_lobby.hpp @@ -72,6 +72,9 @@ public: /** \brief implement callback from parent class GUIEngine::Screen */ virtual void tearDown() OVERRIDE; + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual bool onEscapePressed() OVERRIDE; + /** \brief implement callback from parent class GUIEngine::Screen */ virtual void onDisabledItemClicked(const std::string& item) OVERRIDE; diff --git a/src/states_screens/networking_lobby_settings.cpp b/src/states_screens/networking_lobby_settings.cpp new file mode 100644 index 000000000..62cde1530 --- /dev/null +++ b/src/states_screens/networking_lobby_settings.cpp @@ -0,0 +1,116 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#define DEBUG_MENU_ITEM 0 + +#include "states_screens/networking_lobby_settings.hpp" + +#include +#include + +#include "challenges/game_slot.hpp" +#include "challenges/unlock_manager.hpp" +#include "graphics/irr_driver.hpp" +#include "guiengine/scalable_font.hpp" +#include "input/device_manager.hpp" +#include "input/input_manager.hpp" +#include "io/file_manager.hpp" +#include "main_loop.hpp" +#include "states_screens/online_screen.hpp" +#include "states_screens/state_manager.hpp" +#include "states_screens/dialogs/message_dialog.hpp" +#include "modes/demo_world.hpp" +#include "utils/translation.hpp" + +#include "online/current_user.hpp" + + +using namespace GUIEngine; + +DEFINE_SCREEN_SINGLETON( NetworkingLobbySettings ); + +// ---------------------------------------------------------------------------- + +NetworkingLobbySettings::NetworkingLobbySettings() : Screen("online/lobby_settings.stkgui") +{ + +} // NetworkingLobbySettings + +// ---------------------------------------------------------------------------- + +void NetworkingLobbySettings::loadedFromFile() +{ + +} // loadedFromFile + +// ---------------------------------------------------------------------------- +bool NetworkingLobbySettings::hasLostConnection() +{ + bool return_value = ( Online::CurrentUser::get()->getUserState() != + Online::CurrentUser::US_SIGNED_IN); + return return_value; +} + +// ---------------------------------------------------------------------------- +void NetworkingLobbySettings::beforeAddingWidget() +{ + +} // beforeAddingWidget + + + +// ---------------------------------------------------------------------------- +void NetworkingLobbySettings::init() +{ + Screen::init(); + setInitialFocus(); + DemoWorld::resetIdleTime(); //FIXME : what's this?} // init +} +// ---------------------------------------------------------------------------- +void NetworkingLobbySettings::onUpdate(float delta, irr::video::IVideoDriver* driver) +{ +} // onUpdate + +// ---------------------------------------------------------------------------- + +void NetworkingLobbySettings::eventCallback(Widget* widget, const std::string& name, const int playerID) +{ + +} // eventCallback + +// ---------------------------------------------------------------------------- + +void NetworkingLobbySettings::tearDown() +{ +} // tearDown + +// ---------------------------------------------------------------------------- +void NetworkingLobbySettings::onDisabledItemClicked(const std::string& item) +{ + +} // onDisabledItemClicked + +// ---------------------------------------------------------------------------- +void NetworkingLobbySettings::setInitialFocus() +{ +} // setInitialFocus + +// ---------------------------------------------------------------------------- +void NetworkingLobbySettings::onDialogClose() +{ + setInitialFocus(); +} // onDialogClose() diff --git a/src/states_screens/networking_lobby_settings.hpp b/src/states_screens/networking_lobby_settings.hpp new file mode 100644 index 000000000..234af94df --- /dev/null +++ b/src/states_screens/networking_lobby_settings.hpp @@ -0,0 +1,78 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Glenn De Jonghe +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +#ifndef HEADER_NETWORKING_LOBBY_SETTINGS_HPP +#define HEADER_NETWORKING_LOBBY_SETTINGS_HPP + +#include "guiengine/screen.hpp" +#include "guiengine/widgets/label_widget.hpp" +#include "guiengine/widgets/ribbon_widget.hpp" +#include "guiengine/widgets/icon_button_widget.hpp" + +namespace GUIEngine { class Widget; class ListWidget; } + +/** + * \brief Handles the main menu + * \ingroup states_screens + */ +class NetworkingLobbySettings : public GUIEngine::Screen, + public GUIEngine::ScreenSingleton +{ +private: + friend class GUIEngine::ScreenSingleton; + + NetworkingLobbySettings(); + + /** \brief Checks if the user is still signed in. */ + bool hasLostConnection(); + /** \brief Sets which widget has to be focused. Depends on the user state. */ + void setInitialFocus(); + +public: + + enum Action + { + Create = 1, // A new server should be created + Edit = 2, // The settings of the server should be edited + }; + + virtual void onUpdate(float delta, irr::video::IVideoDriver* driver) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void loadedFromFile() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void eventCallback(GUIEngine::Widget* widget, const std::string& name, + const int playerID) OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void beforeAddingWidget() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void init() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void tearDown() OVERRIDE; + + /** \brief implement callback from parent class GUIEngine::Screen */ + virtual void onDisabledItemClicked(const std::string& item) OVERRIDE; + + /** \brief Implements the callback when a dialog gets closed. */ + virtual void onDialogClose() OVERRIDE; +}; + +#endif diff --git a/src/states_screens/offline_kart_selection.cpp b/src/states_screens/offline_kart_selection.cpp new file mode 100644 index 000000000..ce1b0f647 --- /dev/null +++ b/src/states_screens/offline_kart_selection.cpp @@ -0,0 +1,13 @@ +#include "states_screens/offline_kart_selection.hpp" + +DEFINE_SCREEN_SINGLETON( OfflineKartSelectionScreen ); + +OfflineKartSelectionScreen::OfflineKartSelectionScreen() : KartSelectionScreen() +{ + KartSelectionScreen::m_instance_ptr = this; +} + +OfflineKartSelectionScreen::~OfflineKartSelectionScreen() +{ + //dtor +} diff --git a/src/states_screens/offline_kart_selection.hpp b/src/states_screens/offline_kart_selection.hpp new file mode 100644 index 000000000..a8f3cfccb --- /dev/null +++ b/src/states_screens/offline_kart_selection.hpp @@ -0,0 +1,20 @@ +#ifndef OFFLINE_KART_SELECTION_HPP +#define OFFLINE_KART_SELECTION_HPP + +#include "states_screens/kart_selection.hpp" +#include "guiengine/screen.hpp" + +class OfflineKartSelectionScreen : public KartSelectionScreen, public GUIEngine::ScreenSingleton +{ + friend class GUIEngine::ScreenSingleton; + protected: + OfflineKartSelectionScreen(); + virtual ~OfflineKartSelectionScreen(); + + public: + static bool isRunning() { return singleton!=NULL; } + + // we do not override anything, this class is just there to have a singleton +}; + +#endif // OFFLINE_KART_SELECTION_HPP diff --git a/src/states_screens/online_screen.cpp b/src/states_screens/online_screen.cpp index 2933c5735..df984e03d 100644 --- a/src/states_screens/online_screen.cpp +++ b/src/states_screens/online_screen.cpp @@ -36,10 +36,14 @@ #include "states_screens/networking_lobby.hpp" #include "states_screens/server_selection.hpp" #include "states_screens/create_server_screen.hpp" -#include "modes/demo_world.hpp" +#include "states_screens/networking_lobby_settings.hpp" #include "online/servers_manager.hpp" #include "online/messages.hpp" +#include "online/request.hpp" +#include "modes/demo_world.hpp" +#include "network/protocol_manager.hpp" +#include "network/protocols/connect_to_server.hpp" using namespace GUIEngine; @@ -221,18 +225,40 @@ void OnlineScreen::eventCallback(Widget* widget, const std::string& name, const else if (selection == "quick_play") { //FIXME temporary and the request join + join sequence should be placed in one method somewhere - /* - Server * server = ServersManager::get()->getQuickPlay(); - irr::core::stringw info; - if (Online::CurrentUser::get()->requestJoin( server->getServerId(), info)) + // refresh server list + Online::ServersManager::RefreshRequest* request = ServersManager::get()->refreshRequest(false); + if (request != NULL) // consider request done { - ServersManager::get()->setJoinedServer(server); - StateManager::get()->pushScreen(NetworkingLobby::getInstance()); + Online::HTTPManager::get()->synchronousRequest(request); + delete request; + } + else + { + Log::error("OnlineScreen", "Could not get the server list."); + return; + } + // select first one + const Server * server = ServersManager::get()->getQuickPlay(); + + Online::CurrentUser::ServerJoinRequest* request2 = Online::CurrentUser::get()->requestServerJoin( server->getServerId(), false); + if (request2) + { + Online::HTTPManager::get()->synchronousRequest(request2); + if (request2->isSuccess()) + { + delete request2; + StateManager::get()->pushScreen(NetworkingLobby::getInstance()); + ProtocolManager::getInstance()->requestStart(new ConnectToServer(server->getServerId(), server->getHostId())); + } + else + { + sfx_manager->quickSound( "anvil" ); + } } else { sfx_manager->quickSound( "anvil" ); - }*/ + } } } // eventCallback diff --git a/src/states_screens/race_setup_screen.cpp b/src/states_screens/race_setup_screen.cpp index ef9509c60..0f2a26430 100644 --- a/src/states_screens/race_setup_screen.cpp +++ b/src/states_screens/race_setup_screen.cpp @@ -148,7 +148,7 @@ void RaceSetupScreen::eventCallback(Widget* widget, const std::string& name, con { StateManager::get()->escapePressed(); } - + } // eventCallback // ----------------------------------------------------------------------------- @@ -234,7 +234,7 @@ void RaceSetupScreen::init() { w->setSelection( UserConfigParams::m_difficulty, PLAYER_ID_GAME_MASTER ); } - + SpinnerWidget* kartamount = getWidget("aikartamount"); kartamount->setActivated(); @@ -336,13 +336,13 @@ void RaceSetupScreen::init() m_mode_listener = new GameModeRibbonListener(this); w2->registerHoverListener(m_mode_listener); - - + + if (unlock_manager->getCurrentSlot()->isLocked("difficulty_best")) { RibbonWidget* w = getWidget("difficulty"); assert(w != NULL); - + int index = w->findItemNamed("best"); Widget* hardestWidget = &w->getChildren()[index]; hardestWidget->setBadge(LOCKED_BADGE); diff --git a/src/states_screens/server_selection.cpp b/src/states_screens/server_selection.cpp index 48a3cff33..11ce8d06a 100644 --- a/src/states_screens/server_selection.cpp +++ b/src/states_screens/server_selection.cpp @@ -161,7 +161,8 @@ void ServerSelection::eventCallback( GUIEngine::Widget* widget, { m_selected_index = m_server_list_widget->getSelectionID(); uint32_t server_id = ServersManager::get()->getServerBySort(m_selected_index)->getServerId(); - new ServerInfoDialog(server_id); + uint32_t host_id = ServersManager::get()->getServerBySort(m_selected_index)->getHostId(); + new ServerInfoDialog(server_id, host_id); } } // eventCallback diff --git a/src/states_screens/state_manager.cpp b/src/states_screens/state_manager.cpp index d3f7858a3..d942c8b03 100644 --- a/src/states_screens/state_manager.cpp +++ b/src/states_screens/state_manager.cpp @@ -30,7 +30,9 @@ #include "main_loop.hpp" #include "modes/profile_world.hpp" #include "modes/world.hpp" +#include "online/user.hpp" #include "utils/translation.hpp" +#include "utils/log.hpp" using namespace GUIEngine; @@ -68,7 +70,7 @@ StateManager::ActivePlayer* StateManager::getActivePlayer(const int id) } else { - fprintf(stderr, "getActivePlayer(): id out of bounds\n"); + Log::error("StateManager", "getActivePlayer(): id %d out of bounds", id); assert(false); return NULL; } @@ -100,11 +102,12 @@ void StateManager::updateActivePlayerIDs() // ---------------------------------------------------------------------------- -int StateManager::createActivePlayer(PlayerProfile *profile, InputDevice *device) +int StateManager::createActivePlayer(PlayerProfile *profile, InputDevice *device, + Online::User* user) { ActivePlayer *p; int i; - p = new ActivePlayer(profile, device); + p = new ActivePlayer(profile, device, user); i = m_active_players.size(); m_active_players.push_back(p); @@ -251,7 +254,8 @@ void StateManager::onStackEmptied() #endif StateManager::ActivePlayer::ActivePlayer(PlayerProfile* player, - InputDevice *device) + InputDevice *device, + Online::User* user) { #ifdef DEBUG m_magic_number = 0xAC1EF1AE; @@ -260,6 +264,7 @@ StateManager::ActivePlayer::ActivePlayer(PlayerProfile* player, m_player = player; m_device = NULL; m_kart = NULL; + m_online_user = user; setDevice(device); } // ActivePlayer diff --git a/src/states_screens/state_manager.hpp b/src/states_screens/state_manager.hpp index 7db8c6798..1abe47168 100644 --- a/src/states_screens/state_manager.hpp +++ b/src/states_screens/state_manager.hpp @@ -34,6 +34,10 @@ class AbstractKart; class InputDevice; struct Input; +namespace Online +{ + class User; +} namespace GUIEngine { @@ -72,6 +76,7 @@ public: { friend class StateManager; + Online::User *m_online_user; PlayerProfile *m_player; InputDevice *m_device; @@ -81,7 +86,7 @@ public: /** ID of this player within the list of active players */ int m_id; - ActivePlayer(PlayerProfile* player, InputDevice* device); + ActivePlayer(PlayerProfile* player, InputDevice* device, Online::User* user); #ifdef DEBUG unsigned int m_magic_number; @@ -122,6 +127,16 @@ public: * selecting his identity) */ void setPlayerProfile(PlayerProfile* player); + // -------------------------------------------------------------------- + Online::User* getOnlineUser() + { + return m_online_user; + } + // -------------------------------------------------------------------- + /** Call to change the identity of this player (useful when player is + * selecting his identity) */ + void setOnlineUser(Online::User* user) { m_online_user = user; } + // -------------------------------------------------------------------- /** ID of this player within the list of active players */ int getID() const @@ -178,7 +193,7 @@ public: */ const PlayerProfile* getActivePlayerProfile(const int id); - int createActivePlayer(PlayerProfile *profile, InputDevice *device); + int createActivePlayer(PlayerProfile *profile, InputDevice *device, Online::User* use); void removeActivePlayer(int id); int activePlayerCount(); diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index c16d41fcd..bca42f20b 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -494,8 +494,16 @@ void Track::loadQuadGraph(unsigned int mode_id, const bool reverse) core::dimension2du size = m_mini_map_size .getOptimalSize(!nonpower,!nonsquare); m_mini_map = QuadGraph::get()->makeMiniMap(size, "minimap::"+m_ident); - m_minimap_x_scale = float(m_mini_map_size.Width) / float(m_mini_map->getSize().Width); - m_minimap_y_scale = float(m_mini_map_size.Height) / float(m_mini_map->getSize().Height); + if (m_mini_map) + { + m_minimap_x_scale = float(m_mini_map_size.Width) / float(m_mini_map->getSize().Width); + m_minimap_y_scale = float(m_mini_map_size.Height) / float(m_mini_map->getSize().Height); + } + else + { + m_minimap_x_scale = 0; + m_minimap_y_scale = 0; + } } } // loadQuadGraph // ----------------------------------------------------------------------------- diff --git a/src/utils/types.hpp b/src/utils/types.hpp index dd8ace861..6d094cf91 100644 --- a/src/utils/types.hpp +++ b/src/utils/types.hpp @@ -20,6 +20,8 @@ #define HEADER_TYPES_HPP #ifdef _MSC_VER + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; typedef __int32 int32_t; typedef unsigned __int32 uint32_t; typedef __int64 int64_t;