Skip to content

Commit

Permalink
Implemented autocomplete functionality, example under HandlerAttribut…
Browse files Browse the repository at this point in the history
…es. Fixed response input on web.
  • Loading branch information
derekShaheen committed Apr 6, 2024
1 parent 5fdb47a commit 01b03f4
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 35 deletions.
9 changes: 5 additions & 4 deletions Commands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ private set
}
}

public Dictionary<string, (MethodInfo Method, string?[] Parameters, string Category, string Description)>? CommandHandlers { get => commandHandlers; set => commandHandlers = value; }
public Dictionary<string, (MethodInfo Method, string?[] Parameters, string Category, string Description, string AutoCompleteMethodName)>? CommandHandlers { get => commandHandlers; set => commandHandlers = value; }
public Dictionary<string, Func<object?>>? GameVariableMethods { get => gameVariableMethods; set => gameVariableMethods = value; }

private Dictionary<string, (MethodInfo Method, string?[] Parameters, string Category, string Description)>? commandHandlers;
private Dictionary<string, (MethodInfo Method, string?[] Parameters, string Category, string Description, string AutoCompleteMethodName)>? commandHandlers;
private Dictionary<string, Func<object?>>? gameVariableMethods;

public void DiscoverHandlersAndVariables()
{
CommandHandlers = new Dictionary<string, (MethodInfo, string?[], string, string)>();
CommandHandlers = new Dictionary<string, (MethodInfo, string?[], string, string, string AutoCompleteMethodName)>();
GameVariableMethods = new Dictionary<string, Func<object?>>();

try
Expand All @@ -59,7 +59,8 @@ public void DiscoverHandlersAndVariables()
.Where(param => param.ParameterType != typeof(HttpListenerResponse))
.Select(param => param.Name)
.ToArray();
CommandHandlers[path] = (method, parameters, commandAttribute.Category ?? string.Empty, commandAttribute.Description ?? string.Empty);
// Initialize AutoCompleteOptions as an empty dictionary
CommandHandlers[path] = (method, parameters, commandAttribute.Category ?? string.Empty, commandAttribute.Description ?? string.Empty, commandAttribute.AutoCompleteMethodName ?? string.Empty);
}

// Discover Game Variables
Expand Down
22 changes: 20 additions & 2 deletions HandlerAttributes.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

Expand All @@ -12,16 +13,33 @@ public class CommandHandlerAttribute : Attribute
public string Path { get; }
public string Category { get; }
public string Description { get; }
public string AutoCompleteMethodName { get; set; } // New property

// Adjust the constructor to accept category as an optional parameter
public CommandHandlerAttribute(string path, string category = "Miscellaneous", string description = "")
public CommandHandlerAttribute(string path, string category = "Miscellaneous", string description = "", string autoCompleteMethodName = "")
{
Path = "/" + path;
Category = category;
Description = description;
AutoCompleteMethodName = autoCompleteMethodName;
}

//Example usage with autocomplete

//[CommandHandler("SpawnResource", "Resources", "desc", "SpawnResourceAutoComplete")]
//public static void SpawnResourceHttp(HttpListenerResponse response, string resource = "Gold", string isInfinite = "False")

//public static Dictionary<string, string[]> SpawnResourceAutoComplete()
//{
// Dictionary<string, string[]> responseOptions = new Dictionary<string, string[]>();

// responseOptions["resource"] = Enum.GetNames(typeof(Minerals.MineralTypes));
// responseOptions["isInfinite"] = new[] { "False", "True" };

// return responseOptions;
//}
}


