Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented linux auto tracking #409

Merged
merged 1 commit into from
Oct 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 10 additions & 12 deletions src/Randomizer.App/Windows/OptionsWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
xmlns:vm="clr-namespace:Randomizer.App.ViewModels"
xmlns:options="clr-namespace:Randomizer.Data.Options;assembly=Randomizer.Data"
xmlns:app="clr-namespace:Randomizer.App"
xmlns:enums="clr-namespace:Randomizer.Shared.Enums;assembly=Randomizer.Shared"
x:Name="Self"
ResizeMode="NoResize"
mc:Ignorable="d"
Expand Down Expand Up @@ -177,10 +178,9 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<ComboBox ItemsSource="{Binding QuickLaunchOptions}"
SelectedIndex="{Binding LaunchButton}"
Height="23"
VerticalAlignment="Top">
<ComboBox SelectedItem="{Binding LaunchButtonOption}"
ItemsSource="{Binding Source={app:EnumBindingSource {x:Type options:LaunchButtonOptions}}}"
MinWidth="75">
</ComboBox>
</Grid>
</controls:LabeledControl>
Expand All @@ -192,10 +192,9 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<ComboBox ItemsSource="{Binding TrackerVoiceFrequencyOptions}"
SelectedIndex="{Binding VoiceFrequency}"
Height="23"
VerticalAlignment="Top">
<ComboBox SelectedItem="{Binding TrackerVoiceFrequency}"
ItemsSource="{Binding Source={app:EnumBindingSource {x:Type enums:TrackerVoiceFrequency}}}"
MinWidth="75">
</ComboBox>
</Grid>
</controls:LabeledControl>
Expand All @@ -218,10 +217,9 @@
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>

