diff --git a/Stars Assistant/CSV/PlanetLoader.cs b/Stars Assistant/CSV/PlanetLoader.cs
new file mode 100644
index 0000000..496d27a
--- /dev/null
+++ b/Stars Assistant/CSV/PlanetLoader.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using CsvHelper.Configuration;
+using CsvHelper;
+using System.Globalization;
+using Splat;
+
+namespace StarsAssistant.CSV;
+
+public class PlanetLoader
+{
+ ///
+ /// Reference to the game metadata, retrieved by DI
+ ///
+ protected Services.Game Game = Locator.Current.GetService()!;
+
+ ///
+ /// CSV Configuration to use for importing Planet files.
+ ///
+ protected CsvConfiguration CsvConfig;
+
+ ///
+ /// Construction
+ ///
+ public PlanetLoader()
+ {
+ CreateConfig();
+ }
+
+ ///
+ /// Creates the CSV configuration for planets.
+ ///
+ [MemberNotNull(nameof(CsvConfig))]
+ protected void CreateConfig()
+ {
+ CsvConfig = CsvConfiguration.FromAttributes(CultureInfo.InvariantCulture);
+ CsvConfig.Delimiter = "\t";
+ CsvConfig.Escape = '\0';
+ CsvConfig.MissingFieldFound = null;
+ }
+
+ ///
+ /// Import the planet file for the given Race.
+ ///
+ /// Import File
+ public void ImportForRace(Model.Race race)
+ {
+ using (var db = Locator.Current.GetService()!)
+ using (var reader = new StreamReader(Game.PlanetFileForRace(race), System.Text.Encoding.Latin1))
+ using (var csv = new CsvReader(reader, CsvConfig))
+ {
+ List records = csv.GetRecords().ToList();
+ var planetByPlayer = from csvp in records
+ group csvp by csvp.Owner into byOwners
+ select byOwners;
+
+ foreach (var owner in planetByPlayer)
+ {
+ Model.Race? r;
+ if (owner.Key == race.Name)
+ {
+ r = race;
+ }
+ else
+ {
+ r = db.Race.Find(owner.Key);
+ if (r == null)
+ {
+ r = new()
+ {
+ Name = owner.Key
+ };
+ db.Add(r);
+ }
+ }
+
+ foreach (Planet csvp in owner)
+ {
+ Model.Planet p = new Model.Planet{ Name = csvp.Name };
+ csvp.UpdateDbPlanet(p);
+ db.Add(p);
+ }
+ db.SaveChanges();
+ }
+ }
+ }
+}
diff --git a/Stars Assistant/Helpers/RxExtensions.cs b/Stars Assistant/Helpers/RxExtensions.cs
index 92df866..e8d4a17 100644
--- a/Stars Assistant/Helpers/RxExtensions.cs
+++ b/Stars Assistant/Helpers/RxExtensions.cs
@@ -35,4 +35,23 @@ public static class RxExtensions
return src.Buffer(zeroCrossings);
}
+ ///
+ /// Throttle a FileSystemObserver and eliminate all duplicates.
+ ///
+ ///
+ /// Curtesy of https://endjin.com/blog/2024/05/observe-file-system-changes-with-rx-dotnet
+ ///
+ /// An observer created with ObserveFileSystem
+ /// Throttling window, defaults to 1 second.
+ /// Scheduler to user for throttling, default is set by Quiescent
+ /// A throttled observer with duplicate elimination.
+ public static IObservable> ThrottleAndDistinct (
+ this IObservable watcher,
+ int inactivitySeconds = 1,
+ IScheduler? scheduler = null)
+ {
+ return watcher
+ .Quiescent(TimeSpan.FromSeconds(inactivitySeconds), scheduler)
+ .Select(changes => changes.DistinctBy(x => (x.ChangeType, x.FullPath)));
+ }
}
diff --git a/Stars Assistant/Model/Game.cs b/Stars Assistant/Model/Game.cs
deleted file mode 100644
index 0222ce9..0000000
--- a/Stars Assistant/Model/Game.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-using System.Globalization;
-
-namespace StarsAssistant.Model;
-
-public class Game (string gamePath, string baseName)
-{
- ///
- /// The base path in which all game files reside.
- ///
- public string GamePath { get; private set; } = gamePath;
-
- ///
- /// The base name without extensions of your game, inside the GamePath folder.
- /// All dependant files are resolved using this name.
- ///
- public string BaseName { get; private set; } = baseName;
-
- ///
- /// The number of the player, for example identifying the pxx planet file.
- ///
- public int PlayerId { get; private set; }
-
- ///
- /// Combine into the DatabaseName
- ///
- public string DatabaseName => Path.Combine(GamePath, $"{BaseName}.sqlite");
-
- ///
- /// Search for Planet files using this pattern, will give you all pxx files.
- ///
- public string PlanetFileSearchPattern => Path.Combine(GamePath, $"{BaseName}.p*");
-
-}
diff --git a/Stars Assistant/Model/Race.cs b/Stars Assistant/Model/Race.cs
index c6fc157..c954fec 100644
--- a/Stars Assistant/Model/Race.cs
+++ b/Stars Assistant/Model/Race.cs
@@ -22,6 +22,8 @@ public class Race
public bool PlayerRace { get; set; } = false;
+ public int? PlayerFileId { get; set; }
+
public int? ColonistsPerResource { get; set; }
public int? GrowthRatePercent { get; set; }
diff --git a/Stars Assistant/Model/StarsDatabase.cs b/Stars Assistant/Model/StarsDatabase.cs
index 58bc17d..0181803 100644
--- a/Stars Assistant/Model/StarsDatabase.cs
+++ b/Stars Assistant/Model/StarsDatabase.cs
@@ -37,7 +37,7 @@ public class StarsDatabase(string DbPath) : DbContext
///
/// The record with all game metadata, will only contain a single line at all times.
///
- public DbSet Game { get; set; }
+ // public DbSet Game { get; set; }
#endregion
}
\ No newline at end of file
diff --git a/Stars Assistant/Program.cs b/Stars Assistant/Program.cs
index 22a9089..db574d2 100644
--- a/Stars Assistant/Program.cs
+++ b/Stars Assistant/Program.cs
@@ -21,11 +21,16 @@ sealed class Program
public static void Main(string[] args)
{
ModeDetector.OverrideModeDetector(Splat.ModeDetection.Mode.Run);
-
- Locator.CurrentMutable.Register(
- () => new StarsDatabase("stars.sqlite"),
- typeof(StarsDatabase)
- );
+
+ Services.Game g = new()
+ {
+ BaseName = "GOINGTH",
+ GamePath = "/home/torben/goingth/"
+ };
+
+ Locator.CurrentMutable.RegisterConstant(g, typeof(Services.Game));
+ Locator.CurrentMutable.RegisterConstant(new Services.CSVDataLoader(), typeof(Services.CSVDataLoader));
+ Locator.CurrentMutable.Register(() => new StarsDatabase(g.DatabaseFileName), typeof(StarsDatabase));
__createTestData();
@@ -44,6 +49,7 @@ sealed class Program
public static void __createTestData ()
{
+ Services.Game game = Locator.Current.GetService()!;
using var db = Locator.Current.GetService()!;
db.Database.EnsureDeleted();
@@ -56,6 +62,7 @@ sealed class Program
{
Name = "Atlantis",
PlayerRace = true,
+ PlayerFileId = 1,
ColonistsPerResource = 1000,
GrowthRatePercent = 19,
PRT = PRT.Other,
@@ -72,40 +79,8 @@ sealed class Program
db.SaveChanges();
Race.Player = r;
- var config = CsvConfiguration.FromAttributes(CultureInfo.InvariantCulture);
- config.Delimiter = "\t";
- config.Escape = '\0';
- config.MissingFieldFound = null;
-
- using (var reader = new StreamReader("/home/torben/goingth/GOINGTH.p1", System.Text.Encoding.Latin1))
- using (var csv = new CsvReader(reader, config))
- {
- List records = csv.GetRecords().ToList();
- var planetByPlayer = from csvp in records
- group csvp by csvp.Owner into byOwners
- select byOwners;
-
- foreach (var owner in planetByPlayer)
- {
- if (owner.Key != "Atlantis")
- {
- r = new()
- {
- Name = owner.Key
- };
- db.Add(r);
- db.SaveChanges();
- }
-
- foreach (CSV.Planet csvp in owner)
- {
- Planet p = new Planet{ Name = csvp.Name };
- csvp.UpdateDbPlanet(p);
- db.Add(p);
- }
- db.SaveChanges();
- }
- }
+ var loader = new CSV.PlanetLoader();
+ loader.ImportForRace(Race.Player);
}
}
diff --git a/Stars Assistant/Services/CSVDataLoader.cs b/Stars Assistant/Services/CSVDataLoader.cs
index 14d512e..946d9ea 100644
--- a/Stars Assistant/Services/CSVDataLoader.cs
+++ b/Stars Assistant/Services/CSVDataLoader.cs
@@ -1,20 +1,41 @@
using System;
-using CsvHelper.Configuration;
-using CsvHelper;
+using System.IO;
+using System.Reactive.Linq;
using Splat;
+using StarsAssistant.Helpers;
namespace StarsAssistant.Services;
-public class CSVDataLoader
+public class CSVDataLoader
{
///
/// Reference to the game metadata, retrieved by DI
///
- protected Model.Game game;
+ protected Services.Game Game = Locator.Current.GetService()!;
- public CSVDataLoader()
+ public void CSVDataLoader()
{
- game = Locator.Current.GetService()!;
+
}
-}
+
+ public void StartPlanetCSVWatcher()
+ {
+ // TODO: which scheduler for Throttle?
+ var watcher = FsWatcher
+ .ObserveFileSystem(Game.GamePath, new string[] { Game.PlanetFileSearchPattern });
+ .ThrottleAndDistinct(2);
+
+ /*
+ watcher.Subscribe(x =>
+ {
+ Console.WriteLine($"{DateTime.Now.ToLongTimeString()} got {x.Count()} events:");
+
+ foreach (var fsEvent in x)
+ {
+ Console.WriteLine($"{DateTime.Now.ToLongTimeString()} {i++} {fsEvent.FullPath} - {fsEvent.ChangeType}");
+ }
+ });
+ */
+ }
+}
diff --git a/Stars Assistant/Services/Game.cs b/Stars Assistant/Services/Game.cs
new file mode 100644
index 0000000..019af0c
--- /dev/null
+++ b/Stars Assistant/Services/Game.cs
@@ -0,0 +1,33 @@
+namespace StarsAssistant.Services;
+
+public class Game
+{
+ ///
+ /// The base path in which all game files reside.
+ ///
+ public required string GamePath { get; set; }
+
+ ///
+ /// The base name without extensions of your game, inside the GamePath folder.
+ /// All dependant files are resolved using this name.
+ ///
+ public required string BaseName { get; set; }
+
+ ///
+ /// Combine into the DatabaseName
+ ///
+ public string DatabaseFileName => Path.Combine(GamePath, $"{BaseName}.sqlite");
+
+ ///
+ /// Search for Planet files using this pattern, will give you all pxx files.
+ ///
+ public string PlanetFileSearchPattern => $"{BaseName}.p*";
+
+ ///
+ /// Get the name of a planet file for a given race.
+ ///
+ /// The race to load.
+ /// Fully qualified file path.
+ public string PlanetFileForRace (Model.Race r) => Path.Combine(GamePath, $"{BaseName}.p{r.PlayerFileId}");
+
+}