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 YetAnotherRandomPaintingSwap v1.1.0
YetAnotherRandomPaintingSwap.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.Security; using System.Security.Permissions; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; 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: AssemblyCompany("YetAnotherRandomPaintingSwap")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+1303411e96fb6e125cc75c5af886c19a2f0330ab")] [assembly: AssemblyProduct("Yet Another Random Painting Swap")] [assembly: AssemblyTitle("YetAnotherRandomPaintingSwap")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [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 YetAnotherRandomPaintingSwap { [BepInPlugin("snowtyler.YetAnotherRandomPaintingSwap", "YetAnotherRandomPaintingSwap", "1.1.0")] public class YetAnotherRandomPaintingSwap : 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}"); YetAnotherRandomPaintingSwapSwap.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() == YetAnotherRandomPaintingSwapSwap.ModState.Client) { PhotonNetwork.AddCallbackTarget((object)sync); } if (swapper.GetModState() == YetAnotherRandomPaintingSwapSwap.ModState.Host) { YetAnotherRandomPaintingSwapSwap.HostSeed = Random.Range(0, int.MaxValue); logger.LogInfo($"Generated Hostseed: {YetAnotherRandomPaintingSwapSwap.HostSeed}"); PhotonNetwork.AddCallbackTarget((object)sync); sync.SendSeed(YetAnotherRandomPaintingSwapSwap.HostSeed); } } } [HarmonyPatch(typeof(NetworkConnect), "TryJoiningRoom")] public class JoinLobbyPatch { private static void Prefix() { logger.LogInfo("JoinLobbyPatch Prefix called."); if (swapper.GetModState() == YetAnotherRandomPaintingSwapSwap.ModState.SinglePlayer) { swapper.SetState(YetAnotherRandomPaintingSwapSwap.ModState.Client); } } } [HarmonyPatch(typeof(SteamManager), "HostLobby")] public class HostLobbyPatch { private static bool Prefix() { logger.LogInfo("HostLobbyPatch Prefix called."); if (swapper.GetModState() != 0) { swapper.SetState(YetAnotherRandomPaintingSwapSwap.ModState.Host); } return true; } } [HarmonyPatch(typeof(SteamManager), "LeaveLobby")] public class LeaveLobbyPatch { private static void Postfix() { PhotonNetwork.RemoveCallbackTarget((object)sync); swapper.SetState(YetAnotherRandomPaintingSwapSwap.ModState.SinglePlayer); } } public static List<PaintingGroup> paintingGroups = new List<PaintingGroup> { new PaintingGroup("Landscape", "RandomLandscapePaintingSwap_Images", new HashSet<string> { "Painting_H_Landscape" }), new PaintingGroup("Portrait", "RandomPortraitPaintingSwap_Images", new HashSet<string> { "Painting_V_Furman", "painting teacher01", "painting teacher02", "painting teacher03", "painting teacher04", "Painting_S_Tree" }), new PaintingGroup("Square", "RandomSquarePaintingSwap_Images", 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 YetAnotherRandomPaintingSwapLoader loader = null; private static YetAnotherRandomPaintingSwapSwap swapper = null; private static YetAnotherRandomPaintingSwapSync sync = null; private static GrungeMaterialManager grungeMaterialManager = null; public static int? receivedSeed = null; public static readonly int maxWaitTimeMs = 3000; private readonly Harmony harmony = new Harmony("snowtyler.YetAnotherRandomPaintingSwap"); private void Awake() { logger = new Logger("YetAnotherRandomPaintingSwap"); logger.LogInfo("YetAnotherRandomPaintingSwap mod initialized."); PluginConfig.Init(((BaseUnityPlugin)this).Config); bool value = PluginConfig.Grunge.enableGrunge.Value; logger.LogInfo("Grunge effect is " + (value ? "ENABLED" : "DISABLED") + " in configuration"); grungeMaterialManager = new GrungeMaterialManager(logger); bool flag = false; if (value) { if (grungeMaterialManager.LoadGrungeMaterials()) { logger.LogInfo("Grunge materials loaded successfully"); } else { logger.LogWarning("Failed to load grunge materials"); } } loader = new YetAnotherRandomPaintingSwapLoader(logger, grungeMaterialManager); loader.LoadImagesFromAllPlugins(); swapper = new YetAnotherRandomPaintingSwapSwap(logger, loader); sync = new YetAnotherRandomPaintingSwapSync(logger); harmony.PatchAll(); if (PluginConfig.enableDebugLog.Value) { logger.LogInfo("Debug logging enabled"); } } private void OnDestroy() { harmony.UnpatchSelf(); } } internal static class PluginConfig { internal static class Grunge { internal static ConfigEntry<bool> enableGrunge; internal static ConfigEntry<Color> _BaseColor; internal static ConfigEntry<Color> _MainColor; internal static ConfigEntry<Color> _CracksColor; internal static ConfigEntry<Color> _OutlineColor; internal static ConfigEntry<float> _CracksPower; } internal static ConfigEntry<bool> enableDebugLog; internal static ConfigEntry<float> customPaintingChance; internal static void Init(ConfigFile config) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) enableDebugLog = config.Bind<bool>("General", "DebugLog", false, "Print extra logs for debugging"); customPaintingChance = config.Bind<float>("General", "CustomPaintingChance", 1f, "The chance of a painting being replaced by a custom painting (1 = 100%, 0.5 = 50%)"); Grunge.enableGrunge = config.Bind<bool>("Grunge", "EnableGrunge", true, "Whether the grunge effect is enabled"); Grunge._BaseColor = config.Bind<Color>("Grunge", "_GrungeBaseColor", new Color(0f, 0f, 0f, 1f), "The base color of the grunge"); Grunge._MainColor = config.Bind<Color>("Grunge", "_GrungeMainColor", new Color(0f, 0f, 0f, 1f), "The color of the main overlay of grunge"); Grunge._CracksColor = config.Bind<Color>("Grunge", "_GrungeCracksColor", new Color(0f, 0f, 0f, 1f), "The color of the cracks in the grunge"); Grunge._OutlineColor = config.Bind<Color>("Grunge", "_GrungeOutlineColor", new Color(0f, 0f, 0f, 1f), "The color of the grunge outlining the painting"); Grunge._CracksPower = config.Bind<float>("Grunge", "_GrungeCracksPower", 1f, "The inverse of intensity of the cracks. 1.0 will have plenty of cracks, higher numbers will have less cracks (Values below 1.0 will start to look bad)"); } } public class GrungeMaterialManager { private const string MATERIAL_LANDSCAPE_ASSET_NAME = "GrungeHorizontalMaterial"; private const string MATERIAL_PORTRAIT_ASSET_NAME = "GrungeVerticalMaterial"; private const string ASSET_BUNDLE_NAME = "painting"; private const string PROP_BASE_COLOR = "_BaseColor"; private const string PROP_MAIN_COLOR = "_MainColor"; private const string PROP_CRACKS_COLOR = "_CracksColor"; private const string PROP_OUTLINE_COLOR = "_OutlineColor"; private const string PROP_CRACKS_POWER = "_CracksPower"; private const string PROP_MAIN_TEX = "_MainTex"; private Material _landscapeMaterial; private Material _portraitMaterial; private readonly Logger logger; public GrungeMaterialManager(Logger logger) { this.logger = logger; } public bool LoadGrungeMaterials() { if (!IsGrungeEnabled()) { logger.LogInfo("Skipping grunge material loading as it's disabled in config"); return false; } string location = Assembly.GetExecutingAssembly().Location; string directoryName = Path.GetDirectoryName(location); string text = Path.Combine(directoryName, "painting"); logger.LogInfo("Loading asset bundle from: " + text); if (!File.Exists(text)) { logger.LogWarning("Asset Bundle doesn't exist at: " + text); return false; } AssetBundle val = AssetBundle.LoadFromFile(text); if ((Object)(object)val == (Object)null) { logger.LogError("Failed to load asset bundle: painting"); return false; } _landscapeMaterial = val.LoadAsset<Material>("GrungeHorizontalMaterial"); if ((Object)(object)_landscapeMaterial == (Object)null) { logger.LogError("Could not load landscape painting material [GrungeHorizontalMaterial]!"); return false; } _portraitMaterial = val.LoadAsset<Material>("GrungeVerticalMaterial"); if ((Object)(object)_portraitMaterial == (Object)null) { logger.LogError("Could not load portrait painting material [GrungeVerticalMaterial]!"); return false; } ConfigureGrungeMaterials(); return true; } private void ConfigureGrungeMaterials() { logger.LogInfo($"Configuring grunge materials with CracksPower: {PluginConfig.Grunge._CracksPower.Value}"); ConfigureGrungeMaterial(_landscapeMaterial); ConfigureGrungeMaterial(_portraitMaterial); } private void ConfigureGrungeMaterial(Material material) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)material == (Object)null)) { material.SetColor("_BaseColor", PluginConfig.Grunge._BaseColor.Value); material.SetColor("_MainColor", PluginConfig.Grunge._MainColor.Value); material.SetColor("_CracksColor", PluginConfig.Grunge._CracksColor.Value); material.SetColor("_OutlineColor", PluginConfig.Grunge._OutlineColor.Value); material.SetFloat("_CracksPower", PluginConfig.Grunge._CracksPower.Value); material.shader.maximumLOD = 600; } } public Material CreateMaterialInstance(string paintingType, Texture2D texture) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) if (!IsGrungeEnabled()) { return null; } Material baseMaterial = GetBaseMaterial(paintingType); if ((Object)(object)baseMaterial == (Object)null) { return null; } Material val = new Material(baseMaterial); val.SetTexture("_MainTex", (Texture)(object)texture); val.SetColor("_BaseColor", PluginConfig.Grunge._BaseColor.Value); val.SetColor("_MainColor", PluginConfig.Grunge._MainColor.Value); val.SetColor("_CracksColor", PluginConfig.Grunge._CracksColor.Value); val.SetColor("_OutlineColor", PluginConfig.Grunge._OutlineColor.Value); val.SetFloat("_CracksPower", PluginConfig.Grunge._CracksPower.Value); return val; } private Material GetBaseMaterial(string paintingType) { if (paintingType == "Portrait") { return _portraitMaterial; } return _landscapeMaterial; } public bool IsGrungeEnabled() { return PluginConfig.Grunge.enableGrunge.Value; } } public class YetAnotherRandomPaintingSwapLoader { private readonly Logger logger; private readonly GrungeMaterialManager grungeMaterialManager; private static readonly string[] validExtensions = new string[4] { ".png", ".jpg", ".jpeg", ".bmp" }; public List<Material> LoadedMaterials { get; } = new List<Material>(); public YetAnotherRandomPaintingSwapLoader(Logger logger, GrungeMaterialManager grungeMaterialManager) { this.logger = logger; this.grungeMaterialManager = grungeMaterialManager; } public void LoadImagesFromAllPlugins() { string pluginPath = Paths.PluginPath; if (!Directory.Exists(pluginPath)) { logger.LogWarning("Plugins directory not found: " + pluginPath); return; } logger.LogInfo("Grunge effect is " + (grungeMaterialManager.IsGrungeEnabled() ? "enabled" : "disabled") + " while loading images"); foreach (YetAnotherRandomPaintingSwap.PaintingGroup paintingGroup in YetAnotherRandomPaintingSwap.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; string[] array2 = array; foreach (string text in array2) { logger.LogInfo("Loading images from: " + text); LoadImagesFromDirectory(paintingGroup, text); } } } private void LoadImagesFromDirectory(YetAnotherRandomPaintingSwap.PaintingGroup paintingGroup, string directoryPath) { if (!Directory.Exists(directoryPath)) { logger.LogWarning("Directory does not exist: " + directoryPath); return; } 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; } bool flag = grungeMaterialManager.IsGrungeEnabled(); logger.LogInfo("Creating materials with grunge effect " + (flag ? "enabled" : "disabled")); for (int i = 0; i < array.Length; i++) { string text = array[i]; string fileName = Path.GetFileName(text); Texture2D val = LoadTextureFromFile(text); if ((Object)(object)val != (Object)null) { Material val2; if (flag) { val2 = grungeMaterialManager.CreateMaterialInstance(paintingGroup.paintingType, val); if ((Object)(object)val2 == (Object)null) { val2 = CreateStandardMaterial(val); logger.LogInfo("Using standard shader for image: " + fileName + " (grunge creation failed)"); } else { logger.LogInfo("Created grunge material for image: " + fileName); } } else { val2 = CreateStandardMaterial(val); logger.LogInfo("Using standard shader for image: " + fileName + " (grunge disabled)"); } paintingGroup.loadedMaterials.Add(val2); logger.LogInfo($"Loaded image #{i + 1}: {fileName}"); } else { logger.LogWarning($"Failed to load image #{i + 1}: {text}"); } } logger.LogInfo($"Total images loaded for {paintingGroup.paintingType}: {paintingGroup.loadedMaterials.Count}"); } private Material CreateStandardMaterial(Texture2D texture) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown return new Material(Shader.Find("Standard")) { mainTexture = (Texture)(object)texture }; } 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 YetAnotherRandomPaintingSwapSwap { public enum ModState { Host, Client, SinglePlayer } private readonly Logger logger; private readonly YetAnotherRandomPaintingSwapLoader 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 YetAnotherRandomPaintingSwapSwap(Logger logger, YetAnotherRandomPaintingSwapLoader 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_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) ModState modState = currentState; if (1 == 0) { } int num = modState switch { ModState.SinglePlayer => randomSeed, ModState.Host => HostSeed, ModState.Client => ReceivedSeed, _ => Seed, }; if (1 == 0) { } int seed = num; Seed = seed; Scene activeScene = SceneManager.GetActiveScene(); logger.LogInfo($"Applying seed {Seed} for painting swaps in scene: {((Scene)(ref activeScene)).name}"); logger.LogInfo("Replacing materials with custom images..."); paintingsChangedCount = 0; int num2 = 0; GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects(); GameObject[] array = rootGameObjects; foreach (GameObject val in array) { MeshRenderer[] componentsInChildren = val.GetComponentsInChildren<MeshRenderer>(true); MeshRenderer[] array2 = componentsInChildren; foreach (MeshRenderer val2 in array2) { Material[] sharedMaterials = ((Renderer)val2).sharedMaterials; bool flag = false; for (int k = 0; k < sharedMaterials.Length; k++) { num2++; foreach (YetAnotherRandomPaintingSwap.PaintingGroup paintingGroup in YetAnotherRandomPaintingSwap.paintingGroups) { if (!((Object)(object)sharedMaterials[k] != (Object)null) || !paintingGroup.targetMaterials.Contains(((Object)sharedMaterials[k]).name) || paintingGroup.loadedMaterials.Count <= 0) { continue; } if (Random.value > PluginConfig.customPaintingChance.Value) { if (PluginConfig.enableDebugLog.Value) { logger.LogInfo("Skipping replacement of " + ((Object)sharedMaterials[k]).name + " due to chance setting"); } continue; } int index = Mathf.Abs((Seed + paintingsChangedCount) % paintingGroup.loadedMaterials.Count); sharedMaterials[k] = paintingGroup.loadedMaterials[index]; paintingsChangedCount++; flag = true; if (PluginConfig.enableDebugLog.Value) { logger.LogInfo($"Replaced {((Object)((Component)val2).gameObject).name} material {k} with {paintingGroup.paintingType} painting"); } } } if (flag) { ((Renderer)val2).sharedMaterials = sharedMaterials; } } } logger.LogInfo($"Total materials checked: {num2}"); 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 YetAnotherRandomPaintingSwapSync : MonoBehaviourPunCallbacks, IOnEventCallback { private readonly Logger logger; public const byte SeedEventCode = 1; public YetAnotherRandomPaintingSwapSync(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)0, 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}"); YetAnotherRandomPaintingSwap.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); } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "YetAnotherRandomPaintingSwap"; public const string PLUGIN_NAME = "Yet Another Random Painting Swap"; public const string PLUGIN_VERSION = "1.0.0"; } }