Save and use usernames in replay files (#2754) (#2893)

* 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

* Move Controller::getName definition to avoid unnecessary #include

* Backwards compatibility: use kart name if username is not in replay

* Fix code style issues
This commit is contained in:
Geoffrey Mon 2017-08-03 19:51:42 -04:00 committed by auriamg
parent 623bb460c6
commit a73af6eb0d
10 changed files with 75 additions and 51 deletions

View File

@ -34,3 +34,8 @@ Controller::Controller(AbstractKart *kart)
m_kart = kart; m_kart = kart;
setControllerName("Controller"); setControllerName("Controller");
} // Controller } // Controller
core::stringw Controller::getName() const
{
return translations->fribidize(m_kart->getName());
}

View File

@ -100,17 +100,16 @@ public:
/** Only local players can get achievements. */ /** Only local players can get achievements. */
virtual bool canGetAchievements () const { return false; } virtual bool canGetAchievements () const { return false; }
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** This should only be called for End- and LocalPlayer-Controller. */ /** Display name of the controller.
virtual core::stringw getName() const * Defaults to kart name; overriden by controller classes
{ * (such as player controllers) to display username. */
assert(false); virtual core::stringw getName() const;
return core::stringw("");
} // getName
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
/** Returns the kart controlled by this controller. */ /** Returns the kart controlled by this controller. */
AbstractKart *getKart() const { return m_kart; } AbstractKart *getKart() const { return m_kart; }
}; // Controller }; // Controller
extern Translations* translations;
#endif #endif
/* EOF */ /* EOF */

View File

@ -21,9 +21,10 @@
#include "karts/controller/kart_control.hpp" #include "karts/controller/kart_control.hpp"
#include "modes/world.hpp" #include "modes/world.hpp"
GhostController::GhostController(AbstractKart *kart) GhostController::GhostController(AbstractKart *kart, core::stringw display_name)
: Controller(kart) : Controller(kart)
{ {
m_display_name = display_name;
} // GhostController } // GhostController
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -37,11 +37,14 @@ private:
/** The current world time. */ /** The current world time. */
float m_current_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. */ /** The list of the times at which the events of kart were reached. */
std::vector<float> m_all_times; std::vector<float> m_all_times;
public: public:
GhostController(AbstractKart *kart); GhostController(AbstractKart *kart, core::stringw display_name);
virtual ~GhostController() {}; virtual ~GhostController() {};
virtual void reset() OVERRIDE; virtual void reset() OVERRIDE;
virtual void update (float dt) OVERRIDE; virtual void update (float dt) OVERRIDE;
@ -73,6 +76,11 @@ public:
unsigned int getCurrentReplayIndex() const unsigned int getCurrentReplayIndex() const
{ return m_current_index; } { 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;
}
}; // GhostController }; // GhostController
#endif #endif

View File

