加入用户最近100场战绩记录并实现展示

This commit is contained in:
FloatGaming
2026-03-16 23:18:58 +08:00
parent ec42f2e34b
commit f5c6f143c0
106 changed files with 12120 additions and 642 deletions
+340 -82
View File
@@ -10,6 +10,14 @@ public sealed class DailyTaskService : MonoBehaviour
public event Action OnTasksChanged;
private enum ProgressCombineMode
{
Sum,
Max
}
private const float OnlineDurationFlushStepSeconds = 5f;
private readonly Queue<DailyTaskEventData> pendingEvents = new Queue<DailyTaskEventData>();
private readonly Dictionary<string, userTasksPool.TaskDefinition> definitionById = new Dictionary<string, userTasksPool.TaskDefinition>(StringComparer.Ordinal);
@@ -18,6 +26,8 @@ public sealed class DailyTaskService : MonoBehaviour
private int configuredTaskMaxSize = 5;
private int configuredRefreshMaxTimes = 3;
private bool initialized;
private bool appFocused = true;
private float pendingOnlineDurationSeconds;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void Bootstrap()
@@ -54,10 +64,53 @@ public sealed class DailyTaskService : MonoBehaviour
InitializeStorageIfNeeded();
}
private void Update()
{
if (!Application.isPlaying || !appFocused)
{
return;
}
pendingOnlineDurationSeconds += Time.unscaledDeltaTime;
if (pendingOnlineDurationSeconds >= OnlineDurationFlushStepSeconds)
{
float flushSeconds = Mathf.Floor(pendingOnlineDurationSeconds);
pendingOnlineDurationSeconds = Mathf.Max(0f, pendingOnlineDurationSeconds - flushSeconds);
ReportEvent(new DailyTaskEventData
{
taskType = userTasksPool.TaskType.OnlineDuration,
amount = flushSeconds
});
}
}
private void OnApplicationFocus(bool hasFocus)
{
appFocused = hasFocus;
if (!hasFocus)
{
FlushPendingOnlineDuration();
}
}
private void OnApplicationPause(bool pauseStatus)
{
if (pauseStatus)
{
FlushPendingOnlineDuration();
}
}
private void OnApplicationQuit()
{
FlushPendingOnlineDuration();
}
private void OnDestroy()
{
if (Instance == this)
{
FlushPendingOnlineDuration();
SceneManager.sceneLoaded -= OnSceneLoaded;
}
}
@@ -117,42 +170,39 @@ public sealed class DailyTaskService : MonoBehaviour
}
EnsureTodayTasks();
float accumulatedIncrement = GetAccumulatedIncrement(eventData);
if (accumulatedIncrement <= 0f)
bool changed = ApplyEventToTrackedProgress(eventData);
if (saveData != null && saveData.activeTasks != null)
{
return;
}
float accumulatedValue = AddAccumulatedProgress(eventData.taskType, accumulatedIncrement);
bool changed = false;
for (int i = 0; i < saveData.activeTasks.Count; i++)
{
var runtimeEntry = saveData.activeTasks[i];
if (runtimeEntry == null || runtimeEntry.isClaimed)
for (int i = 0; i < saveData.activeTasks.Count; i++)
{
continue;
}
var runtimeEntry = saveData.activeTasks[i];
if (runtimeEntry == null || runtimeEntry.isClaimed)
{
continue;
}
userTasksPool.TaskDefinition definition;
if (!definitionById.TryGetValue(runtimeEntry.taskID, out definition))
{
continue;
}
userTasksPool.TaskDefinition definition;
if (!definitionById.TryGetValue(runtimeEntry.taskID, out definition))
{
continue;
}
if (!IsEventMatch(definition, eventData))
{
continue;
}
if (!IsEventMatch(definition, eventData))
{
continue;
}
float previousProgress = runtimeEntry.progress;
bool previousCompleted = runtimeEntry.isCompleted;
runtimeEntry.progress = GetInitialProgress(definition, accumulatedValue);
runtimeEntry.isCompleted = runtimeEntry.progress >= Mathf.Max(0.0001f, definition.targetValue);
float nextProgress = CalculateProgressForDefinition(definition);
bool nextCompleted = nextProgress >= Mathf.Max(0.0001f, definition.targetValue);
if (!Mathf.Approximately(previousProgress, runtimeEntry.progress) || previousCompleted != runtimeEntry.isCompleted)
{
changed = true;
if (!Mathf.Approximately(runtimeEntry.progress, nextProgress) || runtimeEntry.isCompleted != nextCompleted)
{
runtimeEntry.progress = nextProgress;
runtimeEntry.isCompleted = nextCompleted;
changed = true;
}
}
}
@@ -177,6 +227,7 @@ public sealed class DailyTaskService : MonoBehaviour
}
saveData.refreshUsedCount += 1;
StampRefreshUsage();
GenerateDailyTasks();
Save();
NotifyTasksChanged();
@@ -278,11 +329,7 @@ public sealed class DailyTaskService : MonoBehaviour
{
if (string.Equals(scene.name, "UI_UI", StringComparison.OrdinalIgnoreCase))
{
ReportEvent(new DailyTaskEventData
{
taskType = userTasksPool.TaskType.Login,
amount = 1f
});
DailyTaskEventHub.ReportLogin();
}
}
@@ -294,6 +341,7 @@ public sealed class DailyTaskService : MonoBehaviour
}
saveData = DailyTaskSaveService.Load() ?? new DailyTaskSaveData();
EnsureRuntimeCollections();
initialized = true;
}
@@ -306,25 +354,36 @@ public sealed class DailyTaskService : MonoBehaviour
return;
}
string todayKey = GetTodayKey();
bool timeStateChanged;
int effectiveTodayStamp = GetEffectiveTodayStamp(out timeStateChanged);
string todayKey = FormatDateKey(effectiveTodayStamp);
if (saveData == null)
{
saveData = new DailyTaskSaveData();
}
bool needRegenerate = !string.Equals(saveData.dateKey, todayKey, StringComparison.Ordinal)
EnsureRuntimeCollections();
bool needRegenerate = saveData.dateStamp != effectiveTodayStamp
|| !string.Equals(saveData.dateKey, todayKey, StringComparison.Ordinal)
|| saveData.activeTasks == null
|| saveData.activeTasks.Count == 0;
if (!needRegenerate)
{
EnsureAccumulatedProgressCompatibility();
if (timeStateChanged)
{
Save();
}
return;
}
saveData.dateKey = todayKey;
saveData.dateStamp = effectiveTodayStamp;
saveData.refreshUsedCount = 0;
saveData.accumulatedProgress = new List<DailyTaskAccumulatedProgress>();
saveData.uniqueIntProgress = new List<DailyTaskUniqueIntProgress>();
GenerateDailyTasks();
Save();
}
@@ -348,7 +407,8 @@ public sealed class DailyTaskService : MonoBehaviour
}
int pickCount = Mathf.Min(configuredTaskMaxSize, candidates.Count);
var random = new System.Random(unchecked(GetTodayKey().GetHashCode() + saveData.refreshUsedCount * 397));
int dateSeed = saveData.dateStamp > 0 ? saveData.dateStamp : GetLocalDateStamp(DateTime.Now);
var random = new System.Random(unchecked(dateSeed + saveData.refreshUsedCount * 397));
var selectedDefinitions = new List<userTasksPool.TaskDefinition>();
while (selectedDefinitions.Count < pickCount && candidates.Count > 0)
@@ -374,7 +434,7 @@ public sealed class DailyTaskService : MonoBehaviour
for (int i = 0; i < selectedDefinitions.Count; i++)
{
var definition = selectedDefinitions[i];
float currentProgress = GetInitialProgress(definition, GetAccumulatedProgress(definition.taskType));
float currentProgress = CalculateProgressForDefinition(definition);
saveData.activeTasks.Add(new DailyTaskRuntimeEntry
{
taskID = definition.taskID,
@@ -415,15 +475,7 @@ public sealed class DailyTaskService : MonoBehaviour
private void EnsureAccumulatedProgressCompatibility()
{
if (saveData == null)
{
return;
}
if (saveData.accumulatedProgress == null)
{
saveData.accumulatedProgress = new List<DailyTaskAccumulatedProgress>();
}
EnsureRuntimeCollections();
if (saveData.accumulatedProgress.Count > 0 || saveData.activeTasks == null || saveData.activeTasks.Count == 0)
{
@@ -445,8 +497,12 @@ public sealed class DailyTaskService : MonoBehaviour
continue;
}
AddAccumulatedProgress(definition.taskType, Mathf.Max(0f, runtimeEntry.progress));
changed = true;
if (definition.taskType == userTasksPool.TaskType.DifferentSongs)
{
continue;
}
changed |= AddOrCombineProgress(definition.taskType, Mathf.Max(0f, runtimeEntry.progress), GetCombineMode(definition.taskType));
}
if (changed)
@@ -468,23 +524,74 @@ public sealed class DailyTaskService : MonoBehaviour
}
}
private static bool IsEventMatch(userTasksPool.TaskDefinition definition, DailyTaskEventData eventData)
private bool ApplyEventToTrackedProgress(DailyTaskEventData eventData)
{
return definition != null && definition.taskType == eventData.taskType;
switch (eventData.taskType)
{
case userTasksPool.TaskType.Login:
case userTasksPool.TaskType.PlaySong:
case userTasksPool.TaskType.FullCombo:
case userTasksPool.TaskType.WatchStory:
case userTasksPool.TaskType.UseItem:
case userTasksPool.TaskType.ShareGame:
{
bool changed = AddOrCombineProgress(eventData.taskType, Mathf.Max(1f, eventData.amount), ProgressCombineMode.Sum);
if (eventData.taskType == userTasksPool.TaskType.PlaySong && eventData.songID > 0)
{
changed |= AddUniqueIntValue(userTasksPool.TaskType.DifferentSongs, eventData.songID);
}
return changed;
}
case userTasksPool.TaskType.TotalScore:
case userTasksPool.TaskType.SpendCoins:
case userTasksPool.TaskType.GameDuration:
case userTasksPool.TaskType.OnlineDuration:
return AddOrCombineProgress(eventData.taskType, Mathf.Max(0f, eventData.amount), ProgressCombineMode.Sum);
case userTasksPool.TaskType.SingleRunCombo:
case userTasksPool.TaskType.SingleRunScore:
return AddOrCombineProgress(eventData.taskType, Mathf.Max(0f, eventData.amount), ProgressCombineMode.Max);
default:
return false;
}
}
private float AddAccumulatedProgress(userTasksPool.TaskType taskType, float amount)
private static bool IsEventMatch(userTasksPool.TaskDefinition definition, DailyTaskEventData eventData)
{
if (saveData == null)
if (definition == null)
{
saveData = new DailyTaskSaveData();
return false;
}
if (saveData.accumulatedProgress == null)
if (definition.taskType == eventData.taskType)
{
saveData.accumulatedProgress = new List<DailyTaskAccumulatedProgress>();
return true;
}
return definition.taskType == userTasksPool.TaskType.DifferentSongs && eventData.taskType == userTasksPool.TaskType.PlaySong;
}
private float CalculateProgressForDefinition(userTasksPool.TaskDefinition definition)
{
if (definition == null)
{
return 0f;
}
float targetValue = Mathf.Max(0.0001f, definition.targetValue);
switch (definition.taskType)
{
case userTasksPool.TaskType.DifferentSongs:
return Mathf.Min(GetUniqueIntCount(userTasksPool.TaskType.DifferentSongs), targetValue);
default:
return Mathf.Min(Mathf.Max(0f, GetAccumulatedProgress(definition.taskType)), targetValue);
}
}
private bool AddOrCombineProgress(userTasksPool.TaskType taskType, float amount, ProgressCombineMode combineMode)
{
EnsureRuntimeCollections();
int taskTypeValue = (int)taskType;
for (int i = 0; i < saveData.accumulatedProgress.Count; i++)
{
@@ -494,17 +601,25 @@ public sealed class DailyTaskService : MonoBehaviour
continue;
}
entry.progress = Mathf.Max(0f, entry.progress + amount);
return entry.progress;
float nextValue = combineMode == ProgressCombineMode.Max
? Mathf.Max(entry.progress, amount)
: Mathf.Max(0f, entry.progress + amount);
if (Mathf.Approximately(entry.progress, nextValue))
{
return false;
}
entry.progress = nextValue;
return true;
}
var newEntry = new DailyTaskAccumulatedProgress
saveData.accumulatedProgress.Add(new DailyTaskAccumulatedProgress
{
taskType = taskTypeValue,
progress = Mathf.Max(0f, amount)
};
saveData.accumulatedProgress.Add(newEntry);
return newEntry.progress;
});
return true;
}
private float GetAccumulatedProgress(userTasksPool.TaskType taskType)
@@ -529,14 +644,151 @@ public sealed class DailyTaskService : MonoBehaviour
return 0f;
}
private static float GetInitialProgress(userTasksPool.TaskDefinition definition, float accumulatedValue)
private bool AddUniqueIntValue(userTasksPool.TaskType taskType, int value)
{
if (definition == null)
if (value <= 0)
{
return 0f;
return false;
}
return Mathf.Min(Mathf.Max(0f, accumulatedValue), Mathf.Max(0.0001f, definition.targetValue));
EnsureRuntimeCollections();
int taskTypeValue = (int)taskType;
for (int i = 0; i < saveData.uniqueIntProgress.Count; i++)
{
var entry = saveData.uniqueIntProgress[i];
if (entry == null || entry.taskType != taskTypeValue)
{
continue;
}
if (entry.values == null)
{
entry.values = new List<int>();
}
if (entry.values.Contains(value))
{
return false;
}
entry.values.Add(value);
return true;
}
saveData.uniqueIntProgress.Add(new DailyTaskUniqueIntProgress
{
taskType = taskTypeValue,
values = new List<int> { value }
});
return true;
}
private int GetUniqueIntCount(userTasksPool.TaskType taskType)
{
if (saveData == null || saveData.uniqueIntProgress == null)
{
return 0;
}
int taskTypeValue = (int)taskType;
for (int i = 0; i < saveData.uniqueIntProgress.Count; i++)
{
var entry = saveData.uniqueIntProgress[i];
if (entry == null || entry.taskType != taskTypeValue || entry.values == null)
{
continue;
}
return entry.values.Count;
}
return 0;
}
private static ProgressCombineMode GetCombineMode(userTasksPool.TaskType taskType)
{
switch (taskType)
{
case userTasksPool.TaskType.SingleRunCombo:
case userTasksPool.TaskType.SingleRunScore:
return ProgressCombineMode.Max;
default:
return ProgressCombineMode.Sum;
}
}
private void EnsureRuntimeCollections()
{
if (saveData == null)
{
saveData = new DailyTaskSaveData();
}
if (saveData.activeTasks == null)
{
saveData.activeTasks = new List<DailyTaskRuntimeEntry>();
}
if (saveData.accumulatedProgress == null)
{
saveData.accumulatedProgress = new List<DailyTaskAccumulatedProgress>();
}
if (saveData.uniqueIntProgress == null)
{
saveData.uniqueIntProgress = new List<DailyTaskUniqueIntProgress>();
}
}
private int GetEffectiveTodayStamp(out bool stateChanged)
{
stateChanged = false;
if (saveData == null)
{
saveData = new DailyTaskSaveData();
}
int currentStamp = GetLocalDateStamp(DateTime.Now);
long nowLocalTicks = DateTime.Now.Ticks;
long nowUtcTicks = DateTime.UtcNow.Ticks;
if (saveData.trustedDateStamp <= 0)
{
saveData.trustedDateStamp = currentStamp;
stateChanged = true;
}
else if (currentStamp < saveData.trustedDateStamp)
{
saveData.timeRollbackDetections += 1;
currentStamp = saveData.trustedDateStamp;
stateChanged = true;
}
else if (currentStamp > saveData.trustedDateStamp)
{
saveData.trustedDateStamp = currentStamp;
stateChanged = true;
}
if (saveData.lastSeenLocalTicks != nowLocalTicks)
{
saveData.lastSeenLocalTicks = nowLocalTicks;
stateChanged = true;
}
if (saveData.lastSeenUtcTicks != nowUtcTicks)
{
saveData.lastSeenUtcTicks = nowUtcTicks;
stateChanged = true;
}
return Mathf.Max(currentStamp, saveData.trustedDateStamp);
}
private void StampRefreshUsage()
{
saveData.lastRefreshDateStamp = saveData.dateStamp;
saveData.lastRefreshLocalTicks = DateTime.Now.Ticks;
saveData.lastRefreshUtcTicks = DateTime.UtcNow.Ticks;
}
private bool TryGetTaskPair(string taskID, out DailyTaskRuntimeEntry runtimeEntry, out userTasksPool.TaskDefinition definition)
@@ -627,27 +879,33 @@ public sealed class DailyTaskService : MonoBehaviour
}
}
private static float GetAccumulatedIncrement(DailyTaskEventData eventData)
private void FlushPendingOnlineDuration()
{
switch (eventData.taskType)
if (pendingOnlineDurationSeconds <= 0f)
{
case userTasksPool.TaskType.TotalScore:
case userTasksPool.TaskType.SpendCoins:
case userTasksPool.TaskType.GameDuration:
return Mathf.Max(0f, eventData.amount);
case userTasksPool.TaskType.Login:
case userTasksPool.TaskType.PlaySong:
case userTasksPool.TaskType.FullCombo:
case userTasksPool.TaskType.UseItem:
return Mathf.Max(1f, eventData.amount);
default:
return Mathf.Max(0f, eventData.amount);
return;
}
float flushSeconds = pendingOnlineDurationSeconds;
pendingOnlineDurationSeconds = 0f;
ReportEvent(new DailyTaskEventData
{
taskType = userTasksPool.TaskType.OnlineDuration,
amount = flushSeconds
});
}
private static string GetTodayKey()
private static int GetLocalDateStamp(DateTime dateTime)
{
return DateTime.Now.ToString("yyyy-MM-dd");
return dateTime.Year * 10000 + dateTime.Month * 100 + dateTime.Day;
}
private static string FormatDateKey(int dateStamp)
{
int year = dateStamp / 10000;
int month = (dateStamp / 100) % 100;
int day = dateStamp % 100;
return year.ToString("D4") + "-" + month.ToString("D2") + "-" + day.ToString("D2");
}
private void Save()