Skip to content

Commit

Permalink
#431: added options for auto-updating song offsets
Browse files Browse the repository at this point in the history
  • Loading branch information
123jimin committed Mar 27, 2021
1 parent c93a381 commit f967664
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 88 deletions.
19 changes: 16 additions & 3 deletions Main/include/GameConfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
#include "Shared/Config.hpp"
#include "Input.hpp"

#ifdef Always
#undef Always
#endif

#ifdef None
#undef None
#endif

DefineEnum(GameConfigKeys,
// Version of the config
ConfigVersion,
Expand Down Expand Up @@ -58,6 +66,8 @@ DefineEnum(GameConfigKeys,
DisableBackgrounds,
ScoreDisplayMode,
AutoComputeSongOffset,
UpdateSongOffsetAfterFirstPlay,
UpdateSongOffsetAfterEveryPlay,

LeadInTime,
PracticeLeadInTime,
Expand Down Expand Up @@ -204,14 +214,17 @@ DefineEnum(LaserAxisOption,
Right,
Both)

#ifdef Always
#undef Always
#endif
DefineEnum(AutoScoreScreenshotSettings,
Off,
Highscore,
Always)

DefineEnum(SongOffsetUpdateMethod,
None,
Play,
PlayWholeChart,
Clear)

DefineEnum(ButtonComboModeSettings,
Disabled,
Hold,
Expand Down
2 changes: 2 additions & 0 deletions Main/src/GameConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ void GameConfig::InitDefaults()
Set(GameConfigKeys::RevertToSetupAfterScoreScreen, false);
Set(GameConfigKeys::DisplayPracticeInfoInGame, true);
Set(GameConfigKeys::AutoComputeSongOffset, false);
SetEnum<Enum_SongOffsetUpdateMethod>(GameConfigKeys::UpdateSongOffsetAfterFirstPlay, SongOffsetUpdateMethod::None);
SetEnum<Enum_SongOffsetUpdateMethod>(GameConfigKeys::UpdateSongOffsetAfterEveryPlay, SongOffsetUpdateMethod::None);

SetEnum<Logger::Enum_Severity>(GameConfigKeys::LogLevel, Logger::Severity::Normal);

Expand Down
243 changes: 159 additions & 84 deletions Main/src/ScoreScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,164 @@ class ScoreScreen_Impl : public ScoreScreen
lua_settable(m_lua, -3);
}

void m_AddNewScore(class Game* game)
{
ScoreIndex* newScore = new ScoreIndex();
ChartIndex* chart = game->GetChartIndex();

// If chart file can't be opened, use existing hash.
String hash = chart->hash;

File chartFile;
if (chartFile.OpenRead(chart->path))
{
char data_buffer[0x80];
uint32_t digest[5];
sha1::SHA1 s;

size_t amount_read = 0;
size_t read_size;
do
{
read_size = chartFile.Read(data_buffer, sizeof(data_buffer));
amount_read += read_size;
s.processBytes(data_buffer, read_size);
} while (read_size != 0);

s.getDigest(digest);
hash = Utility::Sprintf("%08x%08x%08x%08x%08x", digest[0], digest[1], digest[2], digest[3], digest[4]);
}
else
{
Log("Couldn't open the chart file for hashing, using existing hash.", Logger::Severity::Warning);
}

m_chartHash = hash;

Path::CreateDir(Path::Absolute("replays/" + hash));
m_replayPath = Path::Normalize(Path::Absolute("replays/" + chart->hash + "/" + Shared::Time::Now().ToString() + ".urf"));
File replayFile;

if (replayFile.OpenWrite(m_replayPath))
{
FileWriter fw(replayFile);
fw.SerializeObject(m_simpleHitStats);
fw.Serialize(&(m_hitWindow.perfect), 4);
fw.Serialize(&(m_hitWindow.good), 4);
fw.Serialize(&(m_hitWindow.hold), 4);
fw.Serialize(&(m_hitWindow.miss), 4);
}

newScore->score = m_score;
newScore->crit = m_categorizedHits[2];
newScore->almost = m_categorizedHits[1];
newScore->miss = m_categorizedHits[0];
newScore->gauge = m_finalGaugeValue;

newScore->gaugeType = m_gaugeType;
newScore->gaugeOption = m_gaugeOption;
newScore->mirror = m_options.mirror;
newScore->random = m_options.random;
newScore->autoFlags = m_options.autoFlags;

newScore->timestamp = Shared::Time::Now().Data();
newScore->replayPath = m_replayPath;
newScore->chartHash = hash;
newScore->userName = g_gameConfig.GetString(GameConfigKeys::MultiplayerUsername);
newScore->localScore = true;

newScore->hitWindowPerfect = m_hitWindow.perfect;
newScore->hitWindowGood = m_hitWindow.good;
newScore->hitWindowHold = m_hitWindow.hold;
newScore->hitWindowMiss = m_hitWindow.miss;

m_mapDatabase.AddScore(newScore);

if (g_gameConfig.GetString(GameConfigKeys::IRBaseURL) != "")
{
m_irState = IR::ResponseState["Pending"];
m_irResponse = IR::PostScore(*newScore, m_beatmapSettings);
}

const bool cleared = Scoring::CalculateBadge(*newScore) >= ClearMark::NormalClear;
const bool wholeChartPlayed = cleared || newScore->gaugeType == GaugeType::Normal;

bool firstClear = cleared;
bool firstPlayWholeChart = wholeChartPlayed;
bool firstPlay = chart->scores.empty();

if (wholeChartPlayed)
{
for (const ScoreIndex* oldScore : chart->scores)
{
if (oldScore->gaugeType == GaugeType::Normal)
{
firstPlayWholeChart = false;
}

const ClearMark clearMark = Scoring::CalculateBadge(*oldScore);
if (clearMark >= ClearMark::NormalClear)
{
firstPlayWholeChart = false;
firstClear = false;
break;
}
}
}

chart->scores.Add(newScore);
chart->scores.Sort([](ScoreIndex* a, ScoreIndex* b)
{
return a->score > b->score;
});

// Update chart song offset
bool updateSongOffset = false;
switch (g_gameConfig.GetEnum<Enum_SongOffsetUpdateMethod>(GameConfigKeys::UpdateSongOffsetAfterFirstPlay))
{
case SongOffsetUpdateMethod::None:
updateSongOffset = false;
break;
case SongOffsetUpdateMethod::Play:
updateSongOffset = firstPlay;
break;
case SongOffsetUpdateMethod::PlayWholeChart:
updateSongOffset = firstPlayWholeChart;
break;
case SongOffsetUpdateMethod::Clear:
updateSongOffset = firstClear;
break;
}

if (!updateSongOffset)
{
switch (g_gameConfig.GetEnum<Enum_SongOffsetUpdateMethod>(GameConfigKeys::UpdateSongOffsetAfterEveryPlay))
{
case SongOffsetUpdateMethod::None:
updateSongOffset = false;
break;
case SongOffsetUpdateMethod::Play:
updateSongOffset = true;
break;
case SongOffsetUpdateMethod::PlayWholeChart:
updateSongOffset = wholeChartPlayed;
break;
case SongOffsetUpdateMethod::Clear:
updateSongOffset = cleared;
break;
}
}

if (updateSongOffset)
{
const int oldOffset = chart->custom_offset;
chart->custom_offset = oldOffset + m_medianHitDelta[0];

Logf("Updating song offset %d -> %d based on gameplay stat", Logger::Severity::Info, oldOffset, chart->custom_offset);
m_mapDatabase.UpdateChartOffset(chart);
}
}

public:

void loadScoresFromGame(class Game* game)
Expand Down Expand Up @@ -258,8 +416,6 @@ class ScoreScreen_Impl : public ScoreScreen
m_medianHitDelta[1] = scoring.GetMedianHitDelta(true);

m_hitWindow = scoring.hitWindow;


}

void loadScoresFromMultiplayer() {
Expand Down Expand Up @@ -411,92 +567,11 @@ class ScoreScreen_Impl : public ScoreScreen
// also don't save the score if the song was manually exited
if (!m_autoplay && !m_autoButtons && game->GetChartIndex() && game->IsStorableScore())
{
ScoreIndex* newScore = new ScoreIndex();
auto chart = game->GetChartIndex();
String hash = chart->hash; //If chart file can't be opened, use existing hash.


File chartFile;
if (chartFile.OpenRead(chart->path))
{
char data_buffer[0x80];
uint32_t digest[5];
sha1::SHA1 s;

size_t amount_read = 0;
size_t read_size;
do
{
read_size = chartFile.Read(data_buffer, sizeof(data_buffer));
amount_read += read_size;
s.processBytes(data_buffer, read_size);
} while (read_size != 0);

s.getDigest(digest);
hash = Utility::Sprintf("%08x%08x%08x%08x%08x", digest[0], digest[1], digest[2], digest[3], digest[4]);
}
else
{
Log("Couldn't open the chart file for hashing, using existing hash.", Logger::Severity::Warning);
}

m_chartHash = hash;

Path::CreateDir(Path::Absolute("replays/" + hash));
m_replayPath = Path::Normalize(Path::Absolute("replays/" + chart->hash + "/" + Shared::Time::Now().ToString() + ".urf"));
File replayFile;

if (replayFile.OpenWrite(m_replayPath))
{
FileWriter fw(replayFile);
fw.SerializeObject(m_simpleHitStats);
fw.Serialize(&(m_hitWindow.perfect), 4);
fw.Serialize(&(m_hitWindow.good), 4);
fw.Serialize(&(m_hitWindow.hold), 4);
fw.Serialize(&(m_hitWindow.miss), 4);
}

newScore->score = m_score;
newScore->crit = m_categorizedHits[2];
newScore->almost = m_categorizedHits[1];
newScore->miss = m_categorizedHits[0];
newScore->gauge = m_finalGaugeValue;

newScore->gaugeType = m_gaugeType;
newScore->gaugeOption = m_gaugeOption;
newScore->mirror = m_options.mirror;
newScore->random = m_options.random;
newScore->autoFlags = m_options.autoFlags;

newScore->timestamp = Shared::Time::Now().Data();
newScore->replayPath = m_replayPath;
newScore->chartHash = hash;
newScore->userName = g_gameConfig.GetString(GameConfigKeys::MultiplayerUsername);
newScore->localScore = true;

newScore->hitWindowPerfect = m_hitWindow.perfect;
newScore->hitWindowGood = m_hitWindow.good;
newScore->hitWindowHold = m_hitWindow.hold;
newScore->hitWindowMiss = m_hitWindow.miss;

m_mapDatabase.AddScore(newScore);

if(g_gameConfig.GetString(GameConfigKeys::IRBaseURL) != "")
{
m_irState = IR::ResponseState["Pending"];
m_irResponse = IR::PostScore(*newScore, m_beatmapSettings);
}

chart->scores.Add(newScore);
chart->scores.Sort([](ScoreIndex* a, ScoreIndex* b)
{
return a->score > b->score;
});
m_AddNewScore(game);
}

m_startPressed = false;


if (m_challengeManager != nullptr)
{
// Save some score screen info for challenge results later
Expand Down
2 changes: 1 addition & 1 deletion Main/src/SettingsPage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ class SettingsPage_Profile : public SettingsPage

protected:
bool& m_forceReload;

void Load() override
{
m_currentProfile = g_gameConfig.GetString(GameConfigKeys::CurrentProfileName);
Expand Down
3 changes: 3 additions & 0 deletions Main/src/SettingsScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,9 @@ class SettingsPage_Game : public SettingsPage

ToggleSetting(GameConfigKeys::RevertToSetupAfterScoreScreen, "Revert to the practice setup after the score screen is shown");

EnumSetting<Enum_SongOffsetUpdateMethod>(GameConfigKeys::UpdateSongOffsetAfterFirstPlay, "Based on hit stats, update song offset for first:");
EnumSetting<Enum_SongOffsetUpdateMethod>(GameConfigKeys::UpdateSongOffsetAfterEveryPlay, "After having updated first time, update song offset for every:");

SectionHeader("Songs");
Label("Songs folder path:");
m_songsPath.Render(m_nctx);
Expand Down
3 changes: 3 additions & 0 deletions Shared/include/Shared/Jobs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
Additional job flags,
this allows clustering IO operation onto the same thread as to not lock up the system
*/
#ifdef None
#undef None
#endif
enum class JobFlags : uint8
{
None = 0,
Expand Down

0 comments on commit f967664

Please sign in to comment.