reworked app startup
This commit is contained in:
parent
e01a50ec9c
commit
269846f457
@ -4,6 +4,7 @@ using Avalonia.Markup.Xaml;
|
|||||||
using StarsAssistant.ViewModels;
|
using StarsAssistant.ViewModels;
|
||||||
using StarsAssistant.Views;
|
using StarsAssistant.Views;
|
||||||
using Splat;
|
using Splat;
|
||||||
|
using StarsAssistant.Services;
|
||||||
|
|
||||||
namespace StarsAssistant;
|
namespace StarsAssistant;
|
||||||
|
|
||||||
@ -18,15 +19,21 @@ public partial class App : Application
|
|||||||
{
|
{
|
||||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
{
|
{
|
||||||
|
Game game = new(desktop.Args);
|
||||||
|
game.RegisterServicesForGame();
|
||||||
|
|
||||||
desktop.MainWindow = new MainWindow
|
desktop.MainWindow = new MainWindow
|
||||||
{
|
{
|
||||||
DataContext = new MainWindowViewModel(),
|
DataContext = new MainWindowViewModel(),
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
var csvloader = Locator.Current.GetService<Services.CSVDataLoader>()!;
|
|
||||||
csvloader.StartPlanetCSVWatcher();
|
|
||||||
|
|
||||||
base.OnFrameworkInitializationCompleted();
|
base.OnFrameworkInitializationCompleted();
|
||||||
|
|
||||||
|
game.StartBackgroundServices();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("This app supports only Desktop mode.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,7 +13,7 @@ public class Planet
|
|||||||
public string? Owner { get; set; }
|
public string? Owner { get; set; }
|
||||||
|
|
||||||
[Index(2)]
|
[Index(2)]
|
||||||
public string? StarbaseType { get; set; }
|
public string StarbaseType { get; set; } = String.Empty;
|
||||||
|
|
||||||
[Index(3)]
|
[Index(3)]
|
||||||
public int ReportAge { get; set; } = 0;
|
public int ReportAge { get; set; } = 0;
|
||||||
|
@ -14,7 +14,7 @@ public class Planet
|
|||||||
|
|
||||||
public string? OwnerId { get; set; }
|
public string? OwnerId { get; set; }
|
||||||
|
|
||||||
public string? StarbaseType { get; set; }
|
public string StarbaseType { get; set; } = String.Empty;
|
||||||
|
|
||||||
public int ReportAge { get; set; } = 0;
|
public int ReportAge { get; set; } = 0;
|
||||||
|
|
||||||
|
@ -25,37 +25,6 @@ sealed class Program
|
|||||||
var logger = new ConsoleLogger() { Level = LogLevel.Debug };
|
var logger = new ConsoleLogger() { Level = LogLevel.Debug };
|
||||||
Locator.CurrentMutable.RegisterConstant(logger, typeof(ILogger));
|
Locator.CurrentMutable.RegisterConstant(logger, typeof(ILogger));
|
||||||
|
|
||||||
bool newGame = false;
|
|
||||||
string dbPath = "/home/torben/Nextcloud/Documents/Stars!/Games/goingth/GOINGTH.sqlite";
|
|
||||||
Services.Game gameSvc;
|
|
||||||
|
|
||||||
using (StarsDatabase starsDB = new(dbPath))
|
|
||||||
{
|
|
||||||
if (Path.Exists(dbPath))
|
|
||||||
{
|
|
||||||
Model.Game dbGame = starsDB.Game.First();
|
|
||||||
gameSvc = new Services.Game(dbGame);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
starsDB.Database.EnsureCreated();
|
|
||||||
gameSvc = new()
|
|
||||||
{
|
|
||||||
BaseName = "GOINGTH",
|
|
||||||
GamePath = "/home/torben/Nextcloud/Documents/Stars!/Games/goingth/"
|
|
||||||
};
|
|
||||||
gameSvc.SaveToDatabase(starsDB);
|
|
||||||
newGame = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Locator.CurrentMutable.RegisterConstant(gameSvc, typeof(Services.Game));
|
|
||||||
Locator.CurrentMutable.RegisterConstant(new Services.CSVDataLoader(), typeof(Services.CSVDataLoader));
|
|
||||||
Locator.CurrentMutable.Register(() => new StarsDatabase(gameSvc.DatabaseFileName), typeof(StarsDatabase));
|
|
||||||
|
|
||||||
if (newGame)
|
|
||||||
__createTestData();
|
|
||||||
|
|
||||||
BuildAvaloniaApp()
|
BuildAvaloniaApp()
|
||||||
.StartWithClassicDesktopLifetime(args);
|
.StartWithClassicDesktopLifetime(args);
|
||||||
}
|
}
|
||||||
@ -68,93 +37,4 @@ sealed class Program
|
|||||||
.LogToTrace()
|
.LogToTrace()
|
||||||
.UseReactiveUI();
|
.UseReactiveUI();
|
||||||
|
|
||||||
|
|
||||||
public static void __createTestData ()
|
|
||||||
{
|
|
||||||
using var db = Locator.Current.GetService<StarsDatabase>()!;
|
|
||||||
|
|
||||||
// Note: This sample requires the database to be created before running.
|
|
||||||
// Console.WriteLine($"Database path: {db.DbPath}.");
|
|
||||||
|
|
||||||
Race r = new()
|
|
||||||
{
|
|
||||||
Name = "Atlantis",
|
|
||||||
PlayerRace = true,
|
|
||||||
PlayerFileId = 1,
|
|
||||||
ColonistsPerResource = 1000,
|
|
||||||
GrowthRatePercent = 19,
|
|
||||||
PRT = PRT.Other,
|
|
||||||
HasOBRM = true,
|
|
||||||
FactoryCost3 = false,
|
|
||||||
FactoryNumberPer10k = 8,
|
|
||||||
FactoryResCost = 8,
|
|
||||||
FactoryResPer10 = 15,
|
|
||||||
MineResCost = 3,
|
|
||||||
MineMineralsPer10 = 10,
|
|
||||||
MineNumberPer10k = 10
|
|
||||||
};
|
|
||||||
db.Add(r);
|
|
||||||
db.SaveChanges();
|
|
||||||
|
|
||||||
var loader = new CSV.PlanetLoader();
|
|
||||||
loader.ImportForRace(Services.Game.Player);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using CsvHelper.Configuration;
|
|
||||||
using CsvHelper;
|
|
||||||
using StarsAssistant.model;
|
|
||||||
|
|
||||||
|
|
||||||
using var db = new StarsDatabase("stars.sqlite");
|
|
||||||
|
|
||||||
db.Database.EnsureDeleted();
|
|
||||||
db.Database.EnsureCreated();
|
|
||||||
|
|
||||||
// Note: This sample requires the database to be created before running.
|
|
||||||
Console.WriteLine($"Database path: {db.DbPath}.");
|
|
||||||
|
|
||||||
var config = CsvConfiguration.FromAttributes<Planet>(CultureInfo.InvariantCulture);
|
|
||||||
config.Delimiter = "\t";
|
|
||||||
config.Escape = '\0';
|
|
||||||
config.MissingFieldFound = null;
|
|
||||||
|
|
||||||
using (var reader = new StreamReader("/home/torben/goingth/GOINGTH.p1", Encoding.Latin1))
|
|
||||||
using (var csv = new CsvReader(reader, config))
|
|
||||||
{
|
|
||||||
List<Planet> records = csv.GetRecords<Planet>().ToList();
|
|
||||||
foreach (Planet p in records)
|
|
||||||
{
|
|
||||||
db.Add(p);
|
|
||||||
}
|
|
||||||
db.SaveChanges();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Create
|
|
||||||
Console.WriteLine("Inserting a new blog");
|
|
||||||
db.Add(new Blog { Url = "http://blogs.msdn.com/adonet" });
|
|
||||||
db.SaveChanges();
|
|
||||||
|
|
||||||
// Read
|
|
||||||
Console.WriteLine("Querying for a blog");
|
|
||||||
var blog = db.Blogs
|
|
||||||
.OrderBy(b => b.BlogId)
|
|
||||||
.First();
|
|
||||||
|
|
||||||
// Update
|
|
||||||
Console.WriteLine("Updating the blog and adding a post");
|
|
||||||
blog.Url = "https://devblogs.microsoft.com/dotnet";
|
|
||||||
blog.Posts.Add(
|
|
||||||
new Post { Title = "Hello World", Content = "I wrote an app using EF Core!" });
|
|
||||||
db.SaveChanges();
|
|
||||||
|
|
||||||
// Delete
|
|
||||||
// Console.WriteLine("Delete the blog");
|
|
||||||
// db.Remove(blog);
|
|
||||||
// db.SaveChanges();
|
|
||||||
*/
|
|
@ -16,30 +16,58 @@ public partial class CSVDataLoader : IEnableLogger
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected Services.Game Game = Locator.Current.GetService<Services.Game>()!;
|
protected Services.Game Game = Locator.Current.GetService<Services.Game>()!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Regex to match fs watcher results to Stars file types.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Precompiled RegEx</returns>
|
||||||
[GeneratedRegex(@".*\.(?<type>[pf])(?<player>\d)+$")]
|
[GeneratedRegex(@".*\.(?<type>[pf])(?<player>\d)+$")]
|
||||||
private static partial Regex MyRegex();
|
private static partial Regex MyRegex();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// RegEx Instance to match Stars file types.
|
||||||
|
/// </summary>
|
||||||
protected Regex FileTypeRegEx = MyRegex();
|
protected Regex FileTypeRegEx = MyRegex();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Instance of the FSWatcher, to which we subscribe.
|
||||||
|
/// </summary>
|
||||||
|
protected IObservable<FileSystemEventArgs> Watcher;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Active subscription to FsWatcher.
|
||||||
|
/// </summary>
|
||||||
|
protected IDisposable? Subscription;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Construct the instance and prepare our FS Watcher.
|
||||||
|
/// </summary>
|
||||||
public CSVDataLoader()
|
public CSVDataLoader()
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StartPlanetCSVWatcher()
|
|
||||||
{
|
{
|
||||||
// string[] filters = { "*.p*", "*.f*", "*.map" };
|
// string[] filters = { "*.p*", "*.f*", "*.map" };
|
||||||
|
Watcher = FsWatcher
|
||||||
var watcher = FsWatcher
|
|
||||||
.ObserveFileSystem(Game.GamePath, [ Game.PlanetFileSearchPattern ])
|
.ObserveFileSystem(Game.GamePath, [ Game.PlanetFileSearchPattern ])
|
||||||
.ThrottleAndDistinct(2, RxApp.TaskpoolScheduler)
|
.ThrottleAndDistinct(2, RxApp.TaskpoolScheduler)
|
||||||
.Log(this, $"{DateTime.Now.ToLongTimeString()} FsEvent", fsEvent => $"{fsEvent.FullPath} {fsEvent.ChangeType}")
|
.Log(this, $"{DateTime.Now.ToLongTimeString()} FsEvent", fsEvent => $"{fsEvent.FullPath} {fsEvent.ChangeType}")
|
||||||
.ObserveOn(RxApp.TaskpoolScheduler);
|
.ObserveOn(RxApp.TaskpoolScheduler);
|
||||||
|
|
||||||
watcher.Subscribe(fsEvent => this.LoadPlanetFile(fsEvent.FullPath));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void LoadPlanetFile(string fileName)
|
/// <summary>
|
||||||
|
/// Start the CSV watcher, and capture our disposable, so that we can shut
|
||||||
|
/// down the watcher if needed.
|
||||||
|
/// </summary>
|
||||||
|
public void StartCSVWatcher()
|
||||||
|
{
|
||||||
|
if (Subscription != null)
|
||||||
|
throw new InvalidOperationException("CSV Watcher is active, can't start it again.");
|
||||||
|
|
||||||
|
Subscription = Watcher.Subscribe(fsEvent => this.LoadFile(fsEvent.FullPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Subscription to File Processing, called by our subscription to the watcher.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileName">The File to process</param>
|
||||||
|
protected void LoadFile(string fileName)
|
||||||
{
|
{
|
||||||
Match m = FileTypeRegEx.Match(fileName);
|
Match m = FileTypeRegEx.Match(fileName);
|
||||||
if (! m.Success)
|
if (! m.Success)
|
||||||
@ -50,9 +78,18 @@ public partial class CSVDataLoader : IEnableLogger
|
|||||||
|
|
||||||
string type = m.Groups["type"].Value;
|
string type = m.Groups["type"].Value;
|
||||||
string player = m.Groups["player"].Value;
|
string player = m.Groups["player"].Value;
|
||||||
|
|
||||||
this.Log().Debug($"Got file type {type} for player {player}");
|
this.Log().Debug($"Got file type {type} for player {player}");
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "p":
|
||||||
var loader = new CSV.PlanetLoader();
|
var loader = new CSV.PlanetLoader();
|
||||||
loader.ImportForRace(Services.Game.Player);
|
loader.ImportForRace(Services.Game.Player);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
this.Log().Warn($"Planet loader got unknown file type ${type}. Ignoring file.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System.Reflection.PortableExecutable;
|
using System.Reflection.PortableExecutable;
|
||||||
using Splat;
|
using Splat;
|
||||||
|
using StarsAssistant.Model;
|
||||||
|
|
||||||
namespace StarsAssistant.Services;
|
namespace StarsAssistant.Services;
|
||||||
|
|
||||||
@ -22,26 +23,26 @@ public class Game
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Save this record in the database. Uses the service locator to access the
|
/// Helper to construct the game from a command line.
|
||||||
/// database unless you specify an instance. This is needed during initial
|
|
||||||
/// game creation, where the services are not yet established.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="db">Optional DB instance if services are yet unavailable.</param>
|
/// <param name="args">The command line to parse.</param>
|
||||||
public void SaveToDatabase(Model.StarsDatabase? db = null)
|
public Game(string[]? args)
|
||||||
{
|
{
|
||||||
db ??= Locator.Current.GetService<Model.StarsDatabase>()!;
|
GamePath = "/home/torben/Nextcloud/Documents/Stars!/Games/goingth/";
|
||||||
|
BaseName = "GOINGTH";
|
||||||
|
string dbPath = DatabaseFileName;
|
||||||
|
|
||||||
Model.Game? dbGame = db.Game.FirstOrDefault();
|
using StarsDatabase starsDB = new(dbPath);
|
||||||
if (dbGame == null)
|
if (Path.Exists(dbPath))
|
||||||
{
|
{
|
||||||
dbGame = new Model.Game();
|
Model.Game dbGame = starsDB.Game.First();
|
||||||
db.Add(dbGame);
|
}
|
||||||
db.SaveChanges();
|
else
|
||||||
|
{
|
||||||
|
starsDB.Database.EnsureCreated();
|
||||||
|
SaveToDatabase(starsDB);
|
||||||
|
__doCreateTestData = true;
|
||||||
}
|
}
|
||||||
dbGame.GamePath = GamePath;
|
|
||||||
dbGame.BaseName = BaseName;
|
|
||||||
db.Update(dbGame);
|
|
||||||
db.SaveChanges();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -68,9 +69,9 @@ public class Game
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Internal helper to lazily load the current player from the DB.
|
/// Internal helper to lazily load the current player from the DB.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly static Lazy<Model.Race>LazyPlayer = new ( () => {
|
private readonly static Lazy<Race>LazyPlayer = new ( () => {
|
||||||
using var db = Locator.Current.GetService<Model.StarsDatabase>()!;
|
using var db = Locator.Current.GetService<StarsDatabase>()!;
|
||||||
Model.Race result = db.Race
|
Race result = db.Race
|
||||||
.Where(r => r.PlayerRace == true)
|
.Where(r => r.PlayerRace == true)
|
||||||
.First();
|
.First();
|
||||||
return result;
|
return result;
|
||||||
@ -79,13 +80,99 @@ public class Game
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the Race object for the current player, lazy initialized.
|
/// Get the Race object for the current player, lazy initialized.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Model.Race Player => LazyPlayer.Value;
|
public static Race Player => LazyPlayer.Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the name of a planet file for a given race.
|
/// Get the name of a planet file for a given race.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="r">The race to load.</param>
|
/// <param name="r">The race to load.</param>
|
||||||
/// <returns>Fully qualified file path.</returns>
|
/// <returns>Fully qualified file path.</returns>
|
||||||
public string PlanetFileForRace (Model.Race r) => Path.Combine(GamePath, $"{BaseName}.p{r.PlayerFileId}");
|
public string PlanetFileForRace (Race r) => Path.Combine(GamePath, $"{BaseName}.p{r.PlayerFileId}");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save this record in the database. Uses the service locator to access the
|
||||||
|
/// database unless you specify an instance. This is needed during initial
|
||||||
|
/// game creation, where the services are not yet established.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="db">Optional DB instance if services are yet unavailable.</param>
|
||||||
|
public void SaveToDatabase(StarsDatabase? db = null)
|
||||||
|
{
|
||||||
|
db ??= Locator.Current.GetService<StarsDatabase>()!;
|
||||||
|
|
||||||
|
Model.Game? dbGame = db.Game.FirstOrDefault();
|
||||||
|
if (dbGame == null)
|
||||||
|
{
|
||||||
|
dbGame = new Model.Game();
|
||||||
|
db.Add(dbGame);
|
||||||
|
db.SaveChanges();
|
||||||
|
}
|
||||||
|
dbGame.GamePath = GamePath;
|
||||||
|
dbGame.BaseName = BaseName;
|
||||||
|
db.Update(dbGame);
|
||||||
|
db.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers all services depending on this Game, in the order required.
|
||||||
|
/// This is called once the framework is initialized but before the main
|
||||||
|
/// window is active and has its models.
|
||||||
|
/// </summary>
|
||||||
|
public void RegisterServicesForGame()
|
||||||
|
{
|
||||||
|
Locator.CurrentMutable.RegisterConstant(this, typeof(Services.Game));
|
||||||
|
Locator.CurrentMutable.RegisterConstant(new CSVDataLoader(), typeof(CSVDataLoader));
|
||||||
|
Locator.CurrentMutable.Register(() => new StarsDatabase(DatabaseFileName), typeof(StarsDatabase));
|
||||||
|
|
||||||
|
// TESTING HELPER
|
||||||
|
if (__doCreateTestData)
|
||||||
|
__createTestData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts all required background servies, so that we are in normal UI
|
||||||
|
/// operation mode. This is called after the UI is up and running.
|
||||||
|
/// </summary>
|
||||||
|
public void StartBackgroundServices()
|
||||||
|
{
|
||||||
|
CSVDataLoader csvloader = Locator.Current.GetService<Services.CSVDataLoader>()!;
|
||||||
|
csvloader.StartCSVWatcher();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private bool __doCreateTestData = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// TESTING HELPER
|
||||||
|
/// </summary>
|
||||||
|
private void __createTestData ()
|
||||||
|
{
|
||||||
|
using var db = Locator.Current.GetService<StarsDatabase>()!;
|
||||||
|
|
||||||
|
// Note: This sample requires the database to be created before running.
|
||||||
|
// Console.WriteLine($"Database path: {db.DbPath}.");
|
||||||
|
|
||||||
|
Race r = new()
|
||||||
|
{
|
||||||
|
Name = "Atlantis",
|
||||||
|
PlayerRace = true,
|
||||||
|
PlayerFileId = 1,
|
||||||
|
ColonistsPerResource = 1000,
|
||||||
|
GrowthRatePercent = 19,
|
||||||
|
PRT = PRT.Other,
|
||||||
|
HasOBRM = true,
|
||||||
|
FactoryCost3 = false,
|
||||||
|
FactoryNumberPer10k = 8,
|
||||||
|
FactoryResCost = 8,
|
||||||
|
FactoryResPer10 = 15,
|
||||||
|
MineResCost = 3,
|
||||||
|
MineMineralsPer10 = 10,
|
||||||
|
MineNumberPer10k = 10
|
||||||
|
};
|
||||||
|
db.Add(r);
|
||||||
|
db.SaveChanges();
|
||||||
|
|
||||||
|
var loader = new CSV.PlanetLoader();
|
||||||
|
loader.ImportForRace(Services.Game.Player);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user