Split into multiple projects and depend from DasConzoleCore

This commit is contained in:
Lucidiot 2018-08-21 20:17:52 +00:00
parent ea139a3abf
commit bba811e31a
65 changed files with 389 additions and 1347 deletions

4
.gitignore vendored
View File

@ -4,4 +4,6 @@
!.vscode/extensions.json
bin/
obj/
obj/
settings.txt

4
.vscode/tasks.json vendored
View File

@ -7,9 +7,9 @@
"type": "process",
"args": [
"build",
"${workspaceFolder}/LyokoCMD.csproj"
"${workspaceFolder}/src/LyokoCMD.CommandLine/LyokoCMD.CommandLine.csproj"
],
"problemMatcher": "$msCompile"
}
]
}
}

View File

@ -1,28 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Commands
{
class AboutCommand : Command
{
public override string[] Aliases => new string[] { "about" };
public override string Description => "Afficher des informations sur l'application.";
public override string Author => "Lucidiot";
public override Version Version => new Version(1, 0);
public override string HelpText => "Usage : about [short]\nAffiche des informations sur l'application.\nL'argument 'short' donne un affichage moins verbeux.";
public override void Run(Engine e, string[] args)
{
if (args.Length > 0 && args[0].Trim().ToLower() == "short")
Console.WriteLine("{0} v{1}\nCopyright (C) {2}", e.AppName, e.AppVersion, e.AppAuthor);
else
UI.Text.WriteWrapped(String.Format("Nom de l'application : {0}\nVersion : {1}\nAuteur : {2}", e.AppName, e.AppVersion, e.AppAuthor));
}
}
}

View File

@ -1,25 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Commands
{
/// <summary>
/// Commande effaçant l'écran.
/// </summary>
class ClearCommand : Command
{
public override string[] Aliases => new string[] { "clear", "cls" };
public override string Description => "Effacer le terminal.";
public override string Author => "Lucidiot";
public override Version Version => new Version(1, 0);
public override string HelpText => "Usage : clear / cls\nEfface l'écran.";
public override void Run(Engine e, string[] args) => Console.Clear();
}
}

View File

@ -1,44 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Commands
{
public abstract class Command : LyokoCMD.UI.ITableLineOutput
{
/// <summary>
/// Alias disponibles pour la commande.
/// </summary>
public abstract string[] Aliases { get; }
/// <summary>
/// Description courte de la commande.
/// </summary>
public abstract string Description { get; }
/// <summary>
/// Nom de l'auteur de la commande.
/// </summary>
public abstract string Author { get; }
/// <summary>
/// Version de la commande.
/// </summary>
public abstract Version Version { get; }
/// <summary>
/// Texte d'aide complet de la commande.
/// </summary>
public abstract string HelpText { get; }
/// <summary>
/// Exécuter la commande.
/// </summary>
/// <param name="args">Arguments passés à la commande.</param>
public abstract void Run(Engine e, string[] args);
public string[] GetColumnNames() => new string[] { "Nom", "Description" };
public string[] GetLine() => new string[] { Aliases[0], Description };
}
}

View File

@ -1,24 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Commands
{
/// <summary>
/// Commande affichant tous ses arguments.
/// </summary>
class EchoCommand : Command
{
public override string[] Aliases => new string[] { "echo" };
public override string Description => "Afficher un message à l'écran.";
public override string Author => "Lucidiot";
public override Version Version => new Version(1, 0);
public override string HelpText => "Usage : echo [arguments]\nAffiche l'ensemble de ses arguments.";
public override void Run(Engine e, string[] args) => UI.Text.WriteWrapped(String.Join(" ", args));
}
}

View File

@ -1,22 +0,0 @@
using System;
namespace LyokoCMD.Commands
{
/// <summary>
/// Commande d'arrêt de l'application.
/// </summary>
class ExitCommand : Command
{
public override string[] Aliases => new string[] { "exit", "quit", "close" };
public override string Description => "Quitter l'application.";
public override string Author => "Lucidiot";
public override Version Version => new Version(1, 0);
public override string HelpText => "Usage : exit/quit/close\nDemande l'arrêt au moteur d'application.";
public override void Run(Engine e, string[] args) => throw new EngineStopException();
}
}

View File

@ -1,36 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace LyokoCMD.Commands
{
class HelpCommand : Command
{
public override string[] Aliases => new string[] { "help", "man" };
public override string Description => "Obtenir de l'aide sur une commande.";
public override string Author => "Lucidiot";
public override Version Version => new Version(1, 0);
public override string HelpText => "Usage : help/man [commandes]\nAffiche l'aide de chaque commande passée en paramètre.\nSans arguments, affiche la liste des commandes disponibles.";
public override void Run(Engine e, string[] args)
{
if (args.Length < 1)
Console.WriteLine(UI.Text.BuildTable(
((IEnumerable<Command>)e.Commands).Sort(
(x,y) => x.Aliases[0].CompareTo(y.Aliases[0])).ToArray()));
else foreach (string arg in args)
{
Command c;
try
{
c = e.Commands.Single(m => m.Aliases.Contains(arg.ToLower()));
UI.Text.WriteWrapped(c.Description + "\nAuteur : " + c.Author + " - Version : " + c.Version.ToString() + "\n\n" + c.HelpText);
}
catch (InvalidOperationException)
{
UI.Text.WriteWrapped("La commande '" + arg + "' est inconnue.");
}
}
}
}
}

View File

@ -1,80 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Data
{
/// <summary>
/// Décrit un gestionnaire de configuration de l'application.
/// </summary>
public interface ISettingsManager
{
/// <summary>
/// Obtenir la valeur d'un paramètre par sa clé.
/// </summary>
/// <param name="key">Clé recherchée.</param>
/// <returns>Valeur du paramètre.</returns>
string Get(string key);
/// <summary>
/// Obtenir la valeur d'un paramètre par sa clé, ou une valeur par défaut.
/// </summary>
/// <param name="key">Clé recherchée.</param>
/// <param name="ifNotExists">Valeur par défaut si le paramètre n'existe pas.</param>
/// <returns>Valeur du paramètre ou valeur par défaut.</returns>
string Get(string key, string ifNotExists);
/// <summary>
/// Obtenir la valeur d'un paramètre par sa clé.
/// </summary>
/// <param name="key">Clé recherchée.</param>
/// <returns>Valeur du paramètre.</returns>
int GetInt(string key);
/// <summary>
/// Obtenir la valeur d'un paramètre par sa clé, ou une valeur par défaut.
/// </summary>
/// <param name="key">Clé recherchée.</param>
/// <param name="ifNotExists">Valeur par défaut si le paramètre n'existe pas.</param>
/// <returns>Valeur du paramètre ou valeur par défaut.</returns>
int GetInt(string key, int ifNotExists);
/// <summary>
/// Obtenir la valeur d'un paramètre par sa clé.
/// </summary>
/// <param name="key">Clé recherchée.</param>
/// <returns>Valeur du paramètre.</returns>
bool GetBool(string key);
/// <summary>
/// Obtenir la valeur d'un paramètre par sa clé, ou une valeur par défaut.
/// </summary>
/// <param name="key">Clé recherchée.</param>
/// <param name="ifNotExists">Valeur par défaut si le paramètre n'existe pas.</param>
/// <returns>Valeur du paramètre ou valeur par défaut.</returns>
bool GetBool(string key, bool ifNotExists);
/// <summary>
/// Assigner une valeur à un paramètre.
/// </summary>
/// <param name="key">Clé du paramètre.</param>
/// <param name="value">Valeur à assigner.</param>
void Set(string key, object value);
/// <summary>
/// Déterminer si une clé existe parmi les paramètres.
/// </summary>
/// <param name="key">Clé recherchée.</param>
/// <returns>True si la clé existe.</returns>
bool HasKey(string key);
/// <summary>
/// Supprimer un paramètre par sa clé.
/// </summary>
/// <param name="key">Clé du paramètre.</param>
void Remove(string key);
/// <summary>
/// Obtenir l'ensemble des clés de paramètres.
/// </summary>
/// <returns>Tableau des clés de paramètres.</returns>
string[] GetKeys();
/// <summary>
/// Obtenir l'ensemble des paramètres sous la forme d'un dictionnaire.
/// </summary>
/// <returns>Dictionnaire de paramètres.</returns>
Dictionary<string, string> ToDictionary();
}
}

