Skip to content

Commit

Permalink
Merge branch 'master' into publish
Browse files Browse the repository at this point in the history
  • Loading branch information
GHXX committed Jun 18, 2021
2 parents 7426feb + 51f4eaf commit afa9174
Show file tree
Hide file tree
Showing 16 changed files with 389 additions and 15 deletions.
59 changes: 59 additions & 0 deletions DataInterfaceConsole/Actions/BaseAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using DataInterfaceConsole.Types.Exceptions;
using FiveDChessDataInterface;
using System;
using System.Linq;
using System.Threading;

namespace DataInterfaceConsole.Actions
{
internal abstract class BaseAction
{
public abstract string Name { get; }
protected DataInterface di;

public static BaseAction[] GetAndInstantiateAllActions() => typeof(BaseAction).Assembly.GetTypes()
.Where(x => typeof(BaseAction).IsAssignableFrom(x) && typeof(BaseAction) != x)
.Select(t => (BaseAction)Activator.CreateInstance(t))
.ToArray();

public void Run(DataInterface di)
{
this.di = di ?? throw new ArgumentNullException(nameof(di));
Run();
}

protected abstract void Run();

/// <summary>
/// Raises an exception which terminates the currently executing <see cref="BaseAction"/>, if the datainterface has become invalid, likely due to a game crash.
/// </summary>
protected void AbortIfDiInvalid()
{
if (!this.di.IsValid())
throw new DataInterfaceClosedException("The DataInterface instance is not, or no longer, valid.");
}

protected string ConsoleReadLineWhileDiValid() => Util.ConsoleReadLineWhileDiValid(this.di);

protected void WaitForIngame()
{
bool shown = false;
while (!this.di.IsGameRunning())
{
if (!shown)
{
Console.WriteLine("Waiting for a match to be started.");
shown = true;
}
AbortIfDiInvalid();
Thread.Sleep(150);
}
Console.WriteLine("A match has started. Continuing...");
}

protected void WriteLineIndented(string s, int level = 1)
{
Console.WriteLine($"{new string(' ', level * 2)}{s}");
}
}
}
35 changes: 35 additions & 0 deletions DataInterfaceConsole/Actions/LoadPredefinedVariant.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using FiveDChessDataInterface.Variants;
using System;

namespace DataInterfaceConsole.Actions
{
class LoadPredefinedVariant : BaseAction
{
public override string Name => "Load Predefined Variant";

protected override void Run()
{
WaitForIngame();
var variants = GithubVariantGetter.GetAllVariants();

WriteLineIndented("Select a variant from the following:");
for (int i = 0; i < variants.Length; i++)
{
WriteLineIndented($"{(i + 1).ToString().PadLeft((int)Math.Ceiling(Math.Log10(variants.Length)))}. {variants[i].Name} by {variants[i].Author}");
}

if (int.TryParse(Util.ConsoleReadLineWhile(() => this.di.IsValid()), out int input) && input > 0 && input <= variants.Length)
{
var chosenVariant = variants[input - 1];
WriteLineIndented($"Loading variant '{chosenVariant.Name}'...");
var gb = chosenVariant.GetGameBuilder();
this.di.SetChessBoardArrayFromBuilder(gb);
WriteLineIndented($"Variant loaded and written to memory.");
}
else
{
WriteLineIndented("Invalid input. Not loading any variant.");
}
}
}
}
12 changes: 12 additions & 0 deletions DataInterfaceConsole/DataInterfaceConsole.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\FiveDChessDataInterface\FiveDChessDataInterface.csproj" />
</ItemGroup>

</Project>
132 changes: 132 additions & 0 deletions DataInterfaceConsole/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using DataInterfaceConsole.Actions;
using DataInterfaceConsole.Types;
using DataInterfaceConsole.Types.Exceptions;
using FiveDChessDataInterface;
using System;
using System.Globalization;
using System.Linq;
using System.Threading;

