LCEMP/Minecraft.Client/Windows64/Leaderboards/WindowsLeaderboardManager.cpp

592 lines
18 KiB
C++

#include "stdafx.h"
#include "WindowsLeaderboardManager.h"
#include "../../StatsCounter.h"
#include "../../../Minecraft.World/Stats.h"
#include "../../../Minecraft.World/net.minecraft.world.item.h"
#include "../../../Minecraft.World/net.minecraft.world.level.tile.h"
namespace
{
static bool HasAnyTrackedLeaderboardProgress(StatsCounter *statsCounter)
{
if (statsCounter == NULL)
{
return false;
}
for (int difficulty = 0; difficulty < 4; ++difficulty)
{
if (statsCounter->getValue(Stats::walkOneM, difficulty) > 0
|| statsCounter->getValue(Stats::fallOneM, difficulty) > 0
|| statsCounter->getValue(Stats::minecartOneM, difficulty) > 0
|| statsCounter->getValue(Stats::boatOneM, difficulty) > 0
|| statsCounter->getValue(Stats::blocksMined[Tile::dirt->id], difficulty) > 0
|| statsCounter->getValue(Stats::blocksMined[Tile::stoneBrick->id], difficulty) > 0
|| statsCounter->getValue(Stats::blocksMined[Tile::sand->id], difficulty) > 0
|| statsCounter->getValue(Stats::blocksMined[Tile::rock->id], difficulty) > 0
|| statsCounter->getValue(Stats::blocksMined[Tile::gravel->id], difficulty) > 0
|| statsCounter->getValue(Stats::blocksMined[Tile::clay->id], difficulty) > 0
|| statsCounter->getValue(Stats::blocksMined[Tile::obsidian->id], difficulty) > 0
|| statsCounter->getValue(Stats::itemsCollected[Item::egg->id], difficulty) > 0
|| statsCounter->getValue(Stats::blocksMined[Tile::crops_Id], difficulty) > 0
|| statsCounter->getValue(Stats::blocksMined[Tile::mushroom1_Id], difficulty) > 0
|| statsCounter->getValue(Stats::blocksMined[Tile::reeds_Id], difficulty) > 0
|| statsCounter->getValue(Stats::cowsMilked, difficulty) > 0
|| statsCounter->getValue(Stats::itemsCollected[Tile::pumpkin->id], difficulty) > 0
|| statsCounter->getValue(Stats::killsZombie, difficulty) > 0
|| statsCounter->getValue(Stats::killsSkeleton, difficulty) > 0
|| statsCounter->getValue(Stats::killsCreeper, difficulty) > 0
|| statsCounter->getValue(Stats::killsSpider, difficulty) > 0
|| statsCounter->getValue(Stats::killsSpiderJockey, difficulty) > 0
|| statsCounter->getValue(Stats::killsZombiePigman, difficulty) > 0
|| statsCounter->getValue(Stats::killsNetherZombiePigman, difficulty) > 0
|| statsCounter->getValue(Stats::killsSlime, difficulty) > 0)
{
return true;
}
}
return false;
}
static std::wstring TrimWhitespace(const std::wstring &text)
{
size_t start = 0;
while (start < text.length() && iswspace(text[start]))
{
++start;
}
size_t end = text.length();
while (end > start && iswspace(text[end - 1]))
{
--end;
}
return text.substr(start, end - start);
}
static bool IsBooleanText(const std::wstring &text)
{
return (_wcsicmp(text.c_str(), L"false") == 0) || (_wcsicmp(text.c_str(), L"true") == 0);
}
static std::wstring ToWideName(const char *text)
{
if (text == NULL || text[0] == 0)
{
return L"";
}
wchar_t wideName[XUSER_NAME_SIZE + 1];
ZeroMemory(wideName, sizeof(wideName));
MultiByteToWideChar(CP_ACP, 0, text, -1, wideName, XUSER_NAME_SIZE + 1);
wideName[XUSER_NAME_SIZE] = 0;
return wideName;
}
static int GetLocalPad()
{
int iPad = ProfileManager.GetPrimaryPad();
if (iPad < 0 || iPad >= XUSER_MAX_COUNT)
{
iPad = ProfileManager.GetLockedProfile();
}
if (iPad < 0 || iPad >= XUSER_MAX_COUNT)
{
iPad = 0;
}
return iPad;
}
static PlayerUID GetLocalPlayerUID(int iPad)
{
PlayerUID uid = INVALID_XUID;
ProfileManager.GetXUID(iPad, &uid, true);
if (uid == INVALID_XUID)
{
ProfileManager.GetXUID(iPad, &uid, false);
}
return uid;
}
static std::wstring GetLocalDisplayName(int iPad)
{
std::wstring displayName = TrimWhitespace(ProfileManager.GetDisplayName(iPad));
if (displayName.empty() || IsBooleanText(displayName))
{
displayName = TrimWhitespace(ToWideName(ProfileManager.GetGamertag(iPad)));
}
if (displayName.empty() || IsBooleanText(displayName))
{
displayName = L"Player";
}
if (displayName.length() > XUSER_NAME_SIZE)
{
displayName.resize(XUSER_NAME_SIZE);
}
return displayName;
}
static StatsCounter *GetLocalStatsCounter(int iPad)
{
Minecraft *minecraft = Minecraft::GetInstance();
if (minecraft == NULL || iPad < 0 || iPad >= XUSER_MAX_COUNT)
{
return NULL;
}
StatsCounter *statsCounter = minecraft->stats[iPad];
if (statsCounter == NULL)
{
return NULL;
}
if (!app.GetGameStarted())
{
if (!HasAnyTrackedLeaderboardProgress(statsCounter))
{
void *profileData = ProfileManager.GetGameDefinedProfileData(iPad);
if (profileData != NULL)
{
statsCounter->clear();
statsCounter->parse(profileData);
}
}
}
return statsCounter;
}
static unsigned int GetStatValue(StatsCounter *statsCounter, Stat *stat, int difficulty)
{
if (statsCounter == NULL || stat == NULL || difficulty < 0 || difficulty > 3)
{
return 0;
}
return statsCounter->getValue(stat, (unsigned int)difficulty);
}
static unsigned int GetBlockValue(StatsCounter *statsCounter, Tile *tile, int difficulty)
{
if (tile == NULL)
{
return 0;
}
return GetStatValue(statsCounter, Stats::blocksMined[tile->id], difficulty);
}
static unsigned int GetCollectedItemValue(StatsCounter *statsCounter, Item *item, int difficulty)
{
if (item == NULL)
{
return 0;
}
return GetStatValue(statsCounter, Stats::itemsCollected[item->id], difficulty);
}
static unsigned int GetZombiePigmanKills(StatsCounter *statsCounter, int difficulty)
{
unsigned long long total = (unsigned long long)GetStatValue(statsCounter, Stats::killsZombiePigman, difficulty)
+ (unsigned long long)GetStatValue(statsCounter, Stats::killsNetherZombiePigman, difficulty);
return (total > 0xFFFFFFFFULL) ? 0xFFFFFFFFU : (unsigned int)total;
}
static unsigned short GetStatsSize(LeaderboardManager::EStatsType type)
{
switch (type)
{
case LeaderboardManager::eStatsType_Travelling:
return 4;
case LeaderboardManager::eStatsType_Mining:
return 7;
case LeaderboardManager::eStatsType_Farming:
return 6;
case LeaderboardManager::eStatsType_Kills:
return 7;
default:
return 0;
}
}
static void SetColumn(LeaderboardManager::ReadScore &score, unsigned int columnIndex, unsigned long value, unsigned long long &total)
{
if (columnIndex >= LeaderboardManager::ReadScore::STATSDATA_MAX)
{
return;
}
score.m_statsData[columnIndex] = value;
total += value;
}
static unsigned long ClampToUnsignedLong(unsigned long long value)
{
return (value > 0xFFFFFFFFULL) ? 0xFFFFFFFFUL : (unsigned long)value;
}
static const char *GetStatsTypeName(LeaderboardManager::EStatsType type)
{
switch (type)
{
case LeaderboardManager::eStatsType_Travelling:
return "Travelling";
case LeaderboardManager::eStatsType_Mining:
return "Mining";
case LeaderboardManager::eStatsType_Farming:
return "Farming";
case LeaderboardManager::eStatsType_Kills:
return "Kills";
default:
return "Unknown";
}
}
static void SetTravellingColumns(LeaderboardManager::ReadScore &score, StatsCounter *statsCounter, int difficulty, unsigned long long &total)
{
SetColumn(score, 0, GetStatValue(statsCounter, Stats::walkOneM, difficulty), total);
SetColumn(score, 1, GetStatValue(statsCounter, Stats::fallOneM, difficulty), total);
SetColumn(score, 2, GetStatValue(statsCounter, Stats::minecartOneM, difficulty), total);
SetColumn(score, 3, GetStatValue(statsCounter, Stats::boatOneM, difficulty), total);
}
static unsigned long long GetTravellingTotalForDifficulty(StatsCounter *statsCounter, int difficulty)
{
if (difficulty < 0 || difficulty > 3)
{
return 0;
}
unsigned long long total = 0;
total += GetStatValue(statsCounter, Stats::walkOneM, difficulty);
total += GetStatValue(statsCounter, Stats::fallOneM, difficulty);
total += GetStatValue(statsCounter, Stats::minecartOneM, difficulty);
total += GetStatValue(statsCounter, Stats::boatOneM, difficulty);
return total;
}
static void SetMiningColumns(LeaderboardManager::ReadScore &score, StatsCounter *statsCounter, int difficulty, unsigned long long &total)
{
SetColumn(score, 0, GetBlockValue(statsCounter, Tile::dirt, difficulty), total);
SetColumn(score, 1, GetBlockValue(statsCounter, Tile::stoneBrick, difficulty), total);
SetColumn(score, 2, GetBlockValue(statsCounter, Tile::sand, difficulty), total);
SetColumn(score, 3, GetBlockValue(statsCounter, Tile::rock, difficulty), total);
SetColumn(score, 4, GetBlockValue(statsCounter, Tile::gravel, difficulty), total);
SetColumn(score, 5, GetBlockValue(statsCounter, Tile::clay, difficulty), total);
SetColumn(score, 6, GetBlockValue(statsCounter, Tile::obsidian, difficulty), total);
}
static unsigned long long GetMiningTotalForDifficulty(StatsCounter *statsCounter, int difficulty)
{
if (difficulty < 0 || difficulty > 3)
{
return 0;
}
unsigned long long total = 0;
total += GetBlockValue(statsCounter, Tile::dirt, difficulty);
total += GetBlockValue(statsCounter, Tile::stoneBrick, difficulty);
total += GetBlockValue(statsCounter, Tile::sand, difficulty);
total += GetBlockValue(statsCounter, Tile::rock, difficulty);
total += GetBlockValue(statsCounter, Tile::gravel, difficulty);
total += GetBlockValue(statsCounter, Tile::clay, difficulty);
total += GetBlockValue(statsCounter, Tile::obsidian, difficulty);
return total;
}
static void SetFarmingColumns(LeaderboardManager::ReadScore &score, StatsCounter *statsCounter, int difficulty, unsigned long long &total)
{
SetColumn(score, 0, GetCollectedItemValue(statsCounter, Item::egg, difficulty), total);
SetColumn(score, 1, GetStatValue(statsCounter, Stats::blocksMined[Tile::crops_Id], difficulty), total);
SetColumn(score, 2, GetStatValue(statsCounter, Stats::blocksMined[Tile::mushroom1_Id], difficulty), total);
SetColumn(score, 3, GetStatValue(statsCounter, Stats::blocksMined[Tile::reeds_Id], difficulty), total);
SetColumn(score, 4, GetStatValue(statsCounter, Stats::cowsMilked, difficulty), total);
SetColumn(score, 5, GetStatValue(statsCounter, Stats::itemsCollected[Tile::pumpkin->id], difficulty), total);
}
static unsigned long long GetFarmingTotalForDifficulty(StatsCounter *statsCounter, int difficulty)
{
if (difficulty < 0 || difficulty > 3)
{
return 0;
}
unsigned long long total = 0;
total += GetCollectedItemValue(statsCounter, Item::egg, difficulty);
total += GetStatValue(statsCounter, Stats::blocksMined[Tile::crops_Id], difficulty);
total += GetStatValue(statsCounter, Stats::blocksMined[Tile::mushroom1_Id], difficulty);
total += GetStatValue(statsCounter, Stats::blocksMined[Tile::reeds_Id], difficulty);
total += GetStatValue(statsCounter, Stats::cowsMilked, difficulty);
total += GetStatValue(statsCounter, Stats::itemsCollected[Tile::pumpkin->id], difficulty);
return total;
}
static void SetKillsColumns(LeaderboardManager::ReadScore &score, StatsCounter *statsCounter, int difficulty, unsigned long long &total)
{
SetColumn(score, 0, GetStatValue(statsCounter, Stats::killsZombie, difficulty), total);
SetColumn(score, 1, GetStatValue(statsCounter, Stats::killsSkeleton, difficulty), total);
SetColumn(score, 2, GetStatValue(statsCounter, Stats::killsCreeper, difficulty), total);
SetColumn(score, 3, GetStatValue(statsCounter, Stats::killsSpider, difficulty), total);
SetColumn(score, 4, GetStatValue(statsCounter, Stats::killsSpiderJockey, difficulty), total);
SetColumn(score, 5, GetZombiePigmanKills(statsCounter, difficulty), total);
SetColumn(score, 6, GetStatValue(statsCounter, Stats::killsSlime, difficulty), total);
}
static unsigned long long GetKillsTotalForDifficulty(StatsCounter *statsCounter, int difficulty)
{
if (difficulty < 0 || difficulty > 3)
{
return 0;
}
unsigned long long total = 0;
total += GetStatValue(statsCounter, Stats::killsZombie, difficulty);
total += GetStatValue(statsCounter, Stats::killsSkeleton, difficulty);
total += GetStatValue(statsCounter, Stats::killsCreeper, difficulty);
total += GetStatValue(statsCounter, Stats::killsSpider, difficulty);
total += GetStatValue(statsCounter, Stats::killsSpiderJockey, difficulty);
total += GetZombiePigmanKills(statsCounter, difficulty);
total += GetStatValue(statsCounter, Stats::killsSlime, difficulty);
return total;
}
static bool SetColumnsForType(LeaderboardManager::ReadScore &score, StatsCounter *statsCounter, int difficulty, LeaderboardManager::EStatsType type, unsigned long long &total)
{
switch (type)
{
case LeaderboardManager::eStatsType_Travelling:
SetTravellingColumns(score, statsCounter, difficulty, total);
return true;
case LeaderboardManager::eStatsType_Mining:
SetMiningColumns(score, statsCounter, difficulty, total);
return true;
case LeaderboardManager::eStatsType_Farming:
SetFarmingColumns(score, statsCounter, difficulty, total);
return true;
case LeaderboardManager::eStatsType_Kills:
SetKillsColumns(score, statsCounter, difficulty, total);
return true;
default:
return false;
}
}
static unsigned long long GetTypeTotalForDifficulty(StatsCounter *statsCounter, int difficulty, LeaderboardManager::EStatsType type)
{
switch (type)
{
case LeaderboardManager::eStatsType_Travelling:
return GetTravellingTotalForDifficulty(statsCounter, difficulty);
case LeaderboardManager::eStatsType_Mining:
return GetMiningTotalForDifficulty(statsCounter, difficulty);
case LeaderboardManager::eStatsType_Farming:
return GetFarmingTotalForDifficulty(statsCounter, difficulty);
case LeaderboardManager::eStatsType_Kills:
return GetKillsTotalForDifficulty(statsCounter, difficulty);
default:
return 0;
}
}
static void DebugDumpZeroRead(StatsCounter *statsCounter, int iPad, int selectedDifficulty, LeaderboardManager::EStatsType type, unsigned long long selectedTotal)
{
if (selectedTotal != 0)
{
return;
}
unsigned long long totals[4];
for (int d = 0; d < 4; ++d)
{
totals[d] = GetTypeTotalForDifficulty(statsCounter, d, type);
}
app.DebugPrintf(
"[Win64 LB] %s row is zero (pad=%d selectedDiff=%d canRecord=%d gameStarted=%d) totals[p/e/n/h]=%I64u/%I64u/%I64u/%I64u\n",
GetStatsTypeName(type),
iPad,
selectedDifficulty,
app.CanRecordStatsAndAchievements() ? 1 : 0,
app.GetGameStarted() ? 1 : 0,
totals[0],
totals[1],
totals[2],
totals[3]);
}
static bool PopulateLocalReadScore(LeaderboardManager::ReadScore &score, int difficulty, LeaderboardManager::EStatsType type)
{
if (difficulty < 0 || difficulty > 3 || type < LeaderboardManager::eStatsType_Travelling || type >= LeaderboardManager::eStatsType_MAX)
{
return false;
}
const int iPad = GetLocalPad();
StatsCounter *statsCounter = GetLocalStatsCounter(iPad);
score.m_uid = GetLocalPlayerUID(iPad);
score.m_rank = 1;
score.m_name = GetLocalDisplayName(iPad);
score.m_totalScore = 0;
score.m_statsSize = GetStatsSize(type);
for (unsigned int i = 0; i < LeaderboardManager::ReadScore::STATSDATA_MAX; ++i)
{
score.m_statsData[i] = 0;
}
score.m_idsErrorMessage = 0;
unsigned long long totalScore = 0;
if (!SetColumnsForType(score, statsCounter, difficulty, type, totalScore))
{
return false;
}
if (totalScore == 0)
{
unsigned long long bestTotal = 0;
int bestDifficulty = difficulty;
for (int d = 0; d < 4; ++d)
{
unsigned long long altTotal = GetTypeTotalForDifficulty(statsCounter, d, type);
if (altTotal > bestTotal)
{
bestTotal = altTotal;
bestDifficulty = d;
}
}
if (bestTotal > 0 && bestDifficulty != difficulty)
{
totalScore = 0;
SetColumnsForType(score, statsCounter, bestDifficulty, type, totalScore);
app.DebugPrintf("[Win64 LB] %s fallback used alternate difficulty %d for selected difficulty %d (pad=%d)\n", GetStatsTypeName(type), bestDifficulty, difficulty, iPad);
}
}
DebugDumpZeroRead(statsCounter, iPad, difficulty, type, totalScore);
score.m_totalScore = ClampToUnsignedLong(totalScore);
return true;
}
}
LeaderboardManager *LeaderboardManager::m_instance = new WindowsLeaderboardManager();
void WindowsLeaderboardManager::Tick() {}
bool WindowsLeaderboardManager::OpenSession()
{
return true;
}
void WindowsLeaderboardManager::CloseSession() {}
void WindowsLeaderboardManager::DeleteSession() {}
bool WindowsLeaderboardManager::WriteStats(unsigned int viewCount, ViewIn views)
{
UNREFERENCED_PARAMETER(viewCount);
if (views != NULL)
{
delete [] views;
}
return true;
}
bool WindowsLeaderboardManager::ReadStats_Friends(LeaderboardReadListener *callback, int difficulty, EStatsType type, PlayerUID myUID, unsigned int startIndex, unsigned int readCount)
{
if (!LeaderboardManager::ReadStats_Friends(callback, difficulty, type, myUID, startIndex, readCount))
{
return false;
}
return ReadStats_Local(callback, difficulty, type, startIndex, readCount);
}
bool WindowsLeaderboardManager::ReadStats_MyScore(LeaderboardReadListener *callback, int difficulty, EStatsType type, PlayerUID myUID, unsigned int readCount)
{
if (!LeaderboardManager::ReadStats_MyScore(callback, difficulty, type, myUID, readCount))
{
return false;
}
return ReadStats_Local(callback, difficulty, type, 1, readCount);
}
bool WindowsLeaderboardManager::ReadStats_TopRank(LeaderboardReadListener *callback, int difficulty, EStatsType type, unsigned int startIndex, unsigned int readCount)
{
if (!LeaderboardManager::ReadStats_TopRank(callback, difficulty, type, startIndex, readCount))
{
return false;
}
return ReadStats_Local(callback, difficulty, type, startIndex, readCount);
}
void WindowsLeaderboardManager::FlushStats() {}
void WindowsLeaderboardManager::CancelOperation()
{
zeroReadParameters();
m_readListener = NULL;
}
bool WindowsLeaderboardManager::isIdle()
{
return true;
}
bool WindowsLeaderboardManager::ReadStats_Local(LeaderboardReadListener *listener, int difficulty, EStatsType type, unsigned int startIndex, unsigned int readCount)
{
UNREFERENCED_PARAMETER(readCount);
if (listener == NULL)
{
return false;
}
ViewOut view;
view.m_numQueries = 0;
view.m_queries = NULL;
ReadScore localScore;
if (startIndex <= 1 && PopulateLocalReadScore(localScore, difficulty, type))
{
view.m_numQueries = 1;
view.m_queries = &localScore;
listener->OnStatsReadComplete(eStatsReturn_Success, 1, view);
return true;
}
if (difficulty >= 0 && difficulty <= 3 && type >= eStatsType_Travelling && type < eStatsType_MAX)
{
listener->OnStatsReadComplete(eStatsReturn_Success, 1, view);
return true;
}
listener->OnStatsReadComplete(eStatsReturn_NoResults, 0, view);
return false;
}