View File

@ -1,26 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Data
{
public interface ISettingsPersistenceManager
{
/// <summary>
/// Obtenir un gestionnaire de paramètres à partir des données persistantes.
/// </summary>
/// <returns>ISettingsManager capable de fournir les données persistantes.</returns>
ISettingsManager GetSettingsManager();
/// <summary>
/// Remplir les paramètres d'un gestionnaire de paramètres à partir des données persistantes.
/// </summary>
/// <param name="settingsManager">Gestionnaire de paramètres à remplir.</param>
void Fill(ISettingsManager settingsManager);
/// <summary>
/// Rendre persistants les paramètres d'un gestionnaire de paramètres.
/// </summary>
/// <param name="settingsManager">Gestionnaire de paramètres fournissant les paramètres à faire persister.</param>
void Save(ISettingsManager settingsManager);
}
}

View File

@ -1,77 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Data
{
class NonPersistentSettingsManager : ISettingsManager
{
private Dictionary<string, string> Settings;
public NonPersistentSettingsManager() {
Settings = new Dictionary<string,string>();
}
public string Get(string key)
{
string result;
Settings.TryGetValue(key, out result);
if (result == null) throw new KeyNotFoundException("Parameter '" + key + "' does not exist.");
return result;
}
public string Get(string key, string ifNotExists)
{
string result = null;
Settings.TryGetValue(key, out result);
return result == null ? ifNotExists : result;
}
public int GetInt(string key)
{
return Int32.Parse(Get(key));
}
public int GetInt(string key, int ifNotExists)
{
return Int32.Parse(Get(key, ifNotExists.ToString()));
}
public bool GetBool(string key)
{
return Boolean.Parse(Get(key));
}
public bool GetBool(string key, bool ifNotExists)
{
return Boolean.Parse(Get(key, ifNotExists.ToString()));
}
public void Set(string key, object value)
{
if (HasKey(key)) Remove(key);
Settings.Add(key, value.ToString());
}
public bool HasKey(string key)
{
return Settings.ContainsKey(key);
}
public void Remove(string key)
{
Settings.Remove(key);
}
public string[] GetKeys()
{
return Settings.Keys.ToArray<string>();
}
public Dictionary<string, string> ToDictionary()
{
return Settings;
}
}
}

View File

@ -1,46 +0,0 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Data
{
/// <summary>
/// Permet la persistance de paramètres dans un fichier au format texte.
/// </summary>
/// <remarks>Seules des chaînes de caractères sont prises en charge.</remarks>
class TextSettingsPersistenceManager : ISettingsPersistenceManager
{
protected string _FilePath;
public string FilePath { get { return _FilePath; } }
public TextSettingsPersistenceManager(string filePath)
{
this._FilePath = filePath;
}
public ISettingsManager GetSettingsManager()
{
ISettingsManager bucket = new NonPersistentSettingsManager();
Fill(bucket); // ha. ha.
return bucket;
}
public void Fill(ISettingsManager settingsManager)
{
if (!File.Exists(FilePath)) return;
foreach(string line in File.ReadAllLines(FilePath)) {
if(line.TrimStart().StartsWith("#")) continue;
string[] pair = line.Split("\t".ToCharArray(), 2);
if (pair.Length < 2) continue;
settingsManager.Set(pair[0].Trim(), pair[1].TrimStart());
}
}
public void Save(ISettingsManager settingsManager)
{
File.WriteAllLines(FilePath, settingsManager.ToDictionary().Select(pair => pair.Key + "\t" + pair.Value).ToArray());
}
}
}

229
Engine.cs
View File

@ -1,229 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using LyokoCMD.Data;
using LyokoCMD.Commands;
namespace LyokoCMD
{
public class Engine
{
protected string _AppName, _AppAuthor;
protected Version _AppVersion;
protected List<Command> _Commands = new List<Command>();
/// <summary>
/// Nom de l'application exécutée.
/// </summary>
public string AppName => _AppName;
/// <summary>
/// Numéro de version de l'application exécutée.
/// </summary>
public Version AppVersion => _AppVersion;
/// <summary>
/// Nom de l'auteur de l'application.
/// </summary>
public string AppAuthor => _AppAuthor;
/// <summary>
/// Liste des commandes de l'application.
/// </summary>
public List<Command> Commands => _Commands;
/// <summary>
/// Paramètres de l'application.
/// </summary>
public ISettingsManager Settings;
/// <summary>
/// Gestionnaire de persistance des paramètres de l'application.
/// </summary>
/// <remarks>Ce paramètre peut être nul s'il est considéré que les paramètres ne sont pas persistants.</remarks>
public ISettingsPersistenceManager SettingsPersistence;
/// <summary>
/// Moteur de jeu de l'application.
/// </summary>
public Game.Game Game;
/// <summary>
/// Instancier un nouveau moteur d'application avec des paramètres par défaut.
/// </summary>
/// <remarks>Cette surcharge ne devrait pas être utilisée pour des applications réelles ; spécifiez vous-même le nom d'application, d'auteur et la version.</remarks>
public Engine() : this("Untitled Application")
{
}
/// <summary>
/// Instancier un nouveau moteur d'application avec des paramètres par défaut.
/// </summary>
/// <param name="name">Nom de l'application.</param>
/// <remarks>Cette surcharge ne devrait pas être utilisée pour des applications réelles ; spécifiez vous-même le nom d'application, d'auteur et la version.</remarks>
public Engine(string name) : this(name, "Anonymous")
{
}
/// <summary>
/// Instancier un nouveau moteur d'application avec un numéro de version par défaut.
/// </summary>
/// <param name="name">Nom de l'application</param>
/// <param name="author">Nom de l'auteur</param>
/// <remarks>Cette surcharge ne devrait pas être utilisée pour des applications réelles ; spécifiez vous-même le nom d'application, d'auteur et la version.</remarks>
public Engine(string name, string author) : this(name, new Version(1, 0, 0, 0), author)
{
}
/// <summary>
/// Instancier un nouveau moteur d'application.
/// </summary>
/// <param name="name">Nom de l'application.</param>
/// <param name="version">Numéro de version de l'application.</param>
/// <param name="author">Nom de l'auteur de l'application.</param>
public Engine(string name, Version version, string author)
{
this._AppName = name;
this._AppVersion = version;
this._AppAuthor = author;
}
/// <summary>
/// Ajouter une commande au moteur d'application.
/// </summary>
/// <param name="c">Commande à ajouter.</param>
public void AddCommand(Command c) => Commands.Add(c);
/// <summary>
/// Ajouter des commandes au moteur d'application.
/// </summary>
/// <param name="cmds">Collection de commandes à ajouter.</param>
public void AddCommand(IEnumerable<Command> cmds) => Commands.AddRange(cmds);
/// <summary>
/// Ajouter des commandes au moteur d'application.
/// </summary>
/// <param name="cmds">Commandes à ajouter.</param>
public void AddCommand(params Command[] cmds) => Commands.AddRange(cmds);
/// <summary>
/// Réinitialiser le moteur.
/// </summary>
public void Reset() => Commands.Clear();
/// <summary>
/// Exécuter une commande avec des arguments.
/// </summary>
/// <param name="args">Commande et arguments à exécuter.</param>
public void Parse(string[] args)
{
Command c;
try
{
c = this.Commands.Single(m => m.Aliases.Contains(args[0].ToLower()));
}
catch (InvalidOperationException e)
{
if (Settings.GetBool("Debug", false)) throw e;
throw new ArgumentException("Unknown command");
}
c.Run(this, args.Skip(1).ToArray());
}
/// <summary>
/// Exécuter une commande avec des arguments et gérer les erreurs en dehors du mode débogage.
/// </summary>
/// <param name="args">Commande et arguments à exécuter.</param>
public void TryParse(string[] args)
{
try
{
this.Parse(args);
}
catch (EngineStopException e)
{
throw e;
}
catch (Exception e) when (!Settings.GetBool("Debug", false))
{
UI.Text.WriteWrapped(e.Message);
}
}
/// <summary>
/// Scinder une ligne de commande en arguments avec gestion des guillemets.
/// </summary>
/// <param name="line">Ligne de commande à scinder.</param>
/// <returns>Tableau de chaînes de caractères correspondant aux arguments.</returns>
public string[] SplitArgs(string line)
{
return Regex.Matches(line, "\"([^\"]+)\"|([^\\s\"]+)")
.Cast<Match>()
.Select(m => m.Value.Trim(new char[] { '"' }))
.ToArray();
}
/// <summary>
/// Exécuter une ligne de commande.
/// </summary>
/// <param name="line">Ligne de commande à exécuter.</param>
public void Parse(string line)
{
this.Parse(this.SplitArgs(line));
}
/// <summary>
/// Exécuter une ligne de commande et gérer les erreurs en dehors du mode débogage.
/// </summary>
/// <param name="line">Ligne de commande à exécuter.</param>
public void TryParse(string line)
{
try
{
this.Parse(this.SplitArgs(line));
}
catch (Exception e) when (!Settings.GetBool("Debug", false))
{
UI.Text.WriteWrapped(e.Message);
}
}
/// <summary>
/// Démarrer l'application.
/// </summary>
public void Run()
{
if (Settings == null) {
if (SettingsPersistence != null) Settings = SettingsPersistence.GetSettingsManager();
else throw new ArgumentNullException("Settings", "An instance of SettingsManager must be set to an Engine before running it.");
}
OnAppStartup();
System.Diagnostics.Debug.WriteLineIf(SettingsPersistence == null, "Engine started without ISettingsPersistenceManager ; settings will not be saved");
System.Diagnostics.Debug.WriteLineIf(Commands.Count < 1, "Engine started without any registered command");
Console.WriteLine();
do
{
Console.Write(Settings.Get("Prompt", "$ "));
String input = Console.ReadLine();
try
{
this.TryParse(input);
}
catch (EngineStopException)
{
break;
}
Console.WriteLine();
} while (true);
OnAppStop();
if (SettingsPersistence != null) SettingsPersistence.Save(Settings);
}
public event EventHandler AppStartup;
public event EventHandler AppStop;
protected virtual void OnAppStartup() => AppStartup?.Invoke(this, EventArgs.Empty);
protected virtual void OnAppStop() => AppStop?.Invoke(this, EventArgs.Empty);
}
}

