using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Xml.Serialization;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using OutwardModsCommunicator.Enums;
using OutwardModsCommunicator.EventBus;
using OutwardModsCommunicator.Managers;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("OutwardModsCommunicator")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("OutwardModsCommunicator")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("35f7bd74-9a7e-40c3-992f-6e85dc4d12a3")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.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.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 OutwardModsCommunicator
{
[XmlRoot("ConfigOverrides")]
public class ConfigOverrides
{
[XmlElement("Mod")]
public List<ModOverride> Mods { get; set; } = new List<ModOverride>();
}
public class ModOverride
{
[XmlAttribute("GUID")]
public string ModGUID { get; set; } = "";
[XmlElement("Section")]
public List<SectionOverride> Sections { get; set; } = new List<SectionOverride>();
}
public class SectionOverride
{
[XmlAttribute("Name")]
public string Name { get; set; } = "";
[XmlElement("Entry")]
public List<EntryOverride> Entries { get; set; } = new List<EntryOverride>();
}
public class EntryOverride
{
[XmlAttribute("Key")]
public string Key { get; set; } = "";
[XmlAttribute("Value")]
public string Value { get; set; } = "";
}
public static class ConfigProfileLoader
{
public static ConfigOverrides? LoadFromXml(string filePath)
{
if (!File.Exists(filePath))
{
return null;
}
try
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(ConfigOverrides));
using FileStream stream = File.OpenRead(filePath);
return xmlSerializer.Deserialize(stream) as ConfigOverrides;
}
catch
{
return null;
}
}
}
[BepInPlugin("gymmed.outward_mods_communicator", "Outward Mods Communicator", "1.2.0")]
public class OMC : BaseUnityPlugin
{
[HarmonyPatch(typeof(ResourcesPrefabManager), "Load")]
public class ResourcesPrefabManager_Load
{
private static void Postfix(ResourcesPrefabManager __instance)
{
if (!string.IsNullOrEmpty(xmlFilePath))
{
ConfigOverrideManager.OverrideConfigsFromFile(xmlFilePath);
}
string playerOverrideLocation = PathsManager.GetPlayerOverrideLocation();
if (!string.IsNullOrEmpty(playerOverrideLocation))
{
ConfigOverrideManager.OverrideConfigsFromFile(playerOverrideLocation);
}
}
}
public const string GUID = "gymmed.outward_mods_communicator";
public const string NAME = "Outward Mods Communicator";
public const string VERSION = "1.2.0";
public static ConfigEntry<bool> EnableEventsProfiler = null;
public static ConfigEntry<bool> InstantLogEventsProfileData = null;
public static string xmlFilePath { get; set; } = "";
internal void Awake()
{
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
LogManager.InitializeLogger(((BaseUnityPlugin)this).Logger);
EnableEventsProfiler = ((BaseUnityPlugin)this).Config.Bind<bool>("Event Profiler", "EnableEventsProfiler", false, "Enable events profiler for inspecting events timers?(You still need to call it for logging)");
InstantLogEventsProfileData = ((BaseUnityPlugin)this).Config.Bind<bool>("Event Profiler", "InstantLogEventsProfileData", false, "If enabled, logs each event profiling record immediately when it happens.");
EventProfiler.Initialize();
new Harmony("gymmed.outward_mods_communicator").PatchAll();
}
public static string GetProjectLocation()
{
return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
}
public static void Log(string message, ENUM_LOG_LEVELS logType = ENUM_LOG_LEVELS.Info)
{
LogManager.Log(message, logType);
}
}
}
namespace OutwardModsCommunicator.Managers
{
public class ConfigOverrideManager
{
public static void ChangeOriginalConfigs(ConfigOverrides overrides)
{
foreach (ModOverride mod in overrides.Mods)
{
ChangeOriginalModConfig(mod);
}
}
public static void ChangeOriginalModConfig(ModOverride mod)
{
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Expected O, but got Unknown
string text = Path.Combine(Paths.ConfigPath, mod.ModGUID + ".cfg");
if (!File.Exists(text))
{
OMC.Log("Skipping " + mod.ModGUID + " — config file not found.");
return;
}
ConfigFile val = new ConfigFile(text, true);
foreach (SectionOverride section in mod.Sections)
{
foreach (EntryOverride entry in section.Entries)
{
try
{
ConfigEntryBase val2 = val[section.Name, entry.Key];
if (val2 == null)
{
OMC.Log("Missing key " + section.Name + ":" + entry.Key + " in " + mod.ModGUID + ", skipping.");
}
else
{
object boxedValue = Convert.ChangeType(entry.Value, val2.SettingType);
val2.BoxedValue = boxedValue;
OMC.Log("Updated " + mod.ModGUID + ":" + section.Name + ":" + entry.Key + " = " + entry.Value);
}
}
catch (Exception ex)
{
OMC.Log("Error updating " + mod.ModGUID + ":" + section.Name + ":" + entry.Key + ": " + ex.Message);
}
}
}
val.Save();
}
public static void OverrideConfigsFromFile(string filePath = "MyModpackOverrides.xml")
{
ConfigOverrides configOverrides = ConfigProfileLoader.LoadFromXml(filePath);
if (configOverrides == null)
{
OMC.Log("Couldn't open \"" + filePath + "\". You are sure that provided path is correct?", ENUM_LOG_LEVELS.Error);
}
else
{
OverrideConfigsValues(configOverrides);
}
}
public static void OverrideConfigsValues(ConfigOverrides overrides)
{
try
{
if (overrides?.Mods == null || overrides.Mods.Count == 0)
{
OMC.Log("No overrides provided.");
return;
}
foreach (ModOverride mod in overrides.Mods)
{
ApplyOverridesToMod(mod);
}
}
catch (Exception ex)
{
OMC.Log("ConfigOverrideManager@OverrideConfigsValues:" + ex.Message, ENUM_LOG_LEVELS.Error);
}
}
private static void ApplyOverridesToMod(ModOverride mod)
{
if (string.IsNullOrWhiteSpace(mod.ModGUID))
{
OMC.Log("Skipping mod with empty GUID.", ENUM_LOG_LEVELS.Warning);
return;
}
if (!Chainloader.PluginInfos.TryGetValue(mod.ModGUID, out var value))
{
OMC.Log("Plugin not found: " + mod.ModGUID, ENUM_LOG_LEVELS.Warning);
return;
}
BaseUnityPlugin instance = value.Instance;
ConfigFile val = ((instance != null) ? instance.Config : null);
if (val == null)
{
OMC.Log("Plugin has no ConfigFile: " + mod.ModGUID, ENUM_LOG_LEVELS.Warning);
return;
}
if (mod.Sections == null || mod.Sections.Count == 0)
{
OMC.Log("No sections for " + mod.ModGUID + " — nothing to apply.");
return;
}
foreach (SectionOverride section in mod.Sections)
{
ApplyOverridesToSection(mod.ModGUID, val, section);
}
}
private static void ApplyOverridesToSection(string modGUID, ConfigFile configFile, SectionOverride section)
{
if (string.IsNullOrWhiteSpace(section.Name))
{
OMC.Log("Sections without name in " + modGUID, ENUM_LOG_LEVELS.Warning);
return;
}
if (section.Entries == null || section.Entries.Count == 0)
{
OMC.Log("Sections without entries in " + modGUID, ENUM_LOG_LEVELS.Warning);
return;
}
foreach (EntryOverride entry in section.Entries)
{
ApplyOverrideToEntry(modGUID, configFile, section.Name, entry);
}
}
private static void ApplyOverrideToEntry(string modGUID, ConfigFile configFile, string sectionName, EntryOverride entry)
{
if (string.IsNullOrWhiteSpace(entry.Key))
{
OMC.Log("Mod:\"" + modGUID + "\" Section: \"" + sectionName + "\" has empty entry key", ENUM_LOG_LEVELS.Warning);
return;
}
object obj = FindConfigEntry(configFile, sectionName, entry.Key);
if (obj == null)
{
OMC.Log("Config entry not found: " + modGUID + " " + sectionName + ":" + entry.Key, ENUM_LOG_LEVELS.Warning);
return;
}
Type type = GetSettingType(obj);
if (type == null)
{
type = typeof(string);
}
if (!TryConvertValue(entry.Value, type, out object result))
{
OMC.Log("Failed to convert '" + entry.Value + "' to " + type.Name + " for " + modGUID + ":" + sectionName + ":" + entry.Key, ENUM_LOG_LEVELS.Warning);
}
else if (SetBoxedValue(obj, result))
{
OMC.Log("Applied " + modGUID + " " + sectionName + ":" + entry.Key + " = " + entry.Value);
}
else
{
OMC.Log("Cannot set BoxedValue for " + modGUID + ":" + sectionName + ":" + entry.Key, ENUM_LOG_LEVELS.Warning);
}
}
private static object? FindConfigEntry(ConfigFile configFile, string sectionName, string key)
{
try
{
return configFile[sectionName, key];
}
catch
{
try
{
if (!(((object)configFile).GetType().GetProperty("Values", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(configFile) is IEnumerable enumerable))
{
return null;
}
foreach (object item in enumerable)
{
object obj = item.GetType().GetProperty("Definition")?.GetValue(item);
if (obj != null)
{
string obj2 = obj.GetType().GetProperty("Key")?.GetValue(obj) as string;
string text = obj.GetType().GetProperty("Section")?.GetValue(obj) as string;
if (obj2 == key && text == sectionName)
{
return item;
}
}
}
}
catch
{
}
}
return null;
}
private static Type GetSettingType(object configEntryObj)
{
try
{
PropertyInfo property = configEntryObj.GetType().GetProperty("SettingType");
if (property != null)
{
Type type = property.GetValue(configEntryObj) as Type;
if (type != null)
{
return type;
}
}
return (configEntryObj.GetType().GetProperty("BoxedValue")?.GetValue(configEntryObj))?.GetType() ?? typeof(string);
}
catch
{
return typeof(string);
}
}
private static bool TryConvertValue(string value, Type targetType, out object? result)
{
try
{
if (targetType.IsEnum)
{
result = Enum.Parse(targetType, value, ignoreCase: true);
}
else
{
result = Convert.ChangeType(value, targetType);
}
return true;
}
catch
{
result = null;
return false;
}
}
private static bool SetBoxedValue(object configEntryObj, object? value)
{
try
{
PropertyInfo property = configEntryObj.GetType().GetProperty("BoxedValue", BindingFlags.Instance | BindingFlags.Public);
if (property != null && property.CanWrite)
{
property.SetValue(configEntryObj, value);
return true;
}
}
catch
{
}
return false;
}
}
public static class LogManager
{
public static string prefix = "[GymMed-Mods-Communicator]";
internal static ManualLogSource? Logger;
public static void InitializeLogger(ManualLogSource logger)
{
Logger = logger;
}
public static void Log(string message, ENUM_LOG_LEVELS logType = ENUM_LOG_LEVELS.Info)
{
switch (logType)
{
case ENUM_LOG_LEVELS.Info:
LogInfo(message);
break;
case ENUM_LOG_LEVELS.Warning:
LogWarning(message);
break;
case ENUM_LOG_LEVELS.Error:
LogError(message);
break;
default:
LogInfo(message);
break;
}
}
public static void LogInfo(string message)
{
if (Logger != null)
{
Logger.LogInfo((object)(prefix + " " + message));
}
else
{
Console.WriteLine(prefix + " " + message);
}
}
public static void LogWarning(string message)
{
if (Logger != null)
{
Logger.LogWarning((object)(prefix + " " + message));
}
else
{
Console.WriteLine(prefix + "[WARNING] " + message);
}
}
public static void LogError(string message)
{
if (Logger != null)
{
Logger.LogError((object)(prefix + " " + message));
}
else
{
Console.WriteLine(prefix + "[ERROR] " + message);
}
}
}
public static class PathsManager
{
public static string ConfigDirectoryName = "gymmed.Mods_Communicator";
public static string ConfigPath = Path.Combine(Paths.ConfigPath, ConfigDirectoryName);
public static string PlayerOverrideFileName = "PlayerModsOverrides.xml";
public static string GetPlayerOverrideLocation()
{
return Path.Combine(ConfigPath, PlayerOverrideFileName);
}
}
}
namespace OutwardModsCommunicator.EventBus
{
public static class EventBus
{
private static string _logPrefix = "[EventBus]";
private static readonly Dictionary<string, Dictionary<string, List<Action<EventPayload?>>>> _modSubscribers = new Dictionary<string, Dictionary<string, List<Action<EventPayload>>>>();
private static readonly Dictionary<string, Dictionary<string, EventPayload>> _publishedPayloads = new Dictionary<string, Dictionary<string, EventPayload>>();
private static readonly Dictionary<string, Dictionary<string, EventDefinition>> _registeredEvents = new Dictionary<string, Dictionary<string, EventDefinition>>();
public static IReadOnlyDictionary<string, Dictionary<string, List<Action<EventPayload?>>>> GetModSubscribers()
{
return _modSubscribers;
}
public static IReadOnlyDictionary<string, Dictionary<string, EventPayload>> GetModPublishedPayloads()
{
return _publishedPayloads;
}
public static IReadOnlyDictionary<string, Dictionary<string, EventDefinition>> GetRegisteredEvents()
{
return _registeredEvents;
}
private static void Log(string message, ENUM_LOG_LEVELS logLevel = ENUM_LOG_LEVELS.Info)
{
OMC.Log(_logPrefix + " " + message, logLevel);
}
public static void RegisterEvent(string modNamespace, string eventName, EventSchema? schema = null)
{
if (!_registeredEvents.TryGetValue(modNamespace, out Dictionary<string, EventDefinition> value))
{
value = (_registeredEvents[modNamespace] = new Dictionary<string, EventDefinition>());
}
value[eventName] = new EventDefinition(schema ?? new EventSchema());
if (!_publishedPayloads.TryGetValue(modNamespace, out Dictionary<string, EventPayload> value2))
{
value2 = (_publishedPayloads[modNamespace] = new Dictionary<string, EventPayload>());
}
if (!value2.ContainsKey(eventName))
{
value2[eventName] = new EventPayload();
}
Log("Registered event '" + modNamespace + "." + eventName + "'");
}
public static void RegisterEvent(string modNamespace, string eventName, string description, EventSchema? schema = null)
{
if (!_registeredEvents.TryGetValue(modNamespace, out Dictionary<string, EventDefinition> value))
{
value = (_registeredEvents[modNamespace] = new Dictionary<string, EventDefinition>());
}
value[eventName] = new EventDefinition(schema ?? new EventSchema(), description);
if (!_publishedPayloads.TryGetValue(modNamespace, out Dictionary<string, EventPayload> value2))
{
value2 = (_publishedPayloads[modNamespace] = new Dictionary<string, EventPayload>());
}
if (!value2.ContainsKey(eventName))
{
value2[eventName] = new EventPayload();
}
Log("Registered event '" + modNamespace + "." + eventName + "'");
}
public static void RegisterEvent(string modNamespace, string eventName, params (string key, Type type)[] fields)
{
EventSchema eventSchema = new EventSchema();
for (int i = 0; i < fields.Length; i++)
{
var (name, type) = fields[i];
eventSchema.AddField(name, type);
}
RegisterEvent(modNamespace, eventName, eventSchema);
}
public static void RegisterEvent(string modNamespace, string eventName, string eventDescription, params (string key, Type type)[] fields)
{
EventSchema eventSchema = new EventSchema();
for (int i = 0; i < fields.Length; i++)
{
var (name, type) = fields[i];
eventSchema.AddField(name, type);
}
RegisterEvent(modNamespace, eventName, eventDescription, eventSchema);
}
public static void RegisterEvent(string modNamespace, string eventName, params (string key, Type type, string? description)[] fields)
{
EventSchema eventSchema = new EventSchema();
for (int i = 0; i < fields.Length; i++)
{
var (name, type, description) = fields[i];
eventSchema.AddField(name, type, description);
}
RegisterEvent(modNamespace, eventName, eventSchema);
}
public static void RegisterEvent(string modNamespace, string eventName, string eventDescription, params (string key, Type type, string? description)[] fields)
{
EventSchema eventSchema = new EventSchema();
for (int i = 0; i < fields.Length; i++)
{
var (name, type, description) = fields[i];
eventSchema.AddField(name, type, description);
}
RegisterEvent(modNamespace, eventName, eventDescription, eventSchema);
}
public static void RegisterEventFromPayloadTypes(string modNamespace, string eventName, EventPayload payloadWithTypes)
{
EventSchema schema = new EventSchema(payloadWithTypes, treatValuesAsTypes: true);
RegisterEvent(modNamespace, eventName, schema);
}
public static void Subscribe(string modNamespace, string eventName, Action<EventPayload?> callback)
{
if (!_modSubscribers.TryGetValue(modNamespace, out Dictionary<string, List<Action<EventPayload>>> value))
{
value = (_modSubscribers[modNamespace] = new Dictionary<string, List<Action<EventPayload>>>());
}
if (!value.TryGetValue(eventName, out var value2))
{
value2 = (value[eventName] = new List<Action<EventPayload>>());
}
value2.Add(callback);
}
public static void Unsubscribe(string modNamespace, string eventName, Action<EventPayload?> callback)
{
if (_modSubscribers.TryGetValue(modNamespace, out Dictionary<string, List<Action<EventPayload>>> value) && value.TryGetValue(eventName, out var value2))
{
value2.Remove(callback);
}
}
public static void Publish(string modNamespace, string eventName, EventPayload? payload = null)
{
if (!_publishedPayloads.TryGetValue(modNamespace, out Dictionary<string, EventPayload> value))
{
value = (_publishedPayloads[modNamespace] = new Dictionary<string, EventPayload>());
}
value[eventName] = ((payload != null) ? new EventPayload(payload) : new EventPayload());
if (!_modSubscribers.TryGetValue(modNamespace, out Dictionary<string, List<Action<EventPayload>>> value2) || !value2.TryGetValue(eventName, out var value3))
{
return;
}
Stopwatch stopwatch = null;
if (OMC.EnableEventsProfiler.Value)
{
stopwatch = Stopwatch.StartNew();
}
foreach (Action<EventPayload> item in value3)
{
Stopwatch stopwatch2 = null;
if (OMC.EnableEventsProfiler.Value)
{
stopwatch2 = Stopwatch.StartNew();
}
try
{
item(payload);
}
catch (Exception arg)
{
Log($"Error in '{modNamespace}.{eventName}' subscriber: {arg}", ENUM_LOG_LEVELS.Error);
}
if (OMC.EnableEventsProfiler.Value && stopwatch2 != null)
{
stopwatch2.Stop();
string subscriberId = item.Method.DeclaringType?.Name + "." + item.Method.Name;
EventProfiler.RecordSubscriber(modNamespace, eventName, subscriberId, stopwatch2.Elapsed.TotalMilliseconds);
}
}
if (OMC.EnableEventsProfiler.Value && stopwatch != null)
{
stopwatch.Stop();
EventProfiler.Record(modNamespace, eventName, stopwatch.Elapsed.TotalMilliseconds);
}
}
public static void ClearNamespace(string modNamespace)
{
_modSubscribers.Remove(modNamespace);
}
}
public class EventBusDataPresenter
{
public static void LogRegisteredEvents()
{
IReadOnlyDictionary<string, Dictionary<string, EventDefinition>> registeredEvents = EventBus.GetRegisteredEvents();
OMC.Log("==== EventBus Registered Events (ALL MODS) ====");
foreach (KeyValuePair<string, Dictionary<string, EventDefinition>> item in registeredEvents)
{
LogRegisteredEventsForModInternal(item.Key, item.Value);
}
OMC.Log("==== End of Registered Events ====");
}
public static void LogModRegisteredEvents(string modNamespace)
{
if (!EventBus.GetRegisteredEvents().TryGetValue(modNamespace, out Dictionary<string, EventDefinition> value))
{
logForMod(modNamespace, "No registered events found for mod '" + modNamespace + "'");
}
else
{
LogRegisteredEventsForModInternal(modNamespace, value);
}
}
private static void LogRegisteredEventsForModInternal(string modNamespace, Dictionary<string, EventDefinition> modEvents)
{
logForMod(modNamespace, "==== EventBus Registered Events for Mod '" + modNamespace + "' ====");
foreach (KeyValuePair<string, EventDefinition> modEvent in modEvents)
{
string key = modEvent.Key;
EventDefinition value = modEvent.Value;
EventSchema schema = value.Schema;
logEmptyEventLine(modNamespace, key);
if (!string.IsNullOrEmpty(value.Description))
{
logForEvent(modNamespace, key, " Event '" + key + "' Description:'" + value.Description + "'");
}
logForEvent(modNamespace, key, " Event '" + key + "' fields:");
if (schema.Fields.Count == 0)
{
logForEvent(modNamespace, key, " (no fields declared)");
logEmptyEventLine(modNamespace, key);
continue;
}
foreach (KeyValuePair<string, Type> field in schema.Fields)
{
string key2 = field.Key;
string text = field.Value?.Name ?? "null";
logForEvent(modNamespace, key, " Parameter " + key2 + " : Type " + text);
if (schema.Descriptions.TryGetValue(key2, out string value2) && !string.IsNullOrWhiteSpace(value2))
{
logForEvent(modNamespace, key, " Description of parameter " + key2 + ": " + value2);
}
logEmptyEventLine(modNamespace, key);
}
}
logForMod(modNamespace, "==== End of Registered Events for Mod '" + modNamespace + "' ====");
}
public static void LogPublishers()
{
IReadOnlyDictionary<string, Dictionary<string, EventPayload>> modPublishedPayloads = EventBus.GetModPublishedPayloads();
OMC.Log("==== EventBus Publishers ====");
foreach (KeyValuePair<string, Dictionary<string, EventPayload>> item in modPublishedPayloads)
{
LogPublishersEvents(item.Key, item.Value);
}
OMC.Log("==== End of Publishers ====");
}
public static void LogModPublishers(string modNamespace)
{
if (!EventBus.GetModPublishedPayloads().TryGetValue(modNamespace, out Dictionary<string, EventPayload> value))
{
logForMod(modNamespace, "No published events found for mod '" + modNamespace + "'");
}
else
{
LogPublishersEvents(modNamespace, value);
}
}
public static void LogPublishersEvents(string modNamespace, Dictionary<string, EventPayload> modEvents)
{
logForMod(modNamespace, "==== EventBus Publishers for Mod '" + modNamespace + "' ====");
foreach (KeyValuePair<string, EventPayload> modEvent in modEvents)
{
LogPublishersEventsVariables(modNamespace, modEvent.Key, modEvent.Value);
}
logForMod(modNamespace, "==== End of Publishers for Mod '" + modNamespace + "' ====");
}
public static void LogPublishersEventsVariables(string modName, string eventName, EventPayload payload)
{
logForEvent(modName, eventName, " Event '" + eventName + "' publishes keys:");
foreach (KeyValuePair<string, object> item in payload)
{
string key = item.Key;
string text = item.Value?.GetType().Name ?? "null";
string text2 = item.Value?.ToString() ?? "null";
logForEvent(modName, eventName, " " + key + " : " + text + " | Value=" + text2);
}
}
public static void LogAllModsSubsribers()
{
IReadOnlyDictionary<string, Dictionary<string, List<Action<EventPayload?>>>> modSubscribers = EventBus.GetModSubscribers();
OMC.Log("==== EventBus Data: ALL MODS ====");
foreach (KeyValuePair<string, Dictionary<string, List<Action<EventPayload>>>> item in modSubscribers)
{
LogModEventSubscribers(item.Key, item.Value);
}
OMC.Log("==== End of ALL MODS ====");
}
public static void LogModSubscribers(string modNamespace)
{
if (!EventBus.GetModSubscribers().TryGetValue(modNamespace, out Dictionary<string, List<Action<EventPayload>>> value))
{
logForMod(modNamespace, "No subscribers found for mod '" + modNamespace + "'");
}
else
{
LogModEventSubscribers(modNamespace, value);
}
}
private static void LogModEventSubscribers(string modNamespace, Dictionary<string, List<Action<EventPayload?>>> modEvents)
{
logForMod(modNamespace, "==== EventBus Data: Mod '" + modNamespace + "' ====");
foreach (KeyValuePair<string, List<Action<EventPayload>>> modEvent in modEvents)
{
string key = modEvent.Key;
List<Action<EventPayload>> value = modEvent.Value;
logEmptyEventLine(modNamespace, key);
logForEvent(modNamespace, key, $"Event: '{key}' | Subscriber Count: {value.Count}");
int num = 0;
foreach (Action<EventPayload> item in value)
{
MethodInfo method = item.Method;
Type? declaringType = method.DeclaringType;
string text = declaringType?.FullName ?? "UnknownType";
string text2 = declaringType?.Assembly.GetName().Name ?? "UnknownAssembly";
string text3 = item.Target?.GetType().Name ?? "static";
logForEvent(modNamespace, key, $" Subscriber {++num}: Assembly={text2}, DeclaringType={text}, TargetType={text3}, Method={method.Name}");
}
logEmptyEventLine(modNamespace, key);
}
logForMod(modNamespace, "==== End of Mod '" + modNamespace + "' ====");
}
public static void LogAllSubscribers()
{
IReadOnlyDictionary<string, Dictionary<string, List<Action<EventPayload?>>>> modSubscribers = EventBus.GetModSubscribers();
OMC.Log("==== EventBus: All Subscribers ====");
logEmptyLine();
foreach (KeyValuePair<string, Dictionary<string, List<Action<EventPayload>>>> item in modSubscribers)
{
string key = item.Key;
foreach (KeyValuePair<string, List<Action<EventPayload>>> item2 in item.Value)
{
string key2 = item2.Key;
int count = item2.Value.Count;
OMC.Log($"Mod: {key} | Event: {key2} | Subscribers: {count}");
}
}
logEmptyLine();
OMC.Log("==== End of Subscribers ====");
}
public static void LogEventSubscribers(string modNamespace, string eventName)
{
if (!EventBus.GetModSubscribers().TryGetValue(modNamespace, out Dictionary<string, List<Action<EventPayload>>> value))
{
logForMod(modNamespace, "Mod '" + modNamespace + "' not found.");
return;
}
if (!value.TryGetValue(eventName, out var value2))
{
logForEvent(modNamespace, eventName, "Event '" + eventName + "' not found in mod '" + modNamespace + "'.");
return;
}
logForEvent(modNamespace, eventName, "==== EventBus: Subscribers for '" + modNamespace + "." + eventName + "' ====");
logEmptyEventLine(modNamespace, eventName);
int num = 0;
foreach (Action<EventPayload> item in value2)
{
string name = item.Method.Name;
string arg = item.Target?.GetType().Name ?? "static";
OMC.Log($"Subscriber {++num}: TargetType={arg}, Method={name}");
}
logEmptyEventLine(modNamespace, eventName);
logForEvent(modNamespace, eventName, "==== End of Event Subscribers ====");
}
public static void LogPayload(EventPayload? payload)
{
if (payload == null)
{
OMC.Log("[EventBusDataPresenter@LogPayload] Payload is null.");
return;
}
OMC.Log("---- Payload Contents ----");
foreach (KeyValuePair<string, object> item in payload)
{
string key = item.Key;
string text = item.Value?.GetType().Name ?? "null";
string text2 = item.Value?.ToString() ?? "null";
OMC.Log("Key='" + key + "' | Type=" + text + " | Value=" + text2);
}
OMC.Log("--------------------------");
}
private static void logForMod(string modName, string message)
{
OMC.Log("[" + modName + "] " + message);
}
private static void logForEvent(string modName, string eventName, string message)
{
OMC.Log("[" + modName + "] [" + eventName + "] " + message);
}
private static void logEmptyModLine(string modName)
{
logForMod(modName, "");
}
private static void logEmptyEventLine(string modName, string eventName)
{
logForEvent(modName, eventName, "");
}
private static void logEmptyLine()
{
OMC.Log("");
}
}
public class EventDefinition
{
public EventSchema Schema { get; set; }
public string Description { get; set; } = "";
public EventDefinition(EventSchema? schema = null, string description = "")
{
Schema = schema ?? new EventSchema();
Description = description;
}
}
public class EventPayload : Dictionary<string, object>
{
public EventPayload()
{
}
public EventPayload(EventPayload other)
{
if (other == null)
{
return;
}
foreach (KeyValuePair<string, object> item in other)
{
base[item.Key] = item.Value;
}
}
public T Get<T>(string key, T? defaultValue = default(T?))
{
if (TryGetValue(key, out object value) && value is T)
{
return (T)value;
}
return defaultValue;
}
public void Set(string key, object value)
{
base[key] = value;
}
}
public static class EventProfiler
{
public class EventProfileData
{
public readonly Dictionary<string, SubscriberProfile> SubscriberTimes = new Dictionary<string, SubscriberProfile>();
public string ModNamespace { get; }
public string EventName { get; }
public int CallCount { get; private set; }
public double TotalMs { get; private set; }
public double MaxMs { get; private set; }
public EventProfileData(string modNamespace, string eventName)
{
ModNamespace = modNamespace;
EventName = eventName;
}
public void Record(double elapsedMs)
{
CallCount++;
TotalMs += elapsedMs;
if (elapsedMs > MaxMs)
{
MaxMs = elapsedMs;
}
}
public void RecordSubscriber(string subscriberId, double elapsedMs)
{
if (!SubscriberTimes.TryGetValue(subscriberId, out SubscriberProfile value))
{
value = (SubscriberTimes[subscriberId] = new SubscriberProfile());
}
value.Record(elapsedMs);
}
}
public class SubscriberProfile
{
public int CallCount { get; private set; }
public double TotalMs { get; private set; }
public double MaxMs { get; private set; }
public void Record(double elapsedMs)
{
CallCount++;
TotalMs += elapsedMs;
if (elapsedMs > MaxMs)
{
MaxMs = elapsedMs;
}
}
}
private static readonly Dictionary<string, EventProfileData> _profiles = new Dictionary<string, EventProfileData>();
public static bool Enabled { get; private set; }
public static bool InstantLogging { get; private set; }
public static void Initialize()
{
OMC.EnableEventsProfiler.SettingChanged += delegate
{
Enabled = OMC.EnableEventsProfiler.Value;
OMC.Log("[EventProfiler] " + (Enabled ? "Enabled" : "Disabled") + " via config.");
};
if (OMC.InstantLogEventsProfileData != null)
{
OMC.InstantLogEventsProfileData.SettingChanged += delegate
{
InstantLogging = OMC.InstantLogEventsProfileData.Value;
OMC.Log("[EventProfiler] Instant logging " + (InstantLogging ? "enabled" : "disabled") + " via config.");
};
InstantLogging = OMC.InstantLogEventsProfileData.Value;
}
Enabled = OMC.EnableEventsProfiler.Value;
}
public static void Record(string modNamespace, string eventName, double elapsedMs)
{
if (Enabled)
{
string text = modNamespace + "." + eventName;
if (!_profiles.TryGetValue(text, out EventProfileData value))
{
value = (_profiles[text] = new EventProfileData(modNamespace, eventName));
}
value.Record(elapsedMs);
if (InstantLogging)
{
OMC.Log($"[EventProfiler] {text} subscribers executed in {elapsedMs:F2} ms");
}
}
}
public static void RecordSubscriber(string modNamespace, string eventName, string subscriberId, double elapsedMs)
{
if (Enabled)
{
string text = modNamespace + "." + eventName;
if (!_profiles.TryGetValue(text, out EventProfileData value))
{
value = (_profiles[text] = new EventProfileData(modNamespace, eventName));
}
value.RecordSubscriber(subscriberId, elapsedMs);
if (InstantLogging)
{
OMC.Log($"[EventProfiler] {text} subscriber '{subscriberId}' took {elapsedMs:F2} ms");
}
}
}
public static void LogProfiles()
{
if (!Enabled)
{
OMC.Log("[EventProfiler] Profiler is disabled.");
return;
}
OMC.Log("==== EventBus Profiler Summary ====");
foreach (EventProfileData item in _profiles.Values.OrderByDescending((EventProfileData p) => p.TotalMs))
{
OMC.Log($"Event '{item.ModNamespace}.{item.EventName}' — Calls: {item.CallCount}, Total: {item.TotalMs:F2} ms, Max: {item.MaxMs:F2} ms, Avg: {item.TotalMs / (double)item.CallCount:F2} ms");
if (item.SubscriberTimes.Count <= 0)
{
continue;
}
foreach (KeyValuePair<string, SubscriberProfile> subscriberTime in item.SubscriberTimes)
{
OMC.Log($" Subscriber {subscriberTime.Key} — Calls: {subscriberTime.Value.CallCount}, Total: {subscriberTime.Value.TotalMs:F2} ms, Max: {subscriberTime.Value.MaxMs:F2} ms, Avg: {subscriberTime.Value.TotalMs / (double)subscriberTime.Value.CallCount:F2} ms");
}
}
OMC.Log("==== End of Profiler Summary ====");
}
}
public class EventSchema
{
public Dictionary<string, Type> Fields { get; } = new Dictionary<string, Type>();
public Dictionary<string, string> Descriptions { get; } = new Dictionary<string, string>();
public EventSchema()
{
}
public EventSchema(EventPayload payload, bool treatValuesAsTypes = false)
{
if (payload == null)
{
return;
}
foreach (KeyValuePair<string, object> item in payload)
{
if (treatValuesAsTypes && item.Value is Type value)
{
Fields[item.Key] = value;
}
else if (item.Value is Type value2)
{
Fields[item.Key] = value2;
}
else if (item.Value != null)
{
Fields[item.Key] = item.Value.GetType();
}
else
{
Fields[item.Key] = typeof(object);
}
}
}
public void AddField(string name, Type type, string? description = null)
{
Fields[name] = type;
if (!string.IsNullOrWhiteSpace(description))
{
Descriptions[name] = description;
}
}
public string? GetDescription(string field)
{
if (!Descriptions.TryGetValue(field, out string value))
{
return null;
}
return value;
}
}
}
namespace OutwardModsCommunicator.Enums
{
public enum ENUM_LOG_LEVELS
{
Info,
Warning,
Error
}
}