Decompiled source of PeakShock v0.1.5

plugins/addzeey.PeakShock.dll

Decompiled 5 days ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using UnityEngine;

[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("addzeey.PeakShock")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.5.0")]
[assembly: AssemblyInformationalVersion("0.1.5+7124d3ac273467a88dba3e808a05475cae2aa94f")]
[assembly: AssemblyProduct("addzeey.PeakShock")]
[assembly: AssemblyTitle("PeakShock")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.5.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;
		}
	}
}
public interface IShockController
{
	void EnqueueShock(int intensity, int duration, string? code = null);
}
namespace BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace PeakShock
{
	internal class ShockRequestQueue : IDisposable
	{
		private ConcurrentQueue<Func<Task>> _taskQueue;

		private SemaphoreSlim _signal;

		private CancellationTokenSource _cts;

		private Task _worker;

		private readonly object _lock = new object();

		private bool _disposed;

		public ShockRequestQueue()
		{
			Start();
		}

		public void Enqueue(Func<Task> task)
		{
			if (_disposed)
			{
				throw new ObjectDisposedException("ShockRequestQueue");
			}
			_taskQueue.Enqueue(task);
			_signal.Release();
		}

		public void Enqueue<T>(Func<T, Task> task, T args)
		{
			Func<T, Task> task2 = task;
			T args2 = args;
			if (_disposed)
			{
				throw new ObjectDisposedException("ShockRequestQueue");
			}
			_taskQueue.Enqueue(() => task2(args2));
			_signal.Release();
		}

		public void Start()
		{
			_taskQueue = new ConcurrentQueue<Func<Task>>();
			_signal = new SemaphoreSlim(0);
			_cts = new CancellationTokenSource();
			_worker = Task.Run((Func<Task?>)ProcessQueueAsync);
		}

		private async Task ProcessQueueAsync()
		{
			while (!_cts.Token.IsCancellationRequested)
			{
				await _signal.WaitAsync(_cts.Token);
				if (_taskQueue.TryDequeue(out Func<Task> result))
				{
					try
					{
						await result();
					}
					catch (Exception ex)
					{
						Plugin.Log.LogError((object)ex.ToString());
					}
				}
			}
		}

