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 FittingPaintings v1.0.2
FittingPaintings.dll
Decompiled a year agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Threading.Tasks; using BepInEx; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace FittingPaintings { [BepInPlugin("ZeroTails.FittingPaintings", "FittingPaintings", "1.0.2")] public class FittingPaintings : BaseUnityPlugin { public class PaintingGroup { public string paintingType; public string paintingFolderName; public HashSet<string> targetMaterials; public List<Material> loadedMaterials; public PaintingGroup(string paintingType, string paintingFolderName, HashSet<string> targetMaterials) { this.paintingType = paintingType; this.paintingFolderName = paintingFolderName; this.targetMaterials = targetMaterials; loadedMaterials = new List<Material>(); } } [HarmonyPatch(typeof(LoadingUI), "LevelAnimationComplete")] public class PaintingSwapPatch { private static void Postfix() { Task.Run(async delegate { int waited = 0; int interval = 50; while (!receivedSeed.HasValue && waited < maxWaitTimeMs) { await Task.Delay(interval); waited += interval; } if (receivedSeed.HasValue) { logger.LogInfo($"[Postfix] Client using received seed: {receivedSeed.Value}"); FittingPaintingsSwap.ReceivedSeed = receivedSeed.Value; receivedSeed = null; } else { logger.LogWarning("[Postfix] Client did not receive seed in time. Proceeding without it."); } swapper.ReplacePaintings(); }); } private static void Prefix() { if (swapper.GetModState() == FittingPaintingsSwap.ModState.Client) { PhotonNetwork.AddCallbackTarget((object)sync); } if (swapper.GetModState() == FittingPaintingsSwap.ModState.Host) { FittingPaintingsSwap.HostSeed = Random.Range(0, int.MaxValue); logger.LogInfo($"Generated Hostseed: {FittingPaintingsSwap.HostSeed}"); PhotonNetwork.AddCallbackTarget((object)sync); sync.SendSeed(FittingPaintingsSwap.HostSeed); } } } [HarmonyPatch(typeof(NetworkConnect), "TryJoiningRoom")] public class JoinLobbyPatch { private static void Prefix() { logger.LogInfo("JoinLobbyPatch Prefix called."); if (swapper.GetModState() == FittingPaintingsSwap.ModState.SinglePlayer) { swapper.SetState(FittingPaintingsSwap.ModState.Client); } } } [HarmonyPatch(typeof(SteamManager), "HostLobby")] public class HostLobbyPatch { private static bool Prefix() { logger.LogInfo("HostLobbyPatch Prefix called."); if (swapper.GetModState() != 0) { swapper.SetState(FittingPaintingsSwap.ModState.Host); } return true; } } [HarmonyPatch(typeof(SteamManager), "LeaveLobby")] public class LeaveLobbyPatch { private static void Postfix() { PhotonNetwork.RemoveCallbackTarget((object)sync); swapper.SetState(FittingPaintingsSwap.ModState.SinglePlayer); } } public static List<PaintingGroup> paintingGroups = new List<PaintingGroup> { new PaintingGroup("Landscape", "FittingSwapLandscapePaintings", new HashSet<string> { "Painting_H_Landscape" }), new PaintingGroup("Portrait", "FittingSwapPortraitPaintings", new HashSet<string> { "Painting_V_Furman", "painting teacher01", "painting teacher02", "painting teacher03", "painting teacher04", "Painting_S_Tree" }), new PaintingGroup("Square", "FittingSwapSquarePaintings", new HashSet<string> { "Painting_S_Creep", "Painting_S_Creep 2_0", "Painting_S_Creep 2", "Painting Wizard Class" }) }; private static Logger logger = null; private static FittingPaintingsLoader loader = null; private static FittingPaintingsSwap swapper = null; private static FittingPaintingsSync sync = null; public static int? receivedSeed = null; public static readonly int maxWaitTimeMs = 3000; private readonly Harmony harmony = new Harmony("ZeroTails.FittingPaintings"); private void Awake() { logger = new Logger("FittingPaintings"); logger.LogInfo("FittingPaintings mod initialized."); loader = new FittingPaintingsLoader(logger); loader.LoadImagesFromAllPlugins(); swapper = new FittingPaintingsSwap(logger, loader); sync = new FittingPaintingsSync(logger); harmony.PatchAll(); } } public class FittingPaintingsLoader { private readonly Logger logger; public List<Material> LoadedMaterials { get; } = new List<Material>(); public FittingPaintingsLoader(Logger logger) { this.logger = logger; } public void LoadImagesFromAllPlugins() { string pluginPath = Paths.PluginPath; if (!Directory.Exists(pluginPath)) { logger.LogWarning("Plugins directory not found: " + pluginPath); return; } foreach (FittingPaintings.PaintingGroup paintingGroup in FittingPaintings.paintingGroups) { string[] directories = Directory.GetDirectories(pluginPath, paintingGroup.paintingFolderName, SearchOption.AllDirectories); if (directories.Length == 0) { logger.LogWarning("No '" + paintingGroup.paintingFolderName + "' folders found in plugins."); continue; } string[] array = directories; foreach (string text in array) { logger.LogInfo("Loading images from: " + text); LoadImagesFromDirectory(paintingGroup, text); } } } private void LoadImagesFromDirectory(FittingPaintings.PaintingGroup paintingGroup, string directoryPath) { //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Expected O, but got Unknown if (!Directory.Exists(directoryPath)) { logger.LogWarning("Directory does not exist: " + directoryPath); return; } string[] validExtensions = new string[4] { ".png", ".jpg", ".jpeg", ".bmp" }; string[] array = (from file in Directory.EnumerateFiles(directoryPath, "*.*", SearchOption.AllDirectories) where validExtensions.Contains(Path.GetExtension(file).ToLower()) select file).ToArray(); if (array.Length == 0) { logger.LogWarning("No images found in " + directoryPath); return; } for (int i = 0; i < array.Length; i++) { string text = array[i]; Texture2D val = LoadTextureFromFile(text); if ((Object)(object)val != (Object)null) { Material item = new Material(Shader.Find("Standard")) { mainTexture = (Texture)(object)val }; paintingGroup.loadedMaterials.Add(item); logger.LogInfo($"Loaded image #{i + 1}: {Path.GetFileName(text)}"); } else { logger.LogWarning($"Failed to load image #{i + 1}: {text}"); } } logger.LogInfo($"Total images loaded: {paintingGroup.loadedMaterials.Count}"); } private Texture2D? LoadTextureFromFile(string filePath) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown byte[] array = File.ReadAllBytes(filePath); Texture2D val = new Texture2D(2, 2); if (ImageConversion.LoadImage(val, array)) { val.Apply(); return val; } return null; } } public class FittingPaintingsSwap { public enum ModState { Host, Client, SinglePlayer } private readonly Logger logger; private readonly FittingPaintingsLoader loader; private static int randomSeed = 0; public static int HostSeed = 0; public static int ReceivedSeed = 0; public static int Seed = 0; private int paintingsChangedCount; private static ModState currentState = ModState.SinglePlayer; public FittingPaintingsSwap(Logger logger, FittingPaintingsLoader loader) { this.logger = logger; this.loader = loader; logger.LogInfo($"Initial ModState: {currentState}"); if (randomSeed == 0) { randomSeed = Random.Range(0, int.MaxValue); logger.LogInfo($"Generated initial random seed: {randomSeed}"); } } public ModState GetModState() { return currentState; } public void ReplacePaintings() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) ModState modState = currentState; if (1 == 0) { } int seed = modState switch { ModState.SinglePlayer => randomSeed, ModState.Host => HostSeed, ModState.Client => ReceivedSeed, _ => Seed, }; if (1 == 0) { } Seed = seed; Scene activeScene = SceneManager.GetActiveScene(); logger.LogInfo($"Applying seed {Seed} for painting swaps in scene: {((Scene)(ref activeScene)).name}"); logger.LogInfo("Replacing all materials containing 'painting' with custom images..."); paintingsChangedCount = 0; int num = 0; GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects(); foreach (GameObject val in rootGameObjects) { MeshRenderer[] componentsInChildren = val.GetComponentsInChildren<MeshRenderer>(); foreach (MeshRenderer val2 in componentsInChildren) { Material[] sharedMaterials = ((Renderer)val2).sharedMaterials; for (int k = 0; k < sharedMaterials.Length; k++) { num++; foreach (FittingPaintings.PaintingGroup paintingGroup in FittingPaintings.paintingGroups) { if ((Object)(object)sharedMaterials[k] != (Object)null && paintingGroup.targetMaterials.Contains(((Object)sharedMaterials[k]).name) && paintingGroup.loadedMaterials.Count > 0) { int index = Mathf.Abs((Seed + paintingsChangedCount) % paintingGroup.loadedMaterials.Count); sharedMaterials[k] = paintingGroup.loadedMaterials[index]; paintingsChangedCount++; } } } ((Renderer)val2).sharedMaterials = sharedMaterials; } } logger.LogInfo($"Total materials checked: {num}"); logger.LogInfo($"Total paintings changed in this scene: {paintingsChangedCount}"); logger.LogInfo($"RandomSeed = {randomSeed}"); logger.LogInfo($"HostSeed = {HostSeed}"); logger.LogInfo($"ReceivedSeed = {ReceivedSeed}"); } public void SetState(ModState newState) { currentState = newState; logger.LogInfo($"Mod state set to: {currentState}"); } } public class FittingPaintingsSync : MonoBehaviourPunCallbacks, IOnEventCallback { private readonly Logger logger; public const byte SeedEventCode = 1; public FittingPaintingsSync(Logger logger) { this.logger = logger; } public void SendSeed(int seed) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) object[] array = new object[1] { seed }; logger.LogInfo("Sharing seed with other clients"); RaiseEventOptions val = new RaiseEventOptions { Receivers = (ReceiverGroup)1, CachingOption = (EventCaching)4 }; PhotonNetwork.RaiseEvent((byte)1, (object)array, val, SendOptions.SendReliable); } public void OnEvent(EventData photonEvent) { if (photonEvent.Code == 1) { int num = (int)((object[])photonEvent.CustomData)[0]; logger.LogInfo($"Received seed: {num}"); FittingPaintingsSwap.ReceivedSeed = num; } } } public class Logger { private readonly string logFilePath; private readonly ManualLogSource logSource; public Logger(string modName) { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); logFilePath = Path.Combine(directoryName, modName + "_log.txt"); if (File.Exists(logFilePath)) { File.Delete(logFilePath); } logSource = Logger.CreateLogSource(modName); } public void LogInfo(string message) { WriteLog("INFO", message); logSource.LogInfo((object)message); } public void LogWarning(string message) { WriteLog("WARNING", message); logSource.LogWarning((object)message); } public void LogError(string message) { WriteLog("ERROR", message); logSource.LogError((object)message); } private void WriteLog(string level, string message) { string text = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [{level}] {message}"; File.AppendAllText(logFilePath, text + Environment.NewLine); } public void LogMaterial(Material material) { if ((Object)(object)material != (Object)null && ((Object)material).name.ToLower().Contains("painting")) { LogInfo("Material containing 'painting': " + ((Object)material).name); } } public void ClearLog() { if (File.Exists(logFilePath)) { File.Delete(logFilePath); } } } } namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } }