Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of EndlessDays v1.2.1
EndlessDays.dll
Decompiled 14 hours agousing System; using System.Collections; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace EndlessDays; [BepInPlugin("aj.endlessdays", "EndlessDays", "1.2.0")] public class Plugin : BaseUnityPlugin { public const string Guid = "aj.endlessdays"; public const string Name = "EndlessDays"; public const string Version = "1.2.0"; internal static ManualLogSource Log; private static ConfigEntry<bool> cfgEnabled; private const float NewDayAtHour = 22f; private static ConfigEntry<bool> cfgResetMonsters; private static ConfigEntry<bool> cfgShowMessage; private const BindingFlags Any = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static Type tTimeOfDay; private static PropertyInfo pTimeOfDayInstance; private static FieldInfo fCurrentDayTime; private static FieldInfo fTotalTime; private static FieldInfo fNormalizedTime; private static FieldInfo fHour; private static FieldInfo fNumberOfHours; private static FieldInfo fGlobalTime; private static MethodInfo mCalculatePlanetTime; private static PropertyInfo pStartOfRoundInstance; private static FieldInfo fShipHasLanded; private static FieldInfo fShipIsLeaving; private static FieldInfo fInShipPhase; private static FieldInfo fCurrentLevel; private static FieldInfo fPlanetHasTime; private static FieldInfo fLivingPlayers; private static PropertyInfo pRoundManagerInstance; private static PropertyInfo pRoundManagerIsServer; private static FieldInfo fSpawnedEnemies; private static FieldInfo fIsEnemyDead; private static FieldInfo fRmCurrentHour; private static FieldInfo fCannotSpawnMoreInside; private static FieldInfo[] fEnemyPowerFields; private static MethodInfo mDespawnEnemyOnServer; private static MethodInfo mResetEnemyTypesSpawnedCounts; private static PropertyInfo pHudInstance; private static MethodInfo mDisplayTip; private static bool wasOnMoon; private static float morningTime = -1f; private static int loopCount; private static bool loggedUpdateError; private static DateTime lastLoopUtc = DateTime.MinValue; private static bool verifyPending; private static int failedLoops; private static bool pausedThisMoon; private const double LoopCooldownSeconds = 5.0; private static DateTime cycleStartUtc = DateTime.MinValue; private void Awake() { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; cfgEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn the endless day loop on or off."); cfgResetMonsters = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ResetMonstersEachMorning", true, "Despawn all living monsters and reset spawn counters when a new morning starts (host only)."); cfgShowMessage = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowNewDayMessage", true, "Show a popup when the day loops."); if (!ResolveGameMembers()) { Log.LogError((object)"Could not find required game code. The game may have updated; EndlessDays is disabled."); return; } Harmony val = new Harmony("aj.endlessdays"); MethodInfo methodInfo = AccessTools.Method(tTimeOfDay, "Update", (Type[])null, (Type[])null); HarmonyMethod val2 = new HarmonyMethod(typeof(Plugin), "TimeOfDayUpdatePostfix", (Type[])null); val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); MethodInfo methodInfo2 = AccessTools.Method(pStartOfRoundInstance.DeclaringType, "ShipLeaveAutomatically", (Type[])null, (Type[])null); if (methodInfo2 != null) { val.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(Plugin), "ShipLeaveAutomaticallyPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } else { Log.LogWarning((object)"ShipLeaveAutomatically not found; midnight safety net disabled (day loop still works)."); } Log.LogInfo((object)"EndlessDays 1.2.0 loaded. The ship will wait for you."); } private static bool ResolveGameMembers() { tTimeOfDay = AccessTools.TypeByName("TimeOfDay"); Type type = AccessTools.TypeByName("StartOfRound"); Type type2 = AccessTools.TypeByName("RoundManager"); Type type3 = AccessTools.TypeByName("HUDManager"); Type type4 = AccessTools.TypeByName("SelectableLevel"); Type type5 = AccessTools.TypeByName("EnemyAI"); if (tTimeOfDay == null || type == null) { return false; } pTimeOfDayInstance = tTimeOfDay.GetProperty("Instance", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); fCurrentDayTime = tTimeOfDay.GetField("currentDayTime", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); fTotalTime = tTimeOfDay.GetField("totalTime", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); fNormalizedTime = tTimeOfDay.GetField("normalizedTimeOfDay", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); fHour = tTimeOfDay.GetField("hour", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); fNumberOfHours = tTimeOfDay.GetField("numberOfHours", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); fGlobalTime = tTimeOfDay.GetField("globalTime", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (fGlobalTime != null && fGlobalTime.FieldType != typeof(float)) { fGlobalTime = null; } if (type4 != null) { mCalculatePlanetTime = tTimeOfDay.GetMethod("CalculatePlanetTime", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { type4 }, null); } pStartOfRoundInstance = type.GetProperty("Instance", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); fShipHasLanded = type.GetField("shipHasLanded", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); fShipIsLeaving = type.GetField("shipIsLeaving", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); fInShipPhase = type.GetField("inShipPhase", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); fCurrentLevel = type.GetField("currentLevel", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); fLivingPlayers = type.GetField("livingPlayers", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (fLivingPlayers != null && fLivingPlayers.FieldType != typeof(int)) { fLivingPlayers = null; } if (type4 != null) { fPlanetHasTime = type4.GetField("planetHasTime", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } if (type2 != null) { pRoundManagerInstance = type2.GetProperty("Instance", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); pRoundManagerIsServer = type2.GetProperty("IsServer", BindingFlags.Instance | BindingFlags.Public); fSpawnedEnemies = type2.GetField("SpawnedEnemies", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); fRmCurrentHour = type2.GetField("currentHour", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); fCannotSpawnMoreInside = type2.GetField("cannotSpawnMoreInsideEnemies", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); fEnemyPowerFields = new FieldInfo[3] { type2.GetField("currentEnemyPower", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic), type2.GetField("currentOutsideEnemyPower", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic), type2.GetField("currentDaytimeEnemyPower", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) }; mResetEnemyTypesSpawnedCounts = type2.GetMethod("ResetEnemyTypesSpawnedCounts", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); MethodInfo[] methods = type2.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "DespawnEnemyOnServer" && methodInfo.GetParameters().Length == 1) { mDespawnEnemyOnServer = methodInfo; break; } } } if (type5 != null) { fIsEnemyDead = type5.GetField("isEnemyDead", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } if (type3 != null) { pHudInstance = type3.GetProperty("Instance", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo[] methods2 = type3.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo2 in methods2) { if (methodInfo2.Name == "DisplayTip") { mDisplayTip = methodInfo2; break; } } } bool flag = pTimeOfDayInstance != null && fCurrentDayTime != null && fTotalTime != null && pStartOfRoundInstance != null && fShipHasLanded != null && fShipIsLeaving != null && fCurrentLevel != null; if (flag) { if (fGlobalTime == null || mCalculatePlanetTime == null) { Log.LogWarning((object)"Global clock not found; day loop may not stick."); } if (mDespawnEnemyOnServer == null || fSpawnedEnemies == null) { Log.LogWarning((object)"Enemy despawn code not found; monsters will not reset (day loop still works)."); } if (mDisplayTip == null) { Log.LogWarning((object)"HUD popup not found; no new-day message."); } } return flag; } private static float LoopPoint(object tod) { int num = 18; if (fNumberOfHours != null) { int num2 = (int)fNumberOfHours.GetValue(tod); if (num2 > 0 && num2 <= 24) { num = num2; } } float num3 = 24f - (float)num; float num4 = (22f - num3) / (float)num; if (num4 < 0.3f) { num4 = 0.3f; } if (num4 > 0.98f) { num4 = 0.98f; } return num4; } private static float MeasureSlope(object tod, object level) { if (fGlobalTime == null || mCalculatePlanetTime == null || level == null) { return -1f; } float num = (float)fGlobalTime.GetValue(tod); float num2 = (float)mCalculatePlanetTime.Invoke(tod, new object[1] { level }); fGlobalTime.SetValue(tod, num + 10f); float num3 = (float)mCalculatePlanetTime.Invoke(tod, new object[1] { level }); fGlobalTime.SetValue(tod, num); float num4 = (num3 - num2) / 10f; return (!(num4 > 0.0001f)) ? (-1f) : num4; } private static bool OnTimedMoon(object sor) { if (!(bool)fShipHasLanded.GetValue(sor)) { return false; } if ((bool)fShipIsLeaving.GetValue(sor)) { return false; } if (fInShipPhase != null && (bool)fInShipPhase.GetValue(sor)) { return false; } object value = fCurrentLevel.GetValue(sor); if (value == null) { return false; } if (fPlanetHasTime != null && !(bool)fPlanetHasTime.GetValue(value)) { return false; } return true; } private static void TimeOfDayUpdatePostfix(object __instance) { if (!cfgEnabled.Value || __instance == null) { return; } try { object value = pStartOfRoundInstance.GetValue(null, null); if (value == null) { return; } if (!OnTimedMoon(value)) { if (!(bool)fShipHasLanded.GetValue(value)) { wasOnMoon = false; loopCount = 0; morningTime = -1f; verifyPending = false; failedLoops = 0; pausedThisMoon = false; } return; } float num = (float)fCurrentDayTime.GetValue(__instance); float num2 = (float)fTotalTime.GetValue(__instance); if (num2 <= 0f) { return; } if (!wasOnMoon) { wasOnMoon = true; morningTime = num; loopCount = 0; cycleStartUtc = DateTime.UtcNow; } double totalSeconds = (DateTime.UtcNow - lastLoopUtc).TotalSeconds; bool flag = num / num2 >= LoopPoint(__instance); if (verifyPending) { verifyPending = false; if (flag) { failedLoops++; if (failedLoops >= 2) { pausedThisMoon = true; Log.LogError((object)"Clock rewind did not stick; EndlessDays paused until you leave this moon."); } } else { failedLoops = 0; } } if (!pausedThisMoon && flag && totalSeconds > 5.0) { LoopDay(__instance, num2); } } catch (Exception ex) { if (!loggedUpdateError) { loggedUpdateError = true; Log.LogError((object)("Day loop error: " + ex)); } } } private static bool ShipLeaveAutomaticallyPrefix(bool leavingOnMidnight) { if (!cfgEnabled.Value || !leavingOnMidnight) { return true; } try { object value = pStartOfRoundInstance.GetValue(null, null); object value2 = pTimeOfDayInstance.GetValue(null, null); if (value == null || value2 == null || !OnTimedMoon(value)) { return true; } if (fLivingPlayers != null && (int)fLivingPlayers.GetValue(value) <= 0) { return true; } Log.LogInfo((object)"Blocked midnight auto-leave; looping the day instead."); LoopDay(value2, (float)fTotalTime.GetValue(value2)); return false; } catch (Exception ex) { Log.LogError((object)("Midnight safety net error, letting the ship leave: " + ex)); return true; } } private static void LoopDay(object tod, float total) { float num = morningTime; if (num < 0f || num >= total * LoopPoint(tod)) { num = total * 0.1f; } bool flag = false; try { object value = pStartOfRoundInstance.GetValue(null, null); object obj = ((value == null) ? null : fCurrentLevel.GetValue(value)); float num2 = MeasureSlope(tod, obj); if (num2 > 0f) { float num3 = (float)fGlobalTime.GetValue(tod); float num4 = (float)mCalculatePlanetTime.Invoke(tod, new object[1] { obj }); fGlobalTime.SetValue(tod, num3 - (num4 - num) / num2); flag = true; } } catch (Exception ex) { Log.LogWarning((object)("Global clock rewind failed: " + ex.Message)); } if (!flag) { Log.LogWarning((object)"Could not rewind the global clock this loop."); } lastLoopUtc = DateTime.UtcNow; verifyPending = true; fCurrentDayTime.SetValue(tod, num); if (fNormalizedTime != null) { fNormalizedTime.SetValue(tod, num / total); } if (fHour != null && fNumberOfHours != null) { int num5 = (int)fNumberOfHours.GetValue(tod); if (num5 > 0) { fHour.SetValue(tod, (int)(num / (total / (float)num5))); } } loopCount++; double totalMinutes = (DateTime.UtcNow - cycleStartUtc).TotalMinutes; Log.LogInfo((object)("New morning (loop " + loopCount + "). Last cycle took " + totalMinutes.ToString("0.0") + " real min.")); cycleStartUtc = DateTime.UtcNow; if (cfgResetMonsters.Value) { ResetMonsters(); } if (cfgShowMessage.Value) { ShowNewDayMessage(); } } private static void ResetMonsters() { try { if (pRoundManagerInstance == null) { return; } object value = pRoundManagerInstance.GetValue(null, null); if (value == null || !(pRoundManagerIsServer != null) || !(bool)pRoundManagerIsServer.GetValue(value, null)) { return; } int num = 0; if (mDespawnEnemyOnServer != null && fSpawnedEnemies != null && fSpawnedEnemies.GetValue(value) is IList list) { object[] array = new object[list.Count]; list.CopyTo(array, 0); object[] array2 = array; foreach (object obj in array2) { try { if (obj != null && (!(fIsEnemyDead != null) || !(bool)fIsEnemyDead.GetValue(obj))) { PropertyInfo property = obj.GetType().GetProperty("NetworkObject", BindingFlags.Instance | BindingFlags.Public); object obj2 = ((!(property != null)) ? null : property.GetValue(obj, null)); if (obj2 != null) { mDespawnEnemyOnServer.Invoke(value, new object[1] { obj2 }); num++; } } } catch (Exception ex) { Log.LogDebug((object)("Skip enemy despawn: " + ex.Message)); } } } FieldInfo[] array3 = fEnemyPowerFields; foreach (FieldInfo fieldInfo in array3) { if (fieldInfo != null && fieldInfo.FieldType == typeof(float)) { fieldInfo.SetValue(value, 0f); } } if (fRmCurrentHour != null && fRmCurrentHour.FieldType == typeof(int)) { fRmCurrentHour.SetValue(value, 0); } if (fCannotSpawnMoreInside != null && fCannotSpawnMoreInside.FieldType == typeof(bool)) { fCannotSpawnMoreInside.SetValue(value, false); } if (mResetEnemyTypesSpawnedCounts != null) { mResetEnemyTypesSpawnedCounts.Invoke(value, null); } Log.LogInfo((object)("Monsters reset: " + num + " despawned.")); } catch (Exception ex2) { Log.LogError((object)("Monster reset failed: " + ex2)); } } private static void ShowNewDayMessage() { try { if (pHudInstance == null || mDisplayTip == null) { return; } object value = pHudInstance.GetValue(null, null); if (value != null) { ParameterInfo[] parameters = mDisplayTip.GetParameters(); object[] array = new object[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { array[i] = ((!parameters[i].HasDefaultValue) ? ((!parameters[i].ParameterType.IsValueType) ? null : Activator.CreateInstance(parameters[i].ParameterType)) : parameters[i].DefaultValue); } if (parameters.Length > 0) { array[0] = "A NEW DAY"; } if (parameters.Length > 1) { array[1] = "Day " + (loopCount + 1) + " on this moon. The ship waits until you pull the lever."; } mDisplayTip.Invoke(value, array); } } catch (Exception ex) { Log.LogDebug((object)("New day message failed: " + ex.Message)); } } }