View File

@ -1,16 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD
{
/// <summary>
/// Décrit une exception déclenchant l'arrêt du moteur d'application.
/// </summary>
class EngineStopException : Exception
{
public EngineStopException() : base("L'utilisateur a demandé l'arrêt de l'application.") { }
public EngineStopException(string message) : base(message) { }
}
}

View File

@ -1,24 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LyokoCMD.Commands;
namespace LyokoCMD.Game
{
class TaskCommand : Command
{
public override string[] Aliases => new string[] { "task" };
public override string Description => "Afficher et exécuter des programmes.";
public override string Author => "Lucidiot";
public override Version Version => new Version(1, 0);
public override string HelpText => "Usage: task [nom] <arguments>\nExécute une tâche.\nSans paramètres, affiche les tâches disponibles.";
public override void Run(Engine e, string[] args)
{
Supercomputer sc = e.Game.Objects.Get<Supercomputer>();
if (!sc.Running) throw new InvalidOperationException("Impossible de se connecter au supercalculateur.");
if (args.Length < 1) Console.WriteLine(UI.Text.BuildTable(sc.Tasks.ToArray()));
else sc.RunTask(args[0], args.Skip(1).ToArray());
}
}
}

View File

@ -1,8 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
</Project>

View File

@ -1,16 +1,64 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LyokoCMD", "LyokoCMD.csproj", "{B1AFD246-AB13-4A60-992A-1029AE899C19}"
#
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{966AF5F1-1C78-494B-9F92-41811EA71E38}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LyokoCMD.Sim", "src\LyokoCMD.Sim\LyokoCMD.Sim.csproj", "{11A6075C-7FC0-42F3-B1BB-2B421CDD9E18}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LyokoCMD.Xana", "src\LyokoCMD.Xana\LyokoCMD.Xana.csproj", "{7320636A-F45B-4CB1-8527-D5969437DB46}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LyokoCMD.CommandLine", "src\LyokoCMD.CommandLine\LyokoCMD.CommandLine.csproj", "{A72A569E-20C6-43B6-96EE-E0C5130BE127}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B1AFD246-AB13-4A60-992A-1029AE899C19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B1AFD246-AB13-4A60-992A-1029AE899C19}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B1AFD246-AB13-4A60-992A-1029AE899C19}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B1AFD246-AB13-4A60-992A-1029AE899C19}.Release|Any CPU.Build.0 = Release|Any CPU
{11A6075C-7FC0-42F3-B1BB-2B421CDD9E18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11A6075C-7FC0-42F3-B1BB-2B421CDD9E18}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11A6075C-7FC0-42F3-B1BB-2B421CDD9E18}.Debug|x64.ActiveCfg = Debug|Any CPU
{11A6075C-7FC0-42F3-B1BB-2B421CDD9E18}.Debug|x64.Build.0 = Debug|Any CPU
{11A6075C-7FC0-42F3-B1BB-2B421CDD9E18}.Debug|x86.ActiveCfg = Debug|Any CPU
{11A6075C-7FC0-42F3-B1BB-2B421CDD9E18}.Debug|x86.Build.0 = Debug|Any CPU
{11A6075C-7FC0-42F3-B1BB-2B421CDD9E18}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11A6075C-7FC0-42F3-B1BB-2B421CDD9E18}.Release|Any CPU.Build.0 = Release|Any CPU
{11A6075C-7FC0-42F3-B1BB-2B421CDD9E18}.Release|x64.ActiveCfg = Release|Any CPU
{11A6075C-7FC0-42F3-B1BB-2B421CDD9E18}.Release|x64.Build.0 = Release|Any CPU
{11A6075C-7FC0-42F3-B1BB-2B421CDD9E18}.Release|x86.ActiveCfg = Release|Any CPU
{11A6075C-7FC0-42F3-B1BB-2B421CDD9E18}.Release|x86.Build.0 = Release|Any CPU
{7320636A-F45B-4CB1-8527-D5969437DB46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7320636A-F45B-4CB1-8527-D5969437DB46}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7320636A-F45B-4CB1-8527-D5969437DB46}.Debug|x64.ActiveCfg = Debug|Any CPU
{7320636A-F45B-4CB1-8527-D5969437DB46}.Debug|x64.Build.0 = Debug|Any CPU
{7320636A-F45B-4CB1-8527-D5969437DB46}.Debug|x86.ActiveCfg = Debug|Any CPU
{7320636A-F45B-4CB1-8527-D5969437DB46}.Debug|x86.Build.0 = Debug|Any CPU
{7320636A-F45B-4CB1-8527-D5969437DB46}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7320636A-F45B-4CB1-8527-D5969437DB46}.Release|Any CPU.Build.0 = Release|Any CPU
{7320636A-F45B-4CB1-8527-D5969437DB46}.Release|x64.ActiveCfg = Release|Any CPU
{7320636A-F45B-4CB1-8527-D5969437DB46}.Release|x64.Build.0 = Release|Any CPU
{7320636A-F45B-4CB1-8527-D5969437DB46}.Release|x86.ActiveCfg = Release|Any CPU
{7320636A-F45B-4CB1-8527-D5969437DB46}.Release|x86.Build.0 = Release|Any CPU
{A72A569E-20C6-43B6-96EE-E0C5130BE127}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A72A569E-20C6-43B6-96EE-E0C5130BE127}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A72A569E-20C6-43B6-96EE-E0C5130BE127}.Debug|x64.ActiveCfg = Debug|Any CPU
{A72A569E-20C6-43B6-96EE-E0C5130BE127}.Debug|x64.Build.0 = Debug|Any CPU
{A72A569E-20C6-43B6-96EE-E0C5130BE127}.Debug|x86.ActiveCfg = Debug|Any CPU
{A72A569E-20C6-43B6-96EE-E0C5130BE127}.Debug|x86.Build.0 = Debug|Any CPU
{A72A569E-20C6-43B6-96EE-E0C5130BE127}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A72A569E-20C6-43B6-96EE-E0C5130BE127}.Release|Any CPU.Build.0 = Release|Any CPU
{A72A569E-20C6-43B6-96EE-E0C5130BE127}.Release|x64.ActiveCfg = Release|Any CPU
{A72A569E-20C6-43B6-96EE-E0C5130BE127}.Release|x64.Build.0 = Release|Any CPU
{A72A569E-20C6-43B6-96EE-E0C5130BE127}.Release|x86.ActiveCfg = Release|Any CPU
{A72A569E-20C6-43B6-96EE-E0C5130BE127}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{11A6075C-7FC0-42F3-B1BB-2B421CDD9E18} = {966AF5F1-1C78-494B-9F92-41811EA71E38}
{7320636A-F45B-4CB1-8527-D5969437DB46} = {966AF5F1-1C78-494B-9F92-41811EA71E38}
{A72A569E-20C6-43B6-96EE-E0C5130BE127} = {966AF5F1-1C78-494B-9F92-41811EA71E38}
EndGlobalSection
EndGlobal

