From f3a962c391db3e0dcddaea1d9f483c7e089250c5 Mon Sep 17 00:00:00 2001 From: Geoffrey Mon Date: Wed, 2 Aug 2017 12:49:14 -0400 Subject: [PATCH 1/6] Save and use usernames in replay files Fixes #2754. * Store usernames of each racer in recorded replay files * Display those usernames in a column of the replay selection UI and in race result dialogs * RaceResultGUI::getKartDisplayName functionality moved into Controller::getName --- src/karts/controller/controller.hpp | 10 ++++--- src/karts/controller/ghost_controller.cpp | 3 ++- src/karts/controller/ghost_controller.hpp | 6 ++++- src/replay/replay_base.hpp | 2 +- src/replay/replay_play.cpp | 18 +++++++++---- src/replay/replay_play.hpp | 24 ++++++++++------- src/replay/replay_recorder.cpp | 10 ++++--- src/states_screens/ghost_replay_selection.cpp | 6 +++++ src/states_screens/race_result_gui.cpp | 27 ++----------------- src/states_screens/race_result_gui.hpp | 1 - 10 files changed, 57 insertions(+), 50 deletions(-) diff --git a/src/karts/controller/controller.hpp b/src/karts/controller/controller.hpp index 747f9cc0c..98403badf 100644 --- a/src/karts/controller/controller.hpp +++ b/src/karts/controller/controller.hpp @@ -28,9 +28,9 @@ using namespace irr; */ #include "input/input.hpp" +#include "karts/abstract_kart.hpp" #include "states_screens/state_manager.hpp" -class AbstractKart; class Item; class KartControl; class Material; @@ -100,17 +100,19 @@ public: /** Only local players can get achievements. */ virtual bool canGetAchievements () const { return false; } // ------------------------------------------------------------------------ - /** This should only be called for End- and LocalPlayer-Controller. */ + /** Display name of the controller. + * Defaults to kart name; overriden by controller classes + * (such as player controllers) to display username. */ virtual core::stringw getName() const { - assert(false); - return core::stringw(""); + return translations->fribidize(m_kart->getName()); } // getName // ------------------------------------------------------------------------ /** Returns the kart controlled by this controller. */ AbstractKart *getKart() const { return m_kart; } }; // Controller +extern Translations* translations; #endif /* EOF */ diff --git a/src/karts/controller/ghost_controller.cpp b/src/karts/controller/ghost_controller.cpp index ce9fa8d96..99a4c8475 100644 --- a/src/karts/controller/ghost_controller.cpp +++ b/src/karts/controller/ghost_controller.cpp @@ -21,9 +21,10 @@ #include "karts/controller/kart_control.hpp" #include "modes/world.hpp" -GhostController::GhostController(AbstractKart *kart) +GhostController::GhostController(AbstractKart *kart, core::stringw display_name) : Controller(kart) { + m_display_name = display_name; } // GhostController //----------------------------------------------------------------------------- diff --git a/src/karts/controller/ghost_controller.hpp b/src/karts/controller/ghost_controller.hpp index c0eb462f9..c78b4b454 100644 --- a/src/karts/controller/ghost_controller.hpp +++ b/src/karts/controller/ghost_controller.hpp @@ -37,11 +37,14 @@ private: /** The current world time. */ float m_current_time; + /** Player name of the ghost kart. */ + core::stringw m_display_name; + /** The list of the times at which the events of kart were reached. */ std::vector m_all_times; public: - GhostController(AbstractKart *kart); + GhostController(AbstractKart *kart, core::stringw display_name); virtual ~GhostController() {}; virtual void reset() OVERRIDE; virtual void update (float dt) OVERRIDE; @@ -73,6 +76,7 @@ public: unsigned int getCurrentReplayIndex() const { return m_current_index; } // ------------------------------------------------------------------------ + core::stringw getName() const OVERRIDE { return m_display_name; } }; // GhostController #endif diff --git a/src/replay/replay_base.hpp b/src/replay/replay_base.hpp index 2d7f8b408..0655d2daf 100644 --- a/src/replay/replay_base.hpp +++ b/src/replay/replay_base.hpp @@ -81,7 +81,7 @@ protected: /** Returns the version number of the replay file. This is used to check * that a loaded replay file can still be understood by this * executable. */ - unsigned int getReplayVersion() const { return 3; } + unsigned int getReplayVersion() const { return 4; } public: ReplayBase(); diff --git a/src/replay/replay_play.cpp b/src/replay/replay_play.cpp index cc998ae74..942f07175 100644 --- a/src/replay/replay_play.cpp +++ b/src/replay/replay_play.cpp @@ -135,13 +135,20 @@ bool ReplayPlay::addReplayFile(const std::string& fn, bool custom_replay) is_end.trim(); if (is_end == "kart_list_end") break; char s1[1024]; + char display_name_encoded[1024]; - if (sscanf(s,"kart: %s", s1) != 1) + if (sscanf(s,"kart: %s %[^\n]", s1, display_name_encoded) != 2) { Log::warn("Replay", "Could not read ghost karts info!"); break; } rd.m_kart_list.push_back(std::string(s1)); + rd.m_name_list.push_back(StringUtils::xmlDecode(std::string(display_name_encoded))); + + if (rd.m_name_list.size() == 1) { + // First user is the game master and the "owner" of this replay file + rd.m_user_name = rd.m_name_list[0]; + } } int reverse = 0; @@ -249,11 +256,12 @@ void ReplayPlay::readKartData(FILE *fd, char *next_line) { char s[1024]; const unsigned int kart_num = m_ghost_karts.size(); - m_ghost_karts.push_back(new GhostKart(m_replay_file_list - [m_current_replay_file].m_kart_list.at(kart_num), - kart_num, kart_num + 1)); + ReplayData &rd = m_replay_file_list[m_current_replay_file]; + m_ghost_karts.push_back(new GhostKart(rd.m_kart_list.at(kart_num), + kart_num, kart_num + 1)); m_ghost_karts[kart_num].init(RaceManager::KT_GHOST); - Controller* controller = new GhostController(getGhostKart(kart_num)); + Controller* controller = new GhostController(getGhostKart(kart_num), + rd.m_name_list[kart_num]); getGhostKart(kart_num)->setController(controller); unsigned int size; diff --git a/src/replay/replay_play.hpp b/src/replay/replay_play.hpp index c589c7fc2..04b6ed7b4 100644 --- a/src/replay/replay_play.hpp +++ b/src/replay/replay_play.hpp @@ -45,20 +45,23 @@ public: SO_KART_NUM, SO_DIFF, SO_LAPS, - SO_TIME + SO_TIME, + SO_USER }; class ReplayData { public: - std::string m_filename; - std::string m_track_name; - std::vector m_kart_list; - bool m_reverse; - bool m_custom_replay_file; - unsigned int m_difficulty; - unsigned int m_laps; - float m_min_time; + std::string m_filename; + std::string m_track_name; + core::stringw m_user_name; + std::vector m_kart_list; + std::vector m_name_list; + bool m_reverse; + bool m_custom_replay_file; + unsigned int m_difficulty; + unsigned int m_laps; + float m_min_time; bool operator < (const ReplayData& r) const { @@ -82,6 +85,9 @@ public: case SO_TIME: return m_min_time < r.m_min_time; break; + case SO_USER: + return m_user_name < r.m_user_name; + break; } // switch return true; } // operator < diff --git a/src/replay/replay_recorder.cpp b/src/replay/replay_recorder.cpp index 4a1c986e2..e94a111e4 100644 --- a/src/replay/replay_recorder.cpp +++ b/src/replay/replay_recorder.cpp @@ -31,6 +31,7 @@ #include #include #include +#include ReplayRecorder *ReplayRecorder::m_replay_recorder = NULL; @@ -222,9 +223,12 @@ void ReplayRecorder::save() fprintf(fd, "version: %d\n", getReplayVersion()); for (unsigned int real_karts = 0; real_karts < num_karts; real_karts++) { - if (world->getKart(real_karts)->isGhostKart()) continue; - fprintf(fd, "kart: %s\n", - world->getKart(real_karts)->getIdent().c_str()); + const AbstractKart *kart = world->getKart(real_karts); + if (kart->isGhostKart()) continue; + + // XML encode the username to handle Unicode + fprintf(fd, "kart: %s %s\n", kart->getIdent().c_str(), + StringUtils::xmlEncode(kart->getController()->getName()).c_str()); } fprintf(fd, "kart_list_end\n"); diff --git a/src/states_screens/ghost_replay_selection.cpp b/src/states_screens/ghost_replay_selection.cpp index 7828efc4a..92fe70882 100644 --- a/src/states_screens/ghost_replay_selection.cpp +++ b/src/states_screens/ghost_replay_selection.cpp @@ -80,6 +80,7 @@ void GhostReplaySelection::beforeAddingWidget() m_replay_list_widget->addColumn( _("Difficulty"), 1); m_replay_list_widget->addColumn( _("Laps"), 1); m_replay_list_widget->addColumn( _("Finish Time"), 1); + m_replay_list_widget->addColumn( _("User"), 1); } // beforeAddingWidget // ---------------------------------------------------------------------------- @@ -122,6 +123,8 @@ void GhostReplaySelection::loadList() (StringUtils::toWString(rd.m_laps), -1, 1, true)); row.push_back(GUIEngine::ListWidget::ListCell (StringUtils::toWString(rd.m_min_time) + L"s", -1, 1, true)); + row.push_back(GUIEngine::ListWidget::ListCell + (rd.m_user_name, -1, 1, true)); m_replay_list_widget->addItem(StringUtils::toString(i), row); } } // loadList @@ -213,6 +216,9 @@ void GhostReplaySelection::onColumnClicked(int column_id) case 5: ReplayPlay::setSortOrder(ReplayPlay::SO_TIME); break; + case 6: + ReplayPlay::setSortOrder(ReplayPlay::SO_USER); + break; default: assert(0); break; diff --git a/src/states_screens/race_result_gui.cpp b/src/states_screens/race_result_gui.cpp index 7aee51f4f..0a1b65ec6 100644 --- a/src/states_screens/race_result_gui.cpp +++ b/src/states_screens/race_result_gui.cpp @@ -486,7 +486,7 @@ void RaceResultGUI::backToLobby() // Save a pointer to the current row_info entry RowInfo *ri = &(m_all_row_infos[position - first_position]); ri->m_is_player_kart = kart->getController()->isLocalPlayerController(); - ri->m_kart_name = getKartDisplayName(kart); + ri->m_kart_name = kart->getController()->getName(); video::ITexture *icon = kart->getKartProperties()->getIconMaterial()->getTexture(); @@ -855,7 +855,7 @@ void RaceResultGUI::backToLobby() ri->m_kart_icon = kart->getKartProperties()->getIconMaterial()->getTexture(); ri->m_is_player_kart = kart->getController()->isLocalPlayerController(); - ri->m_kart_name = getKartDisplayName(kart); + ri->m_kart_name = kart->getController()->getName(); // In FTL karts do have a time, which is shown even when the kart // is eliminated @@ -907,29 +907,6 @@ void RaceResultGUI::backToLobby() #endif } // determineGPLayout - //----------------------------------------------------------------------------- - /** Returns a string to display next to a kart. For a player that's the name - * of the player, for an AI kart it's the name of the driver. - */ - core::stringw RaceResultGUI::getKartDisplayName(const AbstractKart *kart) const - { - const EndController *ec = - dynamic_cast(kart->getController()); - // If the race was given up, there is no end controller for the - // players, so this case needs to be handled separately - if(ec && ec->isLocalPlayerController()) - return ec->getName(); - else - { - // No end controller, check explicitely for a player controller - const PlayerController *pc = - dynamic_cast(kart->getController()); - // Check if the kart is a player controller to get the real name - if(pc) return pc->getName(); - } - return translations->fribidize(kart->getName()); - } // getKartDisplayName - //----------------------------------------------------------------------------- /** Displays the race results for a single kart. * \param n Index of the kart to be displayed. diff --git a/src/states_screens/race_result_gui.hpp b/src/states_screens/race_result_gui.hpp index 75c663901..0c1960ca0 100644 --- a/src/states_screens/race_result_gui.hpp +++ b/src/states_screens/race_result_gui.hpp @@ -194,7 +194,6 @@ private: void displayPostRaceInfo(); void displaySoccerResults(); void displayScreenShots(); - irr::core::stringw getKartDisplayName(const AbstractKart *kart) const; int getFontHeight () const; From ca5258a46cfeac04704ccf7dfe50849fcc361d6b Mon Sep 17 00:00:00 2001 From: Geoffrey Mon Date: Wed, 2 Aug 2017 13:46:48 -0400 Subject: [PATCH 2/6] Move Controller::getName definition to avoid unnecessary #include --- src/karts/controller/controller.cpp | 4 ++++ src/karts/controller/controller.hpp | 7 ++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/karts/controller/controller.cpp b/src/karts/controller/controller.cpp index fe3450464..6460b0341 100644 --- a/src/karts/controller/controller.cpp +++ b/src/karts/controller/controller.cpp @@ -34,3 +34,7 @@ Controller::Controller(AbstractKart *kart) m_kart = kart; setControllerName("Controller"); } // Controller + +core::stringw Controller::getName() const { + return translations->fribidize(m_kart->getName()); +} diff --git a/src/karts/controller/controller.hpp b/src/karts/controller/controller.hpp index 98403badf..1e101d137 100644 --- a/src/karts/controller/controller.hpp +++ b/src/karts/controller/controller.hpp @@ -28,9 +28,9 @@ using namespace irr; */ #include "input/input.hpp" -#include "karts/abstract_kart.hpp" #include "states_screens/state_manager.hpp" +class AbstractKart; class Item; class KartControl; class Material; @@ -103,10 +103,7 @@ public: /** Display name of the controller. * Defaults to kart name; overriden by controller classes * (such as player controllers) to display username. */ - virtual core::stringw getName() const - { - return translations->fribidize(m_kart->getName()); - } // getName + virtual core::stringw getName() const; // ------------------------------------------------------------------------ /** Returns the kart controlled by this controller. */ AbstractKart *getKart() const { return m_kart; } From 9ec2ff5c4a0b8223a59f7b649581962a2026f81f Mon Sep 17 00:00:00 2001 From: Geoffrey Mon Date: Wed, 2 Aug 2017 14:13:26 -0400 Subject: [PATCH 3/6] Backwards compatibility: use kart name if username is not in replay --- src/karts/controller/ghost_controller.hpp | 5 ++++- src/replay/replay_base.hpp | 2 +- src/replay/replay_play.cpp | 23 ++++++++++++++++------- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/karts/controller/ghost_controller.hpp b/src/karts/controller/ghost_controller.hpp index c78b4b454..746d95544 100644 --- a/src/karts/controller/ghost_controller.hpp +++ b/src/karts/controller/ghost_controller.hpp @@ -76,7 +76,10 @@ public: unsigned int getCurrentReplayIndex() const { return m_current_index; } // ------------------------------------------------------------------------ - core::stringw getName() const OVERRIDE { return m_display_name; } + /** Return the display name; if not set, use default display name (kart name) */ + core::stringw getName() const OVERRIDE { return m_display_name.empty() ? + Controller::getName() : + m_display_name; } }; // GhostController #endif diff --git a/src/replay/replay_base.hpp b/src/replay/replay_base.hpp index 0655d2daf..2d7f8b408 100644 --- a/src/replay/replay_base.hpp +++ b/src/replay/replay_base.hpp @@ -81,7 +81,7 @@ protected: /** Returns the version number of the replay file. This is used to check * that a loaded replay file can still be understood by this * executable. */ - unsigned int getReplayVersion() const { return 4; } + unsigned int getReplayVersion() const { return 3; } public: ReplayBase(); diff --git a/src/replay/replay_play.cpp b/src/replay/replay_play.cpp index 942f07175..130dbad0a 100644 --- a/src/replay/replay_play.cpp +++ b/src/replay/replay_play.cpp @@ -137,17 +137,26 @@ bool ReplayPlay::addReplayFile(const std::string& fn, bool custom_replay) char s1[1024]; char display_name_encoded[1024]; - if (sscanf(s,"kart: %s %[^\n]", s1, display_name_encoded) != 2) + int scanned = sscanf(s,"kart: %s %[^\n]", s1, display_name_encoded); + if (scanned < 1) { Log::warn("Replay", "Could not read ghost karts info!"); break; - } - rd.m_kart_list.push_back(std::string(s1)); - rd.m_name_list.push_back(StringUtils::xmlDecode(std::string(display_name_encoded))); + } else { + rd.m_kart_list.push_back(std::string(s1)); - if (rd.m_name_list.size() == 1) { - // First user is the game master and the "owner" of this replay file - rd.m_user_name = rd.m_name_list[0]; + if (scanned == 2) { + // If username of kart is present, use it + rd.m_name_list.push_back(StringUtils::xmlDecode(std::string(display_name_encoded))); + if (rd.m_name_list.size() == 1) { + // First user is the game master and the "owner" of this replay file + rd.m_user_name = rd.m_name_list[0]; + } + } else { // scanned == 1 + // If username is not present, kart display name will default to kart name + // (see GhostController::getName) + rd.m_name_list.push_back(""); + } } } From 802d70ca8d7f50adc0d39e28ef56984cd1b8e2c8 Mon Sep 17 00:00:00 2001 From: Geoffrey Mon Date: Wed, 2 Aug 2017 17:21:37 -0400 Subject: [PATCH 4/6] Fix code style issues --- src/karts/controller/controller.cpp | 3 ++- src/karts/controller/ghost_controller.hpp | 7 +++--- src/replay/replay_play.cpp | 28 ++++++++++++----------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/karts/controller/controller.cpp b/src/karts/controller/controller.cpp index 6460b0341..26d8311d2 100644 --- a/src/karts/controller/controller.cpp +++ b/src/karts/controller/controller.cpp @@ -35,6 +35,7 @@ Controller::Controller(AbstractKart *kart) setControllerName("Controller"); } // Controller -core::stringw Controller::getName() const { +core::stringw Controller::getName() const +{ return translations->fribidize(m_kart->getName()); } diff --git a/src/karts/controller/ghost_controller.hpp b/src/karts/controller/ghost_controller.hpp index 746d95544..245abae74 100644 --- a/src/karts/controller/ghost_controller.hpp +++ b/src/karts/controller/ghost_controller.hpp @@ -77,9 +77,10 @@ public: { return m_current_index; } // ------------------------------------------------------------------------ /** Return the display name; if not set, use default display name (kart name) */ - core::stringw getName() const OVERRIDE { return m_display_name.empty() ? - Controller::getName() : - m_display_name; } + core::stringw getName() const OVERRIDE + { + return m_display_name.empty() ? Controller::getName() : m_display_name; + } }; // GhostController #endif diff --git a/src/replay/replay_play.cpp b/src/replay/replay_play.cpp index 130dbad0a..923374af9 100644 --- a/src/replay/replay_play.cpp +++ b/src/replay/replay_play.cpp @@ -142,21 +142,23 @@ bool ReplayPlay::addReplayFile(const std::string& fn, bool custom_replay) { Log::warn("Replay", "Could not read ghost karts info!"); break; - } else { - rd.m_kart_list.push_back(std::string(s1)); + } - if (scanned == 2) { - // If username of kart is present, use it - rd.m_name_list.push_back(StringUtils::xmlDecode(std::string(display_name_encoded))); - if (rd.m_name_list.size() == 1) { - // First user is the game master and the "owner" of this replay file - rd.m_user_name = rd.m_name_list[0]; - } - } else { // scanned == 1 - // If username is not present, kart display name will default to kart name - // (see GhostController::getName) - rd.m_name_list.push_back(""); + rd.m_kart_list.push_back(std::string(s1)); + if (scanned == 2) + { + // If username of kart is present, use it + rd.m_name_list.push_back(StringUtils::xmlDecode(std::string(display_name_encoded))); + if (rd.m_name_list.size() == 1) + { + // First user is the game master and the "owner" of this replay file + rd.m_user_name = rd.m_name_list[0]; } + } else + { // scanned == 1 + // If username is not present, kart display name will default to kart name + // (see GhostController::getName) + rd.m_name_list.push_back(""); } } From f589bbea04732f1e2505db1df6646fef7a45070f Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Tue, 8 Aug 2017 20:23:22 -0400 Subject: [PATCH 5/6] Attempt to fix 2617 --- src/input/input_manager.cpp | 6 ++++-- src/input/input_manager.hpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/input/input_manager.cpp b/src/input/input_manager.cpp index f41aafab6..e604e6b82 100644 --- a/src/input/input_manager.cpp +++ b/src/input/input_manager.cpp @@ -488,11 +488,13 @@ void InputManager::inputSensing(Input::InputType type, int deviceID, // We have to save the direction in which the axis was moved. // This is done by storing it as a sign (and since button can // be zero, we add one before changing the sign). - int input_id = value>=0 ? 1+button : -(1+button); + int input_button_id = value>=0 ? 1+button : -(1+button); + std::tuple input_id(deviceID, input_button_id); + std::tuple input_id_inv(deviceID, -input_button_id); bool id_was_high = m_sensed_input_high_gamepad.find(input_id) != m_sensed_input_high_gamepad.end(); - bool inverse_id_was_high = m_sensed_input_high_gamepad.find(-input_id) + bool inverse_id_was_high = m_sensed_input_high_gamepad.find(input_id_inv) != m_sensed_input_high_gamepad.end(); bool id_was_zero = m_sensed_input_zero_gamepad.find(button) != m_sensed_input_zero_gamepad.end(); diff --git a/src/input/input_manager.hpp b/src/input/input_manager.hpp index 2ffff2ea4..8c2ed7ff0 100644 --- a/src/input/input_manager.hpp +++ b/src/input/input_manager.hpp @@ -53,7 +53,7 @@ public: private: DeviceManager *m_device_manager; - std::set m_sensed_input_high_gamepad; + std::set> m_sensed_input_high_gamepad; std::set m_sensed_input_high_kbd; std::set m_sensed_input_zero_gamepad; From 94bb657102cc7cb430916a35de67e60af54477ca Mon Sep 17 00:00:00 2001 From: "auria.mg" Date: Tue, 8 Aug 2017 20:44:31 -0400 Subject: [PATCH 6/6] Ignore input sensing coming from the wrong gamepad --- src/states_screens/options_screen_device.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/states_screens/options_screen_device.cpp b/src/states_screens/options_screen_device.cpp index caa3c43ea..8033226dc 100644 --- a/src/states_screens/options_screen_device.cpp +++ b/src/states_screens/options_screen_device.cpp @@ -434,6 +434,10 @@ void OptionsScreenDevice::gotSensedInput(const Input& sensed_input) // refresh display updateInputButtons(); } + else + { + return; + } } else if (sensed_input.m_type == Input::IT_NONE) {