		public async Task StopAsync()
		{
			if (_disposed)
			{
				return;
			}
			_cts.Cancel();
			_signal.Release();
			try
			{
				await _worker;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)ex.ToString());
			}
		}

		public async Task ResetAsync()
		{
			lock (_lock)
			{
				if (!_worker.IsCompleted)
				{
					StopAsync().Wait();
				}
				Start();
			}
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				StopAsync().Wait();
				_cts.Dispose();
				_signal.Dispose();
				_disposed = true;
			}
		}
	}
	public class OpenShockController : IShockController
	{
		private readonly ShockRequestQueue _queue = new ShockRequestQueue();

		private readonly string _apiUrl;

		private readonly string _deviceId;

		private readonly string _apiKey;

		private readonly HttpClient _client = new HttpClient();

		private DateTime _lastShockTime = DateTime.MinValue;

		private TimeSpan ShockCooldown => TimeSpan.FromSeconds(1.0 + (double)Math.Max(0f, Plugin.ShockCooldownSeconds.Value));

		public OpenShockController()
		{
			_apiUrl = Plugin.OpenShockApiUrl.Value;
			_deviceId = Plugin.OpenShockDeviceId.Value;
			_apiKey = Plugin.OpenShockApiKey.Value;
		}

		public void EnqueueShock(int intensity, int duration, string? code = null)
		{
			string code2 = code;
			DateTime utcNow = DateTime.UtcNow;
			if (utcNow - _lastShockTime < ShockCooldown)
			{
				Plugin.Log.LogInfo((object)"[PeakShock] OpenShock shock skipped due to cooldown.");
				return;
			}
			_lastShockTime = utcNow;
			_queue.Enqueue(() => TriggerShockInternal(intensity, duration, code2));
		}

		private async Task TriggerShockInternal(int intensity, int duration, string? code)
		{
			int num = Math.Clamp(duration * 1000, 300, 65535);
			if (string.IsNullOrEmpty(_apiUrl) || string.IsNullOrEmpty(_apiKey))
			{
				Plugin.Log.LogWarning((object)$"[PeakShock] Would send OpenShock (intensity={intensity}, duration={num}), but OpenShock credentials are not set.");
				return;
			}
			string text = ((!string.IsNullOrEmpty(code)) ? code : _deviceId);
			if (string.IsNullOrEmpty(text))
			{
				Plugin.Log.LogWarning((object)"[PeakShock] No deviceId or share code provided for OpenShock.");
				return;
			}
			Plugin.Log.LogInfo((object)$"[PeakShock] Sending OpenShock: id={text}, intensity={intensity}, duration={num}");
			var anon = new
			{
				Shocks = new[]
				{
					new
					{
						Id = text,
						Type = 1,
						Intensity = intensity,
						Duration = num
					}
				},
				CustomName = "Integrations.PeakShock"
			};
			string content = JsonConvert.SerializeObject((object)anon);
			StringContent content2 = new StringContent(content, Encoding.UTF8, "application/json");
			HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, _apiUrl + "/2/shockers/control")
			{
				Content = content2
			};
			httpRequestMessage.Headers.Add("OpenShockToken", _apiKey);
			httpRequestMessage.Headers.Add("User-Agent", "PeakShock/" + Plugin.Version);
			try
			{
				HttpResponseMessage response = await _client.SendAsync(httpRequestMessage);
				if (!response.IsSuccessStatusCode)
				{
					string arg = await response.Content.ReadAsStringAsync();
					Plugin.Log.LogError((object)$"OpenShock API error: {response.StatusCode} {arg}");
				}
			}
			catch (Exception arg2)
			{
				Plugin.Log.LogError((object)$"OpenShock API exception: {arg2}");
			}
		}
	}
	public class PiShockController : IShockController
	{
		private readonly HttpClient _client = new HttpClient();

		private readonly ShockRequestQueue _queue = new ShockRequestQueue();

		private DateTime _lastShockTime = DateTime.MinValue;

		private TimeSpan ShockCooldown => TimeSpan.FromSeconds(1f + Math.Max(0f, Plugin.ShockCooldownSeconds.Value));

		public void TriggerShock(int intensity, int duration = 1, string? shareCode = null)
		{
			DateTime utcNow = DateTime.UtcNow;
			if (utcNow - _lastShockTime < ShockCooldown)
			{
				Plugin.Log.LogInfo((object)"[PeakShock] Shock skipped due to cooldown.");
				return;
			}
			_lastShockTime = utcNow;
			string code = shareCode ?? Plugin.PiShockShareCode.Value;
			Plugin.Log.LogInfo((object)$"[PeakShock] Enqueue shock: intensity={intensity}, duration={duration}, code={code}");
			_queue.Enqueue(() => TriggerShockInternal(intensity, duration, code));
		}

		public void EnqueueShock(int intensity, int duration, string? code = null)
		{
			TriggerShock(intensity, duration, code);
		}

		private async Task TriggerShockInternal(int intensity, int duration, string code)
		{
			string value = Plugin.PiShockUserName.Value;
			string value2 = Plugin.PiShockAPIKey.Value;
			if (string.IsNullOrEmpty(value) || string.IsNullOrEmpty(value2) || string.IsNullOrEmpty(code))
			{
				Plugin.Log.LogWarning((object)$"[PeakShock] Would send shock (intensity={intensity}, duration={duration}, code={code}), but PiShock credentials are not set.");
				return;
			}
			Plugin.Log.LogInfo((object)$"[PeakShock] Sending shock: intensity={intensity}, duration={duration}, code={code}");
			string content = JsonConvert.SerializeObject((object)new
			{
				Username = value,
				APIKey = value2,
				Code = code,
				Intensity = intensity,
				Duration = duration,
				Op = 0
			});
			StringContent content2 = new StringContent(content, Encoding.UTF8, "application/json");
			try
			{
				Plugin.Log.LogInfo((object)$"[PeakShock] Sending request to PiShock API: {value}, Intensity={intensity}, Duration={duration}");
				HttpResponseMessage httpResponseMessage = await _client.PostAsync("https://do.pishock.com/api/apioperate/", content2);
				if (!httpResponseMessage.IsSuccessStatusCode)
				{
					Plugin.Log.LogError((object)$"PiShock API error: {httpResponseMessage.StatusCode}");
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"PiShock API exception: {arg}");
			}
		}
	}
	[BepInPlugin("addzeey.PeakShock", "PeakShock", "0.1.5")]
	public class Plugin : BaseUnityPlugin
	{
		public enum ShockProvider
		{
			PiShock,
			OpenShock
		}

		public const string PluginVersion = "0.1.5";

		private Harmony? _harmony;

		public const string Id = "addzeey.PeakShock";

		internal static ManualLogSource Log { get; private set; }

		internal static ConfigFile CFG { get; private set; }

		internal static PiShockController PiShockController { get; private set; }

		internal static ConfigEntry<string> PiShockUserName { get; private set; }

		internal static ConfigEntry<string> PiShockAPIKey { get; private set; }

		internal static ConfigEntry<string> PiShockShareCode { get; private set; }

		internal static ConfigEntry<int> MinShock { get; private set; }

		internal static ConfigEntry<int> MaxShock { get; private set; }

		internal static ConfigEntry<int> DeathShock { get; private set; }

		internal static ConfigEntry<int> DeathDuration { get; private set; }

		internal static ConfigEntry<bool> EnableInjuryShock { get; private set; }

		internal static ConfigEntry<bool> EnablePoisonShock { get; private set; }

		internal static ConfigEntry<bool> EnableColdShock { get; private set; }

		internal static ConfigEntry<bool> EnableHotShock { get; private set; }

		internal static ConfigEntry<float> ShockCooldownSeconds { get; private set; }

		internal static ConfigEntry<ShockProvider> ShockProviderType { get; private set; }

		internal static ConfigEntry<string> OpenShockApiUrl { get; private set; }

		internal static ConfigEntry<string> OpenShockDeviceId { get; private set; }

		internal static ConfigEntry<string> OpenShockApiKey { get; private set; }

		internal static IShockController ShockController { get; private set; }

		public static string Name => "PeakShock";

		public static string Version => "0.1.5";

		private void Awake()
		{
			//IL_0328: Unknown result type (might be due to invalid IL or missing references)
			//IL_0332: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			CFG = ((BaseUnityPlugin)this).Config;
			PiShockUserName = CFG.Bind<string>("PiShock", "UserName", "", "Your PiShock username");
			PiShockAPIKey = CFG.Bind<string>("PiShock", "APIKey", "", "Your PiShock API Key");
			PiShockShareCode = CFG.Bind<string>("PiShock", "ShareCode", "", "Your PiShock ShareCode");
			MinShock = CFG.Bind<int>("Shock", "MinShock", 10, "Minimum shock intensity (1-100)");
			MaxShock = CFG.Bind<int>("Shock", "MaxShock", 100, "Maximum shock intensity (1-100)");
			DeathShock = CFG.Bind<int>("Shock", "DeathShock", 80, "Shock intensity on death (1-100)");
			DeathDuration = CFG.Bind<int>("Shock", "DeathDuration", 2, "Shock duration on death (seconds)");
			EnableInjuryShock = CFG.Bind<bool>("ShockTypes", "EnableInjuryShock", true, "Enable shock for Injury damage");
			EnablePoisonShock = CFG.Bind<bool>("ShockTypes", "EnablePoisonShock", false, "Enable shock for Poison damage");
			EnableColdShock = CFG.Bind<bool>("ShockTypes", "EnableColdShock", false, "Enable shock for Cold damage");
			EnableHotShock = CFG.Bind<bool>("ShockTypes", "EnableHotShock", false, "Enable shock for Hot/Fire damage");
			ShockCooldownSeconds = CFG.Bind<float>("Shock", "ShockCooldownSeconds", 2f, "Minimum seconds between shocks (prevents shock spam)");
			ShockProviderType = CFG.Bind<ShockProvider>("Shock", "Provider", ShockProvider.PiShock, "Choose PiShock or OpenShock");
			OpenShockApiUrl = CFG.Bind<string>("OpenShock", "ApiUrl", "https://api.openshock.app", "OpenShock API URL");
			OpenShockDeviceId = CFG.Bind<string>("OpenShock", "DeviceId", "", "OpenShock Device ID");
			OpenShockApiKey = CFG.Bind<string>("OpenShock", "ApiKey", "", "OpenShock API Key");
			Log.LogInfo((object)$"[PeakShock] Config:\nMinShock={MinShock.Value}\nMaxShock={MaxShock.Value}\nDeathShock={DeathShock.Value}\nDeathDuration={DeathDuration.Value}\nEnableInjuryShock={EnableInjuryShock.Value}\nEnablePoisonShock={EnablePoisonShock.Value}\nEnableColdShock={EnableColdShock.Value}\nEnableHotShock={EnableHotShock.Value}\nShockCooldownSeconds={ShockCooldownSeconds.Value}");
			if (ShockProviderType.Value == ShockProvider.PiShock)
			{
				ShockController = new PiShockController();
				Log.LogInfo((object)"[PeakShock] Initialized PiShockController");
			}
			else
			{
				ShockController = new OpenShockController();
				Log.LogInfo((object)"[PeakShock] Initialized OpenShockController");
			}
			_harmony = new Harmony("com.yourname.PeakShock");
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			Log.LogInfo((object)("Plugin " + Name + " is loaded!"));
		}
	}
}
namespace PeakShock.PeakShock.Patches
{
	[HarmonyPatch(typeof(Character), "RPCA_Die")]
	public class CharacterDeathPatch
	{
		[HarmonyPostfix]
		public static void Postfix(Character __instance)
		{
			if (__instance.IsLocal)
			{
				int intensity = Plugin.DeathShock.Value;
				int duration = Plugin.DeathDuration.Value;
				Plugin.Log.LogInfo((object)$"[PeakShock] Player died. Triggering death shock: {intensity}% for {duration}s");
				Task.Run(delegate
				{
					Plugin.ShockController.EnqueueShock(intensity, duration);
				});
			}
		}
	}
	[HarmonyPatch(typeof(CharacterAfflictions), "AddStatus")]
	public class CharacterAfflictions_AddStatus_Patch
	{
		private static readonly Dictionary<STATUSTYPE, bool> ShockTypeEnabled = new Dictionary<STATUSTYPE, bool>
		{
			{
				(STATUSTYPE)1,
				false
			},
			{
				(STATUSTYPE)0,
				Plugin.EnableInjuryShock.Value
			},
			{
				(STATUSTYPE)3,
				Plugin.EnablePoisonShock.Value
			},
			{
				(STATUSTYPE)2,
				Plugin.EnableColdShock.Value
			},
			{
				(STATUSTYPE)8,
				Plugin.EnableHotShock.Value
			}
		};