[AttributeUsage(AttributeTargets.Method)]
public class GameVariableAttribute : Attribute
{
Expand Down
2 changes: 1 addition & 1 deletion Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using MelonLoader;

[assembly: MelonInfo(typeof(Wicker.WickerServer), "WickerREST", "0.93.0", "Skrip")]
[assembly: MelonInfo(typeof(Wicker.WickerServer), "WickerREST", "0.94.0", "Skrip")]
[assembly: MelonGame("Crate Entertainment", "Farthest Frontier")]
35 changes: 28 additions & 7 deletions WickerNetwork.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,16 +166,37 @@ private void ServeCommandHandlers(HttpListenerResponse response)
{
if (Commands.Instance.CommandHandlers != null && Commands.Instance.CommandHandlers.Count > 0)
{
//await EnsureFileExists("/dwyl", DWYL_URL, true);
var commandsInfo = Commands.Instance.CommandHandlers.Select(handler => new {
Path = handler.Key,
Parameters = handler.Value.Method.GetParameters()
.Select(p => new {
p.Name,
Type = p.ParameterType.Name,
DefaultValue = p.HasDefaultValue ? p.DefaultValue?.ToString() : null
})
.ToArray(),
.Select(p => {
Dictionary<string, string[]> autoCompleteOptions = new Dictionary<string, string[]>();
if (!string.IsNullOrEmpty(handler.Value.AutoCompleteMethodName))
{
MethodInfo? autoCompleteMethod;
try
{
autoCompleteMethod = handler.Value.Method.DeclaringType.GetMethod(handler.Value.AutoCompleteMethodName, BindingFlags.Static | BindingFlags.Public);
if (autoCompleteMethod != null && autoCompleteMethod.ReturnType == typeof(Dictionary<string, string[]>))
{
autoCompleteOptions = (Dictionary<string, string[]>)autoCompleteMethod.Invoke(null, null);
}
}
catch (Exception ex)
{
WickerServer.Instance.LogMessage($"Error invoking AutoComplete method: {ex.Message}");
}
}

return new
{
p.Name,
Type = p.ParameterType.Name,
DefaultValue = p.HasDefaultValue ? p.DefaultValue?.ToString() : null,
AutoCompleteOptions = autoCompleteOptions.ContainsKey(p.Name) ? autoCompleteOptions[p.Name] : Array.Empty<string>()
};
})
.ToArray(),
Category = handler.Value.Category,
Description = handler.Value.Description
}).ToArray();
Expand Down
4 changes: 2 additions & 2 deletions WickerREST.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AssemblyVersion>0.93.0.0</AssemblyVersion>
<FileVersion>0.93.0.0</FileVersion>
<AssemblyVersion>0.94.0.0</AssemblyVersion>
<FileVersion>0.94.0.0</FileVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion WickerServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public static class BuildInfo
public const string Name = "WickerREST";
public const string Description = "WickerREST";
public const string Author = "Skrip";
public const string Version = "0.93.0";
public const string Version = "0.94.0";
public const string DownloadLink = "";
}

Expand Down
85 changes: 67 additions & 18 deletions web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ <h4 class="text-warning">Command Output</h4>
<img src="https://hits.dwyl.com/derekShaheen/WickerREST.svg" style="display: none" />
</div>

<!-- Response Modal -->
<!-- Submission Modal -->
<div class="modal modal-dialog-scrollable" id="responseModal" tabindex="-1" aria-labelledby="modalLabel" aria-hidden="true" data-bs-backdrop="true" data-bs-keyboard="true">
<div class="modal-dialog">
<div class="modal-content bg-dark text-light">
Expand Down Expand Up @@ -172,38 +172,87 @@ <h5 class="modal-title" id="modalLabel">Command Response</h5>
if (param.Name === 'response') {
return; // Skip 'response' parameter
}

const inputGroup = document.createElement('div');
inputGroup.className = 'input-group mb-3';

const inputLabel = document.createElement('label');
inputLabel.textContent = param.Name + ': ';
inputLabel.className = 'input-group-text';

const input = document.createElement('input');
input.type = 'text';
input.className = 'form-control';
input.placeholder = param.Name;
input.id = 'input-' + param.Name;
input.setAttribute('data-parameter', param.Name);

// Add event listener for Enter key
input.addEventListener('keypress', function (e) {
if (e.key === 'Enter') {
e.preventDefault(); // Prevent the default action to stop form submission
submitModal(command); // Function to simulate the submit button click
}
});
let inputElement;

if (param.AutoCompleteOptions.length > 0) {
// Create a select element for auto-complete options
inputElement = document.createElement('select');
inputElement.className = 'form-select';
param.AutoCompleteOptions.forEach(option => {
const optionElement = document.createElement('option');
optionElement.value = option;
optionElement.textContent = option;
inputElement.appendChild(optionElement);
});
} else {
// Create an input element for free text
inputElement = document.createElement('input');
inputElement.type = 'text';
inputElement.className = 'form-control';
inputElement.placeholder = param.Name;
//if (param.DefaultValue !== null) {
// inputElement.value = param.DefaultValue;
//}
}

// Set default value if available
if (param.DefaultValue !== null) {
input.value = param.DefaultValue;
inputElement.value = param.DefaultValue;
}

inputElement.id = 'input-' + param.Name;
inputElement.setAttribute('data-parameter', param.Name);

inputGroup.appendChild(inputLabel);
inputGroup.appendChild(input);
inputGroup.appendChild(inputElement);
modalBody.appendChild(inputGroup);
});


//parameters.forEach(param => {
// if (param.Name === 'response') {
// return; // Skip 'response' parameter
// }
// const inputGroup = document.createElement('div');
// inputGroup.className = 'input-group mb-3';

// const inputLabel = document.createElement('label');
// inputLabel.textContent = param.Name + ': ';
// inputLabel.className = 'input-group-text';

// const input = document.createElement('input');
// input.type = 'text';
// input.className = 'form-control';
// input.placeholder = param.Name;
// input.id = 'input-' + param.Name;
// input.setAttribute('data-parameter', param.Name);

// // Add event listener for Enter key
// input.addEventListener('keypress', function (e) {
// if (e.key === 'Enter') {
// e.preventDefault(); // Prevent the default action to stop form submission
// submitModal(command); // Function to simulate the submit button click
// }
// });

// // Set default value if available
// if (param.DefaultValue !== null) {
// input.value = param.DefaultValue;
// }

// inputGroup.appendChild(inputLabel);
// inputGroup.appendChild(input);
// modalBody.appendChild(inputGroup);
//});

// Add a submit button to the modal
const submitButton = document.createElement('button');
submitButton.className = 'btn btn-block btn-primary';
Expand All @@ -230,7 +279,7 @@ <h5 class="modal-title" id="modalLabel">Command Response</h5>
}

function submitModal(command) {
const inputs = document.getElementById('modalBody').querySelectorAll('input');
const inputs = document.getElementById('modalBody').querySelectorAll('input, select');
const inputValues = Array.from(inputs).reduce((acc, input) => {
const paramName = input.getAttribute('data-parameter');
acc[paramName] = input.value;
Expand Down

0 comments on commit 01b03f4

Please sign in to comment.