View File

@ -1,35 +0,0 @@
using LyokoCMD.Commands;
using LyokoCMD.Data;
namespace LyokoCMD
{
class Program
{
static void Main(string[] args)
{
Engine engine = new Engine("LyokoCMD", new System.Version(0,1), "Lucidiot");
engine.SettingsPersistence = new TextSettingsPersistenceManager("settings.txt");
engine.Game = new LyokoCMD.Game.Game(engine);
engine.AddCommand(
new ExitCommand(), new EchoCommand(), new HelpCommand(),
new AboutCommand(), new ClearCommand(), new SettingsCommand());
engine.AppStartup += new System.EventHandler(engine_AppStartup);
engine.AppStop += new System.EventHandler(engine_AppStop);
engine.Run();
}
static void engine_AppStartup(object sender, System.EventArgs e)
{
Engine engine = (Engine)sender;
UI.Colors.SetString(engine.Settings.Get("ForeColor", "White"), engine.Settings.Get("BackColor", "DarkBlue"));
System.Console.Clear();
engine.TryParse("about short");
engine.TryParse("echo Pour obtenir de l'aide, tapez 'help'.");
}
static void engine_AppStop(object sender, System.EventArgs e)
{
System.Console.Clear();
}
}
}

View File

@ -1,25 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.UI
{
public static class Colors
{
public static void Set(ConsoleColor Foreground, ConsoleColor Background)
{
Console.ForegroundColor = Foreground;
Console.BackgroundColor = Background;
}
public static void Invert() => Set(Console.BackgroundColor, Console.ForegroundColor);
public static ConsoleColor Parse(string s) => (ConsoleColor)Enum.Parse(typeof(ConsoleColor), s, true);
public static void SetString(string Foreground, string Background)
{
Console.ForegroundColor = Parse(Foreground);
Console.BackgroundColor = Parse(Background);
}
}
}

View File

@ -1,22 +0,0 @@
namespace LyokoCMD.UI
{
/// <summary>
/// Décrit une classe compatible avec un affichage tabulaire.
/// </summary>
public interface ITableLineOutput
{
/// <summary>
/// Obtenir les noms de colonnes à afficher dans le tableau.
/// Devrait être constant entre chaque instance.
/// </summary>
/// <returns>Tableau de String contenant les noms de colonnes.</returns>
string[] GetColumnNames();
/// <summary>
/// Obtenir la ligne de tableau correspondant à l'instance.
/// </summary>
/// <returns>Tableau de String contenant les valeurs pour chaque colonne.</returns>
string[] GetLine();
}
}

View File

@ -1,116 +0,0 @@
using System;
using System.Text;
namespace LyokoCMD.UI
{
public static class Input
{
public static string ReadLine(string text = "")
{
int InitialLeft = text.Length, InitialTop = Console.CursorTop;
int PosInString = 0; // Position du curseur dans la ligne
bool Overwrite = false; // Mode d'insertion
ConsoleKeyInfo k;
StringBuilder sb = new StringBuilder();
Console.TreatControlCAsInput = true;
SetPosition(InitialTop, 0);
Console.Write(text);
do
{
k = Console.ReadKey(true);
// Effacement de la ligne
SetPosition(InitialTop, 0);
Console.Write(text + new String(' ', sb.Length));
if (k.Modifiers == ConsoleModifiers.Control) switch (k.Key)
{
// CTRL+C et CTRL+BREAK => Annulation
case ConsoleKey.C:
case ConsoleKey.Pause:
throw new InputCancelException();
// CTRL+BACKSPACE => Supprimer le mot avant le curseur
case ConsoleKey.Backspace:
break;
// CTRL+SUPPR => Supprimer le mot après le curseur
case ConsoleKey.Delete:
break;
// CTRL+GAUCHE => Déplacer vers la fin du mot précédent
case ConsoleKey.LeftArrow:
if (PosInString <= 0) break;
do { PosInString--; }
while (PosInString > 0 && sb.ToString(PosInString, 1) != " ");
break;
// CTRL+DROITE => Déplacer vers la fin du mot suivant
case ConsoleKey.RightArrow:
if (PosInString >= sb.Length) break;
do { PosInString++; }
while (PosInString < sb.Length && sb.ToString(PosInString, 1) != " ");
break;
}
else switch (k.Key)
{
// ENTREE => Fin de saisie
case ConsoleKey.Enter:
Console.TreatControlCAsInput = false;
try { Console.CursorSize = 25; }
catch (PlatformNotSupportedException) { }
SetPosition(InitialTop, InitialLeft + sb.Length);
Console.WriteLine();
return sb.ToString();
// BACKSPACE => Effacer avant le curseur
case ConsoleKey.Backspace:
if (PosInString > 0)
{
sb.Remove(PosInString - 1, 1);
PosInString--;
}
break;
// SUPPR => Effacer au curseur
case ConsoleKey.Delete:
if (PosInString < sb.Length) sb.Remove(PosInString, 1);
break;
// GAUCHE => Déplacer à gauche
case ConsoleKey.LeftArrow:
PosInString = Math.Max(0, PosInString - 1);
break;
// DROITE => Déplacer à droite
case ConsoleKey.RightArrow:
PosInString = Math.Min(sb.Length, PosInString + 1);
break;
// DÉBUT => Début de ligne
case ConsoleKey.Home:
PosInString = 0;
break;
// FIN => Fin de ligne
case ConsoleKey.End:
PosInString = sb.Length;
break;
// INSERT => Mode insertion ou réécriture
case ConsoleKey.Insert:
Overwrite = !Overwrite;
try { Console.CursorSize = Overwrite ? 100 : 25; }
catch (PlatformNotSupportedException) { }
break;
default:
// Caractère inconnu ou refusé
if (Char.IsWhiteSpace(k.KeyChar) && k.Key != ConsoleKey.Spacebar) break;
if (Overwrite && PosInString < sb.Length) sb.Remove(PosInString, 1);
sb.Insert(PosInString, k.KeyChar);
PosInString++;
break;
}
// Affichage de la ligne
SetPosition(InitialTop, InitialLeft);
Console.Write(sb.ToString());
SetPosition(InitialTop, InitialLeft + PosInString);
} while (true);
}
private static void SetPosition(int top, int left)
{
Console.CursorTop = top + (int)Math.Floor((double)left / Console.BufferWidth);
Console.CursorLeft = left % Console.BufferWidth;
}
}
}

View File

@ -1,11 +0,0 @@
using System;
namespace LyokoCMD.UI
{
class InputCancelException : Exception
{
public InputCancelException() : base("Touche Ctrl+C enfoncée par l'utilisateur") { }
public InputCancelException(string message) : base(message) { }
}
}

View File

