Files
TierMakerGodot/CommandHandler.cs
2024-04-21 15:43:32 -05:00

203 lines
5.0 KiB
C#

using Godot;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
public partial class CommandHandler : Node
{
private Settings Settings { get; set; }
private game Game { get; set; }
private readonly List<Task> TaskQueue = new();
private System.Net.Http.HttpClient _Client = null;
private System.Net.Http.HttpClient Client
{ get
{
_Client ??= new();
return _Client;
}
}
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
Settings = GetNode<Settings>("/root/Settings");
Game = GetNode<game>("/root/Game");
}
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
//delete/cleanup one finished task per frame
//currently just logs errors
var t = TaskQueue.FirstOrDefault(t => t.IsCompleted, null);
if (t is not null)
{
if (t.Exception is not null)
GD.PrintErr(t.Exception);
TaskQueue.Remove(t);
}
}
private void IncomingCommand(Command command)
{
if (!Settings.IsUserAuthorized(command.User, command.IsStreamer,
command.IsModerator))
return;
var baseArgs = command.GetArgs();
var type = CommandTypeHelper.ParseCommand(baseArgs.Pop());
var args = baseArgs.DuplicateAtState();
switch (type)
{
case CommandType.MoveCard:
MoveCard(args);
break;
case CommandType.MoveRow:
MoveRow(args);
break;
case CommandType.DeleteCard:
DeleteCards(args);
break;
case CommandType.DeleteRow:
DeleteRow(args);
break;
case CommandType.CreateCard:
TaskQueue.Add(Task.Run(() => CreateCard(args)));
break;
case CommandType.CreateRow:
CreateRow(args);
break;
case CommandType.RenameCard:
RenameCard(args);
break;
case CommandType.RenameRow:
RenameRow(args);
break;
case CommandType.RecolorRow:
RecolorRow(args);
break;
case CommandType.ChangeCardImage:
TaskQueue.Add(Task.Run(() => ChangeCardImage(args)));
break;
default:
throw new Exception();
}
}
private void MoveCard(CommandArguments args)
{
var cardId = args.Pop();
var rowId = args.Pop();
var indexStr = args.Pop();
if (int.TryParse(indexStr, out int index))
Game.MoveCard(cardId, rowId, index);
else
Game.MoveCard(cardId, rowId, null);
}
private void MoveRow(CommandArguments args)
{
var rowId = args.Pop();
var newIndexStr = args.Pop();
var newIndex = int.Parse(newIndexStr);
Game.MoveRow(rowId, newIndex);
}
private void DeleteCards(CommandArguments args)
{
var ids = args.Enumerate().ToArray();
Game.DeleteCards(ids);
}
private void DeleteRow(CommandArguments args)
{
var rowId = args.Pop();
var deleteCards = args.Pop();
if (bool.TryParse(deleteCards, out bool del))
Game.DeleteRow(rowId, del);
else
Game.DeleteRow(rowId);
}
private async Task CreateCard(CommandArguments args)
{
var title = args.Pop();
var url = args.Pop();
Image img = await ImageFromUrl(url);
Game.CreateCard(title, img);
}
private void CreateRow(CommandArguments args)
{
var colorStr = args.Pop();
var title = args.Remaining();
var color = Color.FromString(colorStr, new Color(0, 0, 0, 0));
if (color == new Color(0, 0, 0, 0))
Game.CreateRow(null, title);
else
Game.CreateRow(color, title);
}
private void RenameCard(CommandArguments args)
{
var cardId = args.Pop();
var title = args.Remaining();
Game.RenameCard(cardId, title);
}
private void RenameRow(CommandArguments args)
{
var rowId = args.Pop();
var title = args.Remaining();
Game.RenameRow(rowId, title);
}
private void RecolorRow(CommandArguments args)
{
var rowId = args.Pop();
var colorStr = args.Pop();
var color = Color.FromString(colorStr, new Color(0, 0, 0, 0));
if (color == new Color(0, 0, 0, 0))
throw new Exception($"invalid color {colorStr}");
Game.RecolorRow(rowId, color);
}
private async Task ChangeCardImage(CommandArguments args)
{
var cardId = args.Pop();
var img = await ImageFromUrl(args.Pop());
Game.ChangeCardImage(cardId, img);
}
private async Task<Image> ImageFromUrl(string url)
{
var uri = new Uri(url);
var resp = await Client.GetAsync(uri);
if (!resp.IsSuccessStatusCode)
return null;
Image img = new();
var arr = await resp.Content.ReadAsByteArrayAsync();
//TODO detect images by header rather than extension
switch (Path.GetExtension(uri.AbsolutePath).ToLower())
{
case "png":
img.LoadPngFromBuffer(arr);
break;
case "jpg":
case "jpeg":
img.LoadJpgFromBuffer(arr);
break;
case "svg":
img.LoadSvgFromBuffer(arr);
break;
case "webp":
img.LoadWebpFromBuffer(arr);
break;
default:
throw new Exception("unrecognized filetype");
}
return img;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_Client?.Dispose();
}
base.Dispose(disposing);
}
}