Added new Game logic library to assist with game projects
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NetFlow.DocumentationHelper.Library.Attributes;
|
||||
using OmegaLeo.HelperLib.Game.Models;
|
||||
|
||||
namespace OmegaLeo.HelperLib.Game.Services
|
||||
{
|
||||
public class CommandConsole
|
||||
{
|
||||
private readonly Dictionary<string, ConsoleCommand> _commands = new Dictionary<string, ConsoleCommand>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly List<string> _history = new List<string>();
|
||||
private int _maxHistorySize = 100;
|
||||
|
||||
public event Action<string> OnOutput;
|
||||
public event Action<string> OnError;
|
||||
|
||||
public IReadOnlyList<string> History => _history.AsReadOnly();
|
||||
public IReadOnlyDictionary<string, ConsoleCommand> Commands => _commands;
|
||||
|
||||
[Documentation(nameof(RegisterCommand), "Registers a new command with the console.")]
|
||||
public void RegisterCommand(string name, string description, Action<string[]> action, int minArgs = 0, int maxArgs = -1)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
throw new ArgumentException("Command name cannot be empty", nameof(name));
|
||||
|
||||
var command = new ConsoleCommand(name, description, action, minArgs, maxArgs);
|
||||
_commands[name] = command;
|
||||
}
|
||||
|
||||
[Documentation(nameof(RegisterCommand), "Registers a new command with the console using a delegate.")]
|
||||
public void RegisterCommand(string name, string description, Func<string[], string> func, int minArgs = 0, int maxArgs = -1)
|
||||
{
|
||||
RegisterCommand(name, description, args =>
|
||||
{
|
||||
string result = func(args);
|
||||
if (!string.IsNullOrEmpty(result))
|
||||
Output(result);
|
||||
}, minArgs, maxArgs);
|
||||
}
|
||||
|
||||
[Documentation(nameof(UnregisterCommand), "Removes a command from the console.")]
|
||||
public bool UnregisterCommand(string name)
|
||||
{
|
||||
return _commands.Remove(name);
|
||||
}
|
||||
|
||||
[Documentation(nameof(ExecuteCommand), "Executes a command string.")]
|
||||
public bool ExecuteCommand(string input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
return false;
|
||||
|
||||
_history.Add(input);
|
||||
if (_history.Count > _maxHistorySize)
|
||||
_history.RemoveAt(0);
|
||||
|
||||
string[] parts = ParseCommandLine(input);
|
||||
if (parts.Length == 0)
|
||||
return false;
|
||||
|
||||
string commandName = parts[0];
|
||||
string[] args = parts.Skip(1).ToArray();
|
||||
|
||||
if (!_commands.TryGetValue(commandName, out var command))
|
||||
{
|
||||
Error($"Unknown command: {commandName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (command.MinArgs > 0 && args.Length < command.MinArgs)
|
||||
{
|
||||
Error($"Command '{commandName}' requires at least {command.MinArgs} argument(s)");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (command.MaxArgs >= 0 && args.Length > command.MaxArgs)
|
||||
{
|
||||
Error($"Command '{commandName}' accepts at most {command.MaxArgs} argument(s)");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
command.Action(args);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Error($"Error executing command '{commandName}': {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[Documentation(nameof(Output), "Outputs a message to the console.")]
|
||||
public void Output(string message)
|
||||
{
|
||||
OnOutput?.Invoke(message);
|
||||
}
|
||||
|
||||
[Documentation(nameof(Error), "Outputs an error message to the console.")]
|
||||
public void Error(string message)
|
||||
{
|
||||
OnError?.Invoke(message);
|
||||
}
|
||||
|
||||
[Documentation(nameof(ClearHistory), "Clears command history.")]
|
||||
public void ClearHistory()
|
||||
{
|
||||
_history.Clear();
|
||||
}
|
||||
|
||||
[Documentation(nameof(GetAutoComplete), "Gets autocomplete suggestions for partial input.")]
|
||||
public string[] GetAutoComplete(string partial)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(partial))
|
||||
return _commands.Keys.ToArray();
|
||||
|
||||
return _commands.Keys
|
||||
.Where(cmd => cmd.StartsWith(partial, StringComparison.OrdinalIgnoreCase))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private string[] ParseCommandLine(string input)
|
||||
{
|
||||
var result = new List<string>();
|
||||
bool inQuotes = false;
|
||||
string current = "";
|
||||
|
||||
for (int i = 0; i < input.Length; i++)
|
||||
{
|
||||
char c = input[i];
|
||||
|
||||
if (c == '"')
|
||||
{
|
||||
inQuotes = !inQuotes;
|
||||
}
|
||||
else if (char.IsWhiteSpace(c) && !inQuotes)
|
||||
{
|
||||
if (current.Length > 0)
|
||||
{
|
||||
result.Add(current);
|
||||
current = "";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
current += c;
|
||||
}
|
||||
}
|
||||
|
||||
if (current.Length > 0)
|
||||
result.Add(current);
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
[Documentation(nameof(RegisterDefaultCommands), "Registers built-in commands.")]
|
||||
public void RegisterDefaultCommands()
|
||||
{
|
||||
RegisterCommand("help", "Lists all available commands", args =>
|
||||
{
|
||||
if (args.Length == 0)
|
||||
{
|
||||
Output("Available commands:");
|
||||
foreach (var cmd in _commands.Values.OrderBy(c => c.Name))
|
||||
{
|
||||
Output($" {cmd.Name} - {cmd.Description}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string cmdName = args[0];
|
||||
if (_commands.TryGetValue(cmdName, out var cmd))
|
||||
{
|
||||
Output($"{cmd.Name}: {cmd.Description}");
|
||||
Output($"Min args: {cmd.MinArgs}, Max args: {(cmd.MaxArgs >= 0 ? cmd.MaxArgs.ToString() : "unlimited")}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Error($"Unknown command: {cmdName}");
|
||||
}
|
||||
}
|
||||
}, 0, 1);
|
||||
|
||||
RegisterCommand("clear", "Clears command history", _ => ClearHistory());
|
||||
|
||||
RegisterCommand("echo", "Echoes back the input", args =>
|
||||
{
|
||||
Output(string.Join(" ", args));
|
||||
}, 1);
|
||||
|
||||
RegisterCommand("commands", "Lists all registered commands", _ =>
|
||||
{
|
||||
Output($"Total commands: {_commands.Count}");
|
||||
Output(string.Join(", ", _commands.Keys.OrderBy(k => k)));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NetFlow.DocumentationHelper.Library.Attributes;
|
||||
using OmegaLeo.HelperLib.Game.Handlers;
|
||||
using OmegaLeo.HelperLib.Game.Models;
|
||||
|
||||
namespace OmegaLeo.HelperLib.Game.Services
|
||||
{
|
||||
public class DelayedActionManager
|
||||
{
|
||||
private readonly List<DelayedAction> _pendingActions = new List<DelayedAction>();
|
||||
private readonly List<DelayedAction> _actionsToRemove = new List<DelayedAction>();
|
||||
|
||||
[Documentation(nameof(RunAfter), "Schedules an action to run after a specified delay.")]
|
||||
public void RunAfter(float delaySeconds, Action action)
|
||||
{
|
||||
_pendingActions.Add(new DelayedAction(delaySeconds, action, false));
|
||||
}
|
||||
|
||||
[Documentation(nameof(RunRepeating), "Schedules an action to run repeatedly at specified intervals.")]
|
||||
public void RunRepeating(float intervalSeconds, Action action)
|
||||
{
|
||||
_pendingActions.Add(new DelayedAction(intervalSeconds, action, true));
|
||||
}
|
||||
|
||||
[Documentation(nameof(RunAfterCancellable), "Schedules an action to run after a delay and returns a handler to cancel it if needed.")]
|
||||
public DelayedActionHandler RunAfterCancellable(float delaySeconds, Action action)
|
||||
{
|
||||
var delayedAction = new DelayedAction(delaySeconds, action, false);
|
||||
_pendingActions.Add(delayedAction);
|
||||
return new DelayedActionHandler(delayedAction);
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
_actionsToRemove.Clear();
|
||||
|
||||
foreach (var action in _pendingActions)
|
||||
{
|
||||
action.TimeRemaining -= deltaTime;
|
||||
|
||||
if (action.TimeRemaining <= 0)
|
||||
{
|
||||
if (!action.IsCancelled)
|
||||
action.Action?.Invoke();
|
||||
|
||||
if (action.IsRepeating && !action.IsCancelled)
|
||||
{
|
||||
action.TimeRemaining = action.InitialDelay;
|
||||
}
|
||||
else
|
||||
{
|
||||
_actionsToRemove.Add(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var action in _actionsToRemove)
|
||||
{
|
||||
_pendingActions.Remove(action);
|
||||
}
|
||||
}
|
||||
|
||||
[Documentation(nameof(CancelAll), "Cancels all pending delayed actions.")]
|
||||
public void CancelAll()
|
||||
{
|
||||
_pendingActions.Clear();
|
||||
}
|
||||
|
||||
[Documentation(nameof(PendingActionsCount), "Gets the count of pending delayed actions.")]
|
||||
public int PendingActionsCount => _pendingActions.Count;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using NetFlow.DocumentationHelper.Library.Attributes;
|
||||
using Newtonsoft.Json;
|
||||
using OmegaLeo.HelperLib.Game.Interfaces;
|
||||
|
||||
namespace OmegaLeo.HelperLib.Game.Services
|
||||
{
|
||||
public class SaveManager
|
||||
{
|
||||
private readonly ISaveFileProvider _fileProvider;
|
||||
private readonly JsonSerializerSettings _jsonOptions;
|
||||
|
||||
public SaveManager(ISaveFileProvider fileProvider)
|
||||
{
|
||||
_fileProvider = fileProvider ?? throw new ArgumentNullException(nameof(fileProvider));
|
||||
_jsonOptions = new JsonSerializerSettings
|
||||
{
|
||||
TypeNameHandling = TypeNameHandling.Auto,
|
||||
Formatting = Formatting.Indented
|
||||
};
|
||||
}
|
||||
|
||||
[Documentation(nameof(Save), "Saves data to a JSON file.")]
|
||||
public void Save<T>(string fileName, T data)
|
||||
{
|
||||
string json = JsonConvert.SerializeObject(data, _jsonOptions);
|
||||
string path = _fileProvider.GetSavePath(fileName);
|
||||
string directory = Path.GetDirectoryName(path);
|
||||
|
||||
if (!Directory.Exists(directory))
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
File.WriteAllText(path, json);
|
||||
}
|
||||
|
||||
[Documentation(nameof(Load), "Loads data from a JSON file.")]
|
||||
public T Load<T>(string fileName)
|
||||
{
|
||||
string path = _fileProvider.GetSavePath(fileName);
|
||||
|
||||
if (!File.Exists(path))
|
||||
throw new FileNotFoundException($"Save file not found: {fileName}");
|
||||
|
||||
string json = File.ReadAllText(path);
|
||||
return JsonConvert.DeserializeObject<T>(json, _jsonOptions);
|
||||
}
|
||||
|
||||
[Documentation(nameof(TryLoad), "Attempts to load data from a JSON file, returning a success flag.")]
|
||||
public bool TryLoad<T>(string fileName, out T data)
|
||||
{
|
||||
try
|
||||
{
|
||||
data = Load<T>(fileName);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
data = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[Documentation(nameof(SaveExists), "Checks if a save file exists.")]
|
||||
public bool SaveExists(string fileName)
|
||||
{
|
||||
return File.Exists(_fileProvider.GetSavePath(fileName));
|
||||
}
|
||||
|
||||
[Documentation(nameof(DeleteSave), "Deletes a save file.")]
|
||||
public void DeleteSave(string fileName)
|
||||
{
|
||||
string path = _fileProvider.GetSavePath(fileName);
|
||||
if (File.Exists(path))
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
[Documentation(nameof(GetAllSaveFiles), "Retrieves all save files in the save directory.")]
|
||||
public string[] GetAllSaveFiles()
|
||||
{
|
||||
string savePath = _fileProvider.GetSaveDirectory();
|
||||
if (!Directory.Exists(savePath))
|
||||
return Array.Empty<string>();
|
||||
|
||||
return Directory.GetFiles(savePath, "*.json");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user