0.1.11 | Fix for last update

This commit is contained in:
2026-08-21 21:41:47 +02:00
parent 952f3968ce
commit d2c75cfb53
11 changed files with 130 additions and 56 deletions

View File

@@ -8,7 +8,7 @@
<!-- This is the display name of your mod. Example: BepInEx Template -->
<AssemblyTitle>JordanMod</AssemblyTitle>
<!-- This is the version number of your mod. -->
<Version>0.1.10</Version>
<Version>0.1.11</Version>
</PropertyGroup>
<PropertyGroup>

View File

@@ -16,6 +16,12 @@ public partial class Plugin : BaseUnityPlugin
private static Harmony? _harmony;
private ModuleManager? _moduleManager;
private static readonly System.Collections.Concurrent.ConcurrentQueue<Action> _mainThreadQueue = new();
// Unity APIs (e.g. StartCoroutine) can only be called from the main thread;
// background work (Task.Run, etc.) must hop back through here to touch them.
public static void RunOnMainThread(Action action) => _mainThreadQueue.Enqueue(action);
private static readonly List<Type> _globalPatches = [
typeof(Patches.PassportPatch),
];
@@ -84,6 +90,11 @@ public partial class Plugin : BaseUnityPlugin
private void Update()
{
while (_mainThreadQueue.TryDequeue(out Action? action))
{
try { action(); }
catch (Exception e) { Log.LogError($"Main thread action failed: {e}"); }
}
_moduleManager?.Update();
}

View File

@@ -1,3 +1,4 @@
using System;
using pworld.Scripts.Extensions;
namespace JordanMod.Events;
@@ -7,12 +8,16 @@ public class GlobalEventListener
public static void Initialize()
{
GlobalEvents.OnItemThrown += OnItemThrown;
// GlobalEvents.OnItemThrown += OnItemThrown;
}
private static void OnItemThrown(Item item)
{
item.gameObject.GetOrAddComponent<Bonkable>();
// Bonkable bonkable = item.gameObject.GetOrAddComponent<Bonkable>();
// // A Bonkable added at runtime has no serialized prefab data, so its `bonk` SFX array
// // defaults to null (not empty) and Bonkable.Bonk() NREs on bonk.Length. Give it an
// // empty array so a bonk still ragdolls/knocks back the target, just without a sound.
// bonkable.bonk ??= Array.Empty<SFX_Instance>();
}
}

View File

@@ -38,16 +38,25 @@ class EasyBackpackModule : Module
Character localCharacter = Character.localCharacter;
if (localCharacter == null) return;
bool hasBackpack = localCharacter.player.backpackSlot.hasBackpack;
Character carriedCharacter = localCharacter.data.carriedPlayer;
bool carriedHasBackpack = carriedCharacter != null && carriedCharacter.player.backpackSlot.hasBackpack;
BackpackReference backpackRefs;
if (hasBackpack) backpackRefs = BackpackReference.GetFromEquippedBackpack(localCharacter);
else if (carriedHasBackpack) backpackRefs = BackpackReference.GetFromEquippedBackpack(carriedCharacter);
Character targetCharacter;
BackpackSlot targetSlot;
if (localCharacter.player.backpackSlot.backpackType != BackpackSlot.BackpackType.None)
{
targetCharacter = localCharacter;
targetSlot = localCharacter.player.backpackSlot;
}
else if (carriedCharacter != null && carriedCharacter.player.backpackSlot.backpackType != BackpackSlot.BackpackType.None)
{
targetCharacter = carriedCharacter;
targetSlot = carriedCharacter.player.backpackSlot;
}
else return;
GUIManager.instance.OpenBackpackWheel(backpackRefs);
BackpackReference backpackRefs = BackpackReference.GetFromEquippedBackpack(targetCharacter);
int slotCount = (targetSlot.prefab as Backpack)?.slotCount ?? 4;
GUIManager.instance.OpenBackpackWheel(backpackRefs, slotCount, targetSlot.backpackType);
_isBackpackOpen = true;
}

View File

