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 RiskOfPain v1.1.0
OpenShock.Integrations.RiskOfRain2.dll
Decompiled 5 months agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using OpenShock.Integrations.LethalCompany.OpenShockApi.Models; using OpenShock.Integrations.RiskOfRain2.Models; using OpenShock.Integrations.RiskOfRain2.OpenShockApi; using OpenShock.Integrations.RiskOfRain2.OpenShockApi.Models; using OpenShock.Integrations.RiskOfRain2.Utils; using R2API.Utils; using RiskOfOptions; using RiskOfOptions.OptionConfigs; using RiskOfOptions.Options; using RoR2; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("OpenShock")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0")] [assembly: AssemblyInformationalVersion("1.0.0+3412324020c2e4123365d6d29e14b8862e59542a")] [assembly: AssemblyProduct("OpenShock.Integrations.RiskOfRain2")] [assembly: AssemblyTitle("OpenShock.Integrations.RiskOfRain2")] [assembly: AssemblyVersion("1.1.0.0")] [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 OpenShock.Integrations.LethalCompany.OpenShockApi.Models { public class ControlRequest { public IEnumerable<Control> Shocks { get; set; } public string? CustomName { get; set; } } public enum ControlType { Stop, Shock, Vibrate, Sound } } namespace OpenShock.Integrations.RiskOfRain2 { [BepInPlugin("OpenShock.Integrations.RiskOfRain2", "RiskOfPain", "1.0.0")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class RiskOfPain : BaseUnityPlugin { private ConfigEntry<string> _openShockServer; private ConfigEntry<string> _openShockApiToken; private ConfigEntry<string> _shockers; private ConfigEntry<bool> _settingOnDeathEnabled; private ConfigEntry<ControlType> _settingOnDeathBehaviour; private ConfigEntry<int> _settingOnDeathIntensity; private ConfigEntry<int> _settingOnDeathDuration; private ConfigEntry<bool> _settingOnDamageEnabled; private ConfigEntry<ControlType> _settingOnDamageMode; private ConfigEntry<DamageBehaviour> _settingOnDamageBehaviour; private ConfigEntry<int> _settingOnDamageDuration; private ConfigEntry<int> _settingOnDamageIntensityLimit; private ConfigEntry<bool> _settingEnableVerboseLogging; private Timer _actionTimer; private readonly object _receivedDamageLock = new object(); private float _receivedDamage; private double _lastDeathTime = -1.0; private CharacterBody? _localUserCharacterBody; private OpenShock.Integrations.RiskOfRain2.OpenShockApi.OpenShockApi? _openShockApiClient; private IList<Guid> _shockersList; public static RiskOfPain Instance { get; private set; } public static ManualLogSource ModLogger { get; private set; } private void SetupConfiguration() { _settingEnableVerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("OpenShock", "EnableVerboseLogging", false, "Enables verbose logging for debugging purposes"); _openShockServer = ((BaseUnityPlugin)this).Config.Bind<string>("OpenShock", "Server", "https://api.openshock.app", "The URL of the OpenShock backend"); _openShockApiToken = ((BaseUnityPlugin)this).Config.Bind<string>("OpenShock", "ApiToken", "", "API token for authentication, can be found under API Tokens on the OpenShock dashboard (https://shocklink.net/#/dashboard/tokens)"); _shockers = ((BaseUnityPlugin)this).Config.Bind<string>("Shockers", "Shockers", "comma,seperated,list,of,shocker,ids", "A list of shocker IDs to use within the mod. Comma seperated."); _settingOnDeathEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("OnDeath", "Enabled", true, "Enables on death"); _settingOnDeathBehaviour = ((BaseUnityPlugin)this).Config.Bind<ControlType>("OnDeath", "Behaviour", ControlType.Shock, "The action that happens when the player dies"); _settingOnDeathIntensity = ((BaseUnityPlugin)this).Config.Bind<int>("OnDeath", "Intensity", 25, "The intensity of the shocker when the player dies"); _settingOnDeathDuration = ((BaseUnityPlugin)this).Config.Bind<int>("OnDeath", "Duration", 1000, "The duration of the shocker when the player dies"); _settingOnDamageEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("OnDamage", "Enabled", true, "Enables on damage"); _settingOnDamageMode = ((BaseUnityPlugin)this).Config.Bind<ControlType>("OnDamage", "Mode", ControlType.Shock, "The action that happens when you take damage"); _settingOnDamageBehaviour = ((BaseUnityPlugin)this).Config.Bind<DamageBehaviour>("OnDamage", "Behaviour", DamageBehaviour.LowHp, "How the intensity is calculated. LowHp = Higher intensity the lower on HP you are; DamagePercentage = Intensity correlates to how much health you have lost of your max HP when damaged. DamageAbsolute = Intensity is equal to how much damage you recevied"); _settingOnDamageDuration = ((BaseUnityPlugin)this).Config.Bind<int>("OnDamage", "Duration", 300, "The duration of the shocker when the player takes damage"); _settingOnDamageIntensityLimit = ((BaseUnityPlugin)this).Config.Bind<int>("OnDamage", "IntensityLimit", 25, "Intensity limit for the shocker when the player takes damage"); } private void RegisterConfigEvents() { _openShockServer.SettingChanged += UpdateApiClient; _openShockApiToken.SettingChanged += UpdateApiClient; _shockers.SettingChanged += ShockersOnSettingChanged; } private void UpdateApiClient(object sender, EventArgs e) { SetupApiClient(); } private void ShockersOnSettingChanged(object sender, EventArgs e) { SetupShockers(); } private void ActionTimerElapsed(object state) { try { OnDamageActionExecute(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)ex); } } private void OnDamageActionExecute() { float receivedDamage; lock (_receivedDamageLock) { receivedDamage = _receivedDamage; _receivedDamage = 0f; } if (Time.realtimeSinceStartupAsDouble - _lastDeathTime < 1.0) { if (_settingEnableVerboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogDebug((object)"Not sending damage feedback due to recent death"); } return; } byte b = CalculateIntensity(receivedDamage); if (_settingEnableVerboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogDebug((object)$"ActionTimerElapsed - Damage: {receivedDamage:0.00} Intensity: {b}"); } ControlShockersFnf(_settingOnDamageMode.Value, b, (ushort)_settingOnDamageDuration.Value); } private void ClientOnDamage(DamageDealtMessage damageMessage) { if (!((Object)(object)_localUserCharacterBody == (Object)null) && !((Object)(object)damageMessage.victim == (Object)null) && !((Object)(object)damageMessage.victim != (Object)(object)((Component)_localUserCharacterBody).gameObject)) { if (_settingEnableVerboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogDebug((object)$"Player received damage: {damageMessage.damage}"); } lock (_receivedDamageLock) { _receivedDamage += damageMessage.damage; } _actionTimer.Change(TimeSpan.FromMilliseconds(50.0), Timeout.InfiniteTimeSpan); } } private byte CalculateIntensity(float damage) { float fullCombinedHealth = _localUserCharacterBody.healthComponent.fullCombinedHealth; float health = _localUserCharacterBody.healthComponent.health; DamageBehaviour value = _settingOnDamageBehaviour.Value; if (_settingEnableVerboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogDebug((object)$"Calculating Intensity - MaxHp: {fullCombinedHealth:0.00} CurrentHp: {health:0.00} Damage: {damage:0.00} Behaviour: {value}"); } float num2; if ((uint)value <= 1u) { float num = value switch { DamageBehaviour.LowHp => 1f - health / fullCombinedHealth, DamageBehaviour.DamagePercent => damage / fullCombinedHealth, _ => throw new Exception("This is unreachable."), }; num2 = MathUtils.Lerp(0f, _settingOnDamageIntensityLimit.Value, MathUtils.Saturate(num)); if (_settingEnableVerboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogDebug((object)$"Calculated Intensity - CurrentHp: {health:0.00} / {fullCombinedHealth:0.00} Damage: {damage:0.00} Scaled: {num:0.0000} Intensity: {num2:0.00}"); } } else { num2 = Math.Clamp(damage, 0f, _settingOnDamageIntensityLimit.Value); } byte b = Convert.ToByte(num2); if (_settingEnableVerboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogDebug((object)$"Final Intensity Byte: {b}"); } return b; } private void OnCharacterDeath(DamageReport damageReport) { if (!((Object)(object)_localUserCharacterBody == (Object)null) && !((Object)(object)damageReport.victimBody == (Object)null) && !((Object)(object)damageReport.victimBody != (Object)(object)_localUserCharacterBody)) { ((BaseUnityPlugin)this).Logger.LogDebug((object)"Player has died"); if (!_settingOnDeathEnabled.Value) { ((BaseUnityPlugin)this).Logger.LogDebug((object)"OnDeath is disabled, skipping"); return; } _lastDeathTime = Time.realtimeSinceStartupAsDouble; ControlShockersFnf(_settingOnDeathBehaviour.Value, (byte)_settingOnDeathIntensity.Value, (ushort)_settingOnDeathDuration.Value); } } private void OnCharacterBodyStart(CharacterBody characterBody) { if (characterBody.isPlayerControlled) { NetworkUser val = Util.LookUpBodyNetworkUser(characterBody); if (!((Object)(object)val == (Object)null) && ((NetworkBehaviour)val).isLocalPlayer) { _localUserCharacterBody = characterBody; ((BaseUnityPlugin)this).Logger.LogDebug((object)("Local user found. " + val.userName)); } } } private void OnCharacterBodyDestroy(CharacterBody characterBody) { if (characterBody.isPlayerControlled && !((Object)(object)_localUserCharacterBody != (Object)(object)characterBody)) { _localUserCharacterBody = null; ((BaseUnityPlugin)this).Logger.LogDebug((object)"Local user destroyed"); } } private void SetupApiClient() { if (!Uri.TryCreate(_openShockServer.Value, UriKind.Absolute, out Uri result)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Unable to parse OpenShock server URL. E.g. https://api.openshock.app"); } else if (string.IsNullOrWhiteSpace(_openShockApiToken.Value)) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"API Token is not configured"); } else { _openShockApiClient = new OpenShock.Integrations.RiskOfRain2.OpenShockApi.OpenShockApi(result, _openShockApiToken.Value); } } private void SetupShockers() { List<Guid> list = new List<Guid>(); string[] array = _shockers.Value.Split(','); foreach (string text in array) { if (Guid.TryParse(text, out var result)) { ManualLogSource logger = ((BaseUnityPlugin)this).Logger; Guid guid = result; logger.LogInfo((object)("Found shocker ID " + guid)); list.Add(result); } else { ((BaseUnityPlugin)this).Logger.LogError((object)("Failed to parse shocker ID " + text)); } } _shockersList = list; } private async Task ControlShockers(ControlType controlType, byte intensity, ushort duration) { if (_openShockApiClient == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"OpenShock server or token not configured"); return; } if (_settingEnableVerboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogDebug((object)$"Sending control request - Type: {controlType} Intensity: {intensity} Duration: {duration}ms"); } IEnumerable<Control> shocks = _shockersList.Select((Guid shocker) => new Control { Id = shocker, Duration = duration, Intensity = intensity, Type = controlType }); await _openShockApiClient.Control(shocks); if (_settingEnableVerboseLogging.Value) { ((BaseUnityPlugin)this).Logger.LogDebug((object)"Command sent"); } } private void ControlShockersFnf(ControlType controlType, byte intensity, ushort duration) { LucTask.Run(() => ControlShockers(controlType, intensity, duration), default(CancellationToken), "/home/runner/work/Integrations.RiskOfRain2/Integrations.RiskOfRain2/Integrations.RiskOfRain2/OpenShockLogic.cs", "ControlShockersFnf", 71); } private void SetupRiskOfOptionsMenu() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Expected O, but got Unknown //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Expected O, but got Unknown //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Expected O, but got Unknown //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Expected O, but got Unknown //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Expected O, but got Unknown //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Expected O, but got Unknown //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Expected O, but got Unknown //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Expected O, but got Unknown ModSettingsManager.AddOption((BaseOption)new StringInputFieldOption(_openShockServer)); ModSettingsManager.AddOption((BaseOption)new StringInputFieldOption(_openShockApiToken, new InputFieldConfig { submitOn = (SubmitEnum)6 })); ModSettingsManager.AddOption((BaseOption)new StringInputFieldOption(_shockers, new InputFieldConfig { submitOn = (SubmitEnum)6 })); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(_settingEnableVerboseLogging)); ModSettingsManager.AddOption((BaseOption)new GenericButtonOption("TestShockers", "OpenShock", "Will vibrate all configured shockers at 25% and 1s", "Test Shockers", (UnityAction)delegate { ControlShockersFnf(ControlType.Vibrate, 25, 1000); })); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(_settingOnDeathEnabled)); ModSettingsManager.AddOption((BaseOption)new ChoiceOption((ConfigEntryBase)(object)_settingOnDeathBehaviour)); ModSettingsManager.AddOption((BaseOption)new IntSliderOption(_settingOnDeathIntensity, new IntSliderConfig { min = 1, max = 100 })); ModSettingsManager.AddOption((BaseOption)new IntSliderOption(_settingOnDeathDuration, new IntSliderConfig { min = 300, max = 30000 })); ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(_settingOnDamageEnabled)); ModSettingsManager.AddOption((BaseOption)new ChoiceOption((ConfigEntryBase)(object)_settingOnDamageMode)); ModSettingsManager.AddOption((BaseOption)new ChoiceOption((ConfigEntryBase)(object)_settingOnDamageBehaviour)); ModSettingsManager.AddOption((BaseOption)new IntSliderOption(_settingOnDamageDuration, new IntSliderConfig { min = 300, max = 30000 })); ModSettingsManager.AddOption((BaseOption)new IntSliderOption(_settingOnDamageIntensityLimit, new IntSliderConfig { min = 1, max = 100 })); } private void Awake() { ((BaseUnityPlugin)this).Logger.LogDebug((object)"RiskOfPain loading..."); Instance = this; ModLogger = ((BaseUnityPlugin)this).Logger; _actionTimer = new Timer(ActionTimerElapsed, null, -1, -1); SetupConfiguration(); RegisterConfigEvents(); SetupApiClient(); SetupShockers(); SetupRiskOfOptionsMenu(); ((BaseUnityPlugin)this).Logger.LogDebug((object)"RiskOfPain loaded"); } private void OnEnable() { CharacterBody.onBodyStartGlobal += OnCharacterBodyStart; CharacterBody.onBodyDestroyGlobal += OnCharacterBodyDestroy; GlobalEventManager.onClientDamageNotified += ClientOnDamage; GlobalEventManager.onCharacterDeathGlobal += OnCharacterDeath; ((BaseUnityPlugin)this).Logger.LogDebug((object)"Events registered"); } private void OnDisable() { CharacterBody.onBodyStartGlobal -= OnCharacterBodyStart; CharacterBody.onBodyDestroyGlobal -= OnCharacterBodyDestroy; GlobalEventManager.onClientDamageNotified -= ClientOnDamage; GlobalEventManager.onCharacterDeathGlobal -= OnCharacterDeath; ((BaseUnityPlugin)this).Logger.LogDebug((object)"Events unregistered"); } } public static class LucTask { public static Task Run(Func<Task?> function, CancellationToken token = default(CancellationToken), [CallerFilePath] string file = "", [CallerMemberName] string member = "", [CallerLineNumber] int line = -1) { string file2 = file; string member2 = member; return Task.Run(function, token).ContinueWith(delegate(Task t) { if (t.IsFaulted) { int num = file2.LastIndexOf('\\'); if (num == -1) { num = file2.LastIndexOf('/'); } RiskOfPain.ModLogger.LogError((object)$"Error during task execution. {file2.Substring(num + 1, file2.Length - num - 1)}::{member2}:{line} {t.Exception}"); } }, TaskContinuationOptions.OnlyOnFaulted); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "OpenShock.Integrations.RiskOfRain2"; public const string PLUGIN_NAME = "OpenShock.Integrations.RiskOfRain2"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace OpenShock.Integrations.RiskOfRain2.Utils { public class MathUtils { public static float Lerp(float min, float max, float t) { return min + (max - min) * t; } public static float Saturate(float value) { if (!(value < 0f)) { if (!(value > 1f)) { return value; } return 1f; } return 0f; } } } namespace OpenShock.Integrations.RiskOfRain2.OpenShockApi { public class OpenShockApi { private static readonly ManualLogSource Logger = Logger.CreateLogSource("OpenShockApi"); private readonly HttpClient _httpClient; public OpenShockApi(Uri server, string apiToken) { _httpClient = new HttpClient { BaseAddress = server }; _httpClient.DefaultRequestHeaders.Add("User-Agent", "OpenShock.Integrations.RiskOfRain2/1.0.0"); _httpClient.DefaultRequestHeaders.Add("OpenShockToken", apiToken); } public async Task Control(IEnumerable<Control> shocks) { Logger.LogInfo((object)"Sending control request to OpenShock API"); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "/2/shockers/control") { Content = new StringContent(JsonConvert.SerializeObject((object)new ControlRequest { Shocks = shocks, CustomName = "Integrations.RiskOfRain2" }), Encoding.UTF8, "application/json") }; HttpResponseMessage httpResponseMessage = await _httpClient.SendAsync(request); if (!httpResponseMessage.IsSuccessStatusCode) { Logger.LogError((object)$"Failed to send control request to OpenShock API [{httpResponseMessage.StatusCode}]"); } } } } namespace OpenShock.Integrations.RiskOfRain2.OpenShockApi.Models { public class Control { public Guid Id { get; set; } public ControlType Type { get; set; } public byte Intensity { get; set; } public ushort Duration { get; set; } } } namespace OpenShock.Integrations.RiskOfRain2.Models { public enum DamageBehaviour { LowHp, DamagePercent, DamageAbsolute } }