@ -1,52 +0,0 @@
using System;
using LyokoCMD.Data;
namespace LyokoCMD.UI
{
public static class Menu
{
public static int Show(int StartingLine, string[] Items, ISettingsManager Settings) => Show(StartingLine, Items,
Colors.Parse(Settings.Get("MenuForeColor", Console.ForegroundColor.ToString())),
Colors.Parse(Settings.Get("MenuSelColor", ConsoleColor.Red.ToString())),
Colors.Parse(Settings.Get("MenuBackColor", Console.BackgroundColor.ToString())));
public static int Show(int StartingLine, string[] Items, System.ConsoleColor TextColor, ISettingsManager Settings) => Show(StartingLine, Items, TextColor,
Colors.Parse(Settings.Get("MenuSelColor", ConsoleColor.Red.ToString())),
Colors.Parse(Settings.Get("MenuBackColor", Console.BackgroundColor.ToString())));
public static int Show(int StartingLine, string[] Items, System.ConsoleColor TextColor, System.ConsoleColor SelColor, ISettingsManager Settings) => Show(StartingLine, Items, TextColor, SelColor,
Colors.Parse(Settings.Get("MenuBackColor", Console.BackgroundColor.ToString())));
public static int Show(int StartingLine, string[] Items, System.ConsoleColor TextColor, System.ConsoleColor SelColor, ConsoleColor BackgroundColor)
{
if (StartingLine >= Console.BufferHeight) throw new ArgumentOutOfRangeException("Specified menu starting line is too large (" + StartingLine + ").");
else if (StartingLine + Items.Length >= Console.BufferHeight) throw new ArgumentOutOfRangeException("Menu is too large (specified starting line : " + StartingLine + "; menu size : " + Items.Length + ").");
int selected = 0;
do
{
// Drawing
Console.BackgroundColor = BackgroundColor;
for (int i = 0; i <= Items.Length - 1; i += 1)
{
Console.SetCursorPosition(2, StartingLine + i);
Console.ForegroundColor = (ConsoleColor)(selected == i ? SelColor : TextColor);
Console.Write(Convert.ToString((selected == i ? "> " : " ")) + Items[i]);
}
// Interaction
switch (Console.ReadKey(true).Key)
{
case ConsoleKey.UpArrow:
selected -= 1;
if (selected < 0) selected = Items.Length - 1;
break;
case ConsoleKey.DownArrow:
selected += 1;
if (selected >= Items.Length) selected = 0;
break;
case ConsoleKey.Enter:
return selected;
}
} while (true);
}
}
}

View File

@ -1,221 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.UI
{
public static class Text {
public enum TextAlign {
Left,
Center,
Right
}
/// <summary>
/// Découpe le texte sur plusieurs lignes d'un nombre limité de caractères.
/// </summary>
public static string LineWrap(string text, int width) => LineWrap(text, width, TextAlign.Left);
/// <summary>
/// Découpe le texte sur plusieurs lignes d'un nombre limité de caractères.
/// </summary>
public static string LineWrap(string text, int width, TextAlign align = TextAlign.Left)
{
if (text.Length < width) return AlignLine(text, width, align);
StringBuilder builder = new StringBuilder();
if (text.Contains("\n")) {
foreach (string t in text.Split("\n".ToCharArray())) {
builder.Append(LineWrap(t, width, align));
}
} else {
string[] words = text.Split(' ');
string line = "";
foreach (string word in words) {
if (((line + word).Length > width)) {
builder.AppendLine(AlignLine(line, width, align));
line = "";
}
line += string.Format("{0} ", word);
}
if (line.Length > 0) builder.Append(AlignLine(line, width, align));
}
return builder.ToString().TrimEnd('\r', '\n');
}
public static string AlignLine(string text, int width, TextAlign align) {
text = text.Trim();
if (text.Length > width) throw new ArgumentOutOfRangeException("Content is over line length. Try LineWrap(text, width, align)","text");
switch(align) {
case TextAlign.Left:
return text + new string(' ', width - text.Length);
case TextAlign.Center:
text = new string(' ', (int)((width - text.Length) / 2)) + text;
return text + new string(' ', width - text.Length);
case TextAlign.Right:
return new string(' ', width - text.Length) + text;
default:
throw new ArgumentException("How did you even find another alignment ?");
}
}
public static void WriteWrapped(string text) => WriteWrapped(text, TextAlign.Left);
public static void WriteWrapped(string text, TextAlign align) => WriteWrapped(text, Console.BufferWidth, align);
public static void WriteWrapped(string text, int width, TextAlign align) => Console.Write(LineWrap(text, width, align));
public static void WriteAligned(string text, int width, TextAlign align) => Console.WriteLine(AlignLine(text, width, align));
public static void WriteWrapped(string text, int width, TextAlign align, ConsoleColor Foreground, ConsoleColor Background) {
ConsoleColor fore = Console.ForegroundColor;
ConsoleColor back = Console.BackgroundColor;
Colors.Set(Foreground, Background);
WriteWrapped(text, width, align);
Colors.Set(fore, back);
}
public static void WriteAligned(string text, int width, TextAlign align, ConsoleColor Foreground, ConsoleColor Background) {
ConsoleColor fore = Console.ForegroundColor;
ConsoleColor back = Console.BackgroundColor;
Colors.Set(Foreground, Background);
WriteAligned(text, width, align);
Colors.Set(fore, back);
}
public static void Write(object text, ConsoleColor Foreground) => Write(text, Foreground, Console.BackgroundColor);
public static void Write(object text, ConsoleColor Foreground, ConsoleColor Background) {
ConsoleColor fore = Console.ForegroundColor;
ConsoleColor back = Console.BackgroundColor;
Colors.Set(Foreground, Background);
Console.Write(text);
Colors.Set(fore, back);
}
public static void WriteLine(object text, ConsoleColor Foreground) => WriteLine(text, Foreground, Console.BackgroundColor);
public static void WriteLine(object text, ConsoleColor Foreground, ConsoleColor Background) {
ConsoleColor fore = Console.ForegroundColor;
ConsoleColor back = Console.BackgroundColor;
Colors.Set(Foreground, Background);
Console.WriteLine(text);
Colors.Set(fore, back);
}
public static void WriteAt(int x, int y, object text) {
Console.SetCursorPosition(x, y);
Console.Write(text);
}
public static void WriteLineAt(int x, int y, object text) {
Console.SetCursorPosition(x, y);
Console.WriteLine(text);
}
public static void WriteAt(int x, int y, object text, ConsoleColor Foreground, ConsoleColor Background) {
ConsoleColor fore = Console.ForegroundColor;
ConsoleColor back = Console.BackgroundColor;
Colors.Set(Foreground, Background);
WriteAt(x, y, text);
Colors.Set(fore, back);
}
public static void WriteLineAt(int x, int y, object text, ConsoleColor Foreground, ConsoleColor Background) {
ConsoleColor fore = Console.ForegroundColor;
ConsoleColor back = Console.BackgroundColor;
Colors.Set(Foreground, Background);
WriteLineAt(x, y, text);
Colors.Set(fore, back);
}
public static void ClearLine(int LineNumber) {
int CursorTop = Console.CursorTop;
int CursorLeft = Console.CursorLeft;
Console.SetCursorPosition(0, LineNumber);
Console.Write(new String(' ',Console.BufferWidth));
Console.SetCursorPosition(CursorLeft, CursorTop);
}
/// <summary>
/// Construire un affichage sous forme de tableau plus lisible pour une liste de chaînes de caractères.
/// </summary>
/// <param name="data">Liste d'objets dont la méthode ToString() sera affichée.</param>
/// <returns>Affichage tabulaire de la liste, prêt pour la taille actuelle de la console.</returns>
public static string BuildSimpleTable(object[] data) => BuildSimpleTable(data, Console.BufferWidth);
/// <summary>
/// Construire un affichage sous forme de tableau plus lisible pour une liste de chaînes de caractères.
/// </summary>
/// <param name="data">Liste d'objets dont la méthode ToString() sera affichée.</param>
/// <param name="size">Largeur maximale du tableau.</param>
/// <returns>Affichage tabulaire de la liste limité à la largeur spécifiée.</returns>
public static string BuildSimpleTable(object[] data, int size) {
if (data.Length < 1) return "";
if (data.Length < 2) return data[0].ToString();
int maxLength = -1;
foreach(object o in data) if (o.ToString().Length > maxLength) maxLength = o.ToString().Length;
maxLength++; int colCount = Math.Max(1, size / maxLength); int colSize = size / colCount;
StringBuilder sb = new StringBuilder(size);
for(int i = 0; i < data.Length; i++) {
if (i != 0 && i % colCount == 0) sb.AppendLine();
sb.Append(data[i].ToString().PadRight(colSize));
}
return sb.ToString();
}
/// <summary>
/// Affichage tabulaire de classes compatibles avec une ligne d'en-tête.
/// Le comportement de cette fonction est imprévisible si le tableau n'est pas d'un seul type de classe compatible.
/// </summary>
/// <param name="items">Tableau de classes compatibles avec l'affichage tabulaire à afficher.</param>
/// <param name="verticalSeparator">Séparateur des colonnes.</param>
/// <param name="horizontalSeparator">Séparateur de la ligne d'en-tête.</param>
/// <returns>Chaîne de caractères représentant un affichage tabulaire des classes.</returns>
public static string BuildTable(IEnumerable<ITableLineOutput> items, string verticalSeparator = " ", char horizontalSeparator = '-') {
if(items.Count() <= 0) return "";
return BuildTable(items.Select(i => i.GetLine()).Prepend(items.First().GetColumnNames()), verticalSeparator, horizontalSeparator);
}
/// <summary>
/// Affichage tabulaire d'un tableau à deux dimensions de String avec une ligne d'en-tête.
/// </summary>
/// <param name="table">Collection de tableaux de String commençant par une ligne d'en-tête.</param>
/// <returns>Chaîne de caractères représentant un affichage tabulaire des classes.</returns>
public static string BuildTable(IEnumerable<string[]> table)
{
return BuildTable(table, " ", '-');
}
/// <summary>
/// Affichage tabulaire d'un tableau à deux dimensions de String avec une ligne d'en-tête.
/// </summary>
/// <param name="table">Collection de tableaux de String commençant par une ligne d'en-tête.</param>
/// <param name="verticalSeparator">Séparateur des colonnes.</param>
/// <param name="horizontalSeparator">Séparateur de la ligne d'en-tête.</param>
/// <returns>Chaîne de caractères représentant un affichage tabulaire du tableau à deux dimensions.</returns>
public static string BuildTable(IEnumerable<string[]> table, string verticalSeparator = " ", char horizontalSeparator = '-') {
// Recherche des tailles maximales des colonnes
int[] colSize = Enumerable.Range(0, table.First().Count())
.Select(i => table.Max(arr => arr[i].Length)).ToArray();
// Adaptation des lignes avec la taille des colonnes
List<String> output = table.Select(
line => String.Join(verticalSeparator,
line.Select((field, index) => field.PadRight(colSize[index])).ToArray())).ToList();
// Ajout de tirets pour séparer le titre des colonnes
output.Insert(1, String.Join(verticalSeparator,
colSize.Select(s => new String(horizontalSeparator, s)).ToArray()));
return String.Join("\n", output.ToArray());
}
/// <summary>
/// Affichage tabulaire d'un dictionnaire.
/// </summary>
/// <param name="dict">Dictionnaire à afficher.</param>
/// <param name="verticalSeparator">Séparateur des colonnes.</param>
/// <param name="horizontalSeparator">Séparateur de la ligne d'en-tête.</param>
/// <returns>Chaîne de caractères représentant un affichage tabulaire du dictionnaire.</returns>
public static string BuildTable(IDictionary<string, string> dict, string verticalSeparator = " ", char horizontalSeparator = '-') {
if (dict.Count() <= 0) return "";
List<string[]> lines = new List<string[]>();
lines.Add(new string[] { "Clé", "Valeur" });
lines.AddRange(dict.Select(p => new string[] {p.Key, p.Value}));
return BuildTable(lines, verticalSeparator, horizontalSeparator);
}
}
}

