618 lines
25 KiB
C#
618 lines
25 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Drawing;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
using System.Timers;
|
|
using Decal.Adapter;
|
|
using Decal.Adapter.Wrappers;
|
|
|
|
namespace MosswartMassacre
|
|
{
|
|
[FriendlyName("Mosswart Massacre")]
|
|
public class PluginCore : PluginBase
|
|
{
|
|
internal static PluginHost MyHost;
|
|
internal static int totalKills = 0;
|
|
internal static int rareCount = 0;
|
|
internal static DateTime lastKillTime = DateTime.Now;
|
|
internal static double killsPer5Min = 0;
|
|
internal static double killsPerHour = 0;
|
|
internal static DateTime statsStartTime = DateTime.Now;
|
|
internal static Timer updateTimer;
|
|
public static bool RareMetaEnabled { get; set; } = true;
|
|
public static bool RemoteCommandsEnabled { get; set; } = false;
|
|
public static bool HttpServerEnabled { get; set; } = false;
|
|
public static string CharTag { get; set; } = "";
|
|
public static bool TelemetryEnabled { get; set; } = false;
|
|
public bool WebSocketEnabled { get; set; } = false;
|
|
public bool InventoryLogEnabled { get; set; } = false;
|
|
private MossyInventory _inventoryLogger;
|
|
|
|
private static Queue<string> rareMessageQueue = new Queue<string>();
|
|
private static DateTime _lastSent = DateTime.MinValue;
|
|
private static readonly Queue<string> _chatQueue = new Queue<string>();
|
|
|
|
protected override void Startup()
|
|
{
|
|
try
|
|
{
|
|
MyHost = Host;
|
|
|
|
WriteToChat("Mosswart Massacre has started!");
|
|
// Subscribe to chat message event
|
|
CoreManager.Current.ChatBoxMessage += new EventHandler<ChatTextInterceptEventArgs>(OnChatText);
|
|
CoreManager.Current.ChatBoxMessage += new EventHandler<ChatTextInterceptEventArgs>(AllChatText);
|
|
CoreManager.Current.CommandLineText += OnChatCommand;
|
|
CoreManager.Current.CharacterFilter.LoginComplete += CharacterFilter_LoginComplete;
|
|
CoreManager.Current.WorldFilter.CreateObject += OnSpawn;
|
|
CoreManager.Current.WorldFilter.ReleaseObject += OnDespawn;
|
|
|
|
|
|
// Initialize the timer
|
|
updateTimer = new Timer(1000); // Update every second
|
|
updateTimer.Elapsed += UpdateStats;
|
|
updateTimer.Start();
|
|
|
|
// Initialize the view (UI)
|
|
MainView.ViewInit();
|
|
|
|
// Enable TLS1.2
|
|
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
|
|
//Enable vTank interface
|
|
vTank.Enable();
|
|
//lyssna på commands
|
|
WebSocket.OnServerCommand += HandleServerCommand;
|
|
//starta inventory. Hanterar subscriptions i den med
|
|
|
|
_inventoryLogger = new MossyInventory();
|
|
|
|
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
WriteToChat("Error during startup: " + ex.Message);
|
|
}
|
|
}
|
|
|
|
protected override void Shutdown()
|
|
{
|
|
try
|
|
{
|
|
PluginSettings.Save();
|
|
if (TelemetryEnabled)
|
|
Telemetry.Stop(); // ensure no dangling timer / HttpClient
|
|
WriteToChat("Mosswart Massacre is shutting down...");
|
|
|
|
// Unsubscribe from chat message event
|
|
CoreManager.Current.ChatBoxMessage -= new EventHandler<ChatTextInterceptEventArgs>(OnChatText);
|
|
CoreManager.Current.CommandLineText -= OnChatCommand;
|
|
CoreManager.Current.ChatBoxMessage -= new EventHandler<ChatTextInterceptEventArgs>(AllChatText);
|
|
CoreManager.Current.WorldFilter.CreateObject -= OnSpawn;
|
|
CoreManager.Current.WorldFilter.ReleaseObject -= OnDespawn;
|
|
|
|
|
|
// Stop and dispose of the timer
|
|
if (updateTimer != null)
|
|
{
|
|
updateTimer.Stop();
|
|
updateTimer.Dispose();
|
|
updateTimer = null;
|
|
}
|
|
|
|
// Clean up the view
|
|
MainView.ViewDestroy();
|
|
//Disable vtank interface
|
|
vTank.Disable();
|
|
// sluta lyssna på commands
|
|
WebSocket.OnServerCommand -= HandleServerCommand;
|
|
WebSocket.Stop();
|
|
//shutdown inv
|
|
_inventoryLogger.Dispose();
|
|
|
|
MyHost = null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
WriteToChat("Error during shutdown: " + ex.Message);
|
|
}
|
|
}
|
|
private void CharacterFilter_LoginComplete(object sender, EventArgs e)
|
|
{
|
|
CoreManager.Current.CharacterFilter.LoginComplete -= CharacterFilter_LoginComplete;
|
|
|
|
PluginSettings.Initialize(); // Safe to call now
|
|
|
|
// Apply the values
|
|
RareMetaEnabled = PluginSettings.Instance.RareMetaEnabled;
|
|
WebSocketEnabled = PluginSettings.Instance.WebSocketEnabled;
|
|
RemoteCommandsEnabled = PluginSettings.Instance.RemoteCommandsEnabled;
|
|
HttpServerEnabled = PluginSettings.Instance.HttpServerEnabled;
|
|
TelemetryEnabled = PluginSettings.Instance.TelemetryEnabled;
|
|
CharTag = PluginSettings.Instance.CharTag;
|
|
MainView.SetRareMetaToggleState(RareMetaEnabled);
|
|
if (TelemetryEnabled)
|
|
Telemetry.Start();
|
|
if (WebSocketEnabled)
|
|
WebSocket.Start();
|
|
|
|
|
|
}
|
|
|
|
|
|
private async void OnSpawn(object sender, CreateObjectEventArgs e)
|
|
{
|
|
var mob = e.New;
|
|
if (mob.ObjectClass != ObjectClass.Monster) return;
|
|
|
|
try
|
|
{
|
|
var coords = mob.Coordinates();
|
|
const string fmt = "F7";
|
|
string ns = coords.NorthSouth.ToString(fmt, CultureInfo.InvariantCulture);
|
|
string ew = coords.EastWest.ToString(fmt, CultureInfo.InvariantCulture);
|
|
|
|
await WebSocket.SendSpawnAsync(ns, ew, mob.Name);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
PluginCore.WriteToChat($"[WS] Spawn send failed: {ex}");
|
|
}
|
|
}
|
|
|
|
|
|
private void OnDespawn(object sender, ReleaseObjectEventArgs e)
|
|
{
|
|
var mob = e.Released;
|
|
if (mob.ObjectClass != ObjectClass.Monster) return;
|
|
|
|
|
|
// var c = mob.Coordinates();
|
|
// PluginCore.WriteToChat(
|
|
// $"[Despawn] {mob.Name} @ (NS={c.NorthSouth:F1}, EW={c.EastWest:F1})");
|
|
}
|
|
|
|
private async void AllChatText(object sender, ChatTextInterceptEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
string cleaned = NormalizeChatLine(e.Text);
|
|
await WebSocket.SendChatTextAsync(e.Color, cleaned);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
PluginCore.WriteToChat($"[WS] Chat send failed: {ex}");
|
|
}
|
|
}
|
|
|
|
private static string NormalizeChatLine(string raw)
|
|
{
|
|
if (string.IsNullOrEmpty(raw))
|
|
return raw;
|
|
|
|
// 1) Remove all <…> tags
|
|
var noTags = Regex.Replace(raw, "<[^>]+>", "");
|
|
|
|
// 2) Trim trailing newline or carriage-return
|
|
var trimmed = noTags.TrimEnd('\r', '\n');
|
|
|
|
// 3) Collapse multiple spaces into one
|
|
var collapsed = Regex.Replace(trimmed, @"[ ]{2,}", " ");
|
|
|
|
return collapsed;
|
|
}
|
|
private void HandleServerCommand(CommandEnvelope env)
|
|
{
|
|
// Skicka commands
|
|
DispatchChatToBoxWithPluginIntercept(env.Command);
|
|
CoreManager.Current.Actions.InvokeChatParser($"/a Executed '{env.Command}' from Mosswart Overlord");
|
|
}
|
|
private void OnChatText(object sender, ChatTextInterceptEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
// WriteToChat($"[Debug] Chat Color: {e.Color}, Message: {e.Text}");
|
|
|
|
if (IsKilledByMeMessage(e.Text))
|
|
{
|
|
totalKills++;
|
|
lastKillTime = DateTime.Now;
|
|
CalculateKillsPerInterval();
|
|
MainView.UpdateKillStats(totalKills, killsPer5Min, killsPerHour);
|
|
}
|
|
|
|
if (IsRareDiscoveryMessage(e.Text, out string rareText))
|
|
{
|
|
rareCount++;
|
|
MainView.UpdateRareCount(rareCount);
|
|
|
|
if (RareMetaEnabled)
|
|
{
|
|
Decal_DispatchOnChatCommand("/vt setmetastate loot_rare");
|
|
}
|
|
|
|
DelayedCommandManager.AddDelayedCommand($"/a {rareText}", 3000);
|
|
// Fire and forget: we don't await, since sending is not critical and we don't want to block.
|
|
_ = WebSocket.SendRareAsync(rareText);
|
|
}
|
|
|
|
if (e.Color == 18 && e.Text.EndsWith("!report\""))
|
|
{
|
|
TimeSpan elapsed = DateTime.Now - statsStartTime;
|
|
string reportMessage = $"Total Kills: {totalKills}, Kills per Hour: {killsPerHour:F2}, Elapsed Time: {elapsed:dd\\.hh\\:mm\\:ss}, Rares Found: {rareCount}";
|
|
WriteToChat($"[Mosswart Massacre] Reporting to allegiance: {reportMessage}");
|
|
MyHost.Actions.InvokeChatParser($"/a {reportMessage}");
|
|
}
|
|
if (RemoteCommandsEnabled && e.Color == 18)
|
|
{
|
|
string characterName = Regex.Escape(CoreManager.Current.CharacterFilter.Name);
|
|
string pattern = $@"^\[Allegiance\].*Dunking Rares.*say[s]?, \""!do {characterName} (?<command>.+)\""$";
|
|
string tag = Regex.Escape(PluginCore.CharTag);
|
|
string patterntag = $@"^\[Allegiance\].*Dunking Rares.*say[s]?, \""!dot {tag} (?<command>.+)\""$";
|
|
|
|
|
|
var match = Regex.Match(e.Text, pattern);
|
|
var matchtag = Regex.Match(e.Text, patterntag);
|
|
|
|
if (match.Success)
|
|
{
|
|
string command = match.Groups["command"].Value;
|
|
DispatchChatToBoxWithPluginIntercept(command);
|
|
DelayedCommandManager.AddDelayedCommand($"/a [Remote] Executing: {command}", 2000);
|
|
}
|
|
else if (matchtag.Success)
|
|
{
|
|
string command = matchtag.Groups["command"].Value;
|
|
DispatchChatToBoxWithPluginIntercept(command);
|
|
DelayedCommandManager.AddDelayedCommand($"/a [Remote] Executing: {command}", 2000);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
WriteToChat("Error processing chat message: " + ex.Message);
|
|
}
|
|
}
|
|
private void OnChatCommand(object sender, ChatParserInterceptEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
if (e.Text.StartsWith("/mm", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
e.Eat = true; // Prevent the message from showing in chat
|
|
HandleMmCommand(e.Text);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
PluginCore.WriteToChat($"[Error] Failed to process /mm command: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
private void UpdateStats(object sender, ElapsedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
// Update the elapsed time
|
|
TimeSpan elapsed = DateTime.Now - statsStartTime;
|
|
MainView.UpdateElapsedTime(elapsed);
|
|
|
|
// Recalculate kill rates
|
|
CalculateKillsPerInterval();
|
|
MainView.UpdateKillStats(totalKills, killsPer5Min, killsPerHour);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
WriteToChat("Error updating stats: " + ex.Message);
|
|
}
|
|
}
|
|
|
|
private void CalculateKillsPerInterval()
|
|
{
|
|
double minutesElapsed = (DateTime.Now - statsStartTime).TotalMinutes;
|
|
|
|
if (minutesElapsed > 0)
|
|
{
|
|
killsPer5Min = (totalKills / minutesElapsed) * 5;
|
|
killsPerHour = (totalKills / minutesElapsed) * 60;
|
|
}
|
|
}
|
|
|
|
private bool IsKilledByMeMessage(string text)
|
|
{
|
|
string[] killPatterns = new string[]
|
|
{
|
|
@"^You flatten (?<targetname>.+)'s body with the force of your assault!$",
|
|
@"^You bring (?<targetname>.+) to a fiery end!$",
|
|
@"^You beat (?<targetname>.+) to a lifeless pulp!$",
|
|
@"^You smite (?<targetname>.+) mightily!$",
|
|
@"^You obliterate (?<targetname>.+)!$",
|
|
@"^You run (?<targetname>.+) through!$",
|
|
@"^You reduce (?<targetname>.+) to a sizzling, oozing mass!$",
|
|
@"^You knock (?<targetname>.+) into next Morningthaw!$",
|
|
@"^You split (?<targetname>.+) apart!$",
|
|
@"^You cleave (?<targetname>.+) in twain!$",
|
|
@"^You slay (?<targetname>.+) viciously enough to impart death several times over!$",
|
|
@"^You reduce (?<targetname>.+) to a drained, twisted corpse!$",
|
|
@"^Your killing blow nearly turns (?<targetname>.+) inside-out!$",
|
|
@"^Your attack stops (?<targetname>.+) cold!$",
|
|
@"^Your lightning coruscates over (?<targetname>.+)'s mortal remains!$",
|
|
@"^Your assault sends (?<targetname>.+) to an icy death!$",
|
|
@"^You killed (?<targetname>.+)!$",
|
|
@"^The thunder of crushing (?<targetname>.+) is followed by the deafening silence of death!$",
|
|
@"^The deadly force of your attack is so strong that (?<targetname>.+)'s ancestors feel it!$",
|
|
@"^(?<targetname>.+)'s seared corpse smolders before you!$",
|
|
@"^(?<targetname>.+) is reduced to cinders!$",
|
|
@"^(?<targetname>.+) is shattered by your assault!$",
|
|
@"^(?<targetname>.+) catches your attack, with dire consequences!$",
|
|
@"^(?<targetname>.+) is utterly destroyed by your attack!$",
|
|
@"^(?<targetname>.+) suffers a frozen fate!$",
|
|
@"^(?<targetname>.+)'s perforated corpse falls before you!$",
|
|
@"^(?<targetname>.+) is fatally punctured!$",
|
|
@"^(?<targetname>.+)'s death is preceded by a sharp, stabbing pain!$",
|
|
@"^(?<targetname>.+) is torn to ribbons by your assault!$",
|
|
@"^(?<targetname>.+) is liquified by your attack!$",
|
|
@"^(?<targetname>.+)'s last strength dissolves before you!$",
|
|
@"^Electricity tears (?<targetname>.+) apart!$",
|
|
@"^Blistered by lightning, (?<targetname>.+) falls!$",
|
|
@"^(?<targetname>.+)'s last strength withers before you!$",
|
|
@"^(?<targetname>.+) is dessicated by your attack!$",
|
|
@"^(?<targetname>.+) is incinerated by your assault!$"
|
|
};
|
|
|
|
foreach (string pattern in killPatterns)
|
|
{
|
|
if (Regex.IsMatch(text, pattern))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
private bool IsRareDiscoveryMessage(string text, out string rareTextOnly)
|
|
{
|
|
rareTextOnly = null;
|
|
|
|
// Match pattern: "<name> has discovered the <something>!"
|
|
string pattern = @"^(?<name>['A-Za-z ]+)\s(?<text>has discovered the .*!$)";
|
|
Match match = Regex.Match(text, pattern);
|
|
|
|
if (match.Success && match.Groups["name"].Value == CoreManager.Current.CharacterFilter.Name)
|
|
{
|
|
rareTextOnly = match.Groups["text"].Value; // just "has discovered the Ancient Pickle!"
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
public static void WriteToChat(string message)
|
|
{
|
|
MyHost.Actions.AddChatText("[Mosswart Massacre] " + message, 0, 1);
|
|
}
|
|
public static void RestartStats()
|
|
{
|
|
totalKills = 0;
|
|
rareCount = 0;
|
|
statsStartTime = DateTime.Now;
|
|
killsPer5Min = 0;
|
|
killsPerHour = 0;
|
|
|
|
WriteToChat("Stats have been reset.");
|
|
MainView.UpdateKillStats(totalKills, killsPer5Min, killsPerHour);
|
|
MainView.UpdateRareCount(rareCount);
|
|
}
|
|
public static void ToggleRareMeta()
|
|
{
|
|
PluginSettings.Instance.RareMetaEnabled = !PluginSettings.Instance.RareMetaEnabled;
|
|
RareMetaEnabled = PluginSettings.Instance.RareMetaEnabled;
|
|
MainView.SetRareMetaToggleState(RareMetaEnabled);
|
|
}
|
|
|
|
[DllImport("Decal.dll")]
|
|
private static extern int DispatchOnChatCommand(ref IntPtr str, [MarshalAs(UnmanagedType.U4)] int target);
|
|
|
|
public static bool Decal_DispatchOnChatCommand(string cmd)
|
|
{
|
|
IntPtr bstr = Marshal.StringToBSTR(cmd);
|
|
|
|
try
|
|
{
|
|
bool eaten = (DispatchOnChatCommand(ref bstr, 1) & 0x1) > 0;
|
|
return eaten;
|
|
}
|
|
finally
|
|
{
|
|
Marshal.FreeBSTR(bstr);
|
|
}
|
|
}
|
|
public static void DispatchChatToBoxWithPluginIntercept(string cmd)
|
|
{
|
|
if (!Decal_DispatchOnChatCommand(cmd))
|
|
CoreManager.Current.Actions.InvokeChatParser(cmd);
|
|
}
|
|
private void HandleMmCommand(string text)
|
|
{
|
|
// Remove the /mm prefix and trim extra whitespace
|
|
string[] args = text.Substring(3).Trim().Split(' ');
|
|
|
|
if (args.Length == 0 || string.IsNullOrEmpty(args[0]))
|
|
{
|
|
WriteToChat("Usage: /mm <command>. Try /mm help");
|
|
return;
|
|
}
|
|
|
|
string subCommand = args[0].ToLower();
|
|
|
|
switch (subCommand)
|
|
{
|
|
case "telemetry":
|
|
if (args.Length > 1)
|
|
{
|
|
if (args[1].Equals("enable", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
TelemetryEnabled = true;
|
|
Telemetry.Start();
|
|
PluginSettings.Instance.TelemetryEnabled = true;
|
|
WriteToChat("Telemetry streaming ENABLED.");
|
|
}
|
|
else if (args[1].Equals("disable", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
TelemetryEnabled = false;
|
|
Telemetry.Stop();
|
|
PluginSettings.Instance.TelemetryEnabled = false;
|
|
WriteToChat("Telemetry streaming DISABLED.");
|
|
}
|
|
else
|
|
{
|
|
WriteToChat("Usage: /mm telemetry <enable|disable>");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WriteToChat("Usage: /mm telemetry <enable|disable>");
|
|
}
|
|
break;
|
|
case "ws":
|
|
if (args.Length > 1)
|
|
{
|
|
if (args[1].Equals("enable", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
WebSocketEnabled = true;
|
|
WebSocket.Start();
|
|
PluginSettings.Instance.WebSocketEnabled = true;
|
|
WriteToChat("WS streaming ENABLED.");
|
|
}
|
|
else if (args[1].Equals("disable", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
WebSocketEnabled = false;
|
|
WebSocket.Stop();
|
|
PluginSettings.Instance.WebSocketEnabled = false;
|
|
WriteToChat("WS streaming DISABLED.");
|
|
}
|
|
else
|
|
{
|
|
WriteToChat("Usage: /mm ws <enable|disable>");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WriteToChat("Usage: /mm ws <enable|disable>");
|
|
}
|
|
break;
|
|
case "help":
|
|
WriteToChat("Mosswart Massacre Commands:");
|
|
WriteToChat("/mm report - Show current stats");
|
|
WriteToChat("/mm loc - Show current location");
|
|
WriteToChat("/mm telemetry - Telemetry streaming enable|disable");
|
|
WriteToChat("/mm ws - Websocket streaming enable|disable");
|
|
WriteToChat("/mm reset - Reset all counters");
|
|
WriteToChat("/mm meta - Toggle rare meta state");
|
|
WriteToChat("/mm http - Local http-command server enable|disable");
|
|
WriteToChat("/mm remotecommand - Listen to allegiance !do/!dot enable|disable");
|
|
WriteToChat("/mm getmetastate - Gets the current metastate");
|
|
break;
|
|
case "debug":
|
|
DispatchChatToBoxWithPluginIntercept("/ub give bajs to Town Crier");
|
|
break;
|
|
case "report":
|
|
TimeSpan elapsed = DateTime.Now - statsStartTime;
|
|
string reportMessage = $"Total Kills: {totalKills}, Kills per Hour: {killsPerHour:F2}, Elapsed Time: {elapsed:dd\\.hh\\:mm\\:ss}, Rares Found: {rareCount}";
|
|
WriteToChat(reportMessage);
|
|
break;
|
|
case "getmetastate":
|
|
string metaState = VtankControl.VtGetMetaState();
|
|
WriteToChat(metaState);
|
|
break;
|
|
|
|
case "loc":
|
|
Coordinates here = Coordinates.Me;
|
|
var pos = Utils.GetPlayerPosition();
|
|
WriteToChat($"Location: {here} (X={pos.X:F1}, Y={pos.Y:F1}, Z={pos.Z:F1})");
|
|
break;
|
|
case "reset":
|
|
RestartStats();
|
|
break;
|
|
case "meta":
|
|
RareMetaEnabled = !RareMetaEnabled;
|
|
WriteToChat($"Rare meta state is now {(RareMetaEnabled ? "ON" : "OFF")}");
|
|
MainView.SetRareMetaToggleState(RareMetaEnabled); // <-- sync the UI
|
|
break;
|
|
|
|
case "http":
|
|
if (args.Length > 1)
|
|
{
|
|
if (args[1].Equals("enable", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
PluginSettings.Instance.HttpServerEnabled = true;
|
|
HttpServerEnabled = true;
|
|
HttpCommandServer.Start();
|
|
}
|
|
else if (args[1].Equals("disable", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
PluginSettings.Instance.HttpServerEnabled = false;
|
|
HttpServerEnabled = false;
|
|
HttpCommandServer.Stop();
|
|
}
|
|
else
|
|
{
|
|
WriteToChat("Usage: /mm http <enable|disable>");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WriteToChat("Usage: /mm http <enable|disable>");
|
|
}
|
|
break;
|
|
|
|
case "remotecommands":
|
|
if (args.Length > 1)
|
|
{
|
|
if (args[1].Equals("enable", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
PluginSettings.Instance.RemoteCommandsEnabled = true;
|
|
RemoteCommandsEnabled = true;
|
|
WriteToChat("Remote command listening is now ENABLED.");
|
|
}
|
|
else if (args[1].Equals("disable", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
PluginSettings.Instance.RemoteCommandsEnabled = false;
|
|
RemoteCommandsEnabled = false;
|
|
WriteToChat("Remote command listening is now DISABLED.");
|
|
}
|
|
else
|
|
{
|
|
WriteToChat("Invalid remotecommands argument. Use 'enable' or 'disable'.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WriteToChat("Usage: /mm remotecommands <enable|disable>");
|
|
}
|
|
break;
|
|
|
|
default:
|
|
WriteToChat($"Unknown /mm command: {subCommand}. Try /mm help");
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|