Fixup bad threading (no, you cannot access rcheevo's _runtime in a separate thread! main thread use only!). Should fix up some random crashes

Slightly change up the request model, now InternalDoRequest does not block
Fixup logic of ToSoftcoreMode (and comments so it isn't screwed up again)
This commit is contained in:
CasualPokePlayer 2023-05-02 21:47:09 -07:00
parent 8ef1d93891
commit 248b8ab984
4 changed files with 65 additions and 56 deletions

View File

@ -145,50 +145,62 @@ namespace BizHawk.Client.EmuHawk
_activeModeUnlocksRequest.Wait(); _activeModeUnlocksRequest.Wait();
foreach (var cheevo in _gameData.CheevoEnumerable) DeactivateCheevos(HardcoreMode);
{
if (cheevo.IsEnabled && !cheevo.IsUnlocked(HardcoreMode))
{
_lib.rc_runtime_deactivate_achievement(ref _runtime, cheevo.ID);
}
}
AllowUnofficialCheevos ^= true; AllowUnofficialCheevos ^= true;
ActivateCheevos(HardcoreMode);
foreach (var cheevo in _gameData.CheevoEnumerable)
{
if (cheevo.IsEnabled && !cheevo.IsUnlocked(HardcoreMode))
{
_lib.rc_runtime_activate_achievement(ref _runtime, cheevo.ID, cheevo.Definition, IntPtr.Zero, 0);
}
}
} }
private void ToSoftcoreMode() private void ToSoftcoreMode()
{ {
if (_gameData == null || _gameData.GameID == 0) return; if (_gameData == null || _gameData.GameID == 0) return;
// don't worry if the meanings of _active and _inactive are wrong
// if they are, then they're both already finished
// first deactivate any hardcore cheevos
// if _activeModeUnlocksRequest is still active, it's hardcore mode
_activeModeUnlocksRequest.Wait();
DeactivateCheevos(true);
// now activate the softcore cheevos
// if _inactiveModeUnlocksRequest is still active, it's softcore mode
_inactiveModeUnlocksRequest.Wait(); _inactiveModeUnlocksRequest.Wait();
ActivateCheevos(false);
Update();
}
private void DeactivateCheevos(bool hardcore)
{
foreach (var cheevo in _gameData.CheevoEnumerable) foreach (var cheevo in _gameData.CheevoEnumerable)
{ {
if (cheevo.IsEnabled && !cheevo.IsUnlocked(false)) if (cheevo.IsEnabled && !cheevo.IsUnlocked(hardcore))
{ {
_lib.rc_runtime_deactivate_achievement(ref _runtime, cheevo.ID); _lib.rc_runtime_deactivate_achievement(ref _runtime, cheevo.ID);
} }
} }
}
_activeModeUnlocksRequest.Wait(); private bool _activeModeCheevosOnceActivated;
private void ActivateCheevos(bool hardcore)
{
foreach (var cheevo in _gameData.CheevoEnumerable) foreach (var cheevo in _gameData.CheevoEnumerable)
{ {
if (cheevo.IsEnabled && !cheevo.IsUnlocked(true)) if (cheevo.IsEnabled && !cheevo.IsUnlocked(hardcore))
{ {
_lib.rc_runtime_activate_achievement(ref _runtime, cheevo.ID, cheevo.Definition, IntPtr.Zero, 0); _lib.rc_runtime_activate_achievement(ref _runtime, cheevo.ID, cheevo.Definition, IntPtr.Zero, 0);
} }
} }
Update(); _activeModeCheevosOnceActivated = true;
}
private void OneShotActivateActiveModeCheevos()
{
if (_activeModeCheevosOnceActivated) return;
_activeModeUnlocksRequest.Wait();
ActivateCheevos(HardcoreMode);
} }
} }
} }

View File

