From 14c5e2d594896cee1a5e5383a53b750d7110d948 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Fri, 16 Mar 2018 21:44:34 -0300 Subject: [PATCH] ETTPC score sharing --- .../ScreenEvaluation decorations/default.lua | 43 +- .../ScreenNetEvaluation decorations.lua | 59 +- Themes/Til Death/BGAnimations/offsetplot.lua | 16 +- src/HighScore.cpp | 23 +- src/NetworkSyncManager.cpp | 545 ++++++++++++------ src/NetworkSyncManager.h | 19 +- src/PlayerStageStats.cpp | 4 + src/PlayerStageStats.h | 1 + src/ScreenNetEvaluation.cpp | 45 +- src/ScreenNetEvaluation.h | 2 +- src/StageStats.cpp | 3 + 11 files changed, 508 insertions(+), 252 deletions(-) diff --git a/Themes/Til Death/BGAnimations/ScreenEvaluation decorations/default.lua b/Themes/Til Death/BGAnimations/ScreenEvaluation decorations/default.lua index 890e48208b..78aed8b46c 100644 --- a/Themes/Til Death/BGAnimations/ScreenEvaluation decorations/default.lua +++ b/Themes/Til Death/BGAnimations/ScreenEvaluation decorations/default.lua @@ -90,13 +90,6 @@ local frameY = 140 local frameWidth = SCREEN_CENTER_X-120 function scoreBoard(pn,position) - local t = Def.ActorFrame{ - BeginCommand=function(self) - if position == 1 then - self:x(SCREEN_WIDTH-(frameX*2)-frameWidth) - end - end - } local customWindow local judge = enabledCustomWindows and 0 or GetTimingDifficulty() @@ -105,6 +98,21 @@ function scoreBoard(pn,position) local dvt = pss:GetOffsetVector() local totalTaps = pss:GetTotalTaps() + local t = Def.ActorFrame{ + BeginCommand=function(self) + if position == 1 then + self:x(SCREEN_WIDTH-(frameX*2)-frameWidth) + end + end, + UpdateNetEvalStatsMessageCommand = function(self) + local s = SCREENMAN:GetTopScreen():GetHighScore() + if s then + score = s + end + dvt = score:GetOffsetVector() + MESSAGEMAN:Broadcast("ScoreChanged") + end, + } t[#t+1] = Def.Quad{ InitCommand=function(self) self:xy(frameX-5,frameY):zoomto(frameWidth+10,220):halign(0):valign(0):diffuse(color("#333333CC")) @@ -141,6 +149,7 @@ function scoreBoard(pn,position) BeginCommand=function(self) self:queuecommand("Set") end, + ScoreChangedMessageCommand = function(self) self:queuecommand("Set"); end, SetCommand=function(self) local meter = score:GetSkillsetSSR("Overall") self:settextf("%5.2f", meter) @@ -171,13 +180,14 @@ function scoreBoard(pn,position) self:queuecommand("Set") end, SetCommand=function(self) - self:diffuse(getGradeColor(pss:GetWifeGrade())) - self:settextf("%05.2f%% (%s)",notShit.floor(pss:GetWifeScore()*10000)/100, "Wife") + self:diffuse(getGradeColor(score:GetWifeGrade())) + self:settextf("%05.2f%% (%s)",notShit.floor(score:GetWifeScore()*10000)/100, "Wife") end, + ScoreChangedMessageCommand = function(self) self:queuecommand("Set"); end, CodeMessageCommand=function(self,params) local totalHolds = pss:GetRadarPossible():GetValue("RadarCategory_Holds") + pss:GetRadarPossible():GetValue("RadarCategory_Rolls") - local holdsHit = pss:GetRadarActual():GetValue("RadarCategory_Holds") + pss:GetRadarActual():GetValue("RadarCategory_Rolls") - local minesHit = pss:GetRadarPossible():GetValue("RadarCategory_Mines") - pss:GetRadarActual():GetValue("RadarCategory_Mines") + local holdsHit = score:GetRadarValues():GetValue("RadarCategory_Holds") + score:GetRadarValues():GetValue("RadarCategory_Rolls") + local minesHit = pss:GetRadarPossible():GetValue("RadarCategory_Mines") - score:GetRadarValues():GetValue("RadarCategory_Mines") if enabledCustomWindows then if params.Name == "PrevJudge" then judge = judge < 2 and #customWindows or judge - 1 @@ -272,8 +282,9 @@ function scoreBoard(pn,position) self:queuecommand("Set") end, SetCommand=function(self) - self:settext(pss:GetTapNoteScores(v)) + self:settext(score:GetTapNoteScore(v)) end, + ScoreChangedMessageCommand = function(self) self:queuecommand("Set"); end, CodeMessageCommand=function(self,params) if params.Name == "PrevJudge" or params.Name == "NextJudge" then if enabledCustomWindows then @@ -321,7 +332,8 @@ function scoreBoard(pn,position) BeginCommand=function(self) self:queuecommand("Set") end, - Se2tCommand=function(self) + ScoreChangedMessageCommand = function(self) self:queuecommand("Set"); end, + SetCommand=function(self) if score:GetChordCohesion() == true then self:settext("Chord Cohesion: Yes") else @@ -350,8 +362,9 @@ function scoreBoard(pn,position) self:queuecommand("Set") end, SetCommand=function(self) - self:settextf("%03d/%03d",pss:GetRadarActual():GetValue("RadarCategory_"..fart[i]),pss:GetRadarPossible():GetValue("RadarCategory_"..fart[i])) - end + self:settextf("%03d/%03d",score:GetRadarValues():GetValue("RadarCategory_"..fart[i]),pss:GetRadarPossible():GetValue("RadarCategory_"..fart[i])) + end, + ScoreChangedMessageCommand = function(self) self:queuecommand("Set"); end, }; end diff --git a/Themes/Til Death/BGAnimations/ScreenNetEvaluation decorations.lua b/Themes/Til Death/BGAnimations/ScreenNetEvaluation decorations.lua index 041febae83..cf8a7cf175 100644 --- a/Themes/Til Death/BGAnimations/ScreenNetEvaluation decorations.lua +++ b/Themes/Til Death/BGAnimations/ScreenNetEvaluation decorations.lua @@ -203,17 +203,28 @@ end function scoreBoard(pn,position) + local judge = GetTimingDifficulty() + local pss = STATSMAN:GetCurStageStats():GetPlayerStageStats(pn) + local score = SCOREMAN:GetMostRecentScore() + local smallest,largest + local devianceTable local t = Def.ActorFrame{ BeginCommand=function(self) if position == 1 then self:x(SCREEN_WIDTH-(frameX*2)-frameWidth) end - end + end, + UpdateNetEvalStatsMessageCommand = function(self) + local s = SCREENMAN:GetTopScreen():GetHighScore() + if s then + score = s + devianceTable = score:GetOffsetVector() + smallest,largest = wifeRange(devianceTable) + MESSAGEMAN:Broadcast("ScoreChanged") + end + end, } - local judge = GetTimingDifficulty() - local pss = STATSMAN:GetCurStageStats():GetPlayerStageStats(pn) - local score = SCOREMAN:GetMostRecentScore() t[#t+1] = Def.Quad{ InitCommand=function(self) @@ -257,6 +268,7 @@ function scoreBoard(pn,position) BeginCommand=function(self) self:queuecommand("Set") end, + ScoreChangedMessageCommand = function(self) self:queuecommand("Set"); end, SetCommand=function(self) local meter = score:GetSkillsetSSR("Overall") self:settextf("%5.2f", meter) @@ -425,11 +437,6 @@ function scoreBoard(pn,position) InitCommand=function(self) self:Load("StepsDisplayEvaluation",pn):SetFromGameState(pn) end; - UpdateNetEvalStatsMessageCommand=function(self,param) - if GAMESTATE:IsPlayerEnabled(pn) then - self:SetFromSteps(param.Steps) - end; - end; } t[#t+1] = StandardDecorationFromTable( "StepsDisplay" .. ToEnumShortString(pn), t2 ) end @@ -443,9 +450,10 @@ function scoreBoard(pn,position) BeginCommand=function(self) self:queuecommand("Set") end, + ScoreChangedMessageCommand = function(self) self:queuecommand("Set"); end, SetCommand=function(self) - self:diffuse(getGradeColor(pss:GetWifeGrade())) - self:settextf("%05.2f%% (%s)",notShit.floor(pss:GetWifeScore()*10000)/100, "Wife") + self:diffuse(getGradeColor(score:GetWifeGrade())) + self:settextf("%05.2f%% (%s)",notShit.floor(score:GetWifeScore()*10000)/100, "Wife") end, CodeMessageCommand=function(self,params) if params.Name == "PrevJudge" and judge > 1 then @@ -473,7 +481,10 @@ function scoreBoard(pn,position) end, SetCommand=function(self) self:settext(GAMESTATE:GetPlayerState(PLAYER_1):GetPlayerOptionsString('ModsLevel_Current')) - end + end, + ScoreChangedMessageCommand=function(self) + self:settext(SCREENMAN:GetTopScreen():GetOptions() or "") + end, } for k,v in ipairs(judges) do @@ -489,6 +500,10 @@ function scoreBoard(pn,position) BeginCommand=function(self) self:glowshift():effectcolor1(color("1,1,1,"..tostring(pss:GetPercentageOfTaps(v)*0.4))):effectcolor2(color("1,1,1,0")):sleep(0.5):decelerate(2):zoomx(frameWidth*pss:GetPercentageOfTaps(v)) end, + ScoreChangedMessageCommand = function(self) + local rescoreJudges = score:RescoreJudges(judge) + self:zoomx(frameWidth*rescoreJudges[k]/(#(score:GetOffsetVector()))) + end, CodeMessageCommand=function(self,params) if params.Name == "PrevJudge" or params.Name == "NextJudge" then local rescoreJudges = score:RescoreJudges(judge) @@ -517,6 +532,9 @@ function scoreBoard(pn,position) SetCommand=function(self) self:settext(pss:GetTapNoteScores(v)) end, + ScoreChangedMessageCommand = function(self) + self:settext(score:GetTapNoteScore(v)) + end, CodeMessageCommand=function(self,params) if params.Name == "PrevJudge" or params.Name == "NextJudge" then local rescoreJudges = score:RescoreJudges(judge) @@ -531,6 +549,9 @@ function scoreBoard(pn,position) BeginCommand=function(self) self:queuecommand("Set") end, + ScoreChangedMessageCommand = function(self) + self:settextf("(%03.2f%%)",score:GetTapNoteScore(v)/(#(score:GetOffsetVector()))*100) + end, SetCommand=function(self) self:settextf("(%03.2f%%)",pss:GetPercentageOfTaps(v)*100) end, @@ -575,14 +596,14 @@ function scoreBoard(pn,position) self:xy(frameWidth+25,frameY+230):zoomto(frameWidth/2+10,60):halign(1):valign(0):diffuse(color("#333333CC")) end; } - local smallest,largest = wifeRange(devianceTable) + smallest,largest = wifeRange(devianceTable) local doot = {"Mean", "Mean(Abs)", "Sd", "Smallest", "Largest"} local mcscoot = { - wifeMean(devianceTable), - ms.tableSum(devianceTable, 1,true)/#devianceTable, - wifeSd(devianceTable), - smallest, - largest + function() return wifeMean(devianceTable) end, + function() return ms.tableSum(devianceTable, 1,true)/#devianceTable end, + function() return wifeSd(devianceTable) end, + function() smallest end, + function() largest end } for i=1,#doot do @@ -593,7 +614,7 @@ function scoreBoard(pn,position) } t[#t+1] = LoadFont("Common Normal")..{ InitCommand=function(self) - self:xy(frameWidth+20,frameY+230+10*i):zoom(0.4):halign(1):settextf("%5.2fms",mcscoot[i]) + self:xy(frameWidth+20,frameY+230+10*i):zoom(0.4):halign(1):settextf("%5.2fms",mcscoot[i]()) end } end diff --git a/Themes/Til Death/BGAnimations/offsetplot.lua b/Themes/Til Death/BGAnimations/offsetplot.lua index ffae457950..a336aed4e0 100644 --- a/Themes/Til Death/BGAnimations/offsetplot.lua +++ b/Themes/Til Death/BGAnimations/offsetplot.lua @@ -18,6 +18,7 @@ local pss = STATSMAN:GetCurStageStats():GetPlayerStageStats(PLAYER_1) local dvt = pss:GetOffsetVector() local nrt = pss:GetNoteRowVector() local td = GAMESTATE:GetCurrentSteps(PLAYER_1):GetTimingData() +local wuab = {} local finalSecond = GAMESTATE:GetCurrentSong(PLAYER_1):GetLastSecond() local function fitX(x) -- Scale time values to fit within plot width. @@ -73,6 +74,18 @@ local o = Def.ActorFrame{ maxOffset = (enabledCustomWindows and judge ~= 0) and customWindow.judgeWindows.boo or math.max(180, 180*tso) MESSAGEMAN:Broadcast("JudgeDisplayChanged") end, + UpdateNetEvalStatsMessageCommand = function(self) + local s = SCREENMAN:GetTopScreen():GetHighScore() + if s then + score = s + dvt = score:GetOffsetVector() + nrt = score:GetNoteRowVector() + for i=1,#nrt do + wuab[i] = td:GetElapsedTimeFromNoteRow(nrt[i]) + end + end + MESSAGEMAN:Broadcast("JudgeDisplayChanged") + end, } -- Center Bar o[#o+1] = Def.Quad{ @@ -107,7 +120,6 @@ o[#o+1] = Def.Quad{InitCommand=function(self) self:zoomto(plotWidth+plotMargin,plotHeight+plotMargin):diffuse(color("0.05,0.05,0.05,0.05")):diffusealpha(0.8) end} -- Convert noterows to timestamps and plot dots -local wuab = {} for i=1,#nrt do wuab[i] = td:GetElapsedTimeFromNoteRow(nrt[i]) end @@ -136,7 +148,7 @@ o[#o+1] = Def.ActorMultiVertex{ end, JudgeDisplayChangedMessageCommand=function(self) local verts = {}; - for i=1,#nrt do + for i=1,#dvt do local x = fitX(wuab[i]); local y = fitY(dvt[i]); local fit = (enabledCustomWindows and judge ~= 0) and customWindow.judgeWindows.boo + 3 or math.max(183, 183*tso) diff --git a/src/HighScore.cpp b/src/HighScore.cpp index a96599351c..c8e92a3199 100644 --- a/src/HighScore.cpp +++ b/src/HighScore.cpp @@ -1254,12 +1254,14 @@ class LunaHighScore: public Luna // Convert to MS so lua doesn't have to static int GetOffsetVector(T* p, lua_State *L) { - if (p->LoadReplayData()) { - vector doot = p->GetOffsetVector(); - for (size_t i = 0; i < doot.size(); ++i) - doot[i] = doot[i] * 1000; - LuaHelpers::CreateTableFromArray(doot, L); - p->UnloadReplayData(); + auto v = p->GetOffsetVector(); + bool loaded = v.size() > 0; + if (loaded || p->LoadReplayData()) { + for (size_t i = 0; i < v.size(); ++i) + v[i] = v[i] * 1000; + LuaHelpers::CreateTableFromArray(v, L); + if (!loaded) + p->UnloadReplayData(); } else lua_pushnil(L); @@ -1267,9 +1269,12 @@ class LunaHighScore: public Luna } static int GetNoteRowVector(T* p, lua_State *L) { - if (p->LoadReplayData()) { - LuaHelpers::CreateTableFromArray(p->GetNoteRowVector(), L); - p->UnloadReplayData(); + auto& v = p->GetNoteRowVector(); + bool loaded = v.size() > 0; + if (loaded || p->LoadReplayData()) { + LuaHelpers::CreateTableFromArray(v, L); + if(!loaded) + p->UnloadReplayData(); } else lua_pushnil(L); diff --git a/src/NetworkSyncManager.cpp b/src/NetworkSyncManager.cpp index b105c85e72..b064259aa4 100644 --- a/src/NetworkSyncManager.cpp +++ b/src/NetworkSyncManager.cpp @@ -3,6 +3,7 @@ #include "uWS.h" #include "LuaManager.h" #include "ScreenNetRoom.h" +#include "PlayerStageStats.h" #include "ScreenNetSelectMusic.h" #include "LocalizedString.h" #include "JsonUtil.h" @@ -43,6 +44,7 @@ std::map ettClientMessageMap = { { ettpc_openeval, "openeval" }, { ettpc_closeeval, "closeeval" }, { ettpc_logout, "logout" }, + { ettpc_hello, "hello" }, }; std::map ettServerMessageMap = { { "hello", ettps_hello }, @@ -59,6 +61,7 @@ std::map ettServerMessageMap = { { "deleteroom", ettps_deleteroom }, { "newroom", ettps_newroom }, { "updateroom", ettps_updateroom }, + { "userlist", ettps_roomuserlist }, }; @@ -109,6 +112,7 @@ unsigned long NetworkSyncManager::GetCurrentSMBuild( LoadingWindow* ld ) { retur #include "arch/LoadingWindow/LoadingWindow.h" #include "PlayerState.h" #include "CryptManager.h" +#include "HighScore.h" AutoScreenMessage(SM_AddToChat); AutoScreenMessage(SM_ChangeSong); @@ -123,6 +127,7 @@ AutoScreenMessage(ETTP_IncomingChat); AutoScreenMessage(ETTP_RoomsChange); AutoScreenMessage(ETTP_SelectChart); AutoScreenMessage(ETTP_StartChart); +AutoScreenMessage(ETTP_NewScore); extern Preference g_sLastServer; Preference autoConnectMultiplayer("AutoConnectMultiplayer", 1); @@ -257,7 +262,8 @@ void ETTProtocol::OffEval() json j; j["type"] = ettClientMessageMap[ettpc_closeeval]; j["id"] = msgId++; - ws->send(j.dump().c_str()); + Send(j.dump().c_str()); + state = 0; } void ETTProtocol::OnEval() { @@ -266,7 +272,8 @@ void ETTProtocol::OnEval() json j; j["type"] = ettClientMessageMap[ettpc_openeval]; j["id"] = msgId++; - ws->send(j.dump().c_str()); + Send(j.dump().c_str()); + state = 2; } void ETTProtocol::OnOptions() { @@ -275,7 +282,8 @@ void ETTProtocol::OnOptions() json j; j["type"] = ettClientMessageMap[ettpc_openoptions]; j["id"] = msgId++; - ws->send(j.dump().c_str()); + Send(j.dump().c_str()); + state = 3; } void ETTProtocol::OffOptions() { @@ -284,7 +292,8 @@ void ETTProtocol::OffOptions() json j; j["type"] = ettClientMessageMap[ettpc_closeoptions]; j["id"] = msgId++; - ws->send(j.dump().c_str()); + Send(j.dump().c_str()); + state = 0; } void SMOProtocol::close() @@ -477,7 +486,8 @@ void ETTProtocol::FindJsonChart(NetworkSyncManager* n, json& ch) n->m_sArtist = ch.value("artist", ""); n->difficulty = StringToDifficulty(ch.value("difficulty", "Invalid")); n->meter = ch.value("meter", -1); - StepsType st = GAMESTATE->GetCurrentStyle(PLAYER_1)->m_StepsType; + StringToStyleType(ch.value("stepstype", "Invalid")); + if (!n->chartkey.empty()) { auto song = SONGMAN->GetSongByChartkey(n->chartkey); if (song == nullptr) @@ -487,7 +497,7 @@ void ETTProtocol::FindJsonChart(NetworkSyncManager* n, json& ch) (n->m_sSubTitle.empty() || n->m_sSubTitle == song->GetTranslitSubTitle()) && (n->m_sFileHash.empty() || n->m_sFileHash == song->GetFileHash())) { - for (auto& steps : song->GetStepsByStepsType(st)) { + for (auto& steps : song->GetAllSteps()) { if ((n->meter == -1 || n->meter == steps->GetMeter()) && (n->difficulty == Difficulty_Invalid || n->difficulty == steps->GetDifficulty()) && (n->chartkey == steps->GetChartKey())) { @@ -510,7 +520,7 @@ void ETTProtocol::FindJsonChart(NetworkSyncManager* n, json& ch) (n->m_sSubTitle.empty() || n->m_sSubTitle == m_cSong->GetTranslitSubTitle()) && (n->m_sFileHash.empty() || n->m_sFileHash == m_cSong->GetFileHash())) { - for (auto& steps : m_cSong->GetStepsByStepsType(st)) { + for (auto& steps : m_cSong->GetAllSteps()) { if ((n->meter == -1 || n->meter == steps->GetMeter()) && (n->difficulty == Difficulty_Invalid || n->difficulty == steps->GetDifficulty())) { n->song = m_cSong; @@ -534,93 +544,173 @@ void ETTProtocol::Update(NetworkSyncManager* n, float fDeltaTime) auto jType = (*iterator).find("type"); auto payload = (*iterator).find("payload"); auto error = (*iterator).find("error"); - if (jType != iterator->end()) { - if (error != iterator->end()) { - LOG->Trace(("Error on ETTP message " + jType->get() + ":" + error->get()).c_str()); - break; + if (jType == iterator->end()) + break; + if (error != iterator->end()) { + LOG->Trace(("Error on ETTP message " + jType->get() + ":" + error->get()).c_str()); + break; + } + switch (ettServerMessageMap[jType->get()]) { + case ettps_loginresponse: + if (!(n->loggedIn = (*payload)["logged"])) + n->loginResponse = (*payload)["msg"].get(); + else { + n->loginResponse = ""; + n->loggedIn = true; } - switch (ettServerMessageMap[jType->get()]) { - case ettps_loginresponse: - if (!(n->loggedIn = (*payload)["logged"])) - n->loginResponse = (*payload)["msg"].get(); - else { - n->loginResponse = ""; - n->loggedIn = true; - } - SCREENMAN->SendMessageToTopScreen(ETTP_LoginResponse); - break; - case ettps_hello: - serverName = (*payload).value("name", ""); - serverVersion = (*payload).value("version", 1); - LOG->Trace("Ettp server identified: %s (Version:%d)", serverName.c_str(), serverVersion); - n->DisplayStartupStatus(); - break; - case ettps_ping: - if (ws != nullptr) { - json ping; - ping["type"] = ettClientMessageMap[ettpc_ping]; - ping["id"] = msgId++; - ws->send(ping.dump().c_str()); - } - break; - case ettps_selectchart: - { - auto ch = (*payload).at("chart"); - FindJsonChart(n, ch); - json j; - if (n->song != nullptr) { - SCREENMAN->SendMessageToTopScreen(ETTP_SelectChart); - j["type"] = ettClientMessageMap[ettpc_haschart]; - } - else { - j["type"] = ettClientMessageMap[ettpc_missingchart]; - } - j["id"] = msgId++; - ws->send(j.dump().c_str()); - } + SCREENMAN->SendMessageToTopScreen(ETTP_LoginResponse); break; - case ettps_startchart: - { - auto ch = (*payload).at("chart"); - FindJsonChart(n, ch); - json j; - if (n->song != nullptr) { - SCREENMAN->SendMessageToTopScreen(ETTP_StartChart); - j["type"] = ettClientMessageMap[ettpc_startingchart]; - } - else - j["type"] = ettClientMessageMap[ettpc_notstartingchart]; - j["id"] = msgId++; - ws->send(j.dump().c_str()); - } + case ettps_hello: + serverName = (*payload).value("name", ""); + serverVersion = (*payload).value("version", 1); + LOG->Trace("Ettp server identified: %s (Version:%d)", serverName.c_str(), serverVersion); + n->DisplayStartupStatus(); + if (ws != nullptr) { + json hello; + hello["type"] = ettClientMessageMap[ettpc_hello]; + hello["payload"]["version"] = ETTPCVERSION; + Send(hello.dump().c_str()); + } break; - case ettps_recievechat: - { - //chat[tabname, tabtype] = msg - int type = (*payload)["msgtype"].get(); - string tab = (*payload)["tab"].get(); - n->chat[{tab, type}].emplace_back((*payload)["msg"].get()); - SCREENMAN->SendMessageToTopScreen(ETTP_IncomingChat); - Message msg("Chat"); - msg.SetParam("tab", RString(tab.c_str())); - msg.SetParam("msg", RString((*payload)["msg"].get().c_str())); - msg.SetParam("type", type); - MESSAGEMAN->Broadcast(msg); - //Should end here - n->m_sChatText = (n->m_sChatText + "\n" + (*payload)["msg"].get()).c_str(); - SCREENMAN->SendMessageToTopScreen(SM_AddToChat); - } + case ettps_recievescore: + { + json& score = (*payload)["score"]; + HighScore hs; + EndOfGame_PlayerData result; + hs.SetScoreKey(score.value("scorekey", "")); + hs.SetSSRNormPercent(score.value("ssr_norm", 0)); + hs.SetEtternaValid(score.value("valid", 0)); + hs.SetModifiers(score.value("mods", "")); + + result.tapScores[0] = score.value("marv", 0); + hs.SetTapNoteScore(TNS_W1, score.value("marv", 0)); + result.tapScores[1] = score.value("perfect", 0); + hs.SetTapNoteScore(TNS_W2, score.value("perfect", 0)); + result.tapScores[2] = score.value("great", 0); + hs.SetTapNoteScore(TNS_W3, score.value("great", 0)); + result.tapScores[3] = score.value("good", 0); + hs.SetTapNoteScore(TNS_W4, score.value("good", 0)); + result.tapScores[4] = score.value("bad", 0); + hs.SetTapNoteScore(TNS_W5, score.value("bad", 0)); + result.tapScores[5] = score.value("miss", 0); + hs.SetTapNoteScore(TNS_Miss, score.value("miss", 0)); + result.tapScores[6] = 0; + result.tapScores[7] = score.value("max_combo", 0); + hs.SetMaxCombo(score.value("max_combo", 0)); + hs.SetGrade(PlayerStageStats::GetGrade(hs.GetSSRNormPercent())); + hs.SetDateTime(DateTime()); + hs.SetTapNoteScore(TNS_HitMine, score.value("hitmine", 0)); + hs.SetHoldNoteScore(HNS_Held, score.value("held", 0)); + hs.SetChartKey(score.value("chartkey", "")); + hs.SetHoldNoteScore(HNS_LetGo, score.value("letgo", 0)); + hs.SetHoldNoteScore(HNS_Missed, score.value("ng", 0)); + try { + hs.SetChordCohesion(!score["nocc"].get()); + } + catch (exception e) { + hs.SetChordCohesion(true); + } + hs.SetMusicRate(score.value("rate", 0.1)); + try { + json& replay = score["replay"]; + json& jOffsets = replay["offsets"]; + json& jNoterows = replay["noterows"]; + vector offsets; + vector noterows; + for (json::iterator offsetIt = jOffsets.begin(); offsetIt != jOffsets.end(); ++offsetIt) + offsets.emplace_back(static_cast(*offsetIt)); + for (json::iterator noterowIt = jNoterows.begin(); noterowIt != jNoterows.end(); ++noterowIt) + noterows.emplace_back(noterowIt->get()); + hs.SetOffsetVector(offsets); + hs.SetNoteRowVector(noterows); + } + catch(exception e) { } //No replay data for this score, its still valid + result.nameStr = (*payload)["name"].get(); + result.hs = hs; + result.playerOptions = payload->value("options", ""); + n->m_EvalPlayerData.emplace_back(result); + n->m_ActivePlayers = n->m_EvalPlayerData.size(); + SCREENMAN->SendMessageToTopScreen(ETTP_NewScore); break; - case ettps_recievescore: - + } + case ettps_ping: + if (ws != nullptr) { + json ping; + ping["type"] = ettClientMessageMap[ettpc_ping]; + ping["id"] = msgId++; + Send(ping.dump().c_str()); + } break; - case ettps_gameplayleaderboard: + case ettps_selectchart: + { + auto ch = (*payload).at("chart"); + FindJsonChart(n, ch); + json j; + if (n->song != nullptr) { + SCREENMAN->SendMessageToTopScreen(ETTP_SelectChart); + j["type"] = ettClientMessageMap[ettpc_haschart]; + } + else { + j["type"] = ettClientMessageMap[ettpc_missingchart]; + } + j["id"] = msgId++; + Send(j.dump().c_str()); + } + break; + case ettps_startchart: + { + n->m_EvalPlayerData.clear(); + auto ch = (*payload).at("chart"); + FindJsonChart(n, ch); + json j; + if (n->song != nullptr && state == 0) { + SCREENMAN->SendMessageToTopScreen(ETTP_StartChart); + j["type"] = ettClientMessageMap[ettpc_startingchart]; + } + else + j["type"] = ettClientMessageMap[ettpc_notstartingchart]; + j["id"] = msgId++; + Send(j.dump().c_str()); + } + break; + case ettps_recievechat: + { + //chat[tabname, tabtype] = msg + int type = (*payload)["msgtype"].get(); + string tab = (*payload)["tab"].get(); + n->chat[{tab, type}].emplace_back((*payload)["msg"].get()); + SCREENMAN->SendMessageToTopScreen(ETTP_IncomingChat); + Message msg("Chat"); + msg.SetParam("tab", RString(tab.c_str())); + msg.SetParam("msg", RString((*payload)["msg"].get().c_str())); + msg.SetParam("type", type); + MESSAGEMAN->Broadcast(msg); + //Should end here + n->m_sChatText = (n->m_sChatText + "\n" + (*payload)["msg"].get()).c_str(); + SCREENMAN->SendMessageToTopScreen(SM_AddToChat); + } + break; + case ettps_gameplayleaderboard: - break; - case ettps_createroomresponse: - { - bool created = (*payload)["created"]; - if (created) { + break; + case ettps_createroomresponse: + { + bool created = (*payload)["created"]; + if (created) { + Message msg(MessageIDToString(Message_UpdateScreenHeader)); + msg.SetParam("Header", roomName); + msg.SetParam("Subheader", roomDesc); + MESSAGEMAN->Broadcast(msg); + RString SMOnlineSelectScreen = THEME->GetMetric("ScreenNetRoom", "MusicSelectScreen"); + SCREENMAN->SetNewScreen(SMOnlineSelectScreen); + } + } + break; + case ettps_enterroomresponse: + { + bool entered = (*payload)["entered"]; + if (entered) { + try { Message msg(MessageIDToString(Message_UpdateScreenHeader)); msg.SetParam("Header", roomName); msg.SetParam("Subheader", roomDesc); @@ -628,87 +718,103 @@ void ETTProtocol::Update(NetworkSyncManager* n, float fDeltaTime) RString SMOnlineSelectScreen = THEME->GetMetric("ScreenNetRoom", "MusicSelectScreen"); SCREENMAN->SetNewScreen(SMOnlineSelectScreen); } - } - break; - case ettps_enterroomresponse: - { - bool entered = (*payload)["entered"]; - if (entered) { - try { - Message msg(MessageIDToString(Message_UpdateScreenHeader)); - msg.SetParam("Header", roomName); - msg.SetParam("Subheader", roomDesc); - MESSAGEMAN->Broadcast(msg); - RString SMOnlineSelectScreen = THEME->GetMetric("ScreenNetRoom", "MusicSelectScreen"); - SCREENMAN->SetNewScreen(SMOnlineSelectScreen); - } - catch (exception e) { - LOG->Trace("Error while parsing ettp json enter room response: %s", e.what()); - } + catch (exception e) { + LOG->Trace("Error while parsing ettp json enter room response: %s", e.what()); } } - break; - case ettps_newroom: - try { - RoomData tmp = jsonToRoom((*payload)["room"]); - n->m_Rooms.emplace_back(tmp); - SCREENMAN->SendMessageToTopScreen(ETTP_RoomsChange); - } - catch (exception e) { - LOG->Trace("Error while parsing ettp json newroom room: %s", e.what()); + else { + roomDesc = ""; + roomName = ""; } - break; - case ettps_deleteroom: - try { - string name = (*payload)["room"]["name"]; - n->m_Rooms.erase( - std::remove_if(n->m_Rooms.begin(), n->m_Rooms.end(), [&](RoomData const & room) { - return room.Name() == name; - }), - n->m_Rooms.end()); + } + break; + case ettps_newroom: + try { + RoomData tmp = jsonToRoom((*payload)["room"]); + n->m_Rooms.emplace_back(tmp); + SCREENMAN->SendMessageToTopScreen(ETTP_RoomsChange); + } + catch (exception e) { + LOG->Trace("Error while parsing ettp json newroom room: %s", e.what()); + } + break; + case ettps_deleteroom: + try { + string name = (*payload)["room"]["name"]; + n->m_Rooms.erase( + std::remove_if(n->m_Rooms.begin(), n->m_Rooms.end(), [&](RoomData const & room) { + return room.Name() == name; + }), + n->m_Rooms.end()); + SCREENMAN->SendMessageToTopScreen(ETTP_RoomsChange); + } + catch (exception e) { + LOG->Trace("Error while parsing ettp json deleteroom room: %s", e.what()); + } + break; + case ettps_updateroom: + try { + auto updated = jsonToRoom((*payload)["room"]); + auto roomIt = find_if(n->m_Rooms.begin(), n->m_Rooms.end(), + [&](RoomData const & room) { + return room.Name() == updated.Name(); + } + ); + if(roomIt != n->m_Rooms.end()) { + roomIt->SetDescription(updated.Description()); + roomIt->SetState(updated.State()); + roomIt->players = updated.players; SCREENMAN->SendMessageToTopScreen(ETTP_RoomsChange); } - catch (exception e) { - LOG->Trace("Error while parsing ettp json deleteroom room: %s", e.what()); - } - break; - case ettps_updateroom: - try { - auto updated = jsonToRoom((*payload)["room"]); - auto roomIt = find_if(n->m_Rooms.begin(), n->m_Rooms.end(), - [&](RoomData const & room) { - return room.Name() == updated.Name(); + } + catch (exception e) { + LOG->Trace("Error while parsing ettp json roomlist room: %s", e.what()); + } + break; + case ettps_roomlist: + { + RoomData tmp; + n->m_Rooms.clear(); + auto j1 = payload->at("rooms"); + if (j1.is_array()) + for (auto&& room : j1) { + try { + n->m_Rooms.emplace_back(jsonToRoom(room)); } - ); - if(roomIt != n->m_Rooms.end()) { - roomIt->SetDescription(updated.Description()); - roomIt->SetState(updated.State()); - roomIt->players = updated.players; - SCREENMAN->SendMessageToTopScreen(ETTP_RoomsChange); - } - } - catch (exception e) { - LOG->Trace("Error while parsing ettp json roomlist room: %s", e.what()); - } - break; - case ettps_roomlist: - { - RoomData tmp; - n->m_Rooms.clear(); - auto j1 = payload->at("rooms"); - if (j1.is_array()) - for (auto&& room : j1) { - try { - n->m_Rooms.emplace_back(jsonToRoom(room)); - } - catch (exception e) { - LOG->Trace("Error while parsing ettp json roomlist room: %s", e.what()); - } + catch (exception e) { + LOG->Trace("Error while parsing ettp json roomlist room: %s", e.what()); } - SCREENMAN->SendMessageToTopScreen(ETTP_RoomsChange); - } - break; + } + SCREENMAN->SendMessageToTopScreen(ETTP_RoomsChange); } + break; + case ettps_roomuserlist: + { + n->m_ActivePlayer.clear(); + n->m_PlayerNames.clear(); + n->m_PlayerStatus.clear(); + auto j1 = payload->at("players"); + if (j1.is_array()) + for (auto&& player : j1) { + int stored = 0; + try { + n->m_PlayerNames.emplace_back(player["name"]); + stored++; + n->m_PlayerStatus.emplace_back(player["status"]); + stored++; + n->m_ActivePlayer.emplace_back(0); + } + catch (exception e) { + if (stored > 0) + n->m_PlayerNames.pop_back(); + if (stored > 1) + n->m_PlayerStatus.pop_back(); + LOG->Trace("Error while parsing ettp json room player list: %s", e.what()); + } + } + SCREENMAN->SendMessageToTopScreen(SM_UsersUpdate); + } + break; } } catch (exception e) { @@ -916,7 +1022,7 @@ void ETTProtocol::Logout() return; json logout; logout["type"] = ettClientMessageMap[ettpc_logout]; - ws->send(logout.dump().c_str()); + Send(logout.dump().c_str()); } void NetworkSyncManager::Login(RString user, RString pass) { @@ -934,7 +1040,7 @@ void ETTProtocol::SendChat(const RString& message, string tab, int type) payload["tab"] = tab.c_str(); payload["msgtype"] = type; chatMsg["id"] = msgId++; - ws->send(chatMsg.dump().c_str()); + Send(chatMsg.dump().c_str()); } void ETTProtocol::CreateNewRoom(RString name, RString desc, RString password) { @@ -949,7 +1055,7 @@ void ETTProtocol::CreateNewRoom(RString name, RString desc, RString password) payload["pass"] = password.c_str(); payload["desc"] = desc.c_str(); createRoom["id"] = msgId++; - ws->send(createRoom.dump().c_str()); + Send(createRoom.dump().c_str()); } void ETTProtocol::LeaveRoom(NetworkSyncManager* n) { @@ -968,7 +1074,7 @@ void ETTProtocol::LeaveRoom(NetworkSyncManager* n) json leaveRoom; leaveRoom["type"] = ettClientMessageMap[ettpc_leaveroom]; leaveRoom["id"] = msgId++; - ws->send(leaveRoom.dump().c_str()); + Send(leaveRoom.dump().c_str()); } void ETTProtocol::EnterRoom(RString name, RString password) { @@ -985,7 +1091,7 @@ void ETTProtocol::EnterRoom(RString name, RString password) payload["name"] = name.c_str(); payload["pass"] = password.c_str(); enterRoom["id"] = msgId++; - ws->send(enterRoom.dump().c_str()); + Send(enterRoom.dump().c_str()); } void ETTProtocol::Login(RString user, RString pass) { @@ -997,7 +1103,7 @@ void ETTProtocol::Login(RString user, RString pass) payload["user"] = user.c_str(); payload["pass"] = pass.c_str(); login["id"] = msgId++; - ws->send(login.dump().c_str()); + Send(login.dump().c_str()); } void SMOProtocol::Login(RString user, RString pass) { @@ -1062,6 +1168,64 @@ void SMOProtocol::ReportScore(NetworkSyncManager* n, int playerID, int step, int m_packet.Write2((uint16_t)iOffset); NetPlayerClient->SendPack((char*)m_packet.Data, m_packet.Position); } +void NetworkSyncManager::ReportHighScore(HighScore* hs, PlayerStageStats& pss) +{ + if (curProtocol != nullptr) + curProtocol->ReportHighScore(hs, pss); +} + +void ETTProtocol::Send(const char* msg) +{ + if (ws != nullptr) + ws->send(msg); +} +void ETTProtocol::ReportHighScore(HighScore* hs, PlayerStageStats& pss) +{ + if (ws == nullptr) + return; + json j; + j["type"] = ettClientMessageMap[ettpc_sendscore]; + auto& payload = j["payload"]; + payload["scorekey"] = hs->GetScoreKey(); + payload["ssr_norm"] = hs->GetSSRNormPercent(); + payload["max_combo"] = hs->GetMaxCombo(); + payload["valid"] = static_cast(hs->GetEtternaValid()); + payload["mods"] = hs->GetModifiers(); + payload["miss"] = hs->GetTapNoteScore(TNS_Miss); + payload["bad"] = hs->GetTapNoteScore(TNS_W5); + payload["good"] = hs->GetTapNoteScore(TNS_W4); + payload["great"] = hs->GetTapNoteScore(TNS_W3); + payload["perfect"] = hs->GetTapNoteScore(TNS_W2); + payload["marv"] = hs->GetTapNoteScore(TNS_W1); + payload["datetime"] = string(hs->GetDateTime().GetString().c_str()); + payload["hitmine"] = hs->GetTapNoteScore(TNS_HitMine); + payload["held"] = hs->GetHoldNoteScore(HNS_Held); + payload["letgo"] = hs->GetHoldNoteScore(HNS_LetGo); + payload["ng"] = hs->GetHoldNoteScore(HNS_Missed); + payload["chartkey"] = hs->GetChartKey(); + payload["rate"] = hs->GetMusicRate(); + if(GAMESTATE->m_pPlayerState[PLAYER_1]!=nullptr) + payload["options"] = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetCurrent().GetString(); + auto chart = SONGMAN->GetStepsByChartkey(hs->GetChartKey()); + payload["negsolo"] = chart->GetTimingData()->HasWarps() || chart->m_StepsType != StepsType_dance_single; + payload["nocc"] = static_cast(!hs->GetChordCohesion()); + payload["calc_version"] = hs->GetSSRCalcVersion(); + payload["topscore"] = hs->GetTopScore(); + payload["uuid"] = hs->GetMachineGuid(); + payload["hash"] = hs->GetValidationKey(ValidationKey_Brittle); + auto& offsets = pss.GetOffsetVector(); + auto& noterows = pss.GetNoteRowVector(); + if (offsets.size() > 0) { + payload["replay"] = json::object(); + payload["replay"]["noterows"] = json::array(); + payload["replay"]["offsets"] = json::array(); + for (int i = 0; i < noterows.size(); i++) + payload["replay"]["noterows"].push_back(noterows[i]); + for (int i = 0; i < offsets.size(); i++) + payload["replay"]["offsets"].push_back(offsets[i]); + } + Send(j.dump().c_str()); +} void NetworkSyncManager::ReportScore(int playerID, int step, int score, int combo, float offset) { if (curProtocol != nullptr) @@ -1124,7 +1288,7 @@ void ETTProtocol::ReportSongOver(NetworkSyncManager* n) json gameOver; gameOver["type"] = ettClientMessageMap[ettpc_gameover]; gameOver["id"] = msgId++; - ws->send(gameOver.dump().c_str()); + Send(gameOver.dump().c_str()); } void SMOProtocol::ReportSongOver(NetworkSyncManager* n) { @@ -1157,26 +1321,11 @@ void SMOProtocol::ReportStyle(NetworkSyncManager* n) void NetworkSyncManager::StartRequest(short position) { + //This needs to be reset before ScreenEvaluation could possibly be called + m_EvalPlayerData.clear(); if (curProtocol != nullptr) curProtocol->StartRequest(this, position); } -void ETTProtocol::StartRequest(NetworkSyncManager* n, short position) -{ - if (ws == nullptr || position == 0) - return; - json startChart; - startChart["type"] = ettClientMessageMap[ettpc_startchart]; - auto& payload = startChart["payload"]; - payload["title"] = GAMESTATE->m_pCurSong->m_sMainTitle; - payload["subtitle"] = GAMESTATE->m_pCurSong->m_sSubTitle; - payload["artist"] = GAMESTATE->m_pCurSong->m_sArtist; - payload["filehash"] = GAMESTATE->m_pCurSong->GetFileHash(); - payload["chartkey"] = GAMESTATE->m_pCurSteps[PLAYER_1]->GetChartKey(); - payload["options"] = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetCurrent().GetString(); - payload["rate"] = static_cast((GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate * 1000)); - startChart["id"] = msgId++; - ws->send(startChart.dump().c_str()); -} void SMOProtocol::StartRequest(NetworkSyncManager* n, short position) { if( !n->useSMserver ) @@ -1262,8 +1411,6 @@ void SMOProtocol::StartRequest(NetworkSyncManager* n, short position) m_packet.WriteNT(GAMESTATE->m_pCurSong->GetFileHash()); } - //This needs to be reset before ScreenEvaluation could possibly be called - n->m_EvalPlayerData.clear(); //Block until go is recieved. //Switch to blocking mode (this is the only @@ -1483,6 +1630,7 @@ void SMOProtocol::ProcessInput(NetworkSyncManager* n) break; case NSCRSG: //Select Song/Play song { + n->m_EvalPlayerData.clear(); n->m_iSelectMode = m_packet.Read1(); n->m_sMainTitle = m_packet.ReadNT(); n->m_sArtist = m_packet.ReadNT(); @@ -1603,17 +1751,34 @@ int NetworkSyncManager::GetServerVersion() } void NetworkSyncManager::SelectUserSong() { + m_EvalPlayerData.clear(); if (curProtocol != nullptr) curProtocol->SelectUserSong(this, GAMESTATE->m_pCurSong); } void ETTProtocol::SelectUserSong(NetworkSyncManager* n, Song* song) { - if (ws == nullptr) + if (ws == nullptr || song == nullptr) return; if (song == n->song) { - this->StartRequest(n, 1); + if (GAMESTATE->m_pCurSong == nullptr || + GAMESTATE->m_pCurSteps[PLAYER_1] == nullptr || GAMESTATE->m_pPlayerState[PLAYER_1] == nullptr) + return; + json startChart; + startChart["type"] = ettClientMessageMap[ettpc_startchart]; + auto& payload = startChart["payload"]; + payload["title"] = GAMESTATE->m_pCurSong->m_sMainTitle; + payload["subtitle"] = GAMESTATE->m_pCurSong->m_sSubTitle; + payload["artist"] = GAMESTATE->m_pCurSong->m_sArtist; + payload["filehash"] = GAMESTATE->m_pCurSong->GetFileHash(); + payload["chartkey"] = GAMESTATE->m_pCurSteps[PLAYER_1]->GetChartKey(); + payload["options"] = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetCurrent().GetString(); + payload["rate"] = static_cast((GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate * 1000)); + startChart["id"] = msgId++; + Send(startChart.dump().c_str()); } else { + if (GAMESTATE->m_pCurSteps[PLAYER_1] == nullptr) + return; json selectChart; selectChart["type"] = ettClientMessageMap[ettpc_selectchart]; auto& payload = selectChart["payload"]; @@ -1627,7 +1792,7 @@ void ETTProtocol::SelectUserSong(NetworkSyncManager* n, Song* song) payload["options"] = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetCurrent().GetString(); payload["rate"] = static_cast((GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate * 1000)); selectChart["id"] = msgId++; - ws->send(selectChart.dump().c_str()); + Send(selectChart.dump().c_str()); } } void SMOProtocol::SelectUserSong(NetworkSyncManager * n, Song* song) diff --git a/src/NetworkSyncManager.h b/src/NetworkSyncManager.h index 1b2db10158..70c568f60e 100644 --- a/src/NetworkSyncManager.h +++ b/src/NetworkSyncManager.h @@ -8,7 +8,9 @@ #include "ScreenNetSelectMusic.h" #include "ScreenSMOnlineLogin.h" #include "PlayerState.h" +#include "PlayerStageStats.h" #include "Song.h" +#include "HighScore.h" #include "global.h" #include #include "uWS.h" @@ -19,6 +21,7 @@ using json = nlohmann::json; class LoadingWindow; const int NETPROTOCOLVERSION=4; +const int ETTPCVERSION = 1; const int NETMAXBUFFERSIZE=1020; //1024 - 4 bytes for EzSockets const int NETNUMTAPSCORES=8; @@ -67,13 +70,16 @@ enum SMOStepType const NSCommand NSServerOffset = (NSCommand)128; // TODO: Provide a Lua binding that gives access to this data. -aj -struct EndOfGame_PlayerData +class EndOfGame_PlayerData { +public: int name; - int score; + string nameStr; int grade; + int score; Difficulty difficulty; int tapScores[NETNUMTAPSCORES]; //This will be a const soon enough + HighScore hs; RString playerOptions; }; @@ -101,6 +107,7 @@ enum ETTServerMessageTypes { ettps_deleteroom, ettps_newroom, ettps_updateroom, + ettps_roomuserlist, ettps_end }; enum ETTClientMessageTypes { @@ -124,6 +131,7 @@ enum ETTClientMessageTypes { ettpc_openeval, ettpc_closeeval, ettpc_logout, + ettpc_hello, ettpc_end }; /** @brief A special foreach loop going through each NSScoreBoardColumn. */ @@ -178,6 +186,7 @@ class NetProtocol { virtual void ReportNSSOnOff(int i) {} virtual void ReportScore(NetworkSyncManager* n, int playerID, int step, int score, int combo, float offset, int numNotes) {} virtual void ReportScore(NetworkSyncManager* n, int playerID, int step, int score, int combo, float offset) {} + virtual void ReportHighScore(HighScore* hs, PlayerStageStats& pss) {} virtual void ReportSongOver(NetworkSyncManager* n) {} virtual void ReportStyle(NetworkSyncManager* n) {} virtual void StartRequest(NetworkSyncManager* n, short position) {} @@ -233,6 +242,7 @@ class SMOProtocol : public NetProtocol { // Built on raw tcp static void DealWithSMOnlinePack(PacketFunctions &SMOnlinePacket, ScreenNetRoom* s); static int DealWithSMOnlinePack(PacketFunctions &SMOnlinePacket, ScreenSMOnlineLogin* s, RString& response); }; + class ETTProtocol : public NetProtocol { // Websockets using uwebsockets sending json uWS::Hub uWSh; vector newMessages; @@ -240,6 +250,7 @@ class ETTProtocol : public NetProtocol { // Websockets using uwebsockets sending bool error{ false }; uWS::WebSocket* ws; void FindJsonChart(NetworkSyncManager* n, json& ch); + int state = 0; // 0 = ready, 1 = playing, 2 = evalScreen, 3 = options, 4 = notReady(unkown reason) public: string roomName; string roomDesc; @@ -254,11 +265,12 @@ class ETTProtocol : public NetProtocol { // Websockets using uwebsockets sending void LeaveRoom(NetworkSyncManager* n) override; void ReportSongOver(NetworkSyncManager* n) override; void SelectUserSong(NetworkSyncManager* n, Song* song) override; - void StartRequest(NetworkSyncManager* n, short position) override; void OnOptions() override; void OffOptions() override; void OnEval() override; void OffEval() override; + void ReportHighScore(HighScore* hs, PlayerStageStats& pss) override; + void ETTProtocol::Send(const char* msg); /* void ReportScore(NetworkSyncManager* n, int playerID, int step, int score, int combo, float offset, int numNotes) override; void ReportScore(NetworkSyncManager* n, int playerID, int step, int score, int combo, float offset) override; @@ -289,6 +301,7 @@ class NetworkSyncManager // If "useSMserver" then send score to server void ReportScore( int playerID, int step, int score, int combo, float offset ); void ReportScore(int playerID, int step, int score, int combo, float offset, int numNotes); + void ReportHighScore(HighScore* hs, PlayerStageStats& pss); void ReportSongOver(); void ReportStyle(); // Report style, players, and names void ReportNSSOnOff( int i ); // Report song selection screen on/off diff --git a/src/PlayerStageStats.cpp b/src/PlayerStageStats.cpp index b1056beca3..793e45cf3d 100644 --- a/src/PlayerStageStats.cpp +++ b/src/PlayerStageStats.cpp @@ -208,6 +208,10 @@ Grade PlayerStageStats::GetWifeGrade() { return Grade_Tier07; } +Grade PlayerStageStats::GetGrade(float p) +{ + return GetGradeFromPercent(p); +} Grade PlayerStageStats::GetGrade() const { if( m_bFailed ) diff --git a/src/PlayerStageStats.h b/src/PlayerStageStats.h index c5195b936e..6966c366b2 100644 --- a/src/PlayerStageStats.h +++ b/src/PlayerStageStats.h @@ -27,6 +27,7 @@ class PlayerStageStats * @param other the other stats to add to this one. */ void AddStats( const PlayerStageStats& other ); // accumulate + static Grade GetGrade(float p); Grade GetGrade() const; static float MakePercentScore( int iActual, int iPossible ); static RString FormatPercentScore( float fPercentScore ); diff --git a/src/ScreenNetEvaluation.cpp b/src/ScreenNetEvaluation.cpp index 529088e6ba..ca556f14e6 100644 --- a/src/ScreenNetEvaluation.cpp +++ b/src/ScreenNetEvaluation.cpp @@ -20,6 +20,7 @@ static const int NUM_SCORE_DIGITS = 9; #define MAX_COMBO_NUM_DIGITS THEME->GetMetricI("ScreenEvaluation","MaxComboNumDigits") static AutoScreenMessage( SM_GotEval ); +AutoScreenMessage(ETTP_NewScore); REGISTER_SCREEN_CLASS( ScreenNetEvaluation ); @@ -124,7 +125,7 @@ bool ScreenNetEvaluation::MenuDown( const InputEventPlus &input ) void ScreenNetEvaluation::HandleScreenMessage( const ScreenMessage SM ) { - if( SM == SM_GotEval) + if( SM == SM_GotEval || SM == ETTP_NewScore) { m_bHasStats = true; @@ -141,21 +142,24 @@ void ScreenNetEvaluation::HandleScreenMessage( const ScreenMessage SM ) if ( size_t(i) >= NSMAN->m_EvalPlayerData.size() ) break; - if ( size_t(NSMAN->m_EvalPlayerData[i].name) >= NSMAN->m_PlayerNames.size() ) + if ( NSMAN->m_EvalPlayerData[i].nameStr.empty() && + size_t(NSMAN->m_EvalPlayerData[i].name) >= NSMAN->m_PlayerNames.size() ) break; - if ( NSMAN->m_EvalPlayerData[i].name < 0 ) + if ( NSMAN->m_EvalPlayerData[i].nameStr.empty() && + NSMAN->m_EvalPlayerData[i].name < 0 ) break; if ( size_t(i) >= m_textUsers.size() ) break; - m_textUsers[i].SetText( NSMAN->m_PlayerNames[NSMAN->m_EvalPlayerData[i].name] ); + m_textUsers[i].SetText(NSMAN->m_EvalPlayerData[i].nameStr.empty() ? + NSMAN->m_PlayerNames[NSMAN->m_EvalPlayerData[i].name] : NSMAN->m_EvalPlayerData[i].nameStr); // Yes, hardcoded (I'd like to leave it that way) -CNLohr (in reference to Grade_Tier03) // Themes can read this differently. The correct solution depends... // TODO: make this a server-side variable. -aj - if( NSMAN->m_EvalPlayerData[i].grade < Grade_Tier03 ) + if(NSMAN->m_EvalPlayerData[m_iCurrentPlayer].hs.GetGrade() != Grade_NoData ? NSMAN->m_EvalPlayerData[m_iCurrentPlayer].hs.GetGrade() : NSMAN->m_EvalPlayerData[m_iCurrentPlayer].grade < Grade_Tier03 ) m_textUsers[i].PlayCommand("Tier02OrBetter"); ON_COMMAND( m_textUsers[i] ); @@ -186,7 +190,7 @@ void ScreenNetEvaluation::UpdateStats() // Only run these commands if the theme has these things shown; not every // theme has them, so don't assume. -aj if( THEME->GetMetricB(m_sName,"ShowGradeArea") ) - m_Grades[m_pActivePlayer].SetGrade( (Grade)NSMAN->m_EvalPlayerData[m_iCurrentPlayer].grade ); + m_Grades[m_pActivePlayer].SetGrade( static_cast(NSMAN->m_EvalPlayerData[m_iCurrentPlayer].hs.GetGrade() != Grade_NoData ? NSMAN->m_EvalPlayerData[m_iCurrentPlayer].hs.GetGrade() : NSMAN->m_EvalPlayerData[m_iCurrentPlayer].grade)); if( THEME->GetMetricB(m_sName,"ShowScoreArea") ) m_textScore[m_pActivePlayer].SetTargetNumber( NSMAN->m_EvalPlayerData[m_iCurrentPlayer].score ); @@ -215,11 +219,11 @@ void ScreenNetEvaluation::UpdateStats() // broadcast a message so themes know that the active player has changed. -aj Message msg("UpdateNetEvalStats"); - msg.SetParam( "ActivePlayerIndex", m_pActivePlayer ); - msg.SetParam( "Difficulty", NSMAN->m_EvalPlayerData[m_iCurrentPlayer].difficulty ); - msg.SetParam( "Score", NSMAN->m_EvalPlayerData[m_iCurrentPlayer].score ); - msg.SetParam( "Grade", NSMAN->m_EvalPlayerData[m_iCurrentPlayer].grade ); - msg.SetParam( "PlayerOptions", NSMAN->m_EvalPlayerData[m_iCurrentPlayer].playerOptions ); + msg.SetParam("ActivePlayerIndex", m_pActivePlayer); + msg.SetParam("Difficulty", NSMAN->m_EvalPlayerData[m_iCurrentPlayer].difficulty); + msg.SetParam("Score", NSMAN->m_EvalPlayerData[m_iCurrentPlayer].score); + msg.SetParam("Grade", NSMAN->m_EvalPlayerData[m_iCurrentPlayer].hs.GetGrade() != Grade_NoData ? NSMAN->m_EvalPlayerData[m_iCurrentPlayer].hs.GetGrade() : NSMAN->m_EvalPlayerData[m_iCurrentPlayer].grade); + msg.SetParam("PlayerOptions", NSMAN->m_EvalPlayerData[m_iCurrentPlayer].playerOptions); if( pSteps ) msg.SetParam( "Steps", pSteps ); MESSAGEMAN->Broadcast(msg); @@ -233,10 +237,25 @@ class LunaScreenNetEvaluation: public Luna { public: static int GetNumActivePlayers( T* p, lua_State *L ) { lua_pushnumber( L, p->GetNumActivePlayers() ); return 1; } - + static int GetHighScore(T* p, lua_State *L) { + if (NSMAN->m_EvalPlayerData.size() - 1 >= p->m_iCurrentPlayer) + NSMAN->m_EvalPlayerData[p->m_iCurrentPlayer].hs.PushSelf(L); + else + lua_pushnil(L); + return 1; + } + static int GetOptions(T* p, lua_State *L) { + if (NSMAN->m_EvalPlayerData.size() - 1 >= p->m_iCurrentPlayer) + lua_pushstring(L, NSMAN->m_EvalPlayerData[p->m_iCurrentPlayer].playerOptions); + else + lua_pushnil(L); + return 1; + } LunaScreenNetEvaluation() { - ADD_METHOD( GetNumActivePlayers ); + ADD_METHOD(GetNumActivePlayers); + ADD_METHOD(GetHighScore); + ADD_METHOD(GetOptions); } }; diff --git a/src/ScreenNetEvaluation.h b/src/ScreenNetEvaluation.h index 839c4af548..d47d9265c4 100644 --- a/src/ScreenNetEvaluation.h +++ b/src/ScreenNetEvaluation.h @@ -17,6 +17,7 @@ class ScreenNetEvaluation: public ScreenEvaluation // Lua void PushSelf( lua_State *L ) override; + int m_iCurrentPlayer; protected: bool MenuLeft( const InputEventPlus &input ) override; bool MenuUp( const InputEventPlus &input ) override; @@ -35,7 +36,6 @@ class ScreenNetEvaluation: public ScreenEvaluation //StepsDisplay m_StepsDisplays[NUM_PLAYERS]; vector m_textUsers; - int m_iCurrentPlayer; int m_iActivePlayers; PlayerNumber m_pActivePlayer; diff --git a/src/StageStats.cpp b/src/StageStats.cpp index 5da31074e2..c13a81e166 100644 --- a/src/StageStats.cpp +++ b/src/StageStats.cpp @@ -10,6 +10,7 @@ #include "Style.h" #include "Profile.h" #include "ProfileManager.h" +#include "NetworkSyncManager.h" #include #include #include "CryptManager.h" @@ -603,6 +604,8 @@ void StageStats::FinalizeScores(bool bSummary) hs.timeStamps.clear(); hs.timeStamps.shrink_to_fit(); } + if(NSMAN->isSMOnline) + NSMAN->ReportHighScore(&hs, m_player[PLAYER_1]); if (m_player[PLAYER_1].m_fWifeScore > 0.f) { bool writesuccess = hs.WriteReplayData(); if (writesuccess)