diff --git a/AquaMai/AquaMai.csproj b/AquaMai/AquaMai.csproj
index 700ae39a..d5e9722e 100644
--- a/AquaMai/AquaMai.csproj
+++ b/AquaMai/AquaMai.csproj
@@ -277,6 +277,7 @@
+
diff --git a/AquaMai/AquaMai.toml b/AquaMai/AquaMai.toml
index c31a103e..05ce634f 100644
--- a/AquaMai/AquaMai.toml
+++ b/AquaMai/AquaMai.toml
@@ -33,8 +33,10 @@ DemoMaster=true
# Execute some command on game idle or on game start
ExecOnIdle=""
ExecOnEntry=""
-# Change every timer to 200 seconds
+# Disable timers
ExtendTimer=true
+# Save immediate after playing a song
+ImmediateSave=true
[Performance]
# Disable some useless delays to speed up the game boot process
diff --git a/AquaMai/Config.cs b/AquaMai/Config.cs
index e340fdc4..7d036dbc 100644
--- a/AquaMai/Config.cs
+++ b/AquaMai/Config.cs
@@ -27,6 +27,7 @@ namespace AquaMai
public bool DemoMaster { get; set; }
public bool ExtendTimer { get; set; }
public bool SkipEventInfo { get; set; }
+ public bool ImmediateSave { get; set; }
public string CustomVersionString { get; set; }
public string ExecOnIdle { get; set; }
public string ExecOnEntry { get; set; }
diff --git a/AquaMai/UX/ImmediateSave.cs b/AquaMai/UX/ImmediateSave.cs
new file mode 100644
index 00000000..a1203fd2
--- /dev/null
+++ b/AquaMai/UX/ImmediateSave.cs
@@ -0,0 +1,192 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using DB;
+using HarmonyLib;
+using MAI2.Util;
+using MAI2System;
+using Manager;
+using Manager.MaiStudio;
+using Manager.UserDatas;
+using MelonLoader;
+using Monitor.Entry.Parts.Screens;
+using Net.Packet;
+using Net.Packet.Helper;
+using Net.Packet.Mai2;
+using Process;
+using Process.UserDataNet.State.UserDataULState;
+
+namespace AquaMai.UX
+{
+ public class ImmediateSave
+ {
+ [HarmonyPrefix]
+ [HarmonyPatch(typeof(StateULUserAime), "RequestUploadUserPlayLogData")]
+ public static bool PreRequestUploadUserPlayLogData(StateULUserAime __instance)
+ {
+ Traverse.Create(__instance).Method("RequestUploadUserPortraitData").GetValue();
+ return false;
+ }
+
+ [HarmonyPostfix]
+ [HarmonyPatch(typeof(ResultProcess), "OnStart")]
+ public static void ResultProcessOnStart()
+ {
+ SaveDataFix();
+ var userData = Singleton.Instance.GetUserData(0);
+ var accessToken = Singleton.Instance.GetAccessToken(0);
+ PacketHelper.StartPacket(new PacketUploadUserPlaylog(0, userData, (int)GameManager.MusicTrackNumber - 1, accessToken,
+ delegate { MelonLogger.Msg("Playlog saved"); },
+ delegate(PacketStatus err)
+ {
+ SoundManager.PlaySE(Mai2.Mai2Cue.Cue.SE_ENTRY_AIME_ERROR, 0);
+ MelonLogger.Error("Playlog save error");
+ MelonLogger.Error(err);
+ }));
+ PacketHelper.StartPacket(new PacketUpsertUserAll(0, userData, accessToken, delegate(int code)
+ {
+ if (code == 1)
+ {
+ MelonLogger.Msg("UserAll saved");
+ }
+ else
+ {
+ SoundManager.PlaySE(Mai2.Mai2Cue.Cue.SE_ENTRY_AIME_ERROR, 0);
+ MelonLogger.Error("UserAll upsert error");
+ MelonLogger.Error(code);
+ }
+ }, delegate(PacketStatus err)
+ {
+ SoundManager.PlaySE(Mai2.Mai2Cue.Cue.SE_ENTRY_AIME_ERROR, 0);
+ MelonLogger.Error("UserAll upsert error");
+ MelonLogger.Error(err);
+ }));
+ }
+
+
+ private static void SaveDataFix()
+ {
+ UserData userData = Singleton.Instance.GetUserData(0);
+
+ UserDetail detail = userData.Detail;
+ _ = userData.ScoreList;
+ userData.AddPlayCount();
+ detail.EventWatchedDate = TimeManager.GetDateString(TimeManager.PlayBaseTime);
+ userData.CalcTotalValue();
+ float num = 0f;
+ try
+ {
+ if (userData.RatingList.RatingList.Any())
+ {
+ num = userData.RatingList.RatingList.Last().SingleRate;
+ }
+ }
+ catch
+ {
+ }
+
+ num = (float)Math.Ceiling((double)((num + 1f) / GameManager.TheoryRateBorderNum) * 10.0);
+ float num2 = 0f;
+ try
+ {
+ if (userData.RatingList.NextRatingList.Any())
+ {
+ num2 = userData.RatingList.NewRatingList.Last().SingleRate;
+ }
+ }
+ catch
+ {
+ }
+
+ num2 = (float)Math.Ceiling((double)((num2 + 1f) / GameManager.TheoryRateBorderNum) * 10.0);
+ string logDateString = TimeManager.GetLogDateString(TimeManager.PlayBaseTime);
+ string timeJp = (string.IsNullOrEmpty(userData.Detail.DailyBonusDate) ? TimeManager.GetLogDateString(0L) : userData.Detail.DailyBonusDate);
+ if (userData.IsEntry && userData.Detail.IsNetMember >= 2 && !GameManager.IsEventMode && TimeManager.GetUnixTime(logDateString) > TimeManager.GetUnixTime(timeJp) && Singleton.Instance.IsSingleUser() && !GameManager.IsFreedomMode && !GameManager.IsCourseMode && !DoneEntry.IsWeekdayBonus(userData))
+ {
+ userData.Detail.DailyBonusDate = logDateString;
+ }
+
+ List list = new List();
+ List list2 = new List();
+ List[] scoreList = userData.ScoreList;
+ List ratingList = userData.RatingList.RatingList;
+ List newRatingList = userData.RatingList.NewRatingList;
+ int achive = RatingTableID.Rate_22.GetAchive();
+ for (int j = 0; j < scoreList.Length; j++)
+ {
+ if (scoreList[j] == null)
+ {
+ continue;
+ }
+
+ foreach (UserScore item2 in scoreList[j])
+ {
+ if (achive <= item2.achivement)
+ {
+ continue;
+ }
+
+ MusicData music = Singleton.Instance.GetMusic(item2.id);
+ if (music == null)
+ {
+ continue;
+ }
+
+ UserRate item = new UserRate(item2.id, j, item2.achivement, (uint)music.version);
+ if (item.OldFlag)
+ {
+ if (num <= (float)item.Level && !ratingList.Contains(item))
+ {
+ list.Add(item);
+ }
+ }
+ else if (num2 <= (float)item.Level && !newRatingList.Contains(item))
+ {
+ list2.Add(item);
+ }
+ }
+ }
+
+ list.Sort();
+ list.Reverse();
+ if (list.Count > 10)
+ {
+ list.RemoveRange(10, list.Count - 10);
+ }
+
+ userData.RatingList.NextRatingList = list;
+ list2.Sort();
+ list2.Reverse();
+ if (list2.Count > 10)
+ {
+ list2.RemoveRange(10, list2.Count - 10);
+ }
+
+ userData.RatingList.NextNewRatingList = list2;
+ int num3 = SingletonStateMachine.Instance.Credit.GameCostEnoughPlay();
+ if (GameManager.IsFreedomMode)
+ {
+ num3 = SingletonStateMachine.Instance.Credit.GameCostEnoughFreedom();
+ }
+
+ int boughtTicketId = Singleton.Instance.GetBoughtTicketId(0);
+ int ticketCredit = Singleton.Instance.GetTicketCredit(boughtTicketId);
+ int lastPlayCredit = num3 + ticketCredit;
+ userData.Detail.LastPlayCredit = lastPlayCredit;
+ userData.Detail.LastPlayMode = 0;
+ if (GameManager.IsFreedomMode)
+ {
+ userData.Detail.LastPlayMode = 1;
+ }
+
+ if (GameManager.IsCourseMode)
+ {
+ userData.Detail.LastPlayMode = 2;
+ }
+
+ userData.Detail.LastGameId = "SDEZ";
+ userData.Detail.LastRomVersion = Singleton.Instance.config.romVersionInfo.versionNo.versionString;
+ userData.Detail.LastDataVersion = Singleton.Instance.config.dataVersionInfo.versionNo.versionString;
+ }
+ }
+}
\ No newline at end of file