View File

@ -1,3 +0,0 @@
Prompt $
BackColor DarkBlue
ForeColor White

View File

@ -0,0 +1,24 @@
using System;
using DasConzoleCore;
using DasConzoleCore.UI;
using DasConzoleCore.Commands;
namespace LyokoCMD.CommandLine.Commands
{
class AboutCommand : Command
{
public override string[] Names => new string[] { "about" };
public override string Description => "Afficher des informations sur l'application.";
public override string HelpText => "Usage : about [short]\nAffiche des informations sur l'application.\nL'argument 'short' donne un affichage moins verbeux.";
public override void Run(CommandApp app, string[] args)
{
if (args.Length > 0 && args[0].Trim().ToLower() == "short")
Console.WriteLine(app.TitleLine);
else
Text.WriteWrapped($"Nom de l'application : {app.Name}\nVersion : {app.VersionLine}");
}
}
}

View File

@ -0,0 +1,20 @@
using System;
using DasConzoleCore;
using DasConzoleCore.Commands;
namespace LyokoCMD.CommandLine.Commands
{
/// <summary>
/// Commande effaçant l'écran.
/// </summary>
class ClearCommand : Command
{
public override string[] Names => new string[] { "clear", "cls" };
public override string Description => "Effacer le terminal.";
public override string HelpText => "Usage : clear / cls\nEfface l'écran.";
public override void Run(CommandApp app, string[] args) => Console.Clear();
}
}

View File

