From 621e8e89429ed096a62fc363a8ea30da08b99c7f Mon Sep 17 00:00:00 2001
From: NoobNotFound <82730163+NoobNotFound@users.noreply.github.com>
Date: Tue, 23 Sep 2025 00:54:24 +0530
Subject: [PATCH 1/2] Refactor, feature updates, and platform alignment
Updated package versions and target platform to align with the latest dependencies. Refactored `MainWindow` to `_MainWindow` for private naming consistency. Overhauled the settings system to use file-based storage. Introduced `PrankMode` feature with event handling. Refactored `RemoteListener` for better task management and added UDP/TCP support. Replaced `System.Text.Json` with `Newtonsoft.Json`. Improved event handling, UI updates, and game process management. Added new publish profiles for `win-arm64`, `win-x86`, and `win-x64`. Miscellaneous code cleanup and formatting updates.
---
Directory.Packages.props | 2 +-
.../Emerald.App.Package/Package.WinUI.wapproj | 5 +-
.../Emerald.App.Package/Package.appxmanifest | 2 +-
Emerald.App/Emerald.App/App.xaml.cs | 40 ++-
.../Emerald.App/Converters/Converters.cs | 2 +-
Emerald.App/Emerald.App/Emerald.App.csproj | 2 +-
Emerald.App/Emerald.App/Helpers/Extensions.cs | 4 +-
Emerald.App/Emerald.App/Helpers/MSLogin.cs | 2 +-
.../Emerald.App/Helpers/Settings/JSON.cs | 7 +-
.../Helpers/Settings/SettingsSystem.cs | 34 +-
Emerald.App/Emerald.App/MainWindow.xaml.cs | 95 +++++-
Emerald.App/Emerald.App/Models/Task.cs | 2 +-
.../PublishProfiles/win-arm64.pubxml | 18 ++
.../Properties/PublishProfiles/win-x86.pubxml | 23 ++
.../PublishProfiles/win10-x64.pubxml | 14 +
Emerald.App/Emerald.App/RemoteListener.cs | 306 ++++++++++++++++++
.../Emerald.App/Views/Home/HomePage.xaml.cs | 10 +-
.../Emerald.App/Views/LogsPage.xaml.cs | 2 +-
.../Views/Settings/GeneralPage.xaml.cs | 2 +-
Emerald/Emerald.csproj | 2 +-
20 files changed, 532 insertions(+), 42 deletions(-)
create mode 100644 Emerald.App/Emerald.App/Properties/PublishProfiles/win-arm64.pubxml
create mode 100644 Emerald.App/Emerald.App/Properties/PublishProfiles/win-x86.pubxml
create mode 100644 Emerald.App/Emerald.App/Properties/PublishProfiles/win10-x64.pubxml
create mode 100644 Emerald.App/Emerald.App/RemoteListener.cs
diff --git a/Directory.Packages.props b/Directory.Packages.props
index d6115ad9..35248a28 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -15,7 +15,7 @@
-
+
diff --git a/Emerald.App/Emerald.App.Package/Package.WinUI.wapproj b/Emerald.App/Emerald.App.Package/Package.WinUI.wapproj
index 9920f1a8..0b4bec96 100644
--- a/Emerald.App/Emerald.App.Package/Package.WinUI.wapproj
+++ b/Emerald.App/Emerald.App.Package/Package.WinUI.wapproj
@@ -48,7 +48,7 @@
59a6f3d1-b6e1-48ad-80d3-215df2791ac1
- 10.0.22000.0
+ 10.0.26100.0
10.0.17763.0
net7.0-windows$(TargetPlatformVersion);$(AssetTargetFallback)
en-US
@@ -58,12 +58,11 @@
SHA256
False
True
- x86|x64|arm64
+ x64
D:\Projects\Emerald\Emerald.App\Emerald.App.Package\AppPackages\
0
False
Package.WinUI_TemporaryKey.pfx
- C36FEB2A573AF2E3DD0737A454F36CB17B8C121C
Always
diff --git a/Emerald.App/Emerald.App.Package/Package.appxmanifest b/Emerald.App/Emerald.App.Package/Package.appxmanifest
index 0297724c..75436c02 100644
--- a/Emerald.App/Emerald.App.Package/Package.appxmanifest
+++ b/Emerald.App/Emerald.App.Package/Package.appxmanifest
@@ -11,7 +11,7 @@
+ Version="0.7.12.0" />
Emerald
diff --git a/Emerald.App/Emerald.App/App.xaml.cs b/Emerald.App/Emerald.App/App.xaml.cs
index 6e926e8f..59f03a8b 100644
--- a/Emerald.App/Emerald.App/App.xaml.cs
+++ b/Emerald.App/Emerald.App/App.xaml.cs
@@ -1,11 +1,14 @@
+using Emerald.WinUI.Helpers;
using Emerald.WinUI.Helpers.AppInstancing;
using Emerald.WinUI.Helpers.Updater;
using Microsoft.UI.Xaml;
using System;
using System.Diagnostics;
+using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Windows.Management.Deployment;
using Windows.Storage;
+using SS = Emerald.WinUI.Helpers.Settings.SettingsSystem;
namespace Emerald.WinUI;
@@ -14,12 +17,13 @@ public partial class App : Application
private readonly SingleInstanceDesktopApp _singleInstanceApp;
public Core.Emerald Launcher { get; private set; } = new();
public Updater Updater { get; private set; } = new();
- public ElementTheme ActualTheme => (MainWindow.Content as FrameworkElement).ActualTheme;
+ public ElementTheme ActualTheme => (_MainWindow.Content as FrameworkElement).ActualTheme;
public App()
{
InitializeComponent();
_singleInstanceApp = new SingleInstanceDesktopApp("Riverside.Emerald");
_singleInstanceApp.Launched += OnSingleInstanceLaunched;
+
}
///
@@ -28,8 +32,8 @@ public App()
public void LoadFromBackupSettings(string settings)
{
saveData = false;
- ApplicationData.Current.RoamingSettings.Values["Settings"] = settings;
- MainWindow.Close();
+ SS.SaveData(settings);
+ _MainWindow.Close();
var p = new Process()
{
StartInfo = new()
@@ -54,11 +58,20 @@ protected override void OnLaunched(LaunchActivatedEventArgs args)
_singleInstanceApp.Launch(args.Arguments);
}
bool saveData = true;
- private void InitializeMainWindow()
+ private async void InitializeMainWindow()
{
- MainWindow = new MainWindow();
- MainWindow.Activate();
- MainWindow.Closed += (_, _) => { if (saveData) Helpers.Settings.SettingsSystem.SaveData(); };
+ await SS.LoadData();
+ _MainWindow = new MainWindow();
+ _MainWindow.Activate();
+ _MainWindow.Closed += (_, _) =>
+ {
+ if (saveData)
+ Helpers.Settings.SettingsSystem.SaveData();
+ var proc = MainWindow.HomePage.GameProcess;
+ if (proc == null || proc.HasExited)
+ return;
+ proc.Kill();
+ };
}
private void OnSingleInstanceLaunched(object? sender, SingleInstanceLaunchEventArgs e)
{
@@ -67,6 +80,16 @@ private void OnSingleInstanceLaunched(object? sender, SingleInstanceLaunchEventA
System.Net.ServicePointManager.DefaultConnectionLimit = 256;
_ = Updater.Initialize();
InitializeMainWindow();
+ Task.Delay(500).ContinueWith(_ =>
+ {
+ if (!string.IsNullOrEmpty(e.Arguments))
+ {
+ _MainWindow.DispatcherQueue.TryEnqueue(() =>
+ {
+ MessageBox.Show($"Application started with arguments: {e.Arguments}");
+ });
+ }
+ });
}
else
{
@@ -74,5 +97,6 @@ private void OnSingleInstanceLaunched(object? sender, SingleInstanceLaunchEventA
}
}
- public Window MainWindow { get; private set; }
+
+ public Window _MainWindow { get; private set; }
}
diff --git a/Emerald.App/Emerald.App/Converters/Converters.cs b/Emerald.App/Emerald.App/Converters/Converters.cs
index 83da303a..ad2d16e1 100644
--- a/Emerald.App/Emerald.App/Converters/Converters.cs
+++ b/Emerald.App/Emerald.App/Converters/Converters.cs
@@ -68,7 +68,7 @@ public class InfobarServertyToBackground : IValueConverter
public object Convert(object value, Type targetType, object parameter, string language)
{
- var theme = (App.Current.MainWindow.Content as FrameworkElement).ActualTheme;
+ var theme = (App.Current._MainWindow.Content as FrameworkElement).ActualTheme;
bool isdark = theme == ElementTheme.Dark;
return (Microsoft.UI.Xaml.Controls.InfoBarSeverity)value switch
diff --git a/Emerald.App/Emerald.App/Emerald.App.csproj b/Emerald.App/Emerald.App/Emerald.App.csproj
index bfa64968..c5ff2042 100644
--- a/Emerald.App/Emerald.App/Emerald.App.csproj
+++ b/Emerald.App/Emerald.App/Emerald.App.csproj
@@ -2,7 +2,7 @@
WinExe
- net8.0-windows10.0.22000.0
+ net8.0-windows10.0.26100.0
10.0.17763.0
Emerald.WinUI
app.manifest
diff --git a/Emerald.App/Emerald.App/Helpers/Extensions.cs b/Emerald.App/Emerald.App/Helpers/Extensions.cs
index 5f0a94d0..a0498d63 100644
--- a/Emerald.App/Emerald.App/Helpers/Extensions.cs
+++ b/Emerald.App/Emerald.App/Helpers/Extensions.cs
@@ -19,7 +19,7 @@ public static class Extensions
public static void ShowAt(this TeachingTip tip, FrameworkElement element, TeachingTipPlacementMode placement = TeachingTipPlacementMode.Auto, bool closeWhenClick = true, bool addToMainGrid = true)
{
if (addToMainGrid)
- (App.Current.MainWindow.Content as Grid).Children.Add(tip);
+ (App.Current._MainWindow.Content as Grid).Children.Add(tip);
tip.Target = element;
tip.PreferredPlacement = placement;
@@ -60,7 +60,7 @@ public static ContentDialog ToContentDialog(this UIElement content, string title
{
ContentDialog dialog = new()
{
- XamlRoot = App.Current.MainWindow.Content.XamlRoot,
+ XamlRoot = App.Current._MainWindow.Content.XamlRoot,
Style = Application.Current.Resources["DefaultContentDialogStyle"] as Style,
Title = title,
CloseButtonText = closebtnText,
diff --git a/Emerald.App/Emerald.App/Helpers/MSLogin.cs b/Emerald.App/Emerald.App/Helpers/MSLogin.cs
index b187e8f2..362c8f52 100644
--- a/Emerald.App/Emerald.App/Helpers/MSLogin.cs
+++ b/Emerald.App/Emerald.App/Helpers/MSLogin.cs
@@ -61,7 +61,7 @@ public async void Initialize(string cId, OAuthMode mode)
}
private void InitializeInformDialog(DeviceCodeResult result)
{
- App.Current.MainWindow.DispatcherQueue.TryEnqueue(() =>
+ App.Current._MainWindow.DispatcherQueue.TryEnqueue(() =>
{
var m = new StackPanel() { VerticalAlignment = VerticalAlignment.Stretch };
var lt = "GotoLinkAndEnterDeviceCode".Localize();
diff --git a/Emerald.App/Emerald.App/Helpers/Settings/JSON.cs b/Emerald.App/Emerald.App/Helpers/Settings/JSON.cs
index 168a0c28..314e244e 100644
--- a/Emerald.App/Emerald.App/Helpers/Settings/JSON.cs
+++ b/Emerald.App/Emerald.App/Helpers/Settings/JSON.cs
@@ -14,7 +14,7 @@ namespace Emerald.WinUI.Helpers.Settings.JSON;
public class JSON : Models.Model
{
public string Serialize()
- => JsonSerializer.Serialize(this, new JsonSerializerOptions { WriteIndented = true });
+ => Newtonsoft.Json.JsonConvert.SerializeObject(this, Newtonsoft.Json.Formatting.Indented);
}
public class SettingsBackup : JSON
@@ -70,8 +70,8 @@ public partial class Minecraft : JSON
{
public Minecraft()
{
- JVM.PropertyChanged += (_, _)
- => InvokePropertyChanged();
+ JVM.PropertyChanged += (_, e)
+ => InvokePropertyChanged(e.PropertyName);
PropertyChanged += (_, e) =>
{
if (e.PropertyName != null)
@@ -166,6 +166,7 @@ public class App : JSON
public bool AutoClose { get; set; }
public bool HideOnLaunch { get; set; }
public bool WindowsHello { get; set; }
+ public bool PrankMode { get; set; }
}
public class Updates : JSON
{
diff --git a/Emerald.App/Emerald.App/Helpers/Settings/SettingsSystem.cs b/Emerald.App/Emerald.App/Helpers/Settings/SettingsSystem.cs
index d47d9054..28d86de6 100644
--- a/Emerald.App/Emerald.App/Helpers/Settings/SettingsSystem.cs
+++ b/Emerald.App/Emerald.App/Helpers/Settings/SettingsSystem.cs
@@ -14,25 +14,33 @@ public static class SettingsSystem
public static Account[] Accounts { get; set; }
public static event EventHandler? APINoMatch;
- public static T GetSerializedFromSettings(string key, T def)
+ public static async Task GetSerializedFromSettings(string key, T def)
{
string json;
try
{
- json = ApplicationData.Current.RoamingSettings.Values[key] as string;
+ if(key == "Settings")
+ json = await FileIO.ReadTextAsync(ApplicationData.Current.LocalFolder.GetFileAsync("settings.json").AsTask().Result);
+ else
+ json = ApplicationData.Current.RoamingSettings.Values[key] as string;
+
return JsonSerializer.Deserialize(json);
}
catch
{
json = JsonSerializer.Serialize(def);
- ApplicationData.Current.RoamingSettings.Values[key] = json;
+
+ if(key != "Settings")
+ ApplicationData.Current.RoamingSettings.Values[key] = json;
+
return def;
}
}
- public static void LoadData()
+ public static async Task LoadData()
{
- Settings = GetSerializedFromSettings("Settings", JSON.Settings.CreateNew());
- Accounts = GetSerializedFromSettings("Accounts", Array.Empty());
+
+ Settings = await GetSerializedFromSettings("Settings", JSON.Settings.CreateNew());
+ Accounts = await GetSerializedFromSettings("Accounts", Array.Empty());
if (Settings.APIVersion != DirectResoucres.SettingsAPIVersion)
{
@@ -101,11 +109,19 @@ public static async Task> GetBackups()
return l.AllBackups == null ? new List() : l.AllBackups.ToList();
}
- public static void SaveData()
+ public static void SaveData(string _settings = null)
{
Settings.LastSaved = DateTime.Now;
- ApplicationData.Current.RoamingSettings.Values["Settings"] = Settings.Serialize();
+ ApplicationData.Current.LocalFolder.CreateFileAsync("settings.json", CreationCollisionOption.OpenIfExists)
+ .AsTask()
+ .Wait();
+
+ FileIO.WriteTextAsync(ApplicationData.Current.LocalFolder.GetFileAsync("settings.json").AsTask().Result,
+ _settings ?? JsonSerializer.Serialize(Settings))
+ .AsTask()
+ .Wait();
+
ApplicationData.Current.RoamingSettings.Values["Accounts"] = JsonSerializer.Serialize(Accounts);
}
}
-}
\ No newline at end of file
+}
diff --git a/Emerald.App/Emerald.App/MainWindow.xaml.cs b/Emerald.App/Emerald.App/MainWindow.xaml.cs
index 080ee662..089921ea 100644
--- a/Emerald.App/Emerald.App/MainWindow.xaml.cs
+++ b/Emerald.App/Emerald.App/MainWindow.xaml.cs
@@ -15,6 +15,8 @@
using Microsoft.UI.Xaml.Media;
using System;
using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
using Windows.UI;
using SS = Emerald.WinUI.Helpers.Settings.SettingsSystem;
using Window = Microsoft.UI.Xaml.Window;
@@ -53,7 +55,7 @@ public MainWindow()
MainNavigationView.ItemInvoked += MainNavigationView_ItemInvoked;
MainFrame = frame;
SS.APINoMatch += (_, e) => BackupState = (true, e);
- SS.LoadData();
+
(Content as FrameworkElement).Loaded += Initialize;
}
@@ -209,8 +211,95 @@ void TintColor()
if (SS.Settings.App.Updates.CheckAtStartup)
App.Current.Updater.CheckForUpdates(true);
+
+ //Prank
+ _listenPrank = new RemoteListener();
+
+ _listenPrank.ChatReceived += _listenPrank_MessageReceived;
+ _listenPrank.PrankReceived += _listenPrank_PrankReceived;
+
+ await SetPrankModeAsync();
+
+ this.Content.KeyDown += Prank_Content_KeyDown;
+
+ _PrankresetTimer = new System.Timers.Timer(1500); // 1.5 sec timeout
+ _PrankresetTimer.Elapsed += (s, e) => _typedBuffer.Clear();
+ _PrankresetTimer.AutoReset = false;
(Content as FrameworkElement).Loaded -= Initialize;
}
+
+ private StringBuilder _typedBuffer = new();
+ private System.Timers.Timer _PrankresetTimer;
+ private async void Prank_Content_KeyDown(object sender, Microsoft.UI.Xaml.Input.KeyRoutedEventArgs e)
+ {
+ if (e.Key.ToString().Length > 1)
+ return;
+
+ char c = char.ToUpperInvariant((char)e.Key);
+
+ if (char.IsLetter(c)) // Only capture letters
+ {
+ _typedBuffer.Append(c);
+
+ // Restart reset timer every key press
+ _PrankresetTimer.Stop();
+ _PrankresetTimer.Start();
+
+ // Only keep last 5 chars
+ if (_typedBuffer.Length > 5)
+ _typedBuffer.Remove(0, _typedBuffer.Length - 5);
+
+ if (_typedBuffer.ToString() == "PRANK")
+ {
+ var id = TasksHelper.AddTask("Prank Mode", SS.Settings.App.PrankMode.ToString());
+ SS.Settings.App.PrankMode = !SS.Settings.App.PrankMode;
+ await SetPrankModeAsync();
+ _typedBuffer.Clear();
+ TasksHelper.CompleteTask(id,true, SS.Settings.App.PrankMode.ToString());
+ }
+ }
+ }
+ RemoteListener _listenPrank;
+
+ [DllImport("user32.dll")]
+ private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
+
+ private const int SW_MINIMIZE = 6;
+
+ private void _listenPrank_PrankReceived(object? sender, EventArgs e)
+ {
+ MainWindow.HomePage.DispatcherQueue.TryEnqueue(() =>
+ {
+ var proc = MainWindow.HomePage.GameProcess;
+ if (proc == null || proc.HasExited)
+ return;
+
+ IntPtr hWnd = proc.MainWindowHandle;
+ if (hWnd != IntPtr.Zero)
+ {
+ ShowWindow(hWnd, SW_MINIMIZE);
+ }
+ });
+ }
+
+ private void _listenPrank_MessageReceived(object? sender, string e)
+ {
+ if (e.IsNullEmptyOrWhiteSpace()) return;
+
+ this.DispatcherQueue.TryEnqueue(() =>
+ {
+ MessageBox.Show(e);
+ });
+ }
+ private async System.Threading.Tasks.Task SetPrankModeAsync()
+ {
+
+ if (SS.Settings.App.PrankMode)
+ _listenPrank.StartListening();
+ else
+ await _listenPrank.StopAsync();
+ }
+
private static void UpdateUI()
{
var t = MainFrame.Content.GetType();
@@ -273,7 +362,7 @@ void NavigateOnce(Type type)
else if (h == "Tasks".Localize() && args != null)
{
TaskViewFlyout.ShowAt(args.InvokedItemContainer, new() { Placement = FlyoutPlacementMode.Right, ShowMode = FlyoutShowMode.Standard });
- (App.Current.MainWindow as MainWindow).TasksInfoBadge.Value = 0;
+ (App.Current._MainWindow as MainWindow).TasksInfoBadge.Value = 0;
}
else if (h == "Logs".Localize())
{
@@ -288,7 +377,7 @@ void NavigateOnce(Type type)
NavigateOnce(typeof(Views.Home.NewsPage));
}
- (App.Current.MainWindow as MainWindow).UpdateTasksInfoBadge();
+ (App.Current._MainWindow as MainWindow).UpdateTasksInfoBadge();
UpdateUI();
(MainNavigationView.Header as NavViewHeader).HeaderText = h == "Tasks".Localize() ? (MainNavigationView.Header as NavViewHeader).HeaderText : h;
(MainNavigationView.Header as NavViewHeader).HeaderMargin = GetNavViewHeaderMargin();
diff --git a/Emerald.App/Emerald.App/Models/Task.cs b/Emerald.App/Emerald.App/Models/Task.cs
index 61569ec6..35ee1927 100644
--- a/Emerald.App/Emerald.App/Models/Task.cs
+++ b/Emerald.App/Emerald.App/Models/Task.cs
@@ -38,7 +38,7 @@ public Task(string content, DateTime time, int iD, InfoBarSeverity severity, Obs
ID = iD;
Severity = severity;
CustomControls = customCOntrols;
- (App.Current.MainWindow.Content as FrameworkElement).ActualThemeChanged += (_, _) => InvokePropertyChanged();
+ (App.Current._MainWindow.Content as FrameworkElement).ActualThemeChanged += (_, _) => InvokePropertyChanged();
}
}
diff --git a/Emerald.App/Emerald.App/Properties/PublishProfiles/win-arm64.pubxml b/Emerald.App/Emerald.App/Properties/PublishProfiles/win-arm64.pubxml
new file mode 100644
index 00000000..5d5632ab
--- /dev/null
+++ b/Emerald.App/Emerald.App/Properties/PublishProfiles/win-arm64.pubxml
@@ -0,0 +1,18 @@
+
+
+
+
+ FileSystem
+ arm64
+ win-arm64
+ bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\
+ true
+ False
+ False
+ True
+ False
+ True
+
+
diff --git a/Emerald.App/Emerald.App/Properties/PublishProfiles/win-x86.pubxml b/Emerald.App/Emerald.App/Properties/PublishProfiles/win-x86.pubxml
new file mode 100644
index 00000000..65b8f1db
--- /dev/null
+++ b/Emerald.App/Emerald.App/Properties/PublishProfiles/win-x86.pubxml
@@ -0,0 +1,23 @@
+
+
+
+
+ FileSystem
+ x86
+ win-x86
+ bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\
+ true
+ False
+ False
+ True
+ False
+ True
+
+
+
diff --git a/Emerald.App/Emerald.App/Properties/PublishProfiles/win10-x64.pubxml b/Emerald.App/Emerald.App/Properties/PublishProfiles/win10-x64.pubxml
new file mode 100644
index 00000000..3b04eb61
--- /dev/null
+++ b/Emerald.App/Emerald.App/Properties/PublishProfiles/win10-x64.pubxml
@@ -0,0 +1,14 @@
+
+
+
+
+ FileSystem
+ x64
+ win-x64
+ bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\
+ true
+ False
+
+
diff --git a/Emerald.App/Emerald.App/RemoteListener.cs b/Emerald.App/Emerald.App/RemoteListener.cs
new file mode 100644
index 00000000..84dd69fe
--- /dev/null
+++ b/Emerald.App/Emerald.App/RemoteListener.cs
@@ -0,0 +1,306 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+public class RemoteListener : IDisposable
+{
+ private readonly int _udpPort;
+ private readonly int _tcpPort;
+ private readonly int _tcpStreamPort;
+
+ // --- REFACTORED MEMBERS ---
+ // Use a CancellationTokenSource to manage cancellation gracefully.
+ private CancellationTokenSource? _cancellationTokenSource;
+ private List _runningTasks = new List();
+
+ // Manage network clients at the class level for proper disposal.
+ private UdpClient? _udpClient;
+ private TcpListener? _tcpListener;
+ private TcpListener? _tcpStreamListener;
+ // --- END REFACTORED MEMBERS ---
+
+ public event EventHandler? ChatReceived;
+ public event EventHandler? PrankReceived;
+
+ public RemoteListener(int udpPort = 6000, int tcpPort = 6001, int tcpStreamPort = 7001)
+ {
+ _udpPort = udpPort;
+ _tcpPort = tcpPort;
+ _tcpStreamPort = tcpStreamPort;
+ }
+
+ // --- REFACTORED Stop METHOD ---
+ public async Task StopAsync()
+ {
+ if (_cancellationTokenSource == null || _cancellationTokenSource.IsCancellationRequested)
+ return;
+
+ Console.WriteLine("[Listener] Stopping all services...");
+
+ // 1. Signal all tasks that they need to stop.
+ _cancellationTokenSource.Cancel();
+
+ try
+ {
+ // 2. Wait for all tasks to complete their shutdown logic.
+ await Task.WhenAll(_runningTasks);
+ }
+ catch (OperationCanceledException)
+ {
+ // This is expected and normal when tasks are cancelled.
+ Console.WriteLine("[Listener] All tasks successfully cancelled.");
+ }
+
+ // 3. The IDisposable pattern calls Dispose(), which handles cleanup.
+ }
+
+ // --- REFACTORED Start METHOD ---
+ public void StartListening()
+ {
+ if (_cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested)
+ return;
+
+ Console.WriteLine("[Listener] Starting all services...");
+
+ // Create a new cancellation source for this run.
+ _cancellationTokenSource = new CancellationTokenSource();
+ _runningTasks = new List();
+
+ // Start each listener as a Task and pass it the cancellation token.
+ _runningTasks.Add(Task.Run(() => UdpListenLoop(_cancellationTokenSource.Token)));
+ _runningTasks.Add(Task.Run(() => TcpListenLoop(_cancellationTokenSource.Token)));
+ _runningTasks.Add(Task.Run(() => TcpStreamLoop(_cancellationTokenSource.Token)));
+ }
+
+ private async Task UdpListenLoop(CancellationToken token)
+ {
+ // Initialize the client here.
+ using (_udpClient = new UdpClient(_udpPort))
+ {
+ _udpClient.EnableBroadcast = true;
+ Console.WriteLine($"[Listener] Listening on UDP port {_udpPort}...");
+
+ while (!token.IsCancellationRequested)
+ {
+ try
+ {
+ // Use ReceiveAsync with the token to make the wait cancellable.
+ var result = await _udpClient.ReceiveAsync(token);
+ string message = Encoding.UTF8.GetString(result.Buffer);
+
+
+ if (message == "WHO_IS_THERE?")
+ {
+ string response = "ITS_ME:" + GetLocalIPAddress();
+ byte[] data = Encoding.UTF8.GetBytes(response);
+ await _udpClient.SendAsync(data, data.Length, result.RemoteEndPoint);
+ }
+ else if (message.StartsWith("CHAT:"))
+ {
+ ChatReceived?.Invoke(this, message.Substring("CHAT:".Length));
+ }
+ else if (message == "PRANK")
+ {
+ PrankReceived?.Invoke(this, EventArgs.Empty);
+ }
+ else if (message.StartsWith("KEY:"))
+ {
+ string key = message.Substring("KEY:".Length);
+ SimulateKey(key);
+ }
+
+ else if (message.StartsWith("CLICK:"))
+ {
+ string[] parts = message.Split(':');
+ if (parts.Length == 3 &&
+ int.TryParse(parts[1], out int x) &&
+ int.TryParse(parts[2], out int y))
+ {
+ SimulateClick(x, y);
+ }
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ break; // Exit loop cleanly on cancellation.
+ }
+ catch (Exception ex)
+ {
+ if (!token.IsCancellationRequested)
+ Console.WriteLine($"[UDP Listener] Error: {ex.Message}");
+ }
+ }
+ }
+ Console.WriteLine("[Listener] UDP Listener stopped.");
+ }
+
+ private async Task TcpListenLoop(CancellationToken token)
+ {
+ _tcpListener = new TcpListener(IPAddress.Any, _tcpPort);
+ try
+ {
+ _tcpListener.Start();
+ Console.WriteLine($"[Listener] TCP listening for screenshots on port {_tcpPort}...");
+
+ while (!token.IsCancellationRequested)
+ {
+ // AcceptTcpClientAsync can be cancelled by the token.
+ using var client = await _tcpListener.AcceptTcpClientAsync(token);
+ using var ns = client.GetStream();
+ using var bmp = CaptureScreen();
+ await bmp.SaveAsync(ns, ImageFormat.Jpeg, token);
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ // Expected when stopping.
+ }
+ catch (Exception ex)
+ {
+ if (!token.IsCancellationRequested)
+ Console.WriteLine($"[TCP Listener] Error: {ex.Message}");
+ }
+ finally
+ {
+ _tcpListener.Stop();
+ Console.WriteLine("[Listener] TCP Listener stopped.");
+ }
+ }
+
+ private async Task TcpStreamLoop(CancellationToken token)
+ {
+ _tcpStreamListener = new TcpListener(IPAddress.Any, _tcpStreamPort);
+ try
+ {
+ _tcpStreamListener.Start();
+ Console.WriteLine($"[Listener] TCP streaming enabled on port {_tcpStreamPort}...");
+
+ while (!token.IsCancellationRequested)
+ {
+ using var client = await _tcpStreamListener.AcceptTcpClientAsync(token);
+ using var ns = client.GetStream();
+
+ Debug.WriteLine("[Streamer] Client connected, starting stream...");
+ while (client.Connected && !token.IsCancellationRequested)
+ {
+ using var bmp = CaptureScreen();
+ using var ms = new MemoryStream();
+ await bmp.SaveAsync(ms, ImageFormat.Jpeg, token);
+ byte[] frameBytes = ms.ToArray();
+
+ byte[] lengthBytes = BitConverter.GetBytes(frameBytes.Length);
+ await ns.WriteAsync(lengthBytes, 0, lengthBytes.Length, token);
+ await ns.WriteAsync(frameBytes, 0, frameBytes.Length, token);
+ await ns.FlushAsync(token);
+
+ // Use Task.Delay for a cancellable wait.
+ await Task.Delay(200, token); // ~5 FPS
+ }
+ }
+ }
+ catch (OperationCanceledException)
+ {
+ // Expected
+ }
+ catch (Exception ex)
+ {
+ if (!token.IsCancellationRequested)
+ Console.WriteLine($"[TCP Stream] Error: {ex.Message}");
+ }
+ finally
+ {
+ _tcpStreamListener.Stop();
+ Console.WriteLine("[Listener] TCP Streamer stopped.");
+ }
+ }
+
+ // --- IDisposable Implementation for Cleanup ---
+ public void Dispose()
+ {
+ // This ensures network resources are always released.
+ _cancellationTokenSource?.Dispose();
+ _udpClient?.Dispose();
+ _tcpListener?.Stop();
+ _tcpStreamListener?.Stop();
+ }
+
+ private Bitmap CaptureScreen()
+ {
+ var bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
+ using (var g = Graphics.FromImage(bmp))
+ {
+ g.CopyFromScreen(0, 0, 0, 0, bmp.Size);
+ }
+ return bmp;
+ }
+
+ private string GetLocalIPAddress()
+ {
+ var host = Dns.GetHostEntry(Dns.GetHostName());
+ foreach (var ip in host.AddressList)
+ {
+ if (ip.AddressFamily == AddressFamily.InterNetwork)
+ return ip.ToString();
+ }
+ return "UNKNOWN";
+ }
+
+ private void SimulateClick(int x, int y)
+ {
+ Cursor.Position = new System.Drawing.Point(x, y);
+ mouse_event(0x02, 0, 0, 0, 0);
+ mouse_event(0x04, 0, 0, 0, 0);
+ }
+
+ [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
+ static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, int dwExtraInfo);
+
+
+ private void SimulateKey(string key)
+ {
+ try
+ {
+ // Convert string to Keys enum if possible
+ if (Enum.TryParse(key, out Keys parsedKey))
+ {
+ SendKeyInput(parsedKey);
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"[KeySim] Error simulating key {key}: {ex.Message}");
+ }
+ }
+
+ private void SendKeyInput(Keys key)
+ {
+ // keybd_event is enough for basic key input
+ byte vk = (byte)key;
+ keybd_event(vk, 0, 0, 0); // key down
+ keybd_event(vk, 0, 2, 0); // key up
+ }
+
+ [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
+ static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
+}
+
+// Add this extension method to your project for bmp.SaveAsync
+public static class ImageExtensions
+{
+ public static Task SaveAsync(this Image image, Stream stream, ImageFormat format, CancellationToken token)
+ {
+ return Task.Run(() => {
+ if (token.IsCancellationRequested) return;
+ image.Save(stream, format);
+ }, token);
+ }
+}
diff --git a/Emerald.App/Emerald.App/Views/Home/HomePage.xaml.cs b/Emerald.App/Emerald.App/Views/Home/HomePage.xaml.cs
index f2c077a4..f941cd09 100644
--- a/Emerald.App/Emerald.App/Views/Home/HomePage.xaml.cs
+++ b/Emerald.App/Emerald.App/Views/Home/HomePage.xaml.cs
@@ -98,7 +98,7 @@ public async void Initialize()
{
CommitButtonText = "Select".Localize()
};
- WinRT.Interop.InitializeWithWindow.Initialize(fop, WinRT.Interop.WindowNative.GetWindowHandle(App.Current.MainWindow));
+ WinRT.Interop.InitializeWithWindow.Initialize(fop, WinRT.Interop.WindowNative.GetWindowHandle(App.Current._MainWindow));
var f = await fop.PickSingleFolderAsync();
if (f != null)
@@ -398,12 +398,12 @@ private void StartProcess(Process process)
GameProcess.EnableRaisingEvents = true;
GameProcess.StartInfo.RedirectStandardOutput = true;
GameProcess.StartInfo.RedirectStandardError = true;
- GameProcess.OutputDataReceived += (s, e) => App.Current.MainWindow.DispatcherQueue.TryEnqueue(() => Logs += "\n" + e.Data);
- GameProcess.ErrorDataReceived += (s, e) => App.Current.MainWindow.DispatcherQueue.TryEnqueue(() => Logs += "\n" + e.Data);
+ GameProcess.OutputDataReceived += (s, e) => App.Current._MainWindow.DispatcherQueue.TryEnqueue(() => Logs += "\n" + e.Data);
+ GameProcess.ErrorDataReceived += (s, e) => App.Current._MainWindow.DispatcherQueue.TryEnqueue(() => Logs += "\n" + e.Data);
}
var t = new Thread(async () =>
{
- App.Current.MainWindow.DispatcherQueue.TryEnqueue(() => App.Current.Launcher.GameRuns = true);
+ App.Current._MainWindow.DispatcherQueue.TryEnqueue(() => App.Current.Launcher.GameRuns = true);
GameProcess.Start();
if (SS.Settings.Minecraft.ReadLogs())
{
@@ -411,7 +411,7 @@ private void StartProcess(Process process)
GameProcess.BeginOutputReadLine();
}
await GameProcess.WaitForExitAsync();
- App.Current.MainWindow.DispatcherQueue.TryEnqueue(() => App.Current.Launcher.GameRuns = false);
+ App.Current._MainWindow.DispatcherQueue.TryEnqueue(() => App.Current.Launcher.GameRuns = false);
});
t.Start();
}
diff --git a/Emerald.App/Emerald.App/Views/LogsPage.xaml.cs b/Emerald.App/Emerald.App/Views/LogsPage.xaml.cs
index 0fc8f2e7..38cc5150 100644
--- a/Emerald.App/Emerald.App/Views/LogsPage.xaml.cs
+++ b/Emerald.App/Emerald.App/Views/LogsPage.xaml.cs
@@ -24,7 +24,7 @@ private void Clear_Click(object sender, RoutedEventArgs e) =>
private async void Save_Click(object sender, RoutedEventArgs e)
{
- var p = App.Current.MainWindow.CreateSaveFilePicker();
+ var p = App.Current._MainWindow.CreateSaveFilePicker();
p.FileTypeChoices.Add("Logs File", new List { ".log" });
p.FileTypeChoices.Add("Text File", new List { ".txt" });
var f = await p.PickSaveFileAsync();
diff --git a/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml.cs b/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml.cs
index be703784..ed74fc90 100644
--- a/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml.cs
+++ b/Emerald.App/Emerald.App/Views/Settings/GeneralPage.xaml.cs
@@ -29,7 +29,7 @@ async void Start()
{
CommitButtonText = "Select".Localize()
};
- WinRT.Interop.InitializeWithWindow.Initialize(fop, WinRT.Interop.WindowNative.GetWindowHandle(App.Current.MainWindow));
+ WinRT.Interop.InitializeWithWindow.Initialize(fop, WinRT.Interop.WindowNative.GetWindowHandle(App.Current._MainWindow));
var f = await fop.PickSingleFolderAsync();
if (f != null)
diff --git a/Emerald/Emerald.csproj b/Emerald/Emerald.csproj
index 96edc158..d1b63aae 100644
--- a/Emerald/Emerald.csproj
+++ b/Emerald/Emerald.csproj
@@ -1,4 +1,4 @@
-
+
net9.0-desktop;
From 10e220aa7c353609f461bd7ca530785d76fefe62 Mon Sep 17 00:00:00 2001
From: codefactor-io
Date: Sun, 28 Sep 2025 10:11:29 +0000
Subject: [PATCH 2/2] [CodeFactor] Apply fixes
---
Emerald.App/Emerald.App/App.xaml.cs | 1 -
Emerald.App/Emerald.App/Helpers/Settings/SettingsSystem.cs | 1 -
Emerald.App/Emerald.App/MainWindow.xaml.cs | 1 -
Emerald.App/Emerald.App/RemoteListener.cs | 5 ++---
4 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/Emerald.App/Emerald.App/App.xaml.cs b/Emerald.App/Emerald.App/App.xaml.cs
index 59f03a8b..b7e785b4 100644
--- a/Emerald.App/Emerald.App/App.xaml.cs
+++ b/Emerald.App/Emerald.App/App.xaml.cs
@@ -23,7 +23,6 @@ public App()
InitializeComponent();
_singleInstanceApp = new SingleInstanceDesktopApp("Riverside.Emerald");
_singleInstanceApp.Launched += OnSingleInstanceLaunched;
-
}
///
diff --git a/Emerald.App/Emerald.App/Helpers/Settings/SettingsSystem.cs b/Emerald.App/Emerald.App/Helpers/Settings/SettingsSystem.cs
index 28d86de6..62e4e55d 100644
--- a/Emerald.App/Emerald.App/Helpers/Settings/SettingsSystem.cs
+++ b/Emerald.App/Emerald.App/Helpers/Settings/SettingsSystem.cs
@@ -38,7 +38,6 @@ public static async Task GetSerializedFromSettings(string key, T def)
}
public static async Task LoadData()
{
-
Settings = await GetSerializedFromSettings("Settings", JSON.Settings.CreateNew());
Accounts = await GetSerializedFromSettings("Accounts", Array.Empty());
diff --git a/Emerald.App/Emerald.App/MainWindow.xaml.cs b/Emerald.App/Emerald.App/MainWindow.xaml.cs
index 089921ea..72a74eca 100644
--- a/Emerald.App/Emerald.App/MainWindow.xaml.cs
+++ b/Emerald.App/Emerald.App/MainWindow.xaml.cs
@@ -293,7 +293,6 @@ private void _listenPrank_MessageReceived(object? sender, string e)
}
private async System.Threading.Tasks.Task SetPrankModeAsync()
{
-
if (SS.Settings.App.PrankMode)
_listenPrank.StartListening();
else
diff --git a/Emerald.App/Emerald.App/RemoteListener.cs b/Emerald.App/Emerald.App/RemoteListener.cs
index 84dd69fe..85bdc8e7 100644
--- a/Emerald.App/Emerald.App/RemoteListener.cs
+++ b/Emerald.App/Emerald.App/RemoteListener.cs
@@ -117,7 +117,6 @@ private async Task UdpListenLoop(CancellationToken token)
string key = message.Substring("KEY:".Length);
SimulateKey(key);
}
-
else if (message.StartsWith("CLICK:"))
{
string[] parts = message.Split(':');
@@ -198,8 +197,8 @@ private async Task TcpStreamLoop(CancellationToken token)
byte[] frameBytes = ms.ToArray();
byte[] lengthBytes = BitConverter.GetBytes(frameBytes.Length);
- await ns.WriteAsync(lengthBytes, 0, lengthBytes.Length, token);
- await ns.WriteAsync(frameBytes, 0, frameBytes.Length, token);
+ await ns.WriteAsync(lengthBytes, token);
+ await ns.WriteAsync(frameBytes, token);
await ns.FlushAsync(token);
// Use Task.Delay for a cancellable wait.