		[HarmonyPostfix]
		public static void Postfix(CharacterAfflictions __instance, STATUSTYPE statusType, float amount, bool fromRPC)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Invalid comparison between I4 and Unknown
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			if (!__instance.character.IsLocal || (Object)(object)__instance.character != (Object)(object)Character.localCharacter || amount <= 0f)
			{
				return;
			}
			if (__instance.character.data.dead || __instance.character.data.fullyPassedOut || __instance.character.data.passedOut)
			{
				Plugin.Log.LogInfo((object)$"[PeakShock] Ignored status effect shock ({statusType}) because player is dead or passed out.");
				return;
			}
			int value = Plugin.MinShock.Value;
			int value2 = Plugin.MaxShock.Value;
			float num = 0f;
			if (amount < 0.01f)
			{
				num += amount;
				if (num > 0.01f)
				{
					Plugin.Log.LogInfo((object)$"[PeakShock] Accumulated damage {num} exceeds threshold, triggering shock.");
					int intensity = Mathf.Clamp(Mathf.RoundToInt(num * (float)value2), value, value2);
					Task.Run(delegate
					{
						Plugin.ShockController.EnqueueShock(intensity, 1);
					});
					num = 0f;
				}
			}
			else
			{
				if (1 == (int)statusType)
				{
					return;
				}
				if (!ShockTypeEnabled.TryGetValue(statusType, out var value3) || !value3)
				{
					Plugin.Log.LogInfo((object)$"[PeakShock] Ignored status effect shock for {statusType} because it is disabled.");
					return;
				}
				int intensity2 = Mathf.Clamp(Mathf.RoundToInt(amount * (float)value2), value, value2);
				Plugin.Log.LogInfo((object)$"[PeakShock] Status effect {statusType} damage: {amount}, shock: {intensity2}%");
				Task.Run(delegate
				{
					Plugin.ShockController.EnqueueShock(intensity2, 1);
				});
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}