Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add overrides for name and color from rating lookup #4078

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 21 additions & 13 deletions doc/hosting/AutoratingServer.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,36 @@ To activate autorating, you must set `autorating` to `true` in the host's config

When autorating is not activated or the response is incorrect or empty, the default local rating is used.

Autorating is propagated to clients (when hosting) regardless of them having their own requesting enabled.
In case they do have it enabled, their version will override host-provided one.

## Autorating request

The request is set to the url defined in the configuration file, with the following headers:
HTTP(s) request is sent to the url defined in the configuration file, with the following headers:

* `WZ-Player-Hash` the hash of the player's public key.
* `WZ-Player-Key` the player's public key.
* `WZ-Locale` the locale of the player to send back translated information.
* `WZ-Player-Hash` sha256 hash of the player's public key.
* `WZ-Player-Key` player's hex encoded public key.
* `WZ-Locale` locale of the requesting client (to send back translated information).
* `WZ-Version` game version, as in bottom right corner in menus.

See `PlayerKeys.md` for more information about player keys.

## Autorating response

The server must return a response code 200 and some data json format. Otherwise the response is ignored. The json data are a json object with the following properties:
The server must return a response code 200 and some data in json format. Otherwise the response is ignored. The json data must be a single json object with the following properties:

* `autohoster` (boolean): if the player is a dedicated hoster. It will have an hoster icon instead of a medal and no stars.
* `dummy` (boolean): if there aren't enough data to rate the player. It will show a pacifier medal and no stars.
* `star` (array of three integers): the code of each star, top to bottom. `0`: none, `1`: gold, `2`: silver, `3`: bronze.
* `medal` (integer): the code of the medal to display. `0`: none, `1`: gold, `2`: silver, `3`: bronze.
* `level` (integer): 0-10, the player's level to show beside the medal (even for dummy and autohoster). They are the same as unit experience level in-game. `0` for none, up to 10 for each experience level.
* `elo` (string): the text to display under the player's name.
* `autohoster` (boolean): if the player is a dedicated hoster. It will have an hoster icon instead of a medal and no stars.
* `level` (integer): 0-8, the player's level to show beside the medal (even for dummy and autohoster). They are the same as unit experience level in-game. `0` for none, up to 8 for each experience level.
* `details` (string): notes to display in the player's tooltip.
* `name` (string): provides an alternative name of the player in lobby (optional) (ignored if empty).
* `nameTextColorOverride` (array of three integers): overrides alt name color in lobby (rgb 0-255) ([255,255,255] will result in default coloring) (optional).
* `eloTextColorOverride` (array of three integers): overrides elo text color in lobby (rgb 0-255) (optional).
* `elo` (string): the text to display under the player's name.
* `name` (string): provides leaderboard name of the player in lobby (optional) (ignored if empty) (will be replacing player's name).
* `tag` (string): provides an tag of the player in lobby (optional) (ignored if empty) (will be displayed in top right corner).
* `nameTextColorOverride` (array of three integers): overrides name color in lobby (rgb 0-255) ([255,255,255] will result in default coloring) (optional).
* `eloTextColorOverride` (array of three integers): overrides elo text color in lobby (rgb 0-255) ([255,255,255] will result in default coloring) (optional).
* `tagTextColorOverride` (array of three integers): overrides tag text color in lobby (rgb 0-255) ([255,255,255] will result in default coloring) (optional).

### Response sample

Expand All @@ -43,8 +49,10 @@ The server must return a response code 200 and some data json format. Otherwise
"autohoster": false,
"details": "Played 264 games, win rate: 53%",
"name": "Flex seal",
"nameTextColorOverride": [51,255,51],
"eloTextColorOverride": [255,255,255]
"tag": "Admin",
"nameTextColorOverride": [255,255,255],
"tagTextColorOverride": [51,255,51],
"eloTextColorOverride": [51,255,51]
}
```

Expand Down
120 changes: 75 additions & 45 deletions src/multiint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,8 +232,10 @@ struct DisplayPlayerCache {
std::string fullMainText; // the “full” main text (used for storing the full player name when displaying a player)
WzText wzMainText; // the main text

std::string fullAltNameText;
WzText wzAltNameText;
std::string fullTagText;
WzText wzTagText;
std::string fullNameText;
WzText wzNameText;

WzText wzSubText; // the sub text (used for players)
WzText wzEloText; // the elo text (used for players)
Expand Down Expand Up @@ -4177,42 +4179,60 @@ class WzPlayerRow : public WIDGET
std::string autoratingTooltipText;
if (stats.autorating.valid)
{
if (!stats.autorating.altName.empty())
if (!stats.autorating.name.empty())
{
if (!autoratingTooltipText.empty())
{
autoratingTooltipText += "\n";
}
std::string altnameStr = stats.autorating.altName;
if (altnameStr.size() > 128)
std::string nameStr = stats.autorating.name;
if (nameStr.size() > 128)
{
altnameStr = altnameStr.substr(0, 128);
nameStr = nameStr.substr(0, 128);
}
size_t maxLinePos = nthOccurrenceOfChar(altnameStr, '\n', 1);
size_t maxLinePos = nthOccurrenceOfChar(nameStr, '\n', 1);
if (maxLinePos != std::string::npos)
{
altnameStr = altnameStr.substr(0, maxLinePos);
nameStr = nameStr.substr(0, maxLinePos);
}
autoratingTooltipText += std::string(_("Alt Name:")) + " " + altnameStr;
autoratingTooltipText += std::string(_("Leaderboard name:")) + " " + nameStr + "\n";
autoratingTooltipText += std::string(_("Current name:")) + " " + NetPlay.players[playerIdx].name;
}
if (!stats.autorating.details.empty()
&& stats.autoratingFrom == RATING_SOURCE_LOCAL) // do not display host-provided details (for now)
if (!stats.autorating.tag.empty())
{
if (!autoratingTooltipText.empty())
{
autoratingTooltipText += "\n";
}
std::string detailsstr = stats.autorating.details;
if (detailsstr.size() > 512)
std::string tagStr = stats.autorating.tag;
if (tagStr.size() > 128)
{
detailsstr = detailsstr.substr(0, 512);
tagStr = tagStr.substr(0, 128);
}
size_t maxLinePos = nthOccurrenceOfChar(detailsstr, '\n', 10);
size_t maxLinePos = nthOccurrenceOfChar(tagStr, '\n', 1);
if (maxLinePos != std::string::npos)
{
detailsstr = detailsstr.substr(0, maxLinePos);
tagStr = tagStr.substr(0, maxLinePos);
}
autoratingTooltipText += std::string(_("Player rating:")) + "\n" + detailsstr;
autoratingTooltipText += std::string(_("Tag:")) + " " + tagStr;
}
if (!stats.autorating.details.empty())
{
if (!autoratingTooltipText.empty())
{
autoratingTooltipText += "\n";
}
std::string detailsStr = stats.autorating.details;
if (detailsStr.size() > 512)
{
detailsStr = detailsStr.substr(0, 512);
}
size_t maxLinePos = nthOccurrenceOfChar(detailsStr, '\n', 10);
if (maxLinePos != std::string::npos)
{
detailsStr = detailsStr.substr(0, maxLinePos);
}
autoratingTooltipText += std::string(_("Player rating:")) + "\n" + detailsStr;
}
}
if (!autoratingTooltipText.empty())
Expand Down Expand Up @@ -7987,26 +8007,26 @@ static bool isKnownPlayer(std::map<std::string, EcKey::Key> const &knownPlayers,
return i != knownPlayers.end() && key.toBytes(EcKey::Public) == i->second;
}

static void displayAltNameBox(int x, int y, WIDGET *psWidget, DisplayPlayerCache& cache, const PLAYERSTATS::Autorating& ar, bool isHighlight)
static void displayPlayerTagBox(int x, int y, WIDGET *psWidget, DisplayPlayerCache& cache, const PLAYERSTATS::Autorating& ar, bool isHighlight)
{
int altNameBoxWidth = cache.wzAltNameText.width() + 4;
int altNameBoxHeight = cache.wzAltNameText.lineSize() + 2;
int altNameBoxX0 = (x + psWidget->width()) - altNameBoxWidth;
PIELIGHT altNameBoxColor = WZCOL_MENU_BORDER;
altNameBoxColor.byte.a = static_cast<uint8_t>(static_cast<float>(altNameBoxColor.byte.a) * (isHighlight ? 0.3f : 0.75f));
pie_UniTransBoxFill(altNameBoxX0, y, altNameBoxX0 + altNameBoxWidth, y + altNameBoxHeight, altNameBoxColor);
int tagBoxWidth = cache.wzTagText.width() + 4;
int tagBoxHeight = cache.wzTagText.lineSize() + 2;
int tagBoxX0 = (x + psWidget->width()) - tagBoxWidth;
PIELIGHT tagBoxColor = WZCOL_MENU_BORDER;
tagBoxColor.byte.a = static_cast<uint8_t>(static_cast<float>(tagBoxColor.byte.a) * (isHighlight ? 0.3f : 0.75f));
pie_UniTransBoxFill(tagBoxX0, y, tagBoxX0 + tagBoxWidth, y + tagBoxHeight, tagBoxColor);

int altNameTextY0 = y + (altNameBoxHeight - cache.wzAltNameText.lineSize()) / 2 - cache.wzAltNameText.aboveBase();
PIELIGHT altNameTextColor = WZCOL_TEXT_MEDIUM;
if (ar.altNameTextColorOverride[0] != 255 || ar.altNameTextColorOverride[1] != 255 || ar.altNameTextColorOverride[2] != 255)
int tagTextY0 = y + (tagBoxHeight - cache.wzTagText.lineSize()) / 2 - cache.wzTagText.aboveBase();
PIELIGHT tagTextColor = WZCOL_TEXT_MEDIUM;
if (ar.tagTextColorOverride[0] != 255 || ar.tagTextColorOverride[1] != 255 || ar.tagTextColorOverride[2] != 255)
{
altNameTextColor = pal_Colour(ar.altNameTextColorOverride[0], ar.altNameTextColorOverride[1], ar.altNameTextColorOverride[2]);
tagTextColor = pal_Colour(ar.tagTextColorOverride[0], ar.tagTextColorOverride[1], ar.tagTextColorOverride[2]);
}
if (isHighlight)
{
altNameTextColor.byte.a = static_cast<uint8_t>(static_cast<float>(altNameTextColor.byte.a) * 0.3f);
tagTextColor.byte.a = static_cast<uint8_t>(static_cast<float>(tagTextColor.byte.a) * 0.3f);
}
cache.wzAltNameText.render(altNameBoxX0 + 2, altNameTextY0, altNameTextColor);
cache.wzTagText.render(tagBoxX0 + 2, tagTextY0, tagTextColor);
}

// ////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -8041,11 +8061,21 @@ void displayPlayer(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset)
auto ar = stat.autorating;

std::string name = NetPlay.players[j].name;
// if you hover it shows the actual over the wire name provided by the player itself (can be a lie)
// it is also present in tooltip
if (ar.valid && !isHighlight && !ar.name.empty())
{
name = ar.name;
}

std::map<std::string, EcKey::Key> serverPlayers; // TODO Fill this with players known to the server (needs implementing on the server, too). Currently useless.

PIELIGHT colour;
if (ingame.PingTimes[j] >= PING_LIMIT)
if (ar.valid && (ar.nameTextColorOverride[0] != 255 || ar.nameTextColorOverride[1] != 255 || ar.nameTextColorOverride[2] != 255))
{
colour = pal_Colour(ar.nameTextColorOverride[0], ar.nameTextColorOverride[1], ar.nameTextColorOverride[2]);
}
else if (ingame.PingTimes[j] >= PING_LIMIT)
{
colour = WZCOL_FORM_PLAYER_NOPING;
}
Expand Down Expand Up @@ -8119,28 +8149,28 @@ void displayPlayer(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset)
ar.elo.clear();
}

if (cache.fullAltNameText != ar.altName)
if (cache.fullTagText != ar.tag)
{
std::string altName = ar.altName;
int maxAltNameWidth = static_cast<int>(static_cast<float>(psWidget->width() - nameX) * 0.65f);
std::string tagStr = ar.tag;
int maxTagStrWidth = static_cast<int>(static_cast<float>(psWidget->width() - nameX) * 0.65f);
iV_fonts fontID = font_small;
cache.wzAltNameText.setText(WzString::fromUtf8(altName), fontID);
cache.fullAltNameText = altName;
if (cache.wzAltNameText.width() > maxAltNameWidth)
cache.wzTagText.setText(WzString::fromUtf8(tagStr), fontID);
cache.fullTagText = tagStr;
if (cache.wzTagText.width() > maxTagStrWidth)
{
while (!altName.empty() && ((int)iV_GetTextWidth(altName.c_str(), cache.wzAltNameText.getFontID()) + iV_GetEllipsisWidth(cache.wzAltNameText.getFontID())) > maxAltNameWidth)
while (!tagStr.empty() && ((int)iV_GetTextWidth(tagStr.c_str(), cache.wzTagText.getFontID()) + iV_GetEllipsisWidth(cache.wzTagText.getFontID())) > maxTagStrWidth)
{
altName.resize(altName.size() - 1); // Clip alt name.
tagStr.resize(tagStr.size() - 1); // Clip alt name.
}
altName += "\u2026";
cache.wzAltNameText.setText(WzString::fromUtf8(altName), fontID);
tagStr += "\u2026";
cache.wzTagText.setText(WzString::fromUtf8(tagStr), fontID);
}
}

if (!ar.altName.empty() && isHighlight)
if (!ar.tag.empty() && isHighlight)
{
// display first, behind everything
displayAltNameBox(x, y, psWidget, cache, ar, isHighlight);
displayPlayerTagBox(x, y, psWidget, cache, ar, isHighlight);
}

int H = 5;
Expand Down Expand Up @@ -8197,10 +8227,10 @@ void displayPlayer(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset)
cache.wzEloText.render(x + nameX, y + 28 + H*!subText.isEmpty(), eloColour);
}

if (!ar.altName.empty() && !isHighlight)
if (!ar.tag.empty() && !isHighlight)
{
// display last, over top of everything
displayAltNameBox(x, y, psWidget, cache, ar, isHighlight);
displayPlayerTagBox(x, y, psWidget, cache, ar, isHighlight);
}
}
else // AI
Expand Down
26 changes: 20 additions & 6 deletions src/multistat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "multistat.h"
#include "urlrequest.h"
#include "stdinreader.h"
#include "version.h"

#include <utility>
#include <memory>
Expand Down Expand Up @@ -71,8 +72,10 @@ static void NETauto(PLAYERSTATS::Autorating &ar)
NETauto(ar.elo);
NETauto(ar.autohoster);
NETauto(ar.details);
NETauto(ar.altName);
NETauto(ar.altNameTextColorOverride);
NETauto(ar.tag);
NETauto(ar.name);
NETauto(ar.tagTextColorOverride);
NETauto(ar.nameTextColorOverride);
NETauto(ar.eloTextColorOverride);
}
}
Expand All @@ -91,13 +94,23 @@ PLAYERSTATS::Autorating::Autorating(nlohmann::json const &json)
details = json["details"].get<std::string>();
if (json.contains("name"))
{
altName = json["name"].get<std::string>();
name = json["name"].get<std::string>();
}
if (json.contains("tag"))
{
tag = json["tag"].get<std::string>();
}
if (json.contains("nameTextColorOverride"))
{
altNameTextColorOverride[0] = json["nameTextColorOverride"][0].get<uint8_t>();
altNameTextColorOverride[1] = json["nameTextColorOverride"][1].get<uint8_t>();
altNameTextColorOverride[2] = json["nameTextColorOverride"][2].get<uint8_t>();
nameTextColorOverride[0] = json["nameTextColorOverride"][0].get<uint8_t>();
nameTextColorOverride[1] = json["nameTextColorOverride"][1].get<uint8_t>();
nameTextColorOverride[2] = json["nameTextColorOverride"][2].get<uint8_t>();
}
if (json.contains("tagTextColorOverride"))
{
tagTextColorOverride[0] = json["tagTextColorOverride"][0].get<uint8_t>();
tagTextColorOverride[1] = json["tagTextColorOverride"][1].get<uint8_t>();
tagTextColorOverride[2] = json["tagTextColorOverride"][2].get<uint8_t>();
}
if (json.contains("eloTextColorOverride"))
{
Expand Down Expand Up @@ -142,6 +155,7 @@ void lookupRatingAsync(uint32_t playerIndex)
req.setRequestHeader("WZ-Player-Hash", hash);
req.setRequestHeader("WZ-Player-Key", key);
req.setRequestHeader("WZ-Locale", getLanguage());
req.setRequestHeader("WZ-Version", version_getVersionString());
debug(LOG_INFO, "Requesting \"%s\" for player %d (%.32s) (%s)", req.url.c_str(), playerIndex, NetPlay.players[playerIndex].name, hash.c_str());
req.onSuccess = [playerIndex, hash](std::string const &url, HTTPResponseDetails const &response, std::shared_ptr<MemoryStruct> const &data) {
long httpStatusCode = response.httpStatusCode();
Expand Down
6 changes: 4 additions & 2 deletions src/multistat.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,13 @@ struct PLAYERSTATS
uint8_t star[3] = {0, 0, 0};
uint8_t medal = 0;
uint8_t level = 0;
uint8_t altNameTextColorOverride[3] = {255, 255, 255}; // rgb
uint8_t nameTextColorOverride[3] = {255, 255, 255}; // rgb
uint8_t tagTextColorOverride[3] = {255, 255, 255}; // rgb
uint8_t eloTextColorOverride[3] = {255, 255, 255}; // rgb
std::string elo;
std::string details;
std::string altName;
std::string tag;
std::string name;
};
Autorating autorating;
RATING_SOURCE autoratingFrom = RATING_SOURCE_HOST;
Expand Down
Loading