using System;
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 BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using LethalCompanyInputUtils.Api;
using Newtonsoft.Json;
using TerminalApi;
using TerminalApi.Events;
using UnityEngine;
using UnityEngine.InputSystem;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("LethalAutocomplete")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("LethalAutocomplete")]
[assembly: AssemblyCopyright("Copyright © 2023")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("B5A3A159-6BFF-43BE-840B-173ABE0E1D7F")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace LethalAutocomplete;
public class WordNode
{
public string Word { get; set; }
public List<WordNode> Children { get; set; }
public int Weight { get; set; }
public WordNode(string word, int weight)
{
Word = word;
Weight = weight;
Children = new List<WordNode>();
}
public List<WordNode> FindMatchingWords(string[] inputs)
{
List<WordNode> list = new List<WordNode>();
if (inputs.Length == 0 || Word.ToLower().StartsWith(inputs[0].ToLower()))
{
if (inputs.Length == 1)
{
list.Add(this);
}
else
{
foreach (WordNode child in Children)
{
list.AddRange(child.FindMatchingWords(inputs.Skip(1).ToArray()));
}
}
}
return list;
}
}
public class Autocomplete
{
private List<WordNode> _words;
public List<string> blacklist;
public static ManualLogSource Logger;
private readonly int _defaultWeight = 10;
public Autocomplete()
{
_words = new List<WordNode>();
blacklist = new List<string>();
}
public void Insert(TerminalKeyword terminalKeyword)
{
try
{
if ((Object)(object)terminalKeyword == (Object)null)
{
Logger.LogWarning((object)"Tried to add null keyword");
return;
}
string word = ((Object)terminalKeyword).name;
Logger.LogInfo((object)"blacklist");
if (blacklist == null)
{
blacklist = new List<string>();
}
bool flag = blacklist.Contains(word);
if (ListContainsWord(word, _words))
{
if (flag)
{
_words = _words.Where((WordNode x) => x.Word != word).ToList();
}
}
else
{
if (flag)
{
return;
}
Logger.LogInfo((object)"create WordNode node");
WordNode wordNode = new WordNode(word, _defaultWeight);
Logger.LogInfo((object)"terminalKeyword.compatibleNouns");
if (terminalKeyword.compatibleNouns != null)
{
for (int i = 0; i < terminalKeyword.compatibleNouns.Length; i++)
{
string text = "";
text = ((!new string[2] { "route", "info" }.Any(word.ToLower().Contains)) ? ((object)terminalKeyword.compatibleNouns[i].noun).ToString().Split(new char[1] { ' ' })[0] : terminalKeyword.compatibleNouns[i].noun.word);
WordNode wordNode2 = new WordNode(text, _defaultWeight);
if (word.ToLower() == "buy")
{
for (int j = 1; j < 10; j++)
{
wordNode2.Children.Add(new WordNode(j.ToString(), 10 - j));
}
}
wordNode.Children.Add(wordNode2);
}
}
Logger.LogInfo((object)"_words.Add");
_words.Add(wordNode);
}
}
catch (Exception arg2)
{
string arg = "None";
if (Object.op_Implicit((Object)(object)terminalKeyword) && ((Object)terminalKeyword).name != "")
{
arg = ((Object)terminalKeyword).name;
}
Logger.LogError((object)$"Failed to add terminal keyword '{arg}' in to autocomplete dictionary! Exception: {arg2}");
}
}
private bool ListContainsWord(string word, List<WordNode> list)
{
return list.Any((WordNode node) => node.Word == word);
}
public List<string> GetAutocomplete(string input)
{
try
{
string[] array = input.Split(new char[1] { ' ' });
List<WordNode> list = new List<WordNode>();
string matching_start = "";
for (int i = 0; i < array.Length - 1; i++)
{
matching_start = matching_start + array[i] + " ";
}
foreach (WordNode word in _words)
{
list.AddRange(word.FindMatchingWords(array));
}
list = (from n in list.Distinct()
where n.Weight > 0
orderby n.Weight descending
select n).ToList();
return list.Select((WordNode n) => matching_start + n.Word).ToList();
}
catch (Exception arg)
{
Logger.LogError((object)$"Failed on autocomplete search. Error: {arg}");
return null;
}
}
public List<WordNode> GetWords()
{
return _words;
}
public void SetWords(List<WordNode> words)
{
_words = new List<WordNode>(words);
}
}
internal class AutocompleteManager
{
private class SaveData
{
public List<WordNode> Words { get; set; }
public Dictionary<string, List<string>> History { get; set; }
public List<string> CommandsBlacklist { get; set; }
public List<string> HistoryBlacklist { get; set; }
}
public static Keybinds keybinds;
public static string saveFilePath = "";
public static string autocompleteKey = "<Keyboard>/tab";
public static string historyNextKey = "<Keyboard>/upArrow";
public static string historyPrevKey = "<Keyboard>/downArrow";
public static bool saveHistory = true;
public static int historyMaxCount = 20;
public bool exited;
private Terminal _terminal;
private string _input;
private List<string> _terminalCommands;
private List<string> _commandsHistory;
private List<string> _historyBlacklist;
private int _historyIndex;
private string _lastAutocomplete = "";
private bool _startedAutocomplete;
private List<string> _autocompleteOptions;
private int _autocompleteOptionIndex;
public static ManualLogSource Logger;
private Autocomplete _autocomplete;
public void Awake()
{
if (Plugin.IsDebug)
{
Logger.LogInfo((object)("Lethal Autocomplete Plugin is loaded! Autocomplete Key: " + autocompleteKey));
}
_terminalCommands = new List<string>();
_commandsHistory = new List<string>();
_historyBlacklist = new List<string>();
_autocomplete = new Autocomplete();
Autocomplete.Logger = Logger;
LoadFromJson();
SetupTerminalCallbacks();
}
private void OnTerminalTextChanged(object sender, TerminalTextChangedEventArgs e)
{
try
{
_input = TerminalApi.GetTerminalInput();
_input = _input.Replace("\n", "");
if (Plugin.IsDebug)
{
Logger.LogMessage((object)("OnTerminalTextChanged: " + _input));
}
if (_input != _lastAutocomplete)
{
ResetAutocomplete();
}
}
catch (Exception arg)
{
Logger.LogError((object)$"On terminal text changed event. Error: {arg}");
}
}
private void OnTerminalExit(object sender, TerminalEventArgs e)
{
if (Plugin.IsDebug)
{
Logger.LogMessage((object)"Terminal Exited");
}
if (!saveHistory)
{
_commandsHistory = new List<string>();
}
try
{
RemoveKeybindCallbacks();
SaveToJson();
}
catch (Exception arg)
{
Logger.LogError((object)$"On terminal exit. Error: {arg}");
}
}
private void TerminalIsStarting(object sender, TerminalEventArgs e)
{
try
{
if (Plugin.IsDebug)
{
Logger.LogMessage((object)"Terminal is starting");
}
_terminal = TerminalApi.Terminal;
if (autocompleteKey.Contains("tab"))
{
RemoveTerminalExitBinding();
}
}
catch (Exception arg)
{
Logger.LogError((object)$"On terminal starting. Error: {arg}");
}
}
private void RemoveTerminalExitBinding()
{
//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_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
try
{
MovementActions movement = _terminal.playerActions.Movement;
InputAction openMenu = ((MovementActions)(ref movement)).OpenMenu;
if (Plugin.IsDebug)
{
Logger.LogMessage((object)(openMenu.name + " " + InputActionRebindingExtensions.GetBindingDisplayString(openMenu, (DisplayStringOptions)0, (string)null)));
}
openMenu.Disable();
InputBinding val = default(InputBinding);
((InputBinding)(ref val)).path = "<Keyboard>/tab";
((InputBinding)(ref val)).overridePath = "";
InputActionRebindingExtensions.ApplyBindingOverride(openMenu, val);
openMenu.Enable();
if (Plugin.IsDebug)
{
Logger.LogMessage((object)(openMenu.name + " " + InputActionRebindingExtensions.GetBindingDisplayString(openMenu, (DisplayStringOptions)0, (string)null)));
}
}
catch (Exception ex)
{
Logger.LogWarning((object)"Remove terminal tab exit binded key");
Logger.LogError((object)ex);
throw;
}
}
private void TerminalIsStarted(object sender, TerminalEventArgs e)
{
try
{
if (Plugin.IsDebug)
{
Logger.LogMessage((object)"Terminal is started");
}
_terminalCommands.Clear();
for (int i = 0; i < _terminal.terminalNodes.allKeywords.Length; i++)
{
Logger.LogWarning((object)$"Add: {_terminal.terminalNodes.allKeywords[i]}");
_autocomplete.Insert(_terminal.terminalNodes.allKeywords[i]);
}
}
catch (Exception ex)
{
Logger.LogWarning((object)"Terminal is started");
Logger.LogError((object)ex);
throw;
}
}
private void TextSubmitted(object sender, TerminalParseSentenceEventArgs e)
{
if (Plugin.IsDebug)
{
Logger.LogMessage((object)("TextSubmitted: " + _input));
}
try
{
if (e.SubmittedText != "" && !_historyBlacklist.Contains(e.SubmittedText))
{
if (_commandsHistory.Count + 1 > historyMaxCount)
{
_commandsHistory.RemoveAt(0);
}
if (_commandsHistory.Contains(e.SubmittedText))
{
_commandsHistory.Remove(e.SubmittedText);
}
_commandsHistory.Add(e.SubmittedText);
_historyIndex = _commandsHistory.Count;
}
_input = "";
ResetAutocomplete();
}
catch (Exception ex)
{
Logger.LogWarning((object)$"Text submitted: {e.SubmittedText} Node Returned: {e.ReturnedNode}");
Logger.LogError((object)ex);
}
}
private void OnBeginUsing(object sender, TerminalEventArgs e)
{
Logger.LogMessage((object)"Player has just started using the terminal");
SetupKeybindCallbacks();
}
private void OnBeganUsing(object sender, TerminalEventArgs e)
{
if (autocompleteKey.Contains("tab"))
{
HUDManager.Instance.ChangeControlTip(0, "Quit terminal : [Esc]", true);
}
}
private void SetupTerminalCallbacks()
{
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Expected O, but got Unknown
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Expected O, but got Unknown
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Expected O, but got Unknown
//IL_003a: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Expected O, but got Unknown
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Expected O, but got Unknown
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: Expected O, but got Unknown
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
//IL_0077: Expected O, but got Unknown
Events.TerminalStarting += new TerminalEventHandler(TerminalIsStarting);
Events.TerminalStarted += new TerminalEventHandler(TerminalIsStarted);
Events.TerminalParsedSentence += new TerminalParseSentenceEventHandler(TextSubmitted);
Events.TerminalBeginUsing += new TerminalEventHandler(OnBeginUsing);
Events.TerminalBeganUsing += new TerminalEventHandler(OnBeganUsing);
Events.TerminalExited += new TerminalEventHandler(OnTerminalExit);
Events.TerminalTextChanged += new TerminalTextChangedEventHandler(OnTerminalTextChanged);
}
private void SetupKeybindCallbacks()
{
Logger.LogMessage((object)"Setup Keybind Callbacks");
keybinds.AutocompleteAction.performed += OnAutocompleteKey;
keybinds.HistoryNextAction.performed += OnHistoryNextKey;
keybinds.HistoryPrevAction.performed += OnHistoryPrevKey;
}
private void RemoveKeybindCallbacks()
{
Logger.LogMessage((object)"Remove Keybind Callbacks");
keybinds.AutocompleteAction.performed -= OnAutocompleteKey;
keybinds.HistoryNextAction.performed -= OnHistoryNextKey;
keybinds.HistoryPrevAction.performed -= OnHistoryPrevKey;
}
private void OnHistoryNextKey(CallbackContext ctx)
{
try
{
if (_commandsHistory.Count >= 1 && _historyIndex >= 1)
{
_historyIndex--;
Logger.LogInfo((object)("Set input to '" + _commandsHistory[_historyIndex] + "'."));
TerminalApi.SetTerminalInput(_commandsHistory[_historyIndex]);
}
}
catch (Exception arg)
{
Logger.LogError((object)$"Failed on history next key performed. Error: {arg}");
Logger.LogInfo((object)$"_commandsHistory.Count={_commandsHistory.Count}");
Logger.LogInfo((object)$"historyIndex={_historyIndex}");
}
}
private void OnHistoryPrevKey(CallbackContext ctx)
{
TerminalApi.SetTerminalInput("");
try
{
if (_commandsHistory.Count >= 1)
{
if (_historyIndex + 1 >= _commandsHistory.Count)
{
_historyIndex = _commandsHistory.Count;
TerminalApi.SetTerminalInput("");
Logger.LogInfo((object)"Set input to ''.");
Logger.LogInfo((object)("INPUT: " + TerminalApi.GetTerminalInput()));
}
else
{
_historyIndex++;
Logger.LogInfo((object)("Set input to '" + _commandsHistory[_historyIndex] + "'."));
TerminalApi.SetTerminalInput(_commandsHistory[_historyIndex]);
}
}
}
catch (Exception arg)
{
Logger.LogError((object)$"Failed on history prev key performed. Error: {arg}");
Logger.LogInfo((object)$"_commandsHistory.Count={_commandsHistory.Count}");
Logger.LogInfo((object)$"historyIndex={_historyIndex}");
}
}
private void OnAutocompleteKey(CallbackContext ctx)
{
try
{
if (_startedAutocomplete)
{
NextAutocomplete();
}
else
{
StartAutocomplete();
}
}
catch (Exception arg)
{
Logger.LogError((object)$"Failed on autocomplete key performed. Error: {arg}");
Logger.LogInfo((object)$"_startedAutocomplete={_startedAutocomplete}");
}
}
private void StartAutocomplete()
{
try
{
List<string> autocomplete = _autocomplete.GetAutocomplete(_input);
if (Plugin.IsDebug)
{
Logger.LogInfo((object)"Autocomplete options:");
foreach (string item in autocomplete)
{
Logger.LogInfo((object)(item ?? ""));
}
}
if (autocomplete != null && autocomplete.Count > 0)
{
_autocompleteOptions = new List<string>(autocomplete);
_startedAutocomplete = true;
_lastAutocomplete = _autocompleteOptions.First();
Logger.LogMessage((object)("Set Autocomplete " + _lastAutocomplete));
TerminalApi.SetTerminalInput(_lastAutocomplete);
}
}
catch (Exception arg)
{
Logger.LogError((object)$"Failed on autocomplete new options search. Error: {arg}");
}
}
private void NextAutocomplete()
{
try
{
if (Plugin.IsDebug)
{
Logger.LogInfo((object)"Autocomplete Options:");
for (int i = 0; i < _autocompleteOptions.Count; i++)
{
Logger.LogInfo((object)_autocompleteOptions[i]);
}
Logger.LogInfo((object)$"Autocomplete Index: {_autocompleteOptionIndex + 1}");
}
_autocompleteOptionIndex++;
if (_autocompleteOptionIndex >= _autocompleteOptions.Count)
{
_autocompleteOptionIndex = 0;
}
_lastAutocomplete = _autocompleteOptions[_autocompleteOptionIndex];
TerminalApi.SetTerminalInput(_lastAutocomplete);
}
catch (Exception arg)
{
Logger.LogError((object)$"Failed on autocomplete next options search. Error: {arg}");
}
}
private void ResetAutocomplete()
{
try
{
if (Plugin.IsDebug)
{
Logger.LogInfo((object)"Reset autocomplete state");
}
_startedAutocomplete = false;
_autocompleteOptionIndex = 0;
}
catch (Exception arg)
{
Logger.LogError((object)$"Failed on autocomplete reset. Error: {arg}");
}
}
public void SaveToJson()
{
List<WordNode> words = _autocomplete.GetWords();
Dictionary<string, List<string>> history = new Dictionary<string, List<string>> { ["value"] = _commandsHistory };
List<string> list = new List<string>();
list = _autocomplete.blacklist;
List<string> list2 = new List<string>();
list2 = _historyBlacklist;
string contents = JsonConvert.SerializeObject((object)new
{
Words = words,
History = history,
CommandsBlacklist = list,
HistoryBlacklist = list2
}, (Formatting)1);
File.WriteAllText(saveFilePath, contents);
}
private void LoadFromJson()
{
try
{
if (File.Exists(saveFilePath))
{
string text = File.ReadAllText(saveFilePath);
if (!string.IsNullOrEmpty(text))
{
SaveData saveData = JsonConvert.DeserializeObject<SaveData>(text);
_autocomplete.blacklist = saveData.CommandsBlacklist;
_historyBlacklist = saveData.HistoryBlacklist;
List<string> first = saveData.History["value"];
_commandsHistory = new List<string>(first.Except(_historyBlacklist));
_historyIndex = _commandsHistory.Count;
_autocomplete.SetWords(saveData.Words);
Logger.LogMessage((object)"Loaded save from JSON!");
}
}
}
catch (Exception arg)
{
Logger.LogError((object)$"Failed on loading json save file. Error: {arg}");
}
}
}
internal class Keybinds : LcInputActions
{
public InputAction AutocompleteAction => ((LcInputActions)this).Asset["Autocomplete"];
public InputAction HistoryNextAction => ((LcInputActions)this).Asset["HistoryNext"];
public InputAction HistoryPrevAction => ((LcInputActions)this).Asset["HistoryPrev"];
public override void CreateInputActions(in InputActionMapBuilder builder)
{
((LcInputActions)this).CreateInputActions(ref builder);
builder.NewActionBinding().WithActionId("Autocomplete").WithActionType((InputActionType)1)
.WithKbmPath(AutocompleteManager.autocompleteKey)
.WithBindingName("Autocomplete Key")
.Finish();
builder.NewActionBinding().WithActionId("HistoryNext").WithActionType((InputActionType)1)
.WithKbmPath(AutocompleteManager.historyNextKey)
.WithBindingName("HistoryNext Key")
.Finish();
builder.NewActionBinding().WithActionId("HistoryPrev").WithActionType((InputActionType)1)
.WithKbmPath(AutocompleteManager.historyPrevKey)
.WithBindingName("HistoryPrev Key")
.Finish();
}
}
[BepInPlugin("redeye.lethalautocomplete", "Lethal Autocomplete", "0.4.5")]
[BepInDependency("atomic.terminalapi", "1.3.0")]
[BepInDependency("com.rune580.LethalCompanyInputUtils", "0.4.2")]
public class Plugin : BaseUnityPlugin
{
private const string _GUID = "redeye.lethalautocomplete";
private const string _Name = "Lethal Autocomplete";
private const string _Version = "0.4.5";
public static bool IsDebug;
private AutocompleteManager _autocomplete;
public string PluginPath = "";
private void Awake()
{
((BaseUnityPlugin)this).Logger.LogInfo((object)"Lethal Autocomplete Plugin is loaded!");
Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null);
try
{
PluginPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)ex);
}
try
{
_autocomplete = new AutocompleteManager();
AutocompleteManager.Logger = ((BaseUnityPlugin)this).Logger;
ConfigFile();
AutocompleteManager.keybinds = new Keybinds();
_autocomplete.Awake();
}
catch (Exception ex2)
{
((BaseUnityPlugin)this).Logger.LogError((object)ex2);
}
}
private void OnApplicationQuit()
{
_autocomplete.SaveToJson();
}
private void ConfigFile()
{
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Expected O, but got Unknown
string path = "save.json";
string text = Path.Combine(PluginPath, path);
string text2 = ((BaseUnityPlugin)this).Config.Bind<string>("Basic", "Save Data Path", "", "Absolute path to the json file with autocomplete words and commands history. By default save.json generated in plugins/red_eye-LethalAutocomplete folder.").Value;
if (!File.Exists(text2) && text2 != "")
{
text2 = text;
((BaseUnityPlugin)this).Config.Remove(new ConfigDefinition("Basic", "Save Data Path"));
((BaseUnityPlugin)this).Config.Bind<string>("Basic", "Save Data Path", text, "Absolute path to the json file with autocomplete words and commands history. By default save.json generated in plugins/red_eye-LethalAutocomplete folder.");
((BaseUnityPlugin)this).Logger.LogWarning((object)"The save file wasn't found in the directory specified by the configuration file! Using default path.");
}
AutocompleteManager.saveFilePath = ((text2 == "") ? text : text2);
ConfigEntry<string> val = ((BaseUnityPlugin)this).Config.Bind<string>("Keyboard Bindings", "Autocomplete", "<Keyboard>/tab", "Get autocomplete for current input");
AutocompleteManager.autocompleteKey = (val.Value.ToLower().StartsWith("<keyboard>") ? val.Value : ("<Keyboard>/" + val.Value));
ConfigEntry<string> val2 = ((BaseUnityPlugin)this).Config.Bind<string>("Keyboard Bindings", "History Next", "<Keyboard>/upArrow", "Get current terminal session next command");
AutocompleteManager.historyNextKey = (val2.Value.ToLower().StartsWith("<keyboard>") ? val2.Value : ("<Keyboard>/" + val2.Value));
ConfigEntry<string> val3 = ((BaseUnityPlugin)this).Config.Bind<string>("Keyboard Bindings", "History Prev", "<Keyboard>/downArrow", "Get current terminal session prev command");
AutocompleteManager.historyPrevKey = (val3.Value.ToLower().StartsWith("<keyboard>") ? val3.Value : ("<Keyboard>/" + val3.Value));
AutocompleteManager.saveHistory = ((BaseUnityPlugin)this).Config.Bind<bool>("History", "Save History", true, "Regulates if the history be saved after the re-entry").Value;
AutocompleteManager.historyMaxCount = ((BaseUnityPlugin)this).Config.Bind<int>("History", "Buffer Length", 20, "Max amount of commands to remember during terminal session").Value;
IsDebug = ((BaseUnityPlugin)this).Config.Bind<bool>("Other", "Enable Debug", false, "").Value;
}
}