fix: avoid logger dispose errors in free-play flow
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled

- Исправлен баг `GameManager.NewGame()`: в режиме бесплатной игры (`payment.freeGame = true`) больше не выполняется проверка/списание баланса.
- Добавлен явный guard для free-play перед `Balance.CanStartGame()` и `Balance.SpendBalance(...)`, чтобы не было нежелательных списаний.
- Устранена ошибка закрытия лог-файла `Cannot access a disposed object`:
  - `FileLogger` переведен на безопасный паттерн `Dispose(bool)` с идемпотентностью;
  - добавлена защита от повторного закрытия/записи в уже закрытый поток;
  - финализатор `FileLogger` больше не выполняет managed-cleanup.
- Устранен риск повторной очистки в `CompositeLogger`:
  - добавлен флаг `_disposed`;
  - `Dispose(bool)` теперь выполняется только один раз;
  - финализатор вызывает `Dispose(false)` вместо полного `Dispose()`.
This commit is contained in:
Данил Ярош 2026-02-24 14:02:38 +03:00
parent 2963e342c2
commit 1cecbf2c11
3 changed files with 50 additions and 19 deletions

View File

@ -288,9 +288,10 @@ public void TogglePause() => SetPause(!IsPaused);
public void NewGame()
{
Debug.Log("[GameManager] NewGame called");
bool isFreeGame = Settings != null && Settings.GetBool("payment.freeGame");
// Проверка баланса перед началом игры
if (Balance != null && !Balance.CanStartGame())
if (!isFreeGame && Balance != null && !Balance.CanStartGame())
{
Logger?.Log(gameObject.name, "Cannot start game - insufficient balance");
Debug.LogWarning("[GameManager] Cannot start - insufficient balance");
@ -300,7 +301,7 @@ public void TogglePause() => SetPause(!IsPaused);
int livesToGive = StartingLives;
int balanceToSpend = 0;
if (Balance != null)
if (!isFreeGame && Balance != null)
{
int currentBalance = Balance.CurrentBalance;
int impulseCost = Settings?.GetInt("payment.paymentImpulseCost") ?? 10;
@ -331,6 +332,10 @@ public void TogglePause() => SetPause(!IsPaused);
Debug.Log($"[GameManager] Spent entire balance: {balanceToSpend}, Lives given: {livesToGive}");
}
else if (isFreeGame)
{
Debug.Log("[GameManager] Free game mode enabled. Balance spend skipped.");
}
Lives = livesToGive;
LivesChanged?.Invoke(Lives);

View File

@ -14,6 +14,7 @@ namespace RR.Common.Utility
private ILogger _oldLogger;
private readonly List<ILogger> _additiveLoggers = new();
private bool _disposed;
public void Initialize(ILogger additiveLogger = null)
{
@ -329,10 +330,13 @@ namespace RR.Common.Utility
private void Dispose(bool disposing)
{
if (!disposing) return;
if (_disposed || !disposing) return;
lock (_additiveLoggers)
{
if (_disposed)
return;
foreach (ILogger additiveLogger in _additiveLoggers.ToArray())
{
try
@ -346,12 +350,13 @@ namespace RR.Common.Utility
}
}
_additiveLoggers.Clear();
_disposed = true;
}
}
~CompositeLogger()
{
Dispose();
Dispose(false);
}
}
}
}

View File

@ -290,24 +290,45 @@ namespace RR.Common.Utility
public void Dispose()
{
if (_disposed)
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!disposing || _disposed)
return;
lock (_lock)
{
if (_disposed)
return;
try
{
if (_writer == null) return;
_writer.WriteLine();
_writer.WriteLine(
"================================================================================");
_writer.WriteLine($"Сессия логирования завершена: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
_writer.WriteLine(
"================================================================================");
_writer.Flush();
_writer.Close();
// _writer.Dispose();
_writer = null;
if (_writer != null)
{
// Закрывающий блок пишем только если поток ещё доступен.
try
{
_writer.WriteLine();
_writer.WriteLine("================================================================================");
_writer.WriteLine($"Сессия логирования завершена: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
_writer.WriteLine("================================================================================");
_writer.Flush();
}
catch (ObjectDisposedException)
{
// Поток уже закрыт извне — это не ошибка завершения.
}
_writer.Dispose();
_writer = null;
}
}
catch (ObjectDisposedException)
{
// Поток уже закрыт — игнорируем.
}
catch (Exception ex)
{
@ -322,7 +343,7 @@ namespace RR.Common.Utility
~FileLogger()
{
Dispose();
Dispose(false);
}
}
}
}