@ -135,13 +135,31 @@ bool ReplayPlay::addReplayFile(const std::string& fn, bool custom_replay)
is_end.trim(); is_end.trim();
if (is_end == "kart_list_end") break; if (is_end == "kart_list_end") break;
char s1[1024]; char s1[1024];
char display_name_encoded[1024];
if (sscanf(s,"kart: %s", s1) != 1) int scanned = sscanf(s,"kart: %s %[^\n]", s1, display_name_encoded);
if (scanned < 1)
{ {
Log::warn("Replay", "Could not read ghost karts info!"); Log::warn("Replay", "Could not read ghost karts info!");
break; break;
} }
rd.m_kart_list.push_back(std::string(s1)); 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("");
}
} }
int reverse = 0; int reverse = 0;
@ -249,11 +267,12 @@ void ReplayPlay::readKartData(FILE *fd, char *next_line)
{ {
char s[1024]; char s[1024];
const unsigned int kart_num = m_ghost_karts.size(); const unsigned int kart_num = m_ghost_karts.size();
m_ghost_karts.push_back(new GhostKart(m_replay_file_list ReplayData &rd = m_replay_file_list[m_current_replay_file];
[m_current_replay_file].m_kart_list.at(kart_num), m_ghost_karts.push_back(new GhostKart(rd.m_kart_list.at(kart_num),
kart_num, kart_num + 1)); kart_num, kart_num + 1));
m_ghost_karts[kart_num].init(RaceManager::KT_GHOST); 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); getGhostKart(kart_num)->setController(controller);
unsigned int size; unsigned int size;

View File

@ -45,20 +45,23 @@ public:
SO_KART_NUM, SO_KART_NUM,
SO_DIFF, SO_DIFF,
SO_LAPS, SO_LAPS,
SO_TIME SO_TIME,
SO_USER
}; };
class ReplayData class ReplayData
{ {
public: public:
std::string m_filename; std::string m_filename;
std::string m_track_name; std::string m_track_name;
std::vector<std::string> m_kart_list; core::stringw m_user_name;
bool m_reverse; std::vector<std::string> m_kart_list;
bool m_custom_replay_file; std::vector<core::stringw> m_name_list;
unsigned int m_difficulty; bool m_reverse;
unsigned int m_laps; bool m_custom_replay_file;
float m_min_time; unsigned int m_difficulty;
unsigned int m_laps;
float m_min_time;
bool operator < (const ReplayData& r) const bool operator < (const ReplayData& r) const
{ {
@ -82,6 +85,9 @@ public:
case SO_TIME: case SO_TIME:
return m_min_time < r.m_min_time; return m_min_time < r.m_min_time;
break; break;
case SO_USER:
return m_user_name < r.m_user_name;
break;
} // switch } // switch
return true; return true;
} // operator < } // operator <

View File

@ -31,6 +31,7 @@
#include <algorithm> #include <algorithm>
#include <stdio.h> #include <stdio.h>
#include <string> #include <string>
#include <karts/controller/player_controller.hpp>
ReplayRecorder *ReplayRecorder::m_replay_recorder = NULL; ReplayRecorder *ReplayRecorder::m_replay_recorder = NULL;
@ -222,9 +223,12 @@ void ReplayRecorder::save()
fprintf(fd, "version: %d\n", getReplayVersion()); fprintf(fd, "version: %d\n", getReplayVersion());
for (unsigned int real_karts = 0; real_karts < num_karts; real_karts++) for (unsigned int real_karts = 0; real_karts < num_karts; real_karts++)
{ {
if (world->getKart(real_karts)->isGhostKart()) continue; const AbstractKart *kart = world->getKart(real_karts);
fprintf(fd, "kart: %s\n", if (kart->isGhostKart()) continue;
world->getKart(real_karts)->getIdent().c_str());
// 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"); fprintf(fd, "kart_list_end\n");

View File

@ -80,6 +80,7 @@ void GhostReplaySelection::beforeAddingWidget()
m_replay_list_widget->addColumn( _("Difficulty"), 1); m_replay_list_widget->addColumn( _("Difficulty"), 1);
m_replay_list_widget->addColumn( _("Laps"), 1); m_replay_list_widget->addColumn( _("Laps"), 1);
m_replay_list_widget->addColumn( _("Finish Time"), 1); m_replay_list_widget->addColumn( _("Finish Time"), 1);
m_replay_list_widget->addColumn( _("User"), 1);
} // beforeAddingWidget } // beforeAddingWidget
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -122,6 +123,8 @@ void GhostReplaySelection::loadList()
(StringUtils::toWString(rd.m_laps), -1, 1, true)); (StringUtils::toWString(rd.m_laps), -1, 1, true));
row.push_back(GUIEngine::ListWidget::ListCell row.push_back(GUIEngine::ListWidget::ListCell
(StringUtils::toWString(rd.m_min_time) + L"s", -1, 1, true)); (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); m_replay_list_widget->addItem(StringUtils::toString(i), row);
} }
} // loadList } // loadList
@ -213,6 +216,9 @@ void GhostReplaySelection::onColumnClicked(int column_id)
case 5: case 5:
ReplayPlay::setSortOrder(ReplayPlay::SO_TIME); ReplayPlay::setSortOrder(ReplayPlay::SO_TIME);
break; break;
case 6:
ReplayPlay::setSortOrder(ReplayPlay::SO_USER);
break;
default: default:
assert(0); assert(0);
break; break;

View File

@ -486,7 +486,7 @@ void RaceResultGUI::backToLobby()
// Save a pointer to the current row_info entry // Save a pointer to the current row_info entry
RowInfo *ri = &(m_all_row_infos[position - first_position]); RowInfo *ri = &(m_all_row_infos[position - first_position]);
ri->m_is_player_kart = kart->getController()->isLocalPlayerController(); ri->m_is_player_kart = kart->getController()->isLocalPlayerController();
ri->m_kart_name = getKartDisplayName(kart); ri->m_kart_name = kart->getController()->getName();
video::ITexture *icon = video::ITexture *icon =
kart->getKartProperties()->getIconMaterial()->getTexture(); kart->getKartProperties()->getIconMaterial()->getTexture();
@ -855,7 +855,7 @@ void RaceResultGUI::backToLobby()
ri->m_kart_icon = ri->m_kart_icon =
kart->getKartProperties()->getIconMaterial()->getTexture(); kart->getKartProperties()->getIconMaterial()->getTexture();
ri->m_is_player_kart = kart->getController()->isLocalPlayerController(); 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 // In FTL karts do have a time, which is shown even when the kart
// is eliminated // is eliminated
@ -907,29 +907,6 @@ void RaceResultGUI::backToLobby()
#endif #endif
} // determineGPLayout } // 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<const EndController*>(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<const PlayerController*>(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. /** Displays the race results for a single kart.
* \param n Index of the kart to be displayed. * \param n Index of the kart to be displayed.

View File

@ -194,7 +194,6 @@ private:
void displayPostRaceInfo(); void displayPostRaceInfo();
void displaySoccerResults(); void displaySoccerResults();
void displayScreenShots(); void displayScreenShots();
irr::core::stringw getKartDisplayName(const AbstractKart *kart) const;
int getFontHeight () const; int getFontHeight () const;