-
-
Notifications
You must be signed in to change notification settings - Fork 13
Description
Summary
In Schedule I, S1API.GameTime.TimeManager.OnTick fires normally on a fresh load (cold start → load save), but stops firing completely after returning to the main menu and loading a save again (hot load). Handlers remain subscribed and no errors are thrown, but they never get called in the second gameplay session.
Environment
- Game: Schedule I 0.4.1f13 (IL2CPP)
- S1API: 2.7.4 (Forked by Bars)
- MelonLoader: 0.7.0 Open-Beta
Reproduction
-
Create a tiny test mod:
using MelonLoader; using S1API.GameTime; public sealed class TickTesterMod : MelonMod { public override void OnInitializeMelon() { MelonLogger.Msg("[TickTester] Initialized."); TimeManager.OnTick -= OnTick; TimeManager.OnTick += OnTick; MelonLogger.Msg("[TickTester] TimeManager.OnTick handler registered."); } private static void OnTick() { MelonLogger.Msg("[TickTester] OnTick fired."); } }
-
Start the game, load any save from the main menu.
-
In the gameplay scene, wait a few in‑game minutes.
- Observe
[TickTester] OnTick fired.repeatedly in the log.
- Observe
-
Press Esc → Quit to Main Menu (do not close the game).
-
From the main menu, load the same save again.
-
In the second gameplay session, wait again.
- Observe: no
[TickTester] OnTick fired.lines appear anymore.
- Observe: no
Expected vs Actual
- Expected:
TimeManager.OnTickcontinues to fire in every gameplay session within the same process, including after returning to the main menu and loading a save again. - Actual:
TimeManager.OnTickhandlers fire only in the first gameplay session after the process starts. After a hot load, they never fire again, despite the handler still being subscribed.
Relevant S1API Code
From S1API/GameTime/TimeManager.cs:
#if (IL2CPPMELON)
using S1GameTime = Il2CppScheduleOne.GameTime;
#elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX)
using S1GameTime = ScheduleOne.GameTime;
#endif
public static class TimeManager
{
public static Action OnTick = delegate { };
static TimeManager()
{
if (S1GameTime.TimeManager.Instance != null)
{
S1GameTime.TimeManager.Instance.onTick += (Action)(() => OnTick());
// ... other event wiring ...
}
}
// ... properties forwarding to S1GameTime.TimeManager.Instance ...
}In Schedule I the underlying ScheduleOne.GameTime.TimeManager.Instance appears to be recreated when the Main gameplay scene is reloaded. Since the S1API static constructor runs only once, OnTick stays wired to the first instance and never reattaches to later ones.
Likely Cause & Suggestion
- Cause:
S1API.GameTime.TimeManagerhooksonTickonly once in its static constructor and does not rebind whenS1GameTime.TimeManager.Instancechanges on subsequent scene loads. - Suggestion: Rebind S1API’s
OnDayPass/OnWeekPass/OnTick/OnSleepStart/OnSleepEnddelegates whenever the underlyingTimeManager.Instancechanges (e.g. via a scene‑loaded hook or a lazy check before access).