@ -1,58 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Commands
{
class SettingsCommand : Command
{
public override string[] Aliases => new string[] { "settings" };
public override string Description => "Gérer les paramètres de l'application.";
public override string Author => "Lucidiot";
public override Version Version => new Version(1, 0);
public override string HelpText => @"Usage: settings list / load / save
settings get [clé]
settings set [clé] [valeur]
settings remove [clé]
list Liste l'ensemble des paramètres.
load Recharger les paramètres sauvegardés.
save Sauvegarder les paramètres.
get Obtenir la valeur d'un paramètre.
set Définir la valeur d'un paramètre.
remove Supprimer un paramètre.";
public override void Run(Engine e, string[] args)
{
if (args.Length < 1) args = new string[] { "list" };
switch (args[0].Trim().ToLower())
{
case "list":
Console.WriteLine(UI.Text.BuildTable(e.Settings.ToDictionary()));
break;
case "load":
e.Settings = e.SettingsPersistence.GetSettingsManager();
break;
case "save":
e.SettingsPersistence.Save(e.Settings);
break;
case "get":
if (args.Length < 2) throw new ArgumentException(HelpText);
Console.WriteLine(e.Settings.Get(args[1].Trim()));
break;
case "set":
if (args.Length < 3) throw new ArgumentException(HelpText);
e.Settings.Set(args[1].Trim(), args[2]);
Console.WriteLine("Paramètre '{0}' défini à '{1}'.", args[1].Trim(), args[2]);
break;
case "remove":
if (args.Length < 2) throw new ArgumentException(HelpText);
e.Settings.Remove(args[1].Trim());
Console.WriteLine("Paramètre '{0}' supprimé.", args[1].Trim());
break;
default:
throw new ArgumentException(HelpText);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using DasConzoleCore;
using DasConzoleCore.UI;
using DasConzoleCore.Commands;
namespace LyokoCMD.CommandLine.Commands
{
class SettingsCommand : Command
{
public override string[] Names => new string[] { "settings" };
public override string Description => "Gérer les paramètres de l'application.";
public override string HelpText => @"Usage: settings list / load / save
settings get [clé]
settings set [clé] [valeur]
settings remove [clé]
list Liste l'ensemble des paramètres.
load Recharger les paramètres sauvegardés.
save Sauvegarder les paramètres.
get Obtenir la valeur d'un paramètre.
set Définir la valeur d'un paramètre.
remove Supprimer un paramètre.";
public override void Run(CommandApp app, string[] args)
{
if (args.Length < 1) args = new string[] { "list" };
switch (args[0].Trim().ToLower())
{
case "list":
Console.WriteLine(Text.BuildTable(app.Settings.ToDictionary()));
break;
case "get":
if (args.Length < 2) throw new ArgumentException(HelpText);
Console.WriteLine(app.Settings.Get(args[1].Trim()));
break;
case "set":
if (args.Length < 3) throw new ArgumentException(HelpText);
app.Settings.Set(args[1].Trim(), args[2]);
Console.WriteLine("Paramètre '{0}' défini à '{1}'.", args[1].Trim(), args[2]);
break;
case "remove":
if (args.Length < 2) throw new ArgumentException(HelpText);
app.Settings.Remove(args[1].Trim());
Console.WriteLine("Paramètre '{0}' supprimé.", args[1].Trim());
break;
case "load" when app is PersistenceCommandApp:
app.Settings = ((PersistenceCommandApp) app).SettingsPersistence.GetSettingsManager();
break;
case "save" when app is PersistenceCommandApp:
((PersistenceCommandApp) app).SettingsPersistence.Save(app.Settings);
break;
default:
throw new ArgumentException(HelpText);
}
}
}
}

View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../LyokoCMD.Sim/LyokoCMD.Sim.csproj" />
<ProjectReference Include="../../../DasConzole/src/DasConzoleCore/DasConzoleCore.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using DasConzoleCore;
using DasConzoleCore.Commands;
using DasConzoleCore.Data;
namespace LyokoCMD.CommandLine {
public class PersistenceCommandApp : CommandApp {
public ISettingsPersistenceManager SettingsPersistence;
public PersistenceCommandApp(string AppName, Version AppVersion) : this(AppName, AppVersion, new TextSettingsPersistenceManager("settings.txt")) { }
public PersistenceCommandApp(string AppName, Version AppVersion, List<Command> AppCommands) : this(AppName, AppVersion, new TextSettingsPersistenceManager("settings.txt")) { }
public PersistenceCommandApp(string AppName, Version AppVersion, ISettingsPersistenceManager AppPersistence) : base(AppName, AppVersion, AppPersistence.GetSettingsManager()) {
this.SettingsPersistence = AppPersistence;
}
public PersistenceCommandApp(string AppName, Version AppVersion, ISettingsPersistenceManager AppPersistence, List<Command> AppCommands) : base(AppName, AppVersion, AppPersistence.GetSettingsManager()) {
this.SettingsPersistence = AppPersistence;
}
}
}

View File

@ -0,0 +1,49 @@
using DasConzoleCore;
using DasConzoleCore.Data;
using DasConzoleCore.Commands;
using LyokoCMD.Sim;
using LyokoCMD.Sim.Commands;
using LyokoCMD.CommandLine.Commands;
namespace LyokoCMD.CommandLine
{
class Program
{
static void Main(string[] args)
{
CommandApp app = new PersistenceCommandApp("LyokoCMD", new System.Version(0,1), new TextSettingsPersistenceManager("settings.txt"));
Game.registerObjects();
app.AddCommand(
new ExitCommand(new[] {"exit"}),
new EchoCommand(new[] {"echo"}),
new HelpCommand(new[] {"help"}),
new AboutCommand(),
new ClearCommand(),
new SettingsCommand(),
new EnergyCommand(),
new SupercomputerCommand(),
new TaskCommand(),
new ServiceCommand(),
new VirtualizationCommand()
);
app.AppStartup += new System.EventHandler(app_AppStartup);
app.AppStop += new System.EventHandler(app_AppStop);
app.Run();
}
static void app_AppStartup(object sender, System.EventArgs e)
{
CommandApp app = (CommandApp)sender;
System.Console.Clear();
app.TryParse("about short");
app.TryParse("echo Pour obtenir de l'aide, tapez 'help'.");
}
static void app_AppStop(object sender, System.EventArgs e)
{
var app = (PersistenceCommandApp)sender;
app.SettingsPersistence.Save(app.Settings);
System.Console.Clear();
}
}
}

View File

@ -2,31 +2,29 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LyokoCMD.Commands;
using DasConzoleCore;
using DasConzoleCore.UI;
using DasConzoleCore.Commands;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim.Commands
{
class EnergyCommand : Command
public class EnergyCommand : Command
{
public override string[] Aliases => new string[] { "energy" };
public override string[] Names => new string[] { "energy" };
public override string Description => "Afficher l'état énergétique du supercalculateur.";
public override string HelpText => "Usage : energy\nAffiche un état détaillé de l'énergie du supercalculateur.";
public override string Author => "Lucidiot";
public override Version Version => new Version(1, 0);
public override void Run(Engine engine, string[] args)
public override void Run(CommandApp app, string[] args)
{
Supercomputer sc = engine.Game.Objects.Get<Supercomputer>();
Supercomputer sc = Game.Objects.Get<Supercomputer>();
if (!sc.Running) throw new InvalidOperationException("Impossible de se connecter au supercalculateur.");
Console.WriteLine("Énergie totale {0} unités", sc.TotalEnergy.ToString().PadLeft(10));
Console.WriteLine("Énergie utilisée {0} unités", sc.UsedEnergy.ToString().PadLeft(10));
Console.WriteLine("Énergie disponible {0} unités", sc.AvailableEnergy.ToString().PadLeft(10));
Console.WriteLine();
Console.WriteLine(UI.Text.BuildTable(sc.EnergyProviders.ToArray()));
Console.WriteLine(Text.BuildTable(sc.EnergyProviders.ToArray()));
}
}
}

View File

@ -1,17 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LyokoCMD.Commands;
using DasConzoleCore;
using DasConzoleCore.UI;
using DasConzoleCore.Commands;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim.Commands
{
class ServiceCommand : Command
public class ServiceCommand : Command
{
public override string[] Aliases => new string[] { "service" };
public override string[] Names => new string[] { "service" };
public override string Description => "Gérer les services en arrière-plan.";
public override Version Version => new Version(1, 0);
public override string Author => "Lucidiot";
public override string HelpText => @"Usage: service [list]
service [start/stop/suspend] [nom(s)]
@ -21,15 +19,15 @@ stop Arrêter un service.
suspend Suspendre un service.
[noms] Nom(s) du ou des service's) sur le(s)quel(s) agir.";
public override void Run(Engine e, string[] args)
public override void Run(CommandApp app, string[] args)
{
Supercomputer sc = e.Game.Objects.Get<Supercomputer>();
Supercomputer sc = Game.Objects.Get<Supercomputer>();
if (!sc.Running) throw new InvalidOperationException("Impossible de se connecter au supercalculateur.");
if (args.Length < 1) args = new string[] { "list" };
switch (args[0].Trim().ToLower())
{
case "list":
Console.WriteLine(UI.Text.BuildTable(sc.Workers.ToArray()));
Console.WriteLine(Text.BuildTable(sc.Workers.ToArray()));
break;
case "start":
foreach (string name in args.Skip(1))

View File

@ -1,17 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LyokoCMD.Commands;
using DasConzoleCore;
using DasConzoleCore.UI;
using DasConzoleCore.Commands;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim.Commands
{
class SupercomputerCommand : Command
public class SupercomputerCommand : Command
{
public override string[] Aliases => new string[] { "sc" };
public override string[] Names => new string[] { "sc" };
public override string Description => "Contrôler le supercalculateur.";
public override string Author => "Lucidiot";
public override Version Version => new Version(1, 0);
public override string HelpText => @"Usage: sc [cooling] [start/stop/status]
start Démarrer le supercalculateur
stop Arrêter le supercalculateur
@ -20,10 +18,10 @@ cooling start Démarrer le système de refroidissement
cooling stop Arrêter le système de refroidissement
cooling status État du système de refroidissement";
public override void Run(Engine engine, string[] args)
public override void Run(CommandApp app, string[] args)
{
if (args.Length < 1) args = new string[] { "status" };
Supercomputer sc = engine.Game.Objects.Get<Supercomputer>();
Supercomputer sc = Game.Objects.Get<Supercomputer>();
switch (args[0].ToLower().Trim())
{
case "start":
@ -38,11 +36,14 @@ cooling status État du système de refroidissement";
break;
case "status":
Console.WriteLine("Supercalculateur " + (sc.Running ? "allumé" : "éteint"));
UI.Text.WriteLine(String.Format("Température : {0} °C", sc.Temperature), sc.Temperature > 60 ? ConsoleColor.Red : ConsoleColor.White);
Text.WriteLine(
$"Température : {sc.Temperature} °C",
Colors.Parse(sc.Temperature > 60 ? app.Settings.Get("ErrorColor", "Red") : app.Settings.Get("ForeColor", "White"))
);
if (!sc.Running) return;
Console.WriteLine("{0} service(s) en cours d'exécution, {1} au total",
sc.Workers.Count(w => w.Status == SCWorkerStatus.Running), sc.Workers.Count);
Console.WriteLine("{0} tâche(s) disponible(s)", sc.Tasks.Count);
Console.WriteLine($"{sc.Tasks.Count} tâche(s) disponible(s)");
Console.WriteLine("{0} % d'énergie utilisée", Math.Round((sc.UsedEnergy / sc.TotalEnergy) * 100));
break;
case "cooling":

View File

@ -0,0 +1,22 @@
using System;
using System.Linq;
using DasConzoleCore;
using DasConzoleCore.UI;
using DasConzoleCore.Commands;
namespace LyokoCMD.Sim.Commands
{
public class TaskCommand : Command
{
public override string[] Names => new string[] { "task" };
public override string Description => "Afficher et exécuter des programmes.";
public override string HelpText => "Usage: task [nom] <arguments>\nExécute une tâche.\nSans paramètres, affiche les tâches disponibles.";
public override void Run(CommandApp app, string[] args)
{
Supercomputer sc = Game.Objects.Get<Supercomputer>();
if (!sc.Running) throw new InvalidOperationException("Impossible de se connecter au supercalculateur.");
if (args.Length < 1) Console.WriteLine(Text.BuildTable(sc.Tasks.ToArray()));
else sc.RunTask(args[0], args.Skip(1).ToArray());
}
}
}

View File

@ -2,25 +2,25 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LyokoCMD.Commands;
using DasConzoleCore;
using DasConzoleCore.UI;
using DasConzoleCore.Commands;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim.Commands
{
class VirtualizationCommand : Command
public class VirtualizationCommand : Command
{
public override string[] Aliases => new string[] { "virt" };
public override string[] Names => new string[] { "virt" };
public override string Description => "Virtualiser une entité vers Lyoko.";
public override string Author => "Lucidiot";
public override Version Version => new Version(1, 0);
public override string HelpText => "Alias pour la tâche de virtualisation du supercalculateur.\nUsage: virt [entité] [destination]\nPour afficher les entités et destinations disponibles, tapez virt list.\nPour une virtualisation différée : virt [entité] [destination] [délai]";
public override void Run(Engine e, string[] args)
public override void Run(CommandApp app, string[] args)
{
Supercomputer sc = e.Game.Objects.Get<Supercomputer>();
Supercomputer sc = Game.Objects.Get<Supercomputer>();
if (!sc.Running) throw new InvalidOperationException("Impossible de se connecter au supercalculateur.");
if (args.Length < 1) throw new ArgumentException(HelpText);
if (args[0] == "list")
{
Console.WriteLine(UI.Text.BuildSimpleTable(e.Game.Objects.All<UserVirtualizable>()));
Console.WriteLine(Text.BuildSimpleTable(Game.Objects.All<UserVirtualizable>()));
}
else if (args.Length < 2) throw new ArgumentException(HelpText);
else sc.RunTask("Virt", args);

View File

@ -1,14 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DasConzoleCore.UI;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
/// <summary>
/// Décrit un fournisseur d'énergie du supercalculateur.
/// </summary>
abstract class EnergyProvider : LyokoCMD.UI.ITableLineOutput
abstract class EnergyProvider : ITableLineOutput
{
/// <summary>
/// Nom affiché par les systèmes de gestion d'énergie.

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
/// <summary>
/// Décrit une pile nucléaire du supercalculateur.

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
class TimeWarpEnergyProvider : EnergyProvider
{
@ -13,7 +13,7 @@ namespace LyokoCMD.Game
public override string Name => "Retours vers le passé";
public override double AvailableEnergy => _WarpCount * WarpAddedEnergy;
public void Warp(Game game)
public void Warp()
{
// TODO: Retour vers le passé
throw new NotImplementedException();

View File

@ -1,27 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DasConzoleCore;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
public class Game
public static class Game
{
public ObjectsRegistry Objects;
public static ObjectsRegistry Objects = new ObjectsRegistry();
public Engine Engine;
public Game(Engine engine)
public static void registerObjects()
{
this.Engine = engine;
this.Objects = new ObjectsRegistry();
registerObjects();
registerCommands();
}
private void registerObjects()
{
Supercomputer sc = new Supercomputer(this);
Supercomputer sc = new Supercomputer();
sc.EnergyProviders.Add(new NuclearCell());
// Lyoko
VirtualWorld lyoko = new VirtualWorldBuilder("Lyoko")
@ -50,9 +37,5 @@ namespace LyokoCMD.Game
sc.Tasks.Add(new TimeWarpTask());
Objects.Register(sc);
}
private void registerCommands() => Engine.AddCommand(
new EnergyCommand(), new SupercomputerCommand(), new TaskCommand(),
new ServiceCommand(), new VirtualizationCommand());
}
}

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
class Human : UserVirtualizable
{

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../../DasConzole/src/DasConzoleCore/DasConzoleCore.csproj" />
</ItemGroup>
</Project>

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
/// <summary>
/// Registre d'objets utilisé par un jeu en cours.

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
/// <summary>
/// Décrit un supercalculateur de Code Lyoko.
@ -13,7 +13,6 @@ namespace LyokoCMD.Game
private bool _Running = false, _Cooling = true;
public bool Running => _Running;
public bool Cooling => _Cooling;
private Game _Game;
private const double BaseTemperature = 90.0;
public List<EnergyProvider> EnergyProviders = new List<EnergyProvider>();
@ -28,8 +27,6 @@ namespace LyokoCMD.Game
public double AvailableEnergy => TotalEnergy - UsedEnergy;
public Supercomputer(Game game) => _Game = game;
public void Start() => _Running = true;
public void Stop()
@ -57,7 +54,7 @@ namespace LyokoCMD.Game
if (!Tasks.Exists(t => t.Name.ToLower() == name.ToLower())) throw new ArgumentException("Tâche introuvable");
SCTask task = GetTask(name);
if (task.RequiredEnergy > AvailableEnergy) throw new InvalidOperationException("Pas assez d'énergie pour exécuter la tâche");
task.Run(_Game, args);
task.Run(args);
}
public SCWorker GetWorker(String name)

View File

@ -1,14 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DasConzoleCore.UI;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
/// <summary>
/// Décrit une tâche exécutable au premier plan par le supercalculateur.
/// </summary>
public abstract class SCTask : LyokoCMD.UI.ITableLineOutput
public abstract class SCTask : ITableLineOutput
{
/// <summary>
/// Nom de la tâche. Sera utilisé pour demander son exécution.
@ -24,7 +21,7 @@ namespace LyokoCMD.Game
/// <summary>
/// Exécuter une tâche.
/// </summary>
public abstract void Run(Game game, string[] args);
public abstract void Run(string[] args);
public string[] GetColumnNames() => new string[] { "Nom", "Énergie" };
public string[] GetLine() => new string[] { Name, RequiredEnergy.ToString() };

View File

@ -3,12 +3,12 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
class TimeWarpTask : SCTask
{
public override string Name => "TimeWarp";
public override double RequiredEnergy => 200.0;
public override void Run(Game game, string[] args) => game.Objects.Get<TimeWarpEnergyProvider>().Warp(game);
public override void Run(string[] args) => Game.Objects.Get<TimeWarpEnergyProvider>().Warp();
}
}

View File

@ -3,13 +3,13 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
class VirtualizationTask : SCTask
{
public override string Name => "Virt";
public override double RequiredEnergy => 50.0;
public override void Run(Game game, string[] args)
public override void Run(string[] args)
{
throw new NotImplementedException();
}

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
class Coordinates : Tuple<double, double>
{

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
/// <summary>
/// Décrit un territoire de Lyoko.

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
class Tower : Virtualizable
{

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
/// <summary>
/// Spécifie qu'un objet peut être virtualisable sur Lyoko par l'opérateur du supercalculateur.

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
class Vehicle : UserVirtualizable
{
@ -24,4 +24,4 @@ namespace LyokoCMD.Game
if (!_Riders.Remove(h)) throw new ArgumentException("Cette personne n'est pas sur le véhicule.");
}
}
}
}

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
/// <summary>
/// Décrit le monde virtuel Lyoko.

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
/// <summary>
/// Classe simplifiant la création d'un monde virtuel.

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
/// <summary>
/// Spécifie qu'un objet peut être virtualisable sur Lyoko.

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
/// <summary>
/// Décrit une tour de passage.

View File

@ -1,14 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DasConzoleCore.UI;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
/// <summary>
/// Décrit un programme en arrière-plan du super-calculateur.
/// </summary>
abstract class SCWorker : LyokoCMD.UI.ITableLineOutput
abstract class SCWorker : ITableLineOutput
{
protected string _Name;
public string Name => _Name;

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace LyokoCMD.Game
namespace LyokoCMD.Sim
{
/// <summary>
/// Décrit un état de service de supercalculateur.
@ -39,4 +39,4 @@ namespace LyokoCMD.Game
/// </summary>
Waiting
}
}
}

View File

@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../LyokoCMD.Sim/LyokoCMD.Sim.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<DebugType>portable</DebugType>
<AssemblyName>LyokoCMD.Sim.Tests</AssemblyName>
<PackageId>LyokoCMD.Sim.Tests</PackageId>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<RuntimeFrameworkVersion>2.1.1</RuntimeFrameworkVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../src/LyokoCMD.Sim/LyokoCMD.Sim.csproj" />
<PackageReference Include="System.Runtime.Serialization.Primitives" Version="4.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup>
</Project>