<ComboBox ItemsSource="{Binding AutoTrackerConnectorOptions}"
SelectedIndex="{Binding AutoTrackerDefaultConnector}"
Height="23"
VerticalAlignment="Top">
<ComboBox SelectedItem="{Binding AutoTrackerDefaultConnectionType}"
ItemsSource="{Binding Source={app:EnumBindingSource {x:Type options:EmulatorConnectorType}}}"
MinWidth="75">
</ComboBox>
</Grid>
</controls:LabeledControl>
Expand Down
4 changes: 2 additions & 2 deletions src/Randomizer.App/Windows/TrackerWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1048,9 +1048,9 @@ private async void LoadSavedStateMenuItem_Click(object sender, RoutedEventArgs e
private async Task SaveStateAsync()
{
// If there is a rom, save it to the database
if (GeneratedRom.IsValid(Rom))
if (GeneratedRom.IsValid(Tracker.Rom))
{
await Tracker.SaveAsync(Rom);
await Tracker.SaveAsync();
}

SavedState?.Invoke(this, EventArgs.Empty);
Expand Down
284 changes: 284 additions & 0 deletions src/Randomizer.CrossPlatform/ConsoleTrackerDisplayService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Randomizer.Data.Options;
using Randomizer.Data.WorldData;
using Randomizer.Data.WorldData.Regions;
using Randomizer.Shared;
using Randomizer.Shared.Enums;
using Randomizer.Shared.Models;
using Randomizer.SMZ3.Generation;
using Randomizer.SMZ3.Tracking;
using Randomizer.SMZ3.Tracking.AutoTracking;
using Randomizer.SMZ3.Tracking.Services;

namespace Randomizer.CrossPlatform;

public class ConsoleTrackerDisplayService
{
private readonly System.Timers.Timer _timer;
private readonly Smz3GeneratedRomLoader _romLoaderService;
private readonly TrackerOptionsAccessor _trackerOptionsAccessor;
private readonly RandomizerOptions _options;
private readonly IServiceProvider _serviceProvider;
private Tracker _tracker = null!;
private World _world = null!;
private IWorldService _worldService = null!;
private Region _lastRegion = null!;

public ConsoleTrackerDisplayService(IServiceProvider serviceProvider, Smz3GeneratedRomLoader romLoaderService, TrackerOptionsAccessor trackerOptionsAccessor, OptionsFactory optionsFactory)
{
_romLoaderService = romLoaderService;
_trackerOptionsAccessor = trackerOptionsAccessor;
_options = optionsFactory.Create();
_serviceProvider = serviceProvider;
_timer = new System.Timers.Timer(TimeSpan.FromMilliseconds(250));
_timer.Elapsed += delegate { UpdateScreen(); };
}

public async Task StartTracking(GeneratedRom rom, string romPath)
{
_trackerOptionsAccessor.Options = _options.GeneralOptions.GetTrackerOptions();
_world = _romLoaderService.LoadGeneratedRom(rom).First(x => x.IsLocalWorld);
_worldService = _serviceProvider.GetRequiredService<IWorldService>();
_tracker = _serviceProvider.GetRequiredService<Tracker>();
_tracker.Load(rom, romPath);
_tracker.TryStartTracking();
_tracker.AutoTracker?.SetConnector(_options.AutoTrackerDefaultConnector, _options.AutoTrackerQUsb2SnesIp);

if (_tracker.AutoTracker != null)
{
_tracker.AutoTracker.AutoTrackerConnected += delegate
{
UpdateScreen();
if (!_timer.Enabled)
{
_timer.Start();
}
};

_tracker.LocationCleared += delegate(object? _, LocationClearedEventArgs args)
{
_lastRegion = args.Location.Region;
};
}

while (true)
{
Console.ReadKey();
_timer.Stop();
Console.Clear();
Console.Write("Do you want to quit? (y/n) ");
var response = Console.ReadLine();

if ("y".Equals(response, StringComparison.OrdinalIgnoreCase))
{
await _tracker.SaveAsync();
break;
}

_timer.Start();
UpdateScreen();
}
}

private void UpdateScreen()
{
var columnWidth = (Console.WindowWidth-6)/2;

var topLines = GetHeaderLines(columnWidth);

var leftColumnLines = new List<string>();
leftColumnLines.AddRange(GetInventoryLines(columnWidth));
leftColumnLines.Add("");
leftColumnLines.AddRange(GetDungeonLines(columnWidth));

var rightColumnLines = new List<string>();
rightColumnLines.AddRange(GetLocationLines(columnWidth));

while (leftColumnLines.Count < Console.WindowHeight - 1)
{
leftColumnLines.Add("");
}

while (rightColumnLines.Count < Console.WindowHeight - 1)
{
rightColumnLines.Add("");
}

var sb = new StringBuilder();

foreach (var line in topLines)
{
sb.AppendLine(line);
}

for (var i = 0; i < leftColumnLines.Count && i < Console.WindowHeight - topLines.Count - 1; i++)
{
// Retrieve and pad left column, trimming if needed
var leftColumn = leftColumnLines[i];
if (leftColumn.Length > columnWidth)
{
leftColumn = leftColumn[..(columnWidth - 3)] + "... ";
}
else
{
leftColumn = leftColumn.PadRight(columnWidth);
}

// Retrieve right column, trimming if needed
var rightColumn = rightColumnLines[i];
if (rightColumn.Length > columnWidth)
{
rightColumn = rightColumn[..(columnWidth - 3)] + "...";
}

sb.AppendLine($"{leftColumn} {rightColumn}");
}

Console.Clear();
Console.Write(sb.ToString());
}

private List<string> GetHeaderLines(int columnWidth)
{
var lines = new List<string>();

var connected = $"Connected: {_tracker.AutoTracker?.IsConnected == true}";

switch (_tracker.AutoTracker?.CurrentGame)
{
case Game.Zelda:
lines.Add($"{connected} | {_tracker.AutoTracker.ZeldaState}");
break;
case Game.SM:
lines.Add($"{connected} | {_tracker.AutoTracker.MetroidState}");
break;
default:
lines.Add(connected);
break;
}

lines.Add(new string('-', columnWidth * 2 + 5));

return lines;
}

private IEnumerable<string> GetDungeonLines(int columnWidth)
{
var lines = new List<string> { "Dungeons", new('-', columnWidth) };

var dungeons = _world.Dungeons
.Select(x => GetDungeonDetails(x).PadRight(18))
.ToList();

var dungeonLine = "";
foreach (var dungeon in dungeons)
{
if (dungeonLine == "")
{
dungeonLine = dungeon;
}
else if ($"{dungeonLine}{dungeon}".Length > columnWidth)
{
lines.Add(dungeonLine);
dungeonLine = dungeon;
}
else
{
dungeonLine += dungeon;
}
}
lines.Add(dungeonLine);

return lines;
}

private IEnumerable<string> GetLocationLines(int columnWidth)
{
var lines = new List<string> { "Locations", new('-', columnWidth) };

var locations = _worldService.Locations(unclearedOnly: true, outOfLogic: false, assumeKeys: true,
sortByTopRegion: true, regionFilter: RegionFilter.None).ToList();

var regionCounts = locations
.GroupBy(x => x.Region)
.OrderByDescending(x => x.Count())
.ToDictionary(x => x.Key, x => x.Count());

var locationNames = locations
.OrderByDescending(x => x.Region == _lastRegion)
.ThenByDescending(x => regionCounts[x.Region])
.ThenBy(x => x.ToString())
.Select(x => x.ToString());
lines.AddRange(locationNames);

return lines;
}

private IEnumerable<string> GetInventoryLines(int columnWidth)
{
var lines = new List<string> { "Inventory", new('-', columnWidth) };

var itemNames = _world.AllItems.Where(x => x.State.TrackingState > 0)
.DistinctBy(x => x.Type)
.Where(x => !x.Type.IsInAnyCategory(ItemCategory.Junk, ItemCategory.Map, ItemCategory.Compass, ItemCategory.Keycard, ItemCategory.BigKey, ItemCategory.SmallKey) || x.Type is ItemType.Missile or ItemType.Super or ItemType.PowerBomb or ItemType.ETank)
.OrderBy(x => x.Type.IsInCategory(ItemCategory.Metroid))
.ThenBy(x => x.Name)
.Select(x => x.Metadata.HasStages || x.Metadata.Multiple
? $"{x.Name} ({x.State.TrackingState})"
: x.Name)
.ToList();

for (var i = 0; i < itemNames.Count; i += 2)
{
if (i < itemNames.Count - 1)
{
lines.Add(itemNames[i].PadRight(columnWidth/2) + itemNames[i+1]);
}
else
{
lines.Add(itemNames[i]);
}
}

return lines;
}

private string GetDungeonDetails(IDungeon dungeon)
{
var state = dungeon.DungeonState.Cleared ? "\u2713" : "\u274c";
var abbreviation = dungeon.DungeonMetadata.Abbreviation;

var reward = dungeon is IHasReward
? dungeon.MarkedReward switch
{
RewardType.None => "??",
RewardType.CrystalBlue => "BC",
RewardType.CrystalRed => "RC",
RewardType.PendantBlue => "BP",
RewardType.PendantGreen => "GP",
RewardType.PendantRed => "RP",
_ => null
}
: null;

var requirement = dungeon is INeedsMedallion
? dungeon.MarkedMedallion switch
{
ItemType.Quake => "Q",
ItemType.Bombos => "B",
ItemType.Ether => "E",
_ => "?"
}
: null;

var rewardRequirement = "";
if (reward != null)
{
rewardRequirement = requirement == null ? $" ({reward})" : $" ({reward}/{requirement})";
}

return $"{state} {abbreviation}{rewardRequirement}: {dungeon.DungeonState.RemainingTreasure}";
}

}
11 changes: 5 additions & 6 deletions src/Randomizer.CrossPlatform/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using Randomizer.Data.Services;
using Randomizer.SMZ3.Generation;
using Serilog;
using Serilog.Events;

namespace Randomizer.CrossPlatform;

Expand All @@ -17,13 +16,8 @@ public static class Program

public static void Main(string[] args)
{
var logLevel = LogEventLevel.Warning;
if (args.Any(x => x == "-v"))
logLevel = LogEventLevel.Information;

Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console(logLevel)
.WriteTo.File(LogPath, rollingInterval: RollingInterval.Day, retainedFileCountLimit: 30)
.CreateLogger();

Expand All @@ -46,6 +40,8 @@ public static void Main(string[] args)
return;
}

Console.WriteLine($"Using Randomizer Options file at {optionsFile.FullName}");

var result = DisplayMenu("What do you want to do?", new List<string>()
{
"Generate & Play a Rom",
Expand Down Expand Up @@ -82,6 +78,7 @@ public static void Main(string[] args)
var romPath = Path.Combine(randomizerOptions.RomOutputPath, results.Rom!.RomPath);
Console.WriteLine($"Rom generated successfully: {romPath}");
Launch(romPath, randomizerOptions);
_ = s_services.GetRequiredService<ConsoleTrackerDisplayService>().StartTracking(results.Rom, romPath);
}
else
{
Expand All @@ -103,7 +100,9 @@ public static void Main(string[] args)
var selectedRom = roms[result.Value.Item1];
var romPath = Path.Combine(randomizerOptions.RomOutputPath, selectedRom.RomPath);
Launch(romPath, randomizerOptions);
_ = s_services.GetRequiredService<ConsoleTrackerDisplayService>().StartTracking(selectedRom, romPath);
}

}
// Deletes rom(s)
else if (result.Value.Item1 == 2)
Expand Down
Loading