Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get ChatCommands into a (seemingly) functional state #36

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ChatCommands/ChatCommands.csproj
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.13.0</Version>
<TargetFramework>net452</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Pathoschild.Stardew.ModBuildConfig" Version="3.3.0" />
<PackageReference Include="Pathoschild.Stardew.ModBuildConfig" Version="4.3.2" />
</ItemGroup>
</Project>
4 changes: 3 additions & 1 deletion ChatCommands/ChatCommandsMod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class ChatCommandsMod : Mod, ICommandHandler
private NotifyingTextWriter consoleNotifier;
private InputState inputState;
private ChatCommandsConfig modConfig;
private CommandInvoker commandInvoker;

private int repeatWaitPeriod = BaseWaitPeriod;

Expand All @@ -42,7 +43,7 @@ public void Handle(string input)
parts[0] = "help";

this.consoleNotifier.Notify = true;
this.Helper.ConsoleCommands.Trigger(parts[0], parts.Skip(1).ToArray());
this.commandInvoker.InvokeCommand(parts[0], parts.Skip(1).ToArray());
this.consoleNotifier.Notify = false;
}

Expand All @@ -52,6 +53,7 @@ public override void Entry(IModHelper helper)
{
this.commandValidator = new CommandValidator(helper.ConsoleCommands);
this.consoleNotifier = new NotifyingTextWriter(Console.Out, this.OnLineWritten);
this.commandInvoker = new CommandInvoker(this.Helper, this.Monitor);

this.inputState = helper.Reflection.GetField<InputState>(typeof(Game1), "input").GetValue();

Expand Down
5 changes: 4 additions & 1 deletion ChatCommands/ClassReplacements/CommandChatBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;

namespace ChatCommands.ClassReplacements
Expand Down Expand Up @@ -50,7 +51,9 @@ public CommandChatBox(IModHelper helper, ICommandHandler handler, ChatCommandsCo
this.bChoosingEmoji = helper.Reflection.GetField<bool>(this, "choosingEmoji");
Texture2D chatBoxTexture = Game1.content.Load<Texture2D>("LooseSprites\\chatBox");

this.chatBox.OnEnterPressed -= helper.Reflection.GetField<TextBoxEvent>(this, "e").GetValue();
Type[] onEnterDelegateParamTypes = { typeof(TextBox) };
MethodInfo textBoxEnterInfo = this.GetType().GetMethod("textBoxEnter", onEnterDelegateParamTypes);
this.chatBox.OnEnterPressed -= (TextBoxEvent)textBoxEnterInfo.CreateDelegate(typeof(TextBoxEvent), this);
this.chatBox = this.commandChatTextBox = new CommandChatTextBox(chatBoxTexture,
null, Game1.smallFont, Color.White);
Game1.keyboardDispatcher.Subscriber = this.chatBox;
Expand Down
113 changes: 113 additions & 0 deletions ChatCommands/Util/CommandInvoker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using StardewModdingAPI;
using StardewModdingAPI.Events;
using StardewValley;
using System;
using System.Reflection;

namespace ChatCommands.Util
{
/// <summary>
/// Uses disgusting Reflection shenanigans to emulate the old behaviour of `ConsoleCommands.Trigger`
/// </summary>
internal class CommandInvoker
{
private object? commandManager;
private MethodInfo? getCommandMethod;
private PropertyInfo? callbackPropInfo;
private MethodInfo? cmdInvokeMethod;
private IMonitor monitor;

public CommandInvoker(IModHelper helper, IMonitor monitor)
{
this.monitor = monitor;
ObtainGetCommandMethod(helper);
}

private void ObtainGetCommandMethod(IModHelper helper)
{
FieldInfo? commandManagerFieldInfo = helper.ConsoleCommands.GetType().GetField("CommandManager", BindingFlags.NonPublic | BindingFlags.Instance);
if (commandManagerFieldInfo == null)
{
this.monitor.Log($"Could not obtain CommandManager field info", LogLevel.Error);
return;
}
//this.monitor.Log($"CommandManager has type {commandManagerFieldInfo.FieldType.FullName}", LogLevel.Debug);

this.commandManager = commandManagerFieldInfo.GetValue(helper.ConsoleCommands);
if (this.commandManager == null)
{
this.monitor.Log("Could not obtain ConsoleCommands object", LogLevel.Error);
return;
}
//this.monitor.Log("Obtained ConsoleCommands object", LogLevel.Debug);

this.getCommandMethod = commandManager.GetType().GetMethod("Get");
if (this.getCommandMethod == null)
{
this.monitor.Log("Could not obtain ConsoleCommands.Get method", LogLevel.Error);
return;
}
//this.monitor.Log("Obtained ConsoleCommands.Get MethodInfo", LogLevel.Debug);

Type commandType = this.getCommandMethod.ReturnType;
//this.monitor.Log($"Command type is {commandType.AssemblyQualifiedName}", LogLevel.Debug);

PropertyInfo[] propertyInfos = commandType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (PropertyInfo propertyInfo in propertyInfos)
{
if (propertyInfo.Name == "Callback")
{
this.callbackPropInfo = propertyInfo;
}
}

if (this.callbackPropInfo == null)
{
this.monitor.Log($"Could not obtain Command.Callback PropertyInfo", LogLevel.Error);
return;
}
//this.monitor.Log("Obtained Command.Callback PropertyInfo", LogLevel.Debug);
//this.monitor.Log($"Command.Callback type is {this.callbackPropInfo.PropertyType}", LogLevel.Debug);

this.cmdInvokeMethod = this.callbackPropInfo.PropertyType.GetMethod("Invoke");
if (this.cmdInvokeMethod == null)
{
this.monitor.Log($"Could not obtain Action.Invoke MethodInfo", LogLevel.Error);
return;
}
//this.monitor.Log("Obtained Action.Invoke MethodInfo", LogLevel.Debug);
}

/// <summary>
/// Invokes the requested console command.
/// </summary>
/// <param name="name">The command name.</param>
/// <param name="args">The command aarguments.</param>
/// <returns>Returns whether the command was actually triggered; We may not have found the `CommandManager` properly, or the requested command may not exist.</returns>
public bool InvokeCommand(string name, string[] args)
{
object[] getArguments = { name };
object? command = this.getCommandMethod?.Invoke(this.commandManager, getArguments);
if (command == null)
{
this.monitor.Log($"Could not obtain `{name}` command", LogLevel.Error);
return false;
}

object? callback = this.callbackPropInfo?.GetValue(command);
if (callback == null)
{
this.monitor.Log($"Could not obtain Callback value", LogLevel.Error);
return false;
}
//this.monitor.Log($"Obtained callback for command `{name}`, calling with args `{args}`", LogLevel.Debug);

if (this.cmdInvokeMethod == null)
return false;

object[] callbackArgs = { name, args };
this.cmdInvokeMethod.Invoke(callback, callbackArgs);
return true;
}
}
}
6 changes: 3 additions & 3 deletions ChatCommands/manifest.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"Name": "Chat Commands",
"Author": "Cat",
"Version": "1.15.2",
"Description": "Lets you run SMAPI commands from the chat window!",
"Version": "1.15.3",
"Description": "Lets you run SMAPI commands from the chat window! Updated for SV 1.6 by Lino5000",
"UniqueID": "cat.chatcommands",
"EntryDll": "ChatCommands.dll",
"MinimumApiVersion": "3.0.0",
"MinimumApiVersion": "4.0.0",
"UpdateKeys": [ "Nexus:2092" ]
}