namespace DataInterfaceConsole
{
class Program
{
internal static Program instance = new Program();
private Thread backgroundThread;
private DataInterface di;

static void Main()
{
ConsoleNonBlocking.Init();
Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");
instance.Run();
}

private void Run()
{
Console.WriteLine("Some output will occasionally be provided via the console title.");

this.backgroundThread = new Thread(BackgroundThreadRun)
{
Name = "BackgroundThread"
};
this.backgroundThread.Start();

var actions = BaseAction.GetAndInstantiateAllActions().Select((x, i) => (i, x)).ToDictionary(a => a.i + 1, a => a.x);
var idWidth = (int)Math.Log10(actions.Count) + 1;

Thread.Sleep(200);
while (true)
{
if (this.di?.IsValid() != true)
Console.WriteLine("Please launch the game...");

SpinWait.SpinUntil(() => this.di?.IsValid() == true); // wait till the datainterface instance is valid
Console.WriteLine($"Select an action from the following (by typing the number to the left and pressing enter):\n" +
$"{string.Join("\n", actions.Select(a => $"[{a.Key.ToString().PadLeft(idWidth)}] {a.Value.Name}"))}");
try
{
while (true)
{
var choice = Util.ConsoleReadLineThrowIfDiInvalid(this.di);
if (int.TryParse(choice, out int res) && res > 0 && res <= actions.Count)
{
var a = actions[res];
var header = $"====== Executing action: {a.Name} ======";
Console.WriteLine(header);
try
{
a.Run(this.di);

Console.WriteLine($"Action executed. Returning to menu...\n{new string('=', header.Length)}");
Thread.Sleep(1000);
}
catch (DataInterfaceClosedException ex)
{
Util.WriteColored($"Execution of the current action '{a.Name}' was aborted because the game closed or crashed: \n{ex}\n" +
$"To continue, please press ENTER.", ConsoleColor.Red);
ConsoleNonBlocking.ClearInputLines();
}
break;
}
else
{
Console.WriteLine("Invalid number entered. Please try again.");
}
}
}
catch (DataInterfaceClosedException ex)
{
Util.WriteColored($"Execution of the current action selection was aborted because the game closed or crashed: \n{ex}\nTo continue, please press ENTER.", ConsoleColor.Red);
ConsoleNonBlocking.ClearInputLines();
}
}
}

private void BackgroundThreadRun()
{
while (true)
{
bool tooManyProcesses = false;
if (this.di?.IsValid() != true)
{
if (!DataInterface.TryCreateAutomatically(out this.di, out int procCnt))
{
if (procCnt > 1)
{
tooManyProcesses = true;
}
}
else
{
this.di.Initialize();
}
}

if (tooManyProcesses)
{
SetConsoleTitleWithPrefix($"Too many game instances found!");
}
else
{
var isValid = this.di?.IsValid();
string gameStatus = isValid switch
{
true => $"Running - ProcessId: {this.di.GameProcess.Id}",
false => "closed",
null => "Not found"
};

SetConsoleTitleWithPrefix($"GameProcess: {gameStatus}");
}

Thread.Sleep(500);
}
}

private void SetConsoleTitleWithPrefix(string s)
{
Console.Title = $"5D Data Interface Console - {s}";
}
}
}
41 changes: 41 additions & 0 deletions DataInterfaceConsole/Types/ConsoleNonBlocking.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace DataInterfaceConsole.Types
{
internal static class ConsoleNonBlocking
{
private static readonly ConcurrentQueue<string> readLines = new ConcurrentQueue<string>();

internal static void Init()
{
Task.Run(ReadLines);
}

private static async Task ReadLines()
{
while (true)
{
var s = new StreamReader(Console.OpenStandardInput(), Console.InputEncoding);
var l = await s.ReadLineAsync();
readLines.Enqueue(l);
}
}

public static int Count => readLines.Count;
public static bool TryPeek(out string val) => readLines.TryPeek(out val);
public static bool TryDequeue(out string val) => readLines.TryDequeue(out val);

public static string ReadLineBlocking()
{
string s = null;
SpinWait.SpinUntil(() => TryDequeue(out s));
return s;
}

public static void ClearInputLines() => readLines.Clear();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Threading;

namespace DataInterfaceConsole.Types.Exceptions
{

[Serializable]
public class DataInterfaceClosedException : Exception
{
public DataInterfaceClosedException() { }
public DataInterfaceClosedException(string message) : base(message) { }
public DataInterfaceClosedException(string message, Exception inner) : base(message, inner) { }
protected DataInterfaceClosedException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
}
}
53 changes: 53 additions & 0 deletions DataInterfaceConsole/Util.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using DataInterfaceConsole.Types;
using DataInterfaceConsole.Types.Exceptions;
using FiveDChessDataInterface;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace DataInterfaceConsole
{
static class Util
{
public static string ConsoleReadLineWhile(Func<bool> doRead)
{
while (doRead.Invoke())
{
if (ConsoleNonBlocking.TryDequeue(out string res))
return res;
Thread.Sleep(150);
}

return null;
}

public static string ConsoleReadLineWhileDiValid(DataInterface di) => ConsoleReadLineWhile(() => di?.IsValid() == true);

/// <summary>
/// Reads a line from the console, blocking. Aborts if the specified <see cref="DataInterface"/> instance becomes invalid.
/// </summary>
/// <param name="di">The <see cref="DataInterface"/> instance to monitor.</param>
/// <returns>The string that was read from the console input.</returns>
/// <exception cref="DataInterfaceClosedException">if the datainterface instance becomes invalid while reading.</exception>
public static string ConsoleReadLineThrowIfDiInvalid(DataInterface di)
{
var res = ConsoleReadLineWhileDiValid(di);
if (res == null)
throw new DataInterfaceClosedException("Reading the console was interrupted because the datainterface instance became invalid!");

return res;
}

private static readonly object cwlock = new object();
public static void WriteColored(string s, ConsoleColor c)
{
lock (cwlock)
{
var oldc = Console.ForegroundColor;
Console.ForegroundColor = c;
Console.WriteLine(s);
Console.ForegroundColor = oldc;
}
}
}
}
6 changes: 0 additions & 6 deletions DataInterfaceConsoleTest/DataInterfaceConsoleTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,4 @@
</ContentWithTargetPath>
</ItemGroup>

<ItemGroup>
<None Update="Resources\JsonVariants.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions DataInterfaceConsoleTest/Examples/ExampleSnippets.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using FiveDChessDataInterface.MemoryHelpers;
using FiveDChessDataInterface.Saving;
using FiveDChessDataInterface.Util;
using FiveDChessDataInterface.Variants;
using System;
using System.Linq;
using System.Reflection;
Expand Down
Loading

0 comments on commit afa9174

Please sign in to comment.