nitai/projects
rblxtool / src / ui / RblxTool / Views / SniperPage.xaml.cs
101 lines · 3.3 KB Raw
1using System.Collections.ObjectModel;
2using Microsoft.UI.Xaml;
3using Microsoft.UI.Xaml.Controls;
4using RblxTool.Models;
5using RblxTool.Services;
6
7namespace RblxTool.Views;
8
9public sealed partial class SniperPage : Page
10{
11 private const int MaxFeedLines = 3000;
12
13 private readonly ObservableCollection<FeedLine> _feed = new();
14 private JobRunner? _runner;
15
16 public SniperPage()
17 {
18 InitializeComponent();
19 FeedList.ItemsSource = _feed;
20 Loaded += OnLoaded;
21 Unloaded += (_, _) => _runner?.Stop();
22 }
23
24 private void OnLoaded(object sender, RoutedEventArgs e)
25 {
26 _runner = AppState.Current.CreateRunner(DispatcherQueue);
27 _runner.EventReceived += item => Append(FeedLine.From(item));
28 _runner.Completed += _ =>
29 {
30 StartButton.IsEnabled = true;
31 StopButton.IsEnabled = false;
32 Append(new FeedLine(" sniper disarmed", FeedTone.Good));
33 };
34 }
35
36 private void OnStart(object sender, RoutedEventArgs e)
37 {
38 if (AppState.Current.Active is null)
39 {
40 Append(new FeedLine(" select an account first", FeedTone.Bad));
41 return;
42 }
43 var request = new SnipeRequest
44 {
45 IntervalMs = (ulong)Math.Max(120, IntervalBox.Value),
46 Threads = (int)Math.Max(1, ThreadsBox.Value),
47 Buy = BuyToggle.IsOn,
48 DryRun = DryRunToggle.IsOn,
49 WatchReleases = ReleasesToggle.IsOn,
50 ReleaseMaxPrice = (ulong)Math.Max(0, ReleaseMaxPriceBox.Value),
51 };
52 var defaultMax = (ulong)Math.Max(0, MaxPriceBox.Value);
53 var underRap = UnderRapBox.Value;
54 foreach (var line in TargetsBox.Text.Split('\n', StringSplitOptions.RemoveEmptyEntries))
55 {
56 var trimmed = line.Trim();
57 if (trimmed.Length == 0)
58 {
59 continue;
60 }
61 var parts = trimmed.Split(':', 2);
62 if (!ulong.TryParse(parts[0].Trim(), out var assetId))
63 {
64 Append(new FeedLine($" ignoring target {trimmed}", FeedTone.Warn));
65 continue;
66 }
67 var price = parts.Length > 1 && ulong.TryParse(parts[1].Trim(), out var parsed)
68 ? parsed
69 : defaultMax;
70 request.Targets.Add(new SnipeTargetRequest
71 {
72 AssetId = assetId,
73 MaxPrice = price,
74 UnderRap = underRap > 0 ? underRap : null,
75 });
76 }
77 if (request.Targets.Count == 0 && !request.WatchReleases)
78 {
79 Append(new FeedLine(" add at least one target or enable release watching", FeedTone.Bad));
80 return;
81 }
82 _feed.Clear();
83 Append(new FeedLine($" armed on {request.Targets.Count} target(s)", FeedTone.Accent));
84 var jobId = AppState.Current.Engine.StartSnipe(request);
85 _runner?.Start(jobId, 80);
86 StartButton.IsEnabled = false;
87 StopButton.IsEnabled = true;
88 }
89
90 private void OnStop(object sender, RoutedEventArgs e) => _runner?.Cancel();
91
92 private void Append(FeedLine line)
93 {
94 if (_feed.Count >= MaxFeedLines)
95 {
96 _feed.RemoveAt(0);
97 }
98 _feed.Add(line);
99 FeedList.ScrollIntoView(_feed[^1]);
100 }
101}