Decompiled source of LoveLLM v1.0.2

LoveLLM.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Events;

[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("LoveLLM")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("LoveLLM Plugin")]
[assembly: AssemblyTitle("LoveLLM")]
[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.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 LoveLLM
{
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "LoveLLM";

		public const string PLUGIN_NAME = "LoveLLM Plugin";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace repo_mod
{
	public static class Native
	{
		[DllImport("LLMNative.dll")]
		public static extern IntPtr GenerateToken(string str, IntPtr model, int n_ctx);

		[DllImport("LLMNative.dll")]
		public static extern bool PollToken(IntPtr req);

		[DllImport("LLMNative.dll")]
		public static extern IntPtr RetrieveToken(IntPtr req);

		[DllImport("LLMNative.dll")]
		public static extern void FreeToken(IntPtr req);

		[DllImport("LLMNative.dll")]
		public static extern IntPtr LoadModel(string path, int ngl);

		[DllImport("LLMNative.dll")]
		public static extern void FreeModel(IntPtr model);
	}
	internal static class PatchUtils
	{
		public struct OpcodeMatch
		{
			public OpCode opcode;

			public object operandOrNull;

			public OpcodeMatch(OpCode opcode)
			{
				operandOrNull = null;
				this.opcode = opcode;
			}

			public OpcodeMatch(OpCode opcode, object operand)
			{
				operandOrNull = null;
				this.opcode = opcode;
				operandOrNull = operand;
			}
		}

		public static int LocateCodeSegment(int startIndex, List<CodeInstruction> searchSpace, List<OpcodeMatch> searchFor)
		{
			if (startIndex < 0 || startIndex >= searchSpace.Count)
			{
				return -1;
			}
			int num = 0;
			for (int i = startIndex; i < searchSpace.Count; i++)
			{
				CodeInstruction obj = searchSpace[i];
				OpcodeMatch opcodeMatch = searchFor[num];
				if (obj.opcode == opcodeMatch.opcode)
				{
					num++;
					if (num == searchFor.Count)
					{
						return i - num + 1;
					}
				}
				else
				{
					num = 0;
				}
			}
			return -1;
		}
	}
	internal class PromptConfig
	{
		public struct Prompt
		{
			public string PromptString { get; set; }

			public PossessChatID PossessChatID { get; set; }
		}

		public static List<Prompt> Prompts { get; set; }
	}
	[HarmonyPatch(typeof(ValuableLovePotion))]
	internal class ValuableLovePotionPatches
	{
		public class InternalStateData
		{
			public enum EState
			{
				State_Idle,
				State_Cooldown,
				State_WaitingForToken,
				State_TokenAvailable
			}

			public PromptConfig.Prompt? currentPrompt;

			public IntPtr? currentRequest;

			public EState state = EState.State_Cooldown;

			public float coolDownUntilNextToken = 3f;
		}

		public static IntPtr? LoadedModel = null;

		public static ConfigEntry<int> NCtxConfig;

		public static Dictionary<ValuableLovePotion, InternalStateData> internalStateStore = new Dictionary<ValuableLovePotion, InternalStateData>();

		internal static void SelectPrompt(InternalStateData stateData)
		{
			int index = Random.RandomRangeInt(0, PromptConfig.Prompts.Count);
			stateData.currentPrompt = PromptConfig.Prompts[index];
		}

		internal static string GetPrompt(string inPlayerName, PromptConfig.Prompt prompt)
		{
			string text = inPlayerName;
			if (text.Equals("this potion") || text.Equals("[playerName]"))
			{
				text = "Doug";
			}
			return prompt.PromptString.Replace("{playerName}", "\"" + text + "\"");
		}

		internal static string GetCurrentPlayerName(ValuableLovePotion instance)
		{
			return (string)AccessTools.Field(typeof(ValuableLovePotion), "playerName").GetValue(instance);
		}

		[HarmonyPatch("Update")]
		[HarmonyPrefix]
		private static void Update_Prefix(ValuableLovePotion __instance)
		{
			if (!internalStateStore.ContainsKey(__instance))
			{
				InternalStateData value = new InternalStateData
				{
					state = InternalStateData.EState.State_Cooldown,
					coolDownUntilNextToken = 15f
				};
				internalStateStore.Add(__instance, value);
			}
			if (!internalStateStore.TryGetValue(__instance, out var value2))
			{
				return;
			}
			switch (value2.state)
			{
			case InternalStateData.EState.State_Idle:
			{
				SelectPrompt(value2);
				string prompt = GetPrompt(GetCurrentPlayerName(__instance), value2.currentPrompt.Value);
				Plugin.Logger.LogInfo((object)("Requesting prompt: " + prompt));
				value2.currentRequest = Native.GenerateToken(prompt, LoadedModel.Value, NCtxConfig.Value);
				value2.state = InternalStateData.EState.State_WaitingForToken;
				goto case InternalStateData.EState.State_WaitingForToken;
			}
			case InternalStateData.EState.State_WaitingForToken:
				if (Native.PollToken(value2.currentRequest.Value))
				{
					value2.state = InternalStateData.EState.State_TokenAvailable;
				}
				break;
			case InternalStateData.EState.State_Cooldown:
				value2.coolDownUntilNextToken -= Time.deltaTime;
				if (value2.coolDownUntilNextToken <= 0f)
				{
					value2.coolDownUntilNextToken = 0f;
					value2.state = InternalStateData.EState.State_Idle;
				}
				break;
			case InternalStateData.EState.State_TokenAvailable:
				break;
			}
		}

		private static void Hooked_UpdateStateMachine(ValuableLovePotion __instance)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Invalid comparison between Unknown and I4
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Invalid comparison between Unknown and I4
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			if (!internalStateStore.TryGetValue(__instance, out var value) || value.state != InternalStateData.EState.State_TokenAvailable)
			{
				return;
			}
			string text = Marshal.PtrToStringAnsi(Native.RetrieveToken(value.currentRequest.Value));
			PossessChatID possessChatID = value.currentPrompt.Value.PossessChatID;
			Color val = default(Color);
			((Color)(ref val))..ctor(1f, 1f, 1f, 1f);
			if ((int)possessChatID != 1)
			{
				if ((int)possessChatID == 4)
				{
					((Color)(ref val))..ctor(0.5f, 0.01f, 0.01f);
				}
			}
			else
			{
				((Color)(ref val))..ctor(1f, 0.3f, 0.6f, 1f);
			}
			ChatManager.instance.PossessChatScheduleStart(10);
			Plugin.Logger.LogInfo((object)("LoveLLM Got token: " + text));
			ChatManager.instance.PossessChat(value.currentPrompt.Value.PossessChatID, text, 1f, val, 0f, false, 0, (UnityEvent)null);
			ChatManager.instance.PossessChatScheduleEnd();
			value.coolDownUntilNextToken = Random.Range(25f, 30f);
			value.state = InternalStateData.EState.State_Cooldown;
			Native.FreeToken(value.currentRequest.Value);
		}

		[HarmonyPatch("StateIdle")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> StateIdle_Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Expected O, but got Unknown
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Expected O, but got Unknown
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			List<PatchUtils.OpcodeMatch> searchFor = new List<PatchUtils.OpcodeMatch>(new <>z__ReadOnlyArray<PatchUtils.OpcodeMatch>(new PatchUtils.OpcodeMatch[4]
			{
				new PatchUtils.OpcodeMatch(OpCodes.Ldc_R4, 1.0),
				new PatchUtils.OpcodeMatch(OpCodes.Ldc_R4, 0.3),
				new PatchUtils.OpcodeMatch(OpCodes.Ldc_R4, 0.6),
				new PatchUtils.OpcodeMatch(OpCodes.Ldc_R4, 1.0)
			}));
			int num = PatchUtils.LocateCodeSegment(0, list, searchFor);
			if (num == -1)
			{
				Plugin.Logger.LogError((object)"Could not transpile ValuableLovePotion.Update");
				return instructions;
			}
			for (int i = num - 7; !(list[i].opcode == OpCodes.Ret); i++)
			{
				list[i] = new CodeInstruction(OpCodes.Nop, (object)null);
			}
			list[num - 7] = new CodeInstruction(OpCodes.Ldarg_0, (object)null);
			list[num - 6] = new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(ValuableLovePotionPatches), "Hooked_UpdateStateMachine", (Type[])null, (Type[])null));
			return list;
		}
	}
	[BepInPlugin("LoveLLM", "LoveLLM Plugin", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger;

		internal static ConfigEntry<string> ModelPathConfig;

		internal static ConfigEntry<string> PromptFileConfig;

		internal static ConfigEntry<int> NglConfig;

		internal static Harmony? Harmony { get; set; }

		public static Plugin Instance { get; private set; }

		private void InitModel()
		{
			NglConfig = ((BaseUnityPlugin)this).Config.Bind<int>("General", "ngl", 99, "Ngl parameter to pass to llama");
			ValuableLovePotionPatches.NCtxConfig = ((BaseUnityPlugin)this).Config.Bind<int>("General", "n_ctx", 2048, "n_ctx parameter to pass to llama. Increasing this will increase the memory footprint and maybe get some better responses. Idk its finnciky..");
			string text = "Models/SmolLM2-1.7B.gguf";
			ModelPathConfig = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Model", text, "Model to load");
			string text2 = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), ModelPathConfig.Value);
			Logger.LogInfo((object)("INIT MODEL " + text2));
			ValuableLovePotionPatches.LoadedModel = Native.LoadModel(text2, NglConfig.Value);
		}

		private void InitPrompts()
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			string text = "Models/Prompts.txt";
			PromptFileConfig = ((BaseUnityPlugin)this).Config.Bind<string>("General", "Prompts", text, "Prompt config. Each entry in the prompt config is delimited by a new line, and separated with a semicolon. The value to the left of the semicolon is the prompt and to the right is the chat possess id, so 1 is pink love potion and 4 is red.");
			string text2 = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), PromptFileConfig.Value);
			Logger.LogInfo((object)("INIT PROMPTS " + text2));
			string[] array = File.ReadAllText(text2).Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries);
			List<PromptConfig.Prompt> list = new List<PromptConfig.Prompt>();
			string[] array2 = array;
			for (int i = 0; i < array2.Length; i++)
			{
				string[] array3 = array2[i].Split(";");
				PromptConfig.Prompt item = default(PromptConfig.Prompt);
				item.PromptString = array3[0];
				item.PossessChatID = (PossessChatID)Enum.Parse(typeof(PossessChatID), array3[1]);
				list.Add(item);
				Logger.LogInfo((object)$"Loaded prompt {item.PromptString} -- {item.PossessChatID}");
			}
			PromptConfig.Prompts = list;
		}

		private void Awake()
		{
			Logger = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			InitPrompts();
			InitModel();
			Patch();
			Logger.LogInfo((object)"LoveLLM v1.0.0 has loaded!");
		}

		public void OnApplicationQuit()
		{
			Logger.LogInfo((object)"LoveLLM is unloading... Game is shutting down or plugin is disabled.");
			Native.FreeModel(ValuableLovePotionPatches.LoadedModel.Value);
		}

		internal static void Patch()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			if (Harmony == null)
			{
				Harmony = new Harmony("LoveLLM");
			}
			Logger.LogDebug((object)"Patching...");
			Harmony.PatchAll();
			Logger.LogDebug((object)"Finished patching!");
		}

		internal static void Unpatch()
		{
			Logger.LogDebug((object)"Unpatching...");
			Harmony? harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Logger.LogDebug((object)"Finished unpatching!");
		}
	}
}
internal sealed class <>z__ReadOnlyArray<T> : IEnumerable, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>
{
	int IReadOnlyCollection<T>.Count => _items.Length;

