using System.Collections.ObjectModel; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using RblxTool.Models; using RblxTool.Services; namespace RblxTool.Views; public sealed partial class SniperPage : Page { private const int MaxFeedLines = 3000; private readonly ObservableCollection _feed = new(); private JobRunner? _runner; public SniperPage() { InitializeComponent(); FeedList.ItemsSource = _feed; Loaded += OnLoaded; Unloaded += (_, _) => _runner?.Stop(); } private void OnLoaded(object sender, RoutedEventArgs e) { _runner = AppState.Current.CreateRunner(DispatcherQueue); _runner.EventReceived += item => Append(FeedLine.From(item)); _runner.Completed += _ => { StartButton.IsEnabled = true; StopButton.IsEnabled = false; Append(new FeedLine(" sniper disarmed", FeedTone.Good)); }; } private void OnStart(object sender, RoutedEventArgs e) { if (AppState.Current.Active is null) { Append(new FeedLine(" select an account first", FeedTone.Bad)); return; } var request = new SnipeRequest { IntervalMs = (ulong)Math.Max(120, IntervalBox.Value), Threads = (int)Math.Max(1, ThreadsBox.Value), Buy = BuyToggle.IsOn, DryRun = DryRunToggle.IsOn, WatchReleases = ReleasesToggle.IsOn, ReleaseMaxPrice = (ulong)Math.Max(0, ReleaseMaxPriceBox.Value), }; var defaultMax = (ulong)Math.Max(0, MaxPriceBox.Value); var underRap = UnderRapBox.Value; foreach (var line in TargetsBox.Text.Split('\n', StringSplitOptions.RemoveEmptyEntries)) { var trimmed = line.Trim(); if (trimmed.Length == 0) { continue; } var parts = trimmed.Split(':', 2); if (!ulong.TryParse(parts[0].Trim(), out var assetId)) { Append(new FeedLine($" ignoring target {trimmed}", FeedTone.Warn)); continue; } var price = parts.Length > 1 && ulong.TryParse(parts[1].Trim(), out var parsed) ? parsed : defaultMax; request.Targets.Add(new SnipeTargetRequest { AssetId = assetId, MaxPrice = price, UnderRap = underRap > 0 ? underRap : null, }); } if (request.Targets.Count == 0 && !request.WatchReleases) { Append(new FeedLine(" add at least one target or enable release watching", FeedTone.Bad)); return; } _feed.Clear(); Append(new FeedLine($" armed on {request.Targets.Count} target(s)", FeedTone.Accent)); var jobId = AppState.Current.Engine.StartSnipe(request); _runner?.Start(jobId, 80); StartButton.IsEnabled = false; StopButton.IsEnabled = true; } private void OnStop(object sender, RoutedEventArgs e) => _runner?.Cancel(); private void Append(FeedLine line) { if (_feed.Count >= MaxFeedLines) { _feed.RemoveAt(0); } _feed.Add(line); FeedList.ScrollIntoView(_feed[^1]); } }