@@ -35,7 +35,7 @@ class StashedBugleModule : Module
localCharacter.player.EmptySlot(localCharacter.refs.items.currentSelectedSlot);
localCharacter.player.RPCRemoveItemFromSlot(localCharacter.refs.items.currentSelectedSlot.Value);
localCharacter.refs.items.SpawnItemInHand(_megaphoneItemName);
SpawnItemByDisplayName(localCharacter, _megaphoneItemName);
}
else if (heldItem.UIData.itemName == _megaphoneItemName)
{
@@ -54,8 +54,22 @@ class StashedBugleModule : Module
withBugleSlot = itemSlot;
break;
}
if (withBugleSlot == null) localCharacter.refs.items.SpawnItemInHand(_bugleItemName);
if (withBugleSlot == null) SpawnItemByDisplayName(localCharacter, _bugleItemName);
else localCharacter.refs.items.EquipSlot(Optionable<byte>.Some(withBugleSlot.itemSlotID));
}
}
// SpawnItemInHand expects the item's Resources prefab name, which is not
// guaranteed to match its UIData.itemName display string, so resolve it
// via the ItemDatabase instead of assuming the two are identical.
private static void SpawnItemByDisplayName(Character character, string displayName)
{
Item? match = Helper.ItemDatabase.Objects.Find(item => item.UIData != null && item.UIData.itemName == displayName);
if (match == null)
{
Debug.LogWarning($"[StashedBugle] Could not find an item with UIData.itemName '{displayName}' in ItemDatabase.");
return;
}
character.refs.items.SpawnItemInHand(match.name);
}
}

View File

@@ -24,7 +24,7 @@ public class BagsForEveryonePatch
bool isFirstSpawner = spawner.transform.IsChildOf(GameObject.Find("Biome_1").transform);
if (!isFirstSpawner) return;
int playerCount = PhotonNetwork.PlayerList.Length - 1;
int playerCount = PhotonNetwork.PlayerList.Length - 1; // Already one bag
if (playerCount <= 0) return;
for (int i = 1; i <= playerCount; i++)
{

View File

@@ -45,7 +45,7 @@ public class BetterBuglePatch
// BetterBugleUI.Instance?.ShowActionbar("No songs available.");
// return;
// }
if (AudioSyncWorker.IsLoading) return;
if (AudioSyncWorker.IsLoading || AudioSyncWorker.IsSyncing) return;
if (!BetterBugleModule.HadConfirmation)
{
BetterBugleUI.Instance?.ShowActionbar("Are you sure you want to refresh songs ? Right-click again to reload.");
@@ -57,15 +57,18 @@ public class BetterBuglePatch
{
BetterBugleModule.HadConfirmation = false; // Reset confirmation state
BetterBugleUI.Instance?.ShowActionbar("Refreshing songs...");
AudioSyncService.GetAudioClips();
// Refresh = unload everything and reload from local files only (no network).
// For downloading from the audio bank API, use the Sync keybind instead.
AudioSyncService.ClearAudioClips();
AudioSyncWorker.GetAudioClips();
}
}
private static IEnumerator ResetConfirmation()
{
if (!BetterBugleModule.HadConfirmation) yield break;
yield return new WaitForSeconds(2f);
if (!BetterBugleModule.HadConfirmation) yield break; // already confirmed (or reset) in the meantime
BetterBugleUI.Instance?.ShowActionbar("No answer, not refreshing songs.");
BetterBugleModule.HadConfirmation = false;
}

View File

@@ -9,6 +9,12 @@ class EasyBackpackPatch
[HarmonyPrefix]
static bool BackpackWheelUpdatePrefix(BackpackWheel __instance)
{
if (!__instance.backpack.exists || __instance.backpack.locationTransform == null)
{
GUIManager.instance.CloseBackpackWheel();
return false;
}
bool isBackPackOpen = EasyBackpackModule.Instance?._isBackpackOpen ?? false;
if (!Character.localCharacter.input.interactIsPressed && !isBackPackOpen)
@@ -18,13 +24,17 @@ class EasyBackpackPatch
return false;
}
if (__instance.backpack.locationTransform != null && Vector3.Distance(__instance.backpack.locationTransform.position, Character.localCharacter.Center) > 6f)
if (Vector3.Distance(__instance.backpack.locationTransform.position, Character.localCharacter.Center) > 6f)
{
GUIManager.instance.CloseBackpackWheel();
return false;
}
if (__instance.chosenSlice.IsSome && !__instance.chosenSlice.Value.isBackpackWear && !__instance.slices[__instance.chosenSlice.Value.slotID + 1].image.enabled)
if (__instance.chosenSlice.IsSome && __instance.chosenSlice.Value.isJetpackFuelSlice)
{
__instance.currentlyHeldItem.transform.position = Vector3.Lerp(__instance.currentlyHeldItem.transform.position, __instance.jetpackSlice.transform.GetChild(0).GetChild(0).position, Time.deltaTime * 20f);
}
else if (__instance.chosenSlice.IsSome && !__instance.chosenSlice.Value.isBackpackWear && !__instance.slices[__instance.chosenSlice.Value.slotID + 1].image.enabled)
{
__instance.currentlyHeldItem.transform.position = Vector3.Lerp(__instance.currentlyHeldItem.transform.position, __instance.slices[__instance.chosenSlice.Value.slotID + 1].transform.GetChild(0).GetChild(0).position, Time.deltaTime * 20f);
}
@@ -32,7 +42,7 @@ class EasyBackpackPatch
{
__instance.currentlyHeldItem.transform.localPosition = Vector3.Lerp(__instance.currentlyHeldItem.transform.localPosition, Vector3.zero, Time.deltaTime * 20f);
}
return false;
return false;
}
}

