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();
foreach (var cheevo in _gameData.CheevoEnumerable)
{
if (cheevo.IsEnabled && !cheevo.IsUnlocked(HardcoreMode))
{
_lib.rc_runtime_deactivate_achievement(ref _runtime, cheevo.ID);
}
}
DeactivateCheevos(HardcoreMode);
AllowUnofficialCheevos ^= true;
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);
}
}
ActivateCheevos(HardcoreMode);
}
private void ToSoftcoreMode()
{
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();
ActivateCheevos(false);
Update();
}
private void DeactivateCheevos(bool hardcore)
{
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);
}
}
}
_activeModeUnlocksRequest.Wait();
private bool _activeModeCheevosOnceActivated;
private void ActivateCheevos(bool hardcore)
{
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);
}
}
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 readonly IReadOnlyDictionary<int, Cheevo> _cheevos;
private readonly Action _activeModeCallback;
protected override void ResponseCallback(byte[] serv_resp)
{
@ -43,8 +42,6 @@ namespace BizHawk.Client.EmuHawk
}
}
}
_activeModeCallback?.Invoke();
}
else
{
@ -60,12 +57,10 @@ namespace BizHawk.Client.EmuHawk
InternalDoRequest(apiParamsResult, ref api_req);
}
public UserUnlocksRequest(string username, string api_token, int game_id, bool hardcore,
IReadOnlyDictionary<int, Cheevo> cheevos, Action activeModeCallback = null)
public UserUnlocksRequest(string username, string api_token, int game_id, bool hardcore, IReadOnlyDictionary<int, Cheevo> cheevos)
{
_apiParams = new(username, api_token, game_id, hardcore);
_cheevos = cheevos;
_activeModeCallback = activeModeCallback;
}
}
@ -167,9 +162,9 @@ namespace BizHawk.Client.EmuHawk
public Cheevo GetCheevoById(int i) => _cheevos[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()
@ -301,16 +296,7 @@ namespace BizHawk.Client.EmuHawk
private void InitGameData()
{
_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);
}
}
});
_activeModeUnlocksRequest = _gameData.InitUnlocks(Username, ApiToken, HardcoreMode);
_inactiveHttpRequests.Push(_activeModeUnlocksRequest);
_inactiveModeUnlocksRequest = _gameData.InitUnlocks(Username, ApiToken, !HardcoreMode);

View File

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

View File

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