	T IReadOnlyList<T>.this[int index] => _items[index];

	int ICollection<T>.Count => _items.Length;

	bool ICollection<T>.IsReadOnly => true;

	T IList<T>.this[int index]
	{
		get
		{
			return _items[index];
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	public <>z__ReadOnlyArray(T[] items)
	{
		_items = items;
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return ((IEnumerable)_items).GetEnumerator();
	}

	IEnumerator<T> IEnumerable<T>.GetEnumerator()
	{
		return ((IEnumerable<T>)_items).GetEnumerator();
	}

	void ICollection<T>.Add(T item)
	{
		throw new NotSupportedException();
	}

	void ICollection<T>.Clear()
	{
		throw new NotSupportedException();
	}

	bool ICollection<T>.Contains(T item)
	{
		return ((ICollection<T>)_items).Contains(item);
	}

	void ICollection<T>.CopyTo(T[] array, int arrayIndex)
	{
		((ICollection<T>)_items).CopyTo(array, arrayIndex);
	}

	bool ICollection<T>.Remove(T item)
	{
		throw new NotSupportedException();
	}

	int IList<T>.IndexOf(T item)
	{
		return ((IList<T>)_items).IndexOf(item);
	}

	void IList<T>.Insert(int index, T item)
	{
		throw new NotSupportedException();
	}

	void IList<T>.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}
}