View File

@@ -115,7 +115,7 @@ class AudioSyncService
if (File.Exists(filePath)) File.Delete(filePath);
string url = $"{API_BASE_URL}/audio/{Id}/download?hash={Hash}";
Debug.LogError($"Downloading audio from URL: {url}");
Debug.Log($"Downloading audio from URL: {url}");
using UnityWebRequest www = UnityWebRequest.Get(url);
var operation = www.SendWebRequest();
@@ -244,48 +244,58 @@ class AudioSyncWorker
{
if (IsLoading || IsSyncing) return;
IsSyncing = true;
Dictionary<AudioSyncService.APIAudioFormat, Song?> toDownload = new();
string[] existingSongNames = [.. Song.Sounds.Keys];
AudioSyncService.APIAudioFormat[] existingAPIFormats = [.. AudioSyncService.GetAudioClips()];
string[] apiExistingNames = [.. existingAPIFormats.Select(apiAudio => apiAudio.Filename)];
var songsToRemove = existingSongNames.Except(apiExistingNames).ToArray();
foreach (var songName in songsToRemove)
try
{
if (Song.Sounds.TryGetValue(songName, out var songToDispose))
Dictionary<AudioSyncService.APIAudioFormat, Song?> toDownload = new();
string[] existingSongNames = [.. Song.Sounds.Keys];
AudioSyncService.APIAudioFormat[] existingAPIFormats = [.. AudioSyncService.GetAudioClips()];
string[] apiExistingNames = [.. existingAPIFormats.Select(apiAudio => apiAudio.Filename)];
var songsToRemove = existingSongNames.Except(apiExistingNames).ToArray();
foreach (var songName in songsToRemove)
{
songToDispose.Dispose();
songToDispose.DeleteFile();
if (Song.Sounds.TryGetValue(songName, out var songToDispose))
{
songToDispose.Dispose();
songToDispose.DeleteFile();
}
}
foreach (AudioSyncService.APIAudioFormat apiAudio in existingAPIFormats)
{
Song? existingSong = Song.SoundsByHash.GetValueOrDefault(apiAudio.Hash);
if (existingSong == null || existingSong.Hash != apiAudio.Hash)
{
toDownload.Add(apiAudio, existingSong);
}
}
BetterBugleUI.Instance?.ShowActionbar($"Syncing audio bank... {toDownload.Count} changed/new files found.");
string[] filesToOverload = [];
foreach (AudioSyncService.APIAudioFormat apiAudio in toDownload.Keys)
{
bool success = await AudioSyncService.DownloadAPIAudio(apiAudio, SoundsDirectory, toDownload[apiAudio]);
if (success)
{
Debug.Log($"Successfully downloaded audio: {apiAudio.Filename}.{apiAudio.Extension}, adding to forceload");
filesToOverload = [.. filesToOverload, $"{apiAudio.Filename}.{apiAudio.Extension}"];
}
}
IsSyncing = false;
IsLoading = true;
// StartCoroutine is a Unity API - must run on the main thread, not on this Task.Run background thread.
Plugin.RunOnMainThread(() => Plugin.Instance.StartCoroutine(LoadAllAudioClipsCoroutine(SoundsDirectory, filesToOverload)));
}
foreach (AudioSyncService.APIAudioFormat apiAudio in existingAPIFormats)
catch (Exception ex)
{
Song? existingSong = Song.SoundsByHash.GetValueOrDefault(apiAudio.Hash);
if (existingSong == null || existingSong.Hash != apiAudio.Hash)
{
toDownload.Add(apiAudio, existingSong);
}
Debug.LogError($"Audio sync failed: {ex}");
IsSyncing = false;
IsLoading = false;
}
BetterBugleUI.Instance?.ShowActionbar($"Syncing audio bank... {toDownload.Count} changed/new files found.");
string[] filesToOverload = [];
foreach (AudioSyncService.APIAudioFormat apiAudio in toDownload.Keys)
{
bool success = await AudioSyncService.DownloadAPIAudio(apiAudio, SoundsDirectory, toDownload[apiAudio]);
if (success)
{
Debug.Log($"Successfully downloaded audio: {apiAudio.Filename}.{apiAudio.Extension}, adding to forceload");
filesToOverload = [.. filesToOverload, $"{apiAudio.Filename}.{apiAudio.Extension}"];
}
}
IsSyncing = false;
IsLoading = true;
Plugin.Instance.StartCoroutine(LoadAllAudioClipsCoroutine(SoundsDirectory, filesToOverload));
}
}