@ -25,7 +25,6 @@ namespace BizHawk.Client.EmuHawk
{ {
private LibRCheevos.rc_api_fetch_user_unlocks_request_t _apiParams; private LibRCheevos.rc_api_fetch_user_unlocks_request_t _apiParams;
private readonly IReadOnlyDictionary<int, Cheevo> _cheevos; private readonly IReadOnlyDictionary<int, Cheevo> _cheevos;
private readonly Action _activeModeCallback;
protected override void ResponseCallback(byte[] serv_resp) protected override void ResponseCallback(byte[] serv_resp)
{ {
@ -43,8 +42,6 @@ namespace BizHawk.Client.EmuHawk
} }
} }
} }
_activeModeCallback?.Invoke();
} }
else else
{ {
@ -60,12 +57,10 @@ namespace BizHawk.Client.EmuHawk
InternalDoRequest(apiParamsResult, ref api_req); InternalDoRequest(apiParamsResult, ref api_req);
} }
public UserUnlocksRequest(string username, string api_token, int game_id, bool hardcore, public UserUnlocksRequest(string username, string api_token, int game_id, bool hardcore, IReadOnlyDictionary<int, Cheevo> cheevos)
IReadOnlyDictionary<int, Cheevo> cheevos, Action activeModeCallback = null)
{ {
_apiParams = new(username, api_token, game_id, hardcore); _apiParams = new(username, api_token, game_id, hardcore);
_cheevos = cheevos; _cheevos = cheevos;
_activeModeCallback = activeModeCallback;
} }
} }
@ -167,9 +162,9 @@ namespace BizHawk.Client.EmuHawk
public Cheevo GetCheevoById(int i) => _cheevos[i]; public Cheevo GetCheevoById(int i) => _cheevos[i];
public LBoard GetLboardById(int i) => _lboards[i]; public LBoard GetLboardById(int i) => _lboards[i];
public UserUnlocksRequest InitUnlocks(string username, string api_token, bool hardcore, Action callback = null) public UserUnlocksRequest InitUnlocks(string username, string api_token, bool hardcore)
{ {
return new(username, api_token, GameID, hardcore, _cheevos, callback); return new(username, api_token, GameID, hardcore, _cheevos);
} }
public IEnumerable<RCheevoHttpRequest> LoadImages() public IEnumerable<RCheevoHttpRequest> LoadImages()
@ -301,16 +296,7 @@ namespace BizHawk.Client.EmuHawk
private void InitGameData() private void InitGameData()
{ {
_activeModeUnlocksRequest = _gameData.InitUnlocks(Username, ApiToken, HardcoreMode, () => _activeModeUnlocksRequest = _gameData.InitUnlocks(Username, ApiToken, HardcoreMode);
{
foreach (var cheevo in _gameData.CheevoEnumerable)
{
if (cheevo.IsEnabled && !cheevo.IsUnlocked(HardcoreMode))
{
_lib.rc_runtime_activate_achievement(ref _runtime, cheevo.ID, cheevo.Definition, IntPtr.Zero, 0);
}
}
});
_inactiveHttpRequests.Push(_activeModeUnlocksRequest); _inactiveHttpRequests.Push(_activeModeUnlocksRequest);
_inactiveModeUnlocksRequest = _gameData.InitUnlocks(Username, ApiToken, !HardcoreMode); _inactiveModeUnlocksRequest = _gameData.InitUnlocks(Username, ApiToken, !HardcoreMode);

View File

@ -112,20 +112,23 @@ namespace BizHawk.Client.EmuHawk
: HttpGet(request.URL); : HttpGet(request.URL);
apiTask.ConfigureAwait(false); apiTask.ConfigureAwait(false);
_lib.rc_api_destroy_request(ref request); apiTask.ContinueWith(async t =>
var result = apiTask.Result; // FIXME: THIS IS BAD (but kind of needed?)
if (result is null) // likely a timeout
{ {
ShouldRetry = true; var result = await t;
_completionEvent.Set(); if (result is null) // likely a timeout
return; {
} ShouldRetry = true;
_completionEvent.Set();
}
else
{
ResponseCallback(result);
ShouldRetry = false; // this is a bit naive, but if the response callback "fails," retrying will just result in the same thing
_completionEvent.Set();
}
});
ResponseCallback(result); _lib.rc_api_destroy_request(ref request);
ShouldRetry = false; // this is a bit naive, but if the response callback "fails," retrying will just result in the same thing
_completionEvent.Set();
} }
} }
@ -158,14 +161,14 @@ namespace BizHawk.Client.EmuHawk
{ {
while (_inactiveHttpRequests.TryPop(out var request)) while (_inactiveHttpRequests.TryPop(out var request))
{ {
Task.Run(request.DoRequest); request.DoRequest();
_activeHttpRequests.Add(request); _activeHttpRequests.Add(request);
} }
foreach (var activeRequest in _activeHttpRequests.Where(activeRequest => activeRequest.IsCompleted && activeRequest.ShouldRetry).ToArray()) foreach (var activeRequest in _activeHttpRequests.Where(activeRequest => activeRequest.IsCompleted && activeRequest.ShouldRetry).ToArray())
{ {
activeRequest.Reset(); activeRequest.Reset();
Task.Run(activeRequest.DoRequest); activeRequest.DoRequest();
} }
_activeHttpRequests.RemoveAll(activeRequest => _activeHttpRequests.RemoveAll(activeRequest =>

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
using BizHawk.BizInvoke; using BizHawk.BizInvoke;
@ -120,7 +121,6 @@ namespace BizHawk.Client.EmuHawk
enableHardcoreItem.CheckedChanged += (_, _) => enableHardcoreItem.CheckedChanged += (_, _) =>
{ {
_hardcoreMode ^= true; _hardcoreMode ^= true;
enableLboardsItem.Enabled = HardcoreMode;
if (HardcoreMode) if (HardcoreMode)
{ {
@ -130,6 +130,8 @@ namespace BizHawk.Client.EmuHawk
{ {
ToSoftcoreMode(); ToSoftcoreMode();
} }
enableLboardsItem.Enabled = HardcoreMode;
}; };
raDropDownItems.Add(enableHardcoreItem); raDropDownItems.Add(enableHardcoreItem);
@ -195,7 +197,7 @@ namespace BizHawk.Client.EmuHawk
: base(mainForm, inputManager, tools, getConfig, raDropDownItems, shutdownRACallback) : base(mainForm, inputManager, tools, getConfig, raDropDownItems, shutdownRACallback)
{ {
_isActive = true; _isActive = true;
_httpThread = new(HttpRequestThreadProc) { IsBackground = true }; _httpThread = new(HttpRequestThreadProc) { IsBackground = true, Priority = ThreadPriority.BelowNormal };
_httpThread.Start(); _httpThread.Start();
_runtime = default; _runtime = default;
@ -243,7 +245,8 @@ namespace BizHawk.Client.EmuHawk
return; return;
} }
_activeModeUnlocksRequest.Wait(); OneShotActivateActiveModeCheevos();
var size = _lib.rc_runtime_progress_size(ref _runtime, IntPtr.Zero); var size = _lib.rc_runtime_progress_size(ref _runtime, IntPtr.Zero);
if (size > 0) if (size > 0)
{ {
@ -266,7 +269,8 @@ namespace BizHawk.Client.EmuHawk
HandleHardcoreModeDisable("Loading savestates is not allowed in hardcore mode."); HandleHardcoreModeDisable("Loading savestates is not allowed in hardcore mode.");
} }
_activeModeUnlocksRequest.Wait(); OneShotActivateActiveModeCheevos();
_lib.rc_runtime_reset(ref _runtime); _lib.rc_runtime_reset(ref _runtime);
if (!File.Exists(path + ".rap")) return; if (!File.Exists(path + ".rap")) return;
@ -314,6 +318,8 @@ namespace BizHawk.Client.EmuHawk
} }
} }
_activeModeCheevosOnceActivated = false;
if (!LoggedIn) if (!LoggedIn)
{ {
return; return;
@ -599,6 +605,8 @@ namespace BizHawk.Client.EmuHawk
return; return;
} }
OneShotActivateActiveModeCheevos();
var input = _inputManager.ControllerOutput; var input = _inputManager.ControllerOutput;
if (input.Definition.BoolButtons.Any(b => (b.Contains("Power") || b.Contains("Reset")) && input.IsPressed(b))) if (input.Definition.BoolButtons.Any(b => (b.Contains("Power") || b.Contains("Reset")) && input.IsPressed(b)))
{ {