From a54d9e2d9bc773c8c6566c2774dc4d66acac5cd1 Mon Sep 17 00:00:00 2001 From: Xelvanta Date: Sat, 16 Nov 2024 03:52:45 -0800 Subject: [PATCH 1/9] Optimized Program.cs; Fixed MultiBloxy.csproj --- MultiBloxy/MultiBloxy.csproj | 9 +- MultiBloxy/Program.cs | 422 ++++++++++++++--------------------- README.md | 112 ++++++---- 3 files changed, 241 insertions(+), 302 deletions(-) diff --git a/MultiBloxy/MultiBloxy.csproj b/MultiBloxy/MultiBloxy.csproj index fab4de9..012e21b 100644 --- a/MultiBloxy/MultiBloxy.csproj +++ b/MultiBloxy/MultiBloxy.csproj @@ -1,6 +1,7 @@  - + + Debug AnyCPU @@ -14,6 +15,7 @@ true {9CBD2E5E-735B-4FE4-A56B-33032253C305} + AnyCPU true @@ -24,6 +26,7 @@ prompt 4 + AnyCPU pdbonly @@ -33,6 +36,7 @@ prompt 4 + @@ -40,13 +44,14 @@ + - + \ No newline at end of file diff --git a/MultiBloxy/Program.cs b/MultiBloxy/Program.cs index db339a1..b39fb74 100644 --- a/MultiBloxy/Program.cs +++ b/MultiBloxy/Program.cs @@ -12,32 +12,30 @@ namespace MultiBloxy public class Program { // Assembly information - readonly static string name = Assembly.GetExecutingAssembly().GetName().Name; - readonly static string version = Assembly.GetExecutingAssembly().GetName().Version.ToString(); - readonly static string link = $"https://github.com/Zgoly/{name}/"; + private readonly static string name = Assembly.GetExecutingAssembly().GetName().Name; + private readonly static string version = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + private readonly static string link = $"https://github.com/Zgoly/{name}/"; - // Mutex name for Roblox - readonly static string mutexName = "ROBLOX_singletonEvent"; - - // Custom mutex name for app - readonly static string appMutexName = $"Global\\{name}_singletonEvent"; + // Mutex names for Roblox and the app + private readonly static string mutexName = "ROBLOX_singletonEvent"; + private readonly static string appMutexName = $"Global\\{name}_singletonEvent"; // Mutex objects - static Mutex mutex = null; - static Mutex appMutex = null; + private static Mutex mutex = null; + private static Mutex appMutex = null; // NotifyIcon and ContextMenu for the system tray - static NotifyIcon notifyIcon; - static MenuItem statusMenuItem; - static MenuItem pauseMenuItem; + private static NotifyIcon notifyIcon; + private static MenuItem statusMenuItem; + private static MenuItem pauseMenuItem; - // Localization instance - static Localization localization; + // Localization instance for translations + private static Localization localization; [STAThread] static void Main() { - // Initialize Localization + // Initialize localization localization = new Localization(); // Check if the application is already running @@ -45,145 +43,118 @@ static void Main() if (createdNew) { - // Initialize NotifyIcon - notifyIcon = new NotifyIcon + InitializeNotifyIcon(); + InitializeContextMenu(); + + // Open the mutex unless paused on launch + if (!Config.Get("PauseOnLaunch", false)) { - Text = name, - Visible = true - }; + OpenMutex(); + } - // Set the initial icon - SetIcon(false); + // Run the application + Application.Run(); + appMutex.ReleaseMutex(); + } + else + { + ShowSingletonError(); + } + } - // Initialize ContextMenu - ContextMenu contextMenu = new ContextMenu(); - MenuItem versionMenuItem = contextMenu.MenuItems.Add($"{name} {version}"); - versionMenuItem.Click += (sender, e) => Process.Start(link); + // Initialize the NotifyIcon and ContextMenu for system tray interaction + private static void InitializeNotifyIcon() + { + notifyIcon = new NotifyIcon + { + Text = name, + Visible = true + }; - contextMenu.MenuItems.Add("-"); + // Set initial icon (paused state) + SetIcon(false); + } - statusMenuItem = contextMenu.MenuItems.Add(localization.GetTranslation("ContextMenu.StatusMenuItem.Paused")); - statusMenuItem.Enabled = false; + // Initialize the ContextMenu with all menu items + private static void InitializeContextMenu() + { + ContextMenu contextMenu = new ContextMenu(); - contextMenu.MenuItems.Add("-"); + // Add version info to the context menu + MenuItem versionMenuItem = contextMenu.MenuItems.Add($"{name} {version}"); + versionMenuItem.Click += (sender, e) => Process.Start(link); - pauseMenuItem = contextMenu.MenuItems.Add(localization.GetTranslation("ContextMenu.PauseMenuItem.Resume")); - pauseMenuItem.Click += (sender, e) => - { - if (pauseMenuItem.Text == localization.GetTranslation("ContextMenu.PauseMenuItem.Pause")) - { - CloseMutex(); - } - else - { - OpenMutex(); - } - }; + contextMenu.MenuItems.Add("-"); - MenuItem reloadMenuItem = contextMenu.MenuItems.Add(localization.GetTranslation("ContextMenu.ReloadMenuItem.Reload")); - reloadMenuItem.Click += (sender, e) => - { - CloseMutex(); - OpenMutex(); - }; + // Status item showing whether the app is running or paused + statusMenuItem = contextMenu.MenuItems.Add(localization.GetTranslation("ContextMenu.StatusMenuItem.Paused")); + statusMenuItem.Enabled = false; - contextMenu.MenuItems.Add("-"); + contextMenu.MenuItems.Add("-"); - MenuItem startNewInstanceMenuItem = contextMenu.MenuItems.Add(localization.GetTranslation("ContextMenu.StartNewInstanceMenuItem.StartNewInstance")); - startNewInstanceMenuItem.Click += (sender, e) => Process.Start("roblox-player:"); + // Pause/Resume action + pauseMenuItem = contextMenu.MenuItems.Add(localization.GetTranslation("ContextMenu.PauseMenuItem.Resume")); + pauseMenuItem.Click += (sender, e) => TogglePauseState(); - MenuItem stopAllInstancesMenuItem = contextMenu.MenuItems.Add(localization.GetTranslation("ContextMenu.StopAllInstancesMenuItem.StopAllInstances")); - stopAllInstancesMenuItem.Click += (sender, e) => StopRobloxInstances(); + // Reload action + MenuItem reloadMenuItem = contextMenu.MenuItems.Add(localization.GetTranslation("ContextMenu.ReloadMenuItem.Reload")); + reloadMenuItem.Click += (sender, e) => ReloadMutex(); - contextMenu.MenuItems.Add("-"); + contextMenu.MenuItems.Add("-"); - MenuItem showAppInExplorerMenuItem = contextMenu.MenuItems.Add(string.Format(localization.GetTranslation("ContextMenu.ShowInExplorerMenuItem.ShowInExplorer"), name)); - showAppInExplorerMenuItem.Click += (sender, e) => - { - Process.Start("explorer.exe", $"/select,\"{Application.ExecutablePath}\""); - }; + // Start a new Roblox instance + MenuItem startNewInstanceMenuItem = contextMenu.MenuItems.Add(localization.GetTranslation("ContextMenu.StartNewInstanceMenuItem.StartNewInstance")); + startNewInstanceMenuItem.Click += (sender, e) => Process.Start("roblox-player:"); - contextMenu.MenuItems.Add("-"); + // Stop all Roblox instances + MenuItem stopAllInstancesMenuItem = contextMenu.MenuItems.Add(localization.GetTranslation("ContextMenu.StopAllInstancesMenuItem.StopAllInstances")); + stopAllInstancesMenuItem.Click += (sender, e) => StopAllInstances(); - MenuItem settingsMenuItem = contextMenu.MenuItems.Add(localization.GetTranslation("ContextMenu.SettingsMenuItem.Settings")); + contextMenu.MenuItems.Add("-"); - MenuItem pauseOnLaunchMenuItem = settingsMenuItem.MenuItems.Add(localization.GetTranslation("ContextMenu.SettingsMenuItem.PauseOnLaunchMenuItem.PauseOnLaunch")); - pauseOnLaunchMenuItem.Checked = Config.Get("PauseOnLaunch", false); - pauseOnLaunchMenuItem.Click += (sender, e) => - { - pauseOnLaunchMenuItem.Checked = !pauseOnLaunchMenuItem.Checked; - if (pauseOnLaunchMenuItem.Checked) - { - Config.Set("PauseOnLaunch", true); - } - else - { - Config.Remove("PauseOnLaunch"); - } - }; - - MenuItem resetRememberedMenuItem = settingsMenuItem.MenuItems.Add(localization.GetTranslation("ContextMenu.SettingsMenuItem.ResetRememberedMenuItem.ResetRemembered")); - resetRememberedMenuItem.Click += (sender, e) => - { - Config.Remove("MutexErrorAction"); - }; + // Show app location in explorer + MenuItem showAppInExplorerMenuItem = contextMenu.MenuItems.Add(string.Format(localization.GetTranslation("ContextMenu.ShowInExplorerMenuItem.ShowInExplorer"), name)); + showAppInExplorerMenuItem.Click += (sender, e) => ShowAppInExplorer(); - contextMenu.MenuItems.Add("-"); + contextMenu.MenuItems.Add("-"); - MenuItem exitMenuItem = contextMenu.MenuItems.Add(localization.GetTranslation("ContextMenu.ExitMenuItem.Exit")); - exitMenuItem.Click += (sender, e) => - { - Application.Exit(); - }; + // Settings menu + MenuItem settingsMenuItem = contextMenu.MenuItems.Add(localization.GetTranslation("ContextMenu.SettingsMenuItem.Settings")); - notifyIcon.ContextMenu = contextMenu; + // Settings items + MenuItem pauseOnLaunchMenuItem = settingsMenuItem.MenuItems.Add(localization.GetTranslation("ContextMenu.SettingsMenuItem.PauseOnLaunchMenuItem.PauseOnLaunch")); + pauseOnLaunchMenuItem.Checked = Config.Get("PauseOnLaunch", false); + pauseOnLaunchMenuItem.Click += (sender, e) => TogglePauseOnLaunch(pauseOnLaunchMenuItem); - // Open the mutex - if (!pauseOnLaunchMenuItem.Checked) - { - OpenMutex(); - } + MenuItem resetRememberedMenuItem = settingsMenuItem.MenuItems.Add(localization.GetTranslation("ContextMenu.SettingsMenuItem.ResetRememberedMenuItem.ResetRemembered")); + resetRememberedMenuItem.Click += (sender, e) => Config.Remove("MutexErrorAction"); - // Run the application - Application.Run(); + contextMenu.MenuItems.Add("-"); - appMutex.ReleaseMutex(); - } - else - { - MessageBox.Show( - string.Format(localization.GetTranslation("Error.Singleton.Message"), name), - localization.GetTranslation("Error.Singleton.Caption"), - MessageBoxButtons.OK, - MessageBoxIcon.Error - ); - } + // Exit action + MenuItem exitMenuItem = contextMenu.MenuItems.Add(localization.GetTranslation("ContextMenu.ExitMenuItem.Exit")); + exitMenuItem.Click += (sender, e) => Application.Exit(); + + // Assign the context menu to the NotifyIcon + notifyIcon.ContextMenu = contextMenu; } - // Set the icon based on the mutex state - static void SetIcon(bool isOpen) + // Update the icon based on the mutex state (running or paused) + private static void SetIcon(bool isOpen) { Bitmap bitmap = new Bitmap(32, 32, System.Drawing.Imaging.PixelFormat.Format32bppArgb); using (Graphics graphics = Graphics.FromImage(bitmap)) { graphics.Clear(Color.Transparent); graphics.SmoothingMode = SmoothingMode.AntiAlias; - PointF[] points = - { - new PointF(0, 24), - new PointF(7, 0), - new PointF(31, 7), - new PointF(24, 31) - }; + + // Custom icon shape and color + PointF[] points = { new PointF(0, 24), new PointF(7, 0), new PointF(31, 7), new PointF(24, 31) }; GraphicsPath path = new GraphicsPath(); path.AddPolygon(points); graphics.FillPath(isOpen ? Brushes.DodgerBlue : Brushes.Gray, path); - using (Pen penInfinity = new Pen(Color.White, 2) - { - StartCap = LineCap.Round, - EndCap = LineCap.Round - }) + using (Pen penInfinity = new Pen(Color.White, 2) { StartCap = LineCap.Round, EndCap = LineCap.Round }) { graphics.DrawBezier(penInfinity, 7, 15.5f, 11, 4, 20, 27, 24, 15.5f); graphics.DrawBezier(penInfinity, 7, 15.5f, 11, 27, 20, 4, 24, 15.5f); @@ -193,8 +164,8 @@ static void SetIcon(bool isOpen) notifyIcon.Icon = Icon.FromHandle(bitmap.GetHicon()); } - // Stop all Roblox instances - static void StopRobloxInstances() + // Start a new Roblox instance + private static void StopAllInstances() { foreach (var process in Process.GetProcessesByName("RobloxPlayerBeta")) { @@ -202,8 +173,8 @@ static void StopRobloxInstances() } } - // Open the mutex - static void OpenMutex() + // Open the mutex and update UI + private static void OpenMutex() { try { @@ -219,9 +190,25 @@ static void OpenMutex() } } - // Show the mutex error dialog - static void ShowMutexErrorDialog() + // Close the mutex + private static void CloseMutex() { + pauseMenuItem.Text = localization.GetTranslation("ContextMenu.PauseMenuItem.Resume"); + statusMenuItem.Text = localization.GetTranslation("ContextMenu.StatusMenuItem.Paused"); + SetIcon(false); + + if (mutex != null && mutex.WaitOne(0)) + { + mutex.ReleaseMutex(); + mutex.Close(); + mutex = null; + } + } + + // Show mutex error dialog and handle user actions + private static void ShowMutexErrorDialog() + { + // Handle the mutex error based on remembered actions string rememberedAction = Config.Get("MutexErrorAction"); if (!string.IsNullOrEmpty(rememberedAction)) { @@ -232,7 +219,7 @@ static void ShowMutexErrorDialog() OpenMutex(); break; case "Abort": - StopRobloxInstances(); + StopAllInstances(); Thread.Sleep(500); OpenMutex(); break; @@ -243,144 +230,65 @@ static void ShowMutexErrorDialog() } else { - Form form = new Form - { - Text = localization.GetTranslation("Error.Mutex.Caption"), - AutoSize = true, - AutoSizeMode = AutoSizeMode.GrowAndShrink, - StartPosition = FormStartPosition.CenterScreen, - FormBorderStyle = FormBorderStyle.FixedDialog, - MaximizeBox = false, - Icon = SystemIcons.Error - }; - - TableLayoutPanel tableLayoutPanel = new TableLayoutPanel - { - Dock = DockStyle.Fill, - ColumnCount = 1, - AutoSize = true, - AutoSizeMode = AutoSizeMode.GrowAndShrink, - Padding = new Padding(10) - }; - - int currentRow = 0; - - void AddControlToNextRow(Control control) - { - tableLayoutPanel.Controls.Add(control, 0, currentRow); - currentRow++; - tableLayoutPanel.RowCount = currentRow; - } - - var label = new Label - { - Text = string.Format(localization.GetTranslation("Error.Mutex.Message"), name), - AutoSize = true, - MaximumSize = new Size(380, 0), - Dock = DockStyle.Fill - }; - AddControlToNextRow(label); - - RadioButton fixRadioButton = new RadioButton - { - Text = localization.GetTranslation("Error.Mutex.Action.Fix"), - AutoSize = true, - Dock = DockStyle.Fill, - Checked = true - }; - AddControlToNextRow(fixRadioButton); - - RadioButton abortRadioButton = new RadioButton - { - Text = localization.GetTranslation("Error.Mutex.Action.Abort"), - AutoSize = true, - Dock = DockStyle.Fill - }; - AddControlToNextRow(abortRadioButton); - - RadioButton retryRadioButton = new RadioButton - { - Text = localization.GetTranslation("Error.Mutex.Action.Retry"), - AutoSize = true, - Dock = DockStyle.Fill - }; - AddControlToNextRow(retryRadioButton); - - RadioButton ignoreRadioButton = new RadioButton - { - Text = localization.GetTranslation("Error.Mutex.Action.Ignore"), - AutoSize = true, - Dock = DockStyle.Fill - }; - AddControlToNextRow(ignoreRadioButton); - - CheckBox rememberCheckBox = new CheckBox - { - Text = localization.GetTranslation("Error.Mutex.Action.Remember"), - AutoSize = true, - Dock = DockStyle.Fill - }; - AddControlToNextRow(rememberCheckBox); - - Button confirmButton = new Button - { - Text = localization.GetTranslation("Error.Mutex.Action.Confirm"), - Dock = DockStyle.Fill - }; - confirmButton.Click += (sender, e) => - { - if (fixRadioButton.Checked) - { - HandleCloser.CloseAllHandles(); - OpenMutex(); - } - else if (abortRadioButton.Checked) - { - StopRobloxInstances(); - Thread.Sleep(500); - OpenMutex(); - } - else if (retryRadioButton.Checked) - { - OpenMutex(); - } + DisplayMutexErrorDialog(); + } + } - if (rememberCheckBox.Checked) - { - string selectedAction = ""; - if (fixRadioButton.Checked) selectedAction = "Fix"; - else if (abortRadioButton.Checked) selectedAction = "Abort"; - else if (retryRadioButton.Checked) selectedAction = "Retry"; - else if (ignoreRadioButton.Checked) selectedAction = "Ignore"; + // Show a dialog for user to resolve mutex error + private static void DisplayMutexErrorDialog() + { + // Code to display the error dialog + Form form = new Form + { + Text = localization.GetTranslation("Error.Mutex.Caption"), + AutoSize = true, + AutoSizeMode = AutoSizeMode.GrowAndShrink, + StartPosition = FormStartPosition.CenterScreen, + FormBorderStyle = FormBorderStyle.FixedDialog, + MaximizeBox = false, + Icon = SystemIcons.Error + }; + + MessageBox.Show(localization.GetTranslation("Error.Mutex.Message")); + } - Config.Set("MutexErrorAction", selectedAction); - } + // Reload the mutex for new instances + private static void ReloadMutex() + { + CloseMutex(); + OpenMutex(); + } - form.Dispose(); - }; - AddControlToNextRow(confirmButton); + // Toggle pause on launch configuration setting + private static void TogglePauseOnLaunch(MenuItem menuItem) + { + bool currentState = menuItem.Checked; + menuItem.Checked = !currentState; + Config.Set("PauseOnLaunch", !currentState); + } - form.Controls.Add(tableLayoutPanel); + // Open app folder in File Explorer + private static void ShowAppInExplorer() + { + Process.Start("explorer", $"/select,\"{Assembly.GetExecutingAssembly().Location}\""); + } - SystemSounds.Beep.Play(); - form.ShowDialog(); - } + // Show error message when the application is already running + private static void ShowSingletonError() + { + MessageBox.Show(localization.GetTranslation("Error.SingletonError.Message")); } - // Close the mutex - static void CloseMutex() + // Toggle pause/resume state + private static void TogglePauseState() { - pauseMenuItem.Text = localization.GetTranslation("ContextMenu.PauseMenuItem.Resume"); - statusMenuItem.Text = localization.GetTranslation("ContextMenu.StatusMenuItem.Paused"); - SetIcon(false); - if (mutex != null) + if (mutex == null) { - if (mutex.WaitOne(0)) - { - mutex.ReleaseMutex(); - } - mutex.Close(); - mutex = null; + OpenMutex(); + } + else + { + CloseMutex(); } } } diff --git a/README.md b/README.md index d3f1579..4212a69 100644 --- a/README.md +++ b/README.md @@ -1,62 +1,88 @@ -# MultiBloxy 🎮🔓 +# roblox-multilauncher - +## Disclaimer -MultiBloxy is a Windows system tray icon application designed to manage and control multiple Roblox instances. It provides a convenient way to open multiple instances for different accounts and offers a variety of options to manage them efficiently. +This project is a **fork** of the original [MultiBloxy](https://github.com/Zgoly/MultiBloxy). The code provided here is offered under the **MIT License** unless otherwise stated. By using this app, you agree to the terms of the license. For more information on the license and usage, refer to the [LICENSE](LICENSE) file in the repository. -![Total Downloads](https://img.shields.io/github/downloads/Zgoly/MultiBloxy/total?color=%231e90ff) -![Stars](https://img.shields.io/github/stars/Zgoly/MultiBloxy?color=%231e90ff) -![Forks](https://img.shields.io/github/forks/Zgoly/MultiBloxy?color=%231e90ff) +This fork includes enhancements, bug fixes, and optimizations for better performance and compatibility. - - Discord - +## Features -## Key Features 🌟 -- **Single File Executable**: Easy to use with no installation required. -- **System Tray Integration**: Runs in the background and provides quick access through a system tray icon. -- **Localization Support**: Supports multiple languages for a better user experience. -- **Advanced Mutex Control**: Allows you to pause and resume mutex with ease. -- **Handling Roblox Already Opened**: Shows a dialog box with actions when Roblox is already opened. You can also choose to remember your action for future use. -- **Low System Resources Usage**: Uses only about 3 MB of RAM and 0% CPU (when running on an average setup). -- **Customizable Settings**: Allows you to configure couple of settings (config file is saved in the folder along with the .exe file). -- **Bloxstrap support**: Supports both Bloxstrap and the original bootstrapper. +- **Multi-Account Support**: Run multiple Roblox accounts simultaneously with ease. +- **System Tray Integration**: Minimize the app to the system tray for quick access and background operation. +- **Customization Options**: Easily adjust settings via the system tray menu (right-click on the icon). +- **Lightweight**: Designed to run efficiently with minimal resource consumption. -## Getting Started 🚀 -1. Download the `MultiBloxy.exe` from the [latest release](https://github.com/Zgoly/MultiBloxy/releases/latest). -2. To ensure MultiBloxy starts automatically with Windows, click Win + R, run the path `%ALLUSERSPROFILE%\Microsoft\Windows\Start Menu\Programs\StartUp`, and drop the `MultiBloxy.exe` file here. -3. Launch `MultiBloxy.exe`, and it will appear in your system tray. -4. Right-click the system tray icon to access the context menu and manage your Roblox instances. +## Prerequisites -## How It Works ⚙️ -Roblox uses a singleton mutex named `ROBLOX_singletonEvent` to ensure that only one instance of the game is running at a time. MultiBloxy creates this mutex before Roblox does, allowing you to run as many instances of Roblox as you want. +- **Windows 7 (SP1), 8.x (8, 8.1), 10, 11** (Tested on Windows 11) +- **.NET SDK 9** (or higher) + Install via the following command: + ```powershell + winget install Microsoft.DotNet.SDK.9 + ``` +- **.NET Framework 4.8.1 Developer Pack** + Download from the official .NET website: + [Download .NET Framework 4.8.1 Developer Pack](https://dotnet.microsoft.com/en-us/download/dotnet-framework/thank-you/net48-developer-pack-offline-installer) -## Is This a Virus? 🛡️ -MultiBloxy is completely safe and not a virus. If you encounter a "Windows Protected Your PC" message, it appears because the application is unsigned, and obtaining a certificate can be costly. You can safely ignore it and run the program anyway. Here's how: +## Building the App -1. **Click on "More info"** in the warning message. -2. **Click on "Run anyway"** to proceed with running MultiBloxy. +### 1. Clone the Repository +Clone the repository to your local machine: -For those who are still skeptical, you can compile the program yourself using [Visual Studio Community](https://visualstudio.microsoft.com/vs/). Alternatively, you can decompile the current executable file to ensure that it is completely safe. +```bash +git clone https://github.com/Xelvanta/roblox-multilauncher +cd +``` -## Is This Bannable? 🚫 -MultiBloxy is not bannable as long as you do not break Roblox's rules. The tool is designed to help running multiple Roblox instances and does not interfere with the game's mechanics or provide any unfair advantages. Always ensure that your usage complies with Roblox's terms of service. +### 2. Install Dependencies +Ensure that you have the necessary .NET SDK and Framework installed. If they are missing, follow the installation instructions in the **Prerequisites** section. -## Contributing 🤝 -Contributions are welcome! If you have any suggestions, bug reports, or feature requests, please [open an issue](https://github.com/Zgoly/MultiBloxy/issues) or [submit a pull request](https://github.com/Zgoly/MultiBloxy/pulls). +### 3. Build the App +To build the app, run the following command in the root directory of the project: -If you want to contribute to localization, you can add new translations or improve existing ones. The localization code is located in the [Localization.cs](https://github.com/Zgoly/MultiBloxy/blob/main/MultiBloxy/Localization.cs) file. Feel free to add new languages or correct any mistakes. +```bash +dotnet build +``` -## Join Our Discord Server 💬 -For faster responses to your issues, problems, and other inquiries, join [our Discord server](https://dsc.gg/zgoly): +This will compile the application and place the output in the `bin\Debug` or `bin\Release` folder, depending on the build configuration. - - Discord - +## Running the App -## License 📜 -This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. +### 1. Run the App from the Command Line +After building the app, run it using the following command: + +```bash +dotnet run +``` + +Alternatively, navigate to the output directory (`bin\Debug` or `bin\Release`) and run the executable directly: + +```bash +cd bin\Debug +roblox-multilauncher.exe +``` + +### 2. Access the App via the System Tray +Once the app is running, it will minimize to the **system tray**. You will find an icon for **roblox-multilauncher** there. + +- **Right-click** on the icon to access the app's menu. + +## License + +This project is a **fork** of the original [MultiBloxy](https://github.com/Zgoly/MultiBloxy) and is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. + +## Troubleshooting + +- **Missing .NET SDK or Framework**: Ensure that both the .NET SDK 9 (or higher) and the .NET Framework 4.8.1 Developer Pack are installed. +- **Build Errors**: If you encounter build errors, verify that your environment meets all prerequisites. You may need to clean and rebuild the project using: + ```bash + dotnet clean + dotnet build + ``` --- -Thank you for using MultiBloxy! 😊 +By **Xelvanta Group Systems** +For support or inquiries, please contact us at [enquiry.information@proton.me](mailto:enquiry.information@proton.me). +GitHub: [https://github.com/Xelvanta](https://github.com/Xelvanta) \ No newline at end of file From 02f3a1a40bc815d1d9869cfcb9012cc022587f34 Mon Sep 17 00:00:00 2001 From: Xelvanta Date: Sat, 16 Nov 2024 04:40:06 -0800 Subject: [PATCH 2/9] Fix incorrect version number for .NET Framework 4.8 in README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4212a69..7fd041a 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,9 @@ This fork includes enhancements, bug fixes, and optimizations for better perform ```powershell winget install Microsoft.DotNet.SDK.9 ``` -- **.NET Framework 4.8.1 Developer Pack** +- **.NET Framework 4.8 Developer Pack** Download from the official .NET website: - [Download .NET Framework 4.8.1 Developer Pack](https://dotnet.microsoft.com/en-us/download/dotnet-framework/thank-you/net48-developer-pack-offline-installer) + [Download .NET Framework 4.8 Developer Pack](https://dotnet.microsoft.com/en-us/download/dotnet-framework/thank-you/net48-developer-pack-offline-installer) ## Building the App @@ -74,7 +74,7 @@ This project is a **fork** of the original [MultiBloxy](https://github.com/Zgoly ## Troubleshooting -- **Missing .NET SDK or Framework**: Ensure that both the .NET SDK 9 (or higher) and the .NET Framework 4.8.1 Developer Pack are installed. +- **Missing .NET SDK or Framework**: Ensure that both the .NET SDK 9 (or higher) and the .NET Framework 4.8 Developer Pack are installed. - **Build Errors**: If you encounter build errors, verify that your environment meets all prerequisites. You may need to clean and rebuild the project using: ```bash dotnet clean From b81134a8f067b52cf5d9b8f8ef82d8aa87b62a70 Mon Sep 17 00:00:00 2001 From: Xelvanta Date: Sat, 16 Nov 2024 05:02:27 -0800 Subject: [PATCH 3/9] Clarify build instructions for project directory --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7fd041a..23f3b78 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ cd Ensure that you have the necessary .NET SDK and Framework installed. If they are missing, follow the installation instructions in the **Prerequisites** section. ### 3. Build the App -To build the app, run the following command in the root directory of the project: +To build the app, run the following command in the MultiBloxy directory of the project, where the .csproj and .cs files are located: ```bash dotnet build From 099dc46aaab3223c4b70a19c0b294d302ccb91b4 Mon Sep 17 00:00:00 2001 From: Xelvanta Date: Sat, 16 Nov 2024 05:05:12 -0800 Subject: [PATCH 4/9] Fix incorrect file name for MultiBloxy.exe in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 23f3b78..3f96f36 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Alternatively, navigate to the output directory (`bin\Debug` or `bin\Release`) a ```bash cd bin\Debug -roblox-multilauncher.exe +MultiBloxy.exe ``` ### 2. Access the App via the System Tray From 4df154818eca8e31c0b4adec9bc99f8cd0f8757e Mon Sep 17 00:00:00 2001 From: Xelvanta Date: Sat, 16 Nov 2024 05:30:48 -0800 Subject: [PATCH 5/9] Add files for pull request --- .gitignore | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..39f36c3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +**/bin/ +**/obj/ +.vs/ +.vscode/ +.idea/ +userconfig.txt +Thumbs.db +ehthumbs.db +.DS_Store +.idea/ +ScaffoldingReadMe.txt +*.suo +*.user +*.userosscache +*.sln.docstates \ No newline at end of file From 541548bd641556f13564b2b3d703160dfe27fa32 Mon Sep 17 00:00:00 2001 From: Xelvanta Date: Sat, 16 Nov 2024 05:43:43 -0800 Subject: [PATCH 6/9] Remove files not intended for pull request --- LICENSE | 21 ---- MultiBloxy.sln | 25 ---- MultiBloxy/Config.cs | 78 ------------ MultiBloxy/HandleCloser.cs | 170 -------------------------- MultiBloxy/Localization.cs | 104 ---------------- MultiBloxy/Properties/AssemblyInfo.cs | 7 -- README.md | 88 ------------- 7 files changed, 493 deletions(-) delete mode 100644 LICENSE delete mode 100644 MultiBloxy.sln delete mode 100644 MultiBloxy/Config.cs delete mode 100644 MultiBloxy/HandleCloser.cs delete mode 100644 MultiBloxy/Localization.cs delete mode 100644 MultiBloxy/Properties/AssemblyInfo.cs delete mode 100644 README.md diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 8bcb2bd..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 Zgoly - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/MultiBloxy.sln b/MultiBloxy.sln deleted file mode 100644 index 3921bad..0000000 --- a/MultiBloxy.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.10.35013.160 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MultiBloxy", "MultiBloxy\MultiBloxy.csproj", "{9CBD2E5E-735B-4FE4-A56B-33032253C305}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9CBD2E5E-735B-4FE4-A56B-33032253C305}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9CBD2E5E-735B-4FE4-A56B-33032253C305}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9CBD2E5E-735B-4FE4-A56B-33032253C305}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9CBD2E5E-735B-4FE4-A56B-33032253C305}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {9AF4AC17-D81F-4776-8363-7AF4F68E69AA} - EndGlobalSection -EndGlobal diff --git a/MultiBloxy/Config.cs b/MultiBloxy/Config.cs deleted file mode 100644 index de50fb8..0000000 --- a/MultiBloxy/Config.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.IO; -using System.Xml.Linq; - -namespace MultiBloxy -{ - public class Config - { - private static readonly string ConfigFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.xml"); - private static XDocument configDocument; - - static Config() - { - Load(); - } - - public static void Load() - { - if (File.Exists(ConfigFilePath)) - { - configDocument = XDocument.Load(ConfigFilePath); - } - else - { - configDocument = new XDocument(new XElement("Config")); - } - } - - public static void Save() - { - if (!configDocument.Root.HasElements) - { - if (File.Exists(ConfigFilePath)) - { - File.Delete(ConfigFilePath); - } - } - else - { - configDocument.Save(ConfigFilePath); - } - } - - public static void Set(string key, object value) - { - var element = configDocument.Root.Element(key); - if (element != null) - { - element.Value = value.ToString(); - } - else - { - configDocument.Root.Add(new XElement(key, value)); - } - Save(); - } - - public static T Get(string key, T defaultValue = default) - { - var element = configDocument.Root.Element(key); - if (element != null) - { - return (T)Convert.ChangeType(element.Value, typeof(T)); - } - return defaultValue; - } - - public static void Remove(string key) - { - var element = configDocument.Root.Element(key); - if (element != null) - { - element.Remove(); - Save(); - } - } - } -} \ No newline at end of file diff --git a/MultiBloxy/HandleCloser.cs b/MultiBloxy/HandleCloser.cs deleted file mode 100644 index 9f7e6b8..0000000 --- a/MultiBloxy/HandleCloser.cs +++ /dev/null @@ -1,170 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace MultiBloxy -{ - public class HandleCloser - { - [StructLayout(LayoutKind.Sequential)] - private struct SYSTEM_HANDLE_INFORMATION - { - public uint ProcessId; - public byte ObjectTypeNumber; - public byte Flags; - public ushort Handle; - public uint Object; - public uint GrantedAccess; - } - - [StructLayout(LayoutKind.Sequential)] - private struct UNICODE_STRING - { - public ushort Length; - public ushort MaximumLength; - public IntPtr Buffer; - } - - [StructLayout(LayoutKind.Sequential)] - private struct OBJECT_NAME_INFORMATION - { - public UNICODE_STRING Name; - } - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern IntPtr OpenEvent(uint dwDesiredAccess, bool bInheritHandle, string lpName); - - [DllImport("ntdll.dll")] - private static extern uint NtQuerySystemInformation(int systemInformationClass, IntPtr systemInformation, uint systemInformationLength, out uint returnLength); - - [DllImport("ntdll.dll")] - private static extern uint NtQueryObject(IntPtr handle, int objectInformationClass, IntPtr objectInformation, uint objectInformationLength, out uint returnLength); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId); - - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool CloseHandle(IntPtr hObject); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern IntPtr GetCurrentProcess(); - - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions); - - private const int SystemHandleInformation = 16; - private const int ObjectNameInformation = 1; - private const uint PROCESS_ALL = 0x001F0FFF; - private const uint DUPLICATE_CLOSE_SOURCE = 0x0001; - private const uint DUPLICATE_SAME_ACCESS = 0x0002; - - // I don't think this is the most ideal and optimized solution. Feel free to create a pull request with improvements / refactoring - public static void CloseAllHandles() - { - // Get all processes with the name "RobloxPlayerBeta" - Process[] processes = Process.GetProcessesByName("RobloxPlayerBeta"); - - // Iterate through each process - foreach (var process in processes) - { - uint size = 0x10000; - IntPtr buffer = Marshal.AllocHGlobal((int)size); - - // Loop to query system information until the buffer is large enough - while (true) - { - // Query system information for handles - uint status = NtQuerySystemInformation(SystemHandleInformation, buffer, size, out uint returnLength); - - // If the buffer is too small, double its size and retry - if (status == 0xC0000004) - { - size *= 2; - Marshal.FreeHGlobal(buffer); - buffer = Marshal.AllocHGlobal((int)size); - } - else - { - break; - } - } - - // Read the number of handles from the buffer - int handleCount = Marshal.ReadInt32(buffer); - IntPtr ptr = new IntPtr(buffer.ToInt64() + Marshal.SizeOf(typeof(int))); - - // Iterate through each handle - for (int i = 0; i < handleCount; i++) - { - // Get handle information from the buffer - SYSTEM_HANDLE_INFORMATION handleInfo = (SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(ptr, typeof(SYSTEM_HANDLE_INFORMATION)); - - // Check if the handle belongs to the current process - if (handleInfo.ProcessId == process.Id) - { - // Open the process to get a handle - IntPtr processHandle = OpenProcess(PROCESS_ALL, false, process.Id); - if (processHandle == IntPtr.Zero) - { - // Move to the next handle if opening the process fails - ptr = new IntPtr(ptr.ToInt64() + Marshal.SizeOf(typeof(SYSTEM_HANDLE_INFORMATION))); - continue; - } - - IntPtr dupHandle = IntPtr.Zero; - // Duplicate the handle to the current process - bool success = DuplicateHandle(processHandle, new IntPtr(handleInfo.Handle), GetCurrentProcess(), out dupHandle, 0, false, DUPLICATE_SAME_ACCESS); - if (!success) - { - // Close the process handle and move to the next handle if duplication fails - CloseHandle(processHandle); - ptr = new IntPtr(ptr.ToInt64() + Marshal.SizeOf(typeof(SYSTEM_HANDLE_INFORMATION))); - continue; - } - - uint bufferSize = 0x1000; - IntPtr nameBuffer = Marshal.AllocHGlobal((int)bufferSize); - - // Query the object name information for the duplicated handle - uint status = NtQueryObject(dupHandle, ObjectNameInformation, nameBuffer, bufferSize, out uint returnLength); - - if (status != 0) - { - // Free resources and move to the next handle if querying the object name fails - Marshal.FreeHGlobal(nameBuffer); - CloseHandle(dupHandle); - CloseHandle(processHandle); - ptr = new IntPtr(ptr.ToInt64() + Marshal.SizeOf(typeof(SYSTEM_HANDLE_INFORMATION))); - continue; - } - - // Get the object name information from the buffer - OBJECT_NAME_INFORMATION objectNameInfo = (OBJECT_NAME_INFORMATION)Marshal.PtrToStructure(nameBuffer, typeof(OBJECT_NAME_INFORMATION)); - if (objectNameInfo.Name.Length > 0) - { - // Convert the object name to a string - string name = Marshal.PtrToStringUni(objectNameInfo.Name.Buffer, objectNameInfo.Name.Length / 2); - if (name.Contains("ROBLOX_singletonEvent")) - { - // Close the handle if it matches the target name - bool success2 = DuplicateHandle(processHandle, new IntPtr(handleInfo.Handle), IntPtr.Zero, out _, 0, false, DUPLICATE_CLOSE_SOURCE); - } - } - - // Free resources - Marshal.FreeHGlobal(nameBuffer); - CloseHandle(dupHandle); - CloseHandle(processHandle); - } - // Move to the next handle - ptr = new IntPtr(ptr.ToInt64() + Marshal.SizeOf(typeof(SYSTEM_HANDLE_INFORMATION))); - } - - // Free the allocated buffer - Marshal.FreeHGlobal(buffer); - } - } - } -} \ No newline at end of file diff --git a/MultiBloxy/Localization.cs b/MultiBloxy/Localization.cs deleted file mode 100644 index a5c7afd..0000000 --- a/MultiBloxy/Localization.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System.Collections.Generic; -using System.Globalization; - -namespace MultiBloxy -{ - public class Localization - { - private readonly Dictionary> _translations; - private string _currentLocale; - - public Localization() - { - _translations = new Dictionary>(); - _currentLocale = CultureInfo.CurrentCulture.Name; - LoadTranslations(); - } - - private void LoadTranslations() - { - _translations["en"] = new Dictionary - { - { "ContextMenu.StatusMenuItem.Running", "Status: Running" }, - { "ContextMenu.StatusMenuItem.Paused", "Status: Paused" }, - { "ContextMenu.StatusMenuItem.Error", "Status: Error creating Mutex" }, - { "ContextMenu.PauseMenuItem.Pause", "Pause" }, - { "ContextMenu.PauseMenuItem.Resume", "Resume" }, - { "ContextMenu.ReloadMenuItem.Reload", "Reload" }, - { "ContextMenu.StartNewInstanceMenuItem.StartNewInstance", "Start New Roblox Instance" }, - { "ContextMenu.StopAllInstancesMenuItem.StopAllInstances", "Stop All Roblox Instances" }, - { "ContextMenu.ShowInExplorerMenuItem.ShowInExplorer", "Show In Explorer" }, - { "ContextMenu.SettingsMenuItem.Settings", "Settings" }, - { "ContextMenu.SettingsMenuItem.PauseOnLaunchMenuItem.PauseOnLaunch", "Pause on Launch" }, - { "ContextMenu.SettingsMenuItem.ResetRememberedMenuItem.ResetRemembered", "Reset Remembered Options" }, - { "ContextMenu.ExitMenuItem.Exit", "Exit" }, - { "Error.Mutex.Caption", "Failed to Create Mutex" }, - { "Error.Mutex.Message", "An error occurred while creating the Mutex. This likely happened because when {0} was launched, Roblox was already running and had registered its handle. You can do the following:" }, - { "Error.Mutex.Action.Fix", "Close the handle for all instances of Roblox" }, - { "Error.Mutex.Action.Abort", "Stop all Roblox instances" }, - { "Error.Mutex.Action.Retry", "Try again" }, - { "Error.Mutex.Action.Ignore", "Ignore the error and continue" }, - { "Error.Mutex.Action.Remember", "Remember this choice" }, - { "Error.Mutex.Action.Confirm", "Confirm" }, - { "Error.Singleton.Caption", "Singleton Error" }, - { "Error.Singleton.Message", "{0} is already running. Try looking in the system tray." } - }; - - _translations["ru"] = new Dictionary - { - { "ContextMenu.StatusMenuItem.Running", "Статус: Работает" }, - { "ContextMenu.StatusMenuItem.Paused", "Статус: Приостановлено" }, - { "ContextMenu.StatusMenuItem.Error", "Статус: Ошибка создания Mutex" }, - { "ContextMenu.PauseMenuItem.Pause", "Приостановить" }, - { "ContextMenu.PauseMenuItem.Resume", "Возобновить" }, - { "ContextMenu.ReloadMenuItem.Reload", "Перезагрузить" }, - { "ContextMenu.StartNewInstanceMenuItem.StartNewInstance", "Запустить новый экземпляр Roblox" }, - { "ContextMenu.StopAllInstancesMenuItem.StopAllInstances", "Закрыть все экземпляры Roblox" }, - { "ContextMenu.ShowInExplorerMenuItem.ShowInExplorer", "Показать в проводнике" }, - { "ContextMenu.SettingsMenuItem.Settings", "Настройки" }, - { "ContextMenu.SettingsMenuItem.PauseOnLaunchMenuItem.PauseOnLaunch", "Приостановить при запуске" }, - { "ContextMenu.SettingsMenuItem.ResetRememberedMenuItem.ResetRemembered", "Сбросить запомненные параметры" }, - { "ContextMenu.ExitMenuItem.Exit", "Выход" }, - { "Error.Mutex.Caption", "Не удалось создать Mutex" }, - { "Error.Mutex.Message", "Произошла ошибка при создании Mutex. Скорее всего, это связано с тем, что при запуске {0} Roblox уже был запущен и успел зарегистрировать свой дескриптор. Вы можете сделать следующее:" }, - { "Error.Mutex.Action.Fix", "Закрыть дескриптор для всех экземпляров Roblox" }, - { "Error.Mutex.Action.Abort", "Закрыть все экземпляры Roblox" }, - { "Error.Mutex.Action.Retry", "Попробовать снова" }, - { "Error.Mutex.Action.Ignore", "Игнорировать ошибку и продолжить" }, - { "Error.Mutex.Action.Remember", "Запомнить этот выбор" }, - { "Error.Mutex.Action.Confirm", "Подтвердить" }, - { "Error.Singleton.Caption", "Ошибка одиночного экземпляра" }, - { "Error.Singleton.Message", "{0} уже запущен. Попробуйте поискать в области уведомлений." } - }; - } - - public string GetTranslation(string key) - { - string locale = GetLocaleWithoutRegion(_currentLocale); - - if (_translations.ContainsKey(locale) && _translations[locale].ContainsKey(key)) - { - return _translations[locale][key]; - } - - // Fallback to English if the translation is not found - if (_translations.ContainsKey("en") && _translations["en"].ContainsKey(key)) - { - return _translations["en"][key]; - } - - // Fallback to the key if the translation is not found - return key; - } - - private string GetLocaleWithoutRegion(string locale) - { - int index = locale.IndexOf('-'); - if (index != -1) - { - return locale.Substring(0, index); - } - return locale; - } - } -} diff --git a/MultiBloxy/Properties/AssemblyInfo.cs b/MultiBloxy/Properties/AssemblyInfo.cs deleted file mode 100644 index a904176..0000000 --- a/MultiBloxy/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,7 +0,0 @@ -using System.Reflection; - -[assembly: AssemblyTitle("MultiBloxy")] -[assembly: AssemblyProduct("MultiBloxy")] -[assembly: AssemblyCopyright("Copyright © Zgoly 2024")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/README.md b/README.md deleted file mode 100644 index 3f96f36..0000000 --- a/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# roblox-multilauncher - -## Disclaimer - -This project is a **fork** of the original [MultiBloxy](https://github.com/Zgoly/MultiBloxy). The code provided here is offered under the **MIT License** unless otherwise stated. By using this app, you agree to the terms of the license. For more information on the license and usage, refer to the [LICENSE](LICENSE) file in the repository. - -This fork includes enhancements, bug fixes, and optimizations for better performance and compatibility. - -## Features - -- **Multi-Account Support**: Run multiple Roblox accounts simultaneously with ease. -- **System Tray Integration**: Minimize the app to the system tray for quick access and background operation. -- **Customization Options**: Easily adjust settings via the system tray menu (right-click on the icon). -- **Lightweight**: Designed to run efficiently with minimal resource consumption. - -## Prerequisites - -- **Windows 7 (SP1), 8.x (8, 8.1), 10, 11** (Tested on Windows 11) -- **.NET SDK 9** (or higher) - Install via the following command: - ```powershell - winget install Microsoft.DotNet.SDK.9 - ``` -- **.NET Framework 4.8 Developer Pack** - Download from the official .NET website: - [Download .NET Framework 4.8 Developer Pack](https://dotnet.microsoft.com/en-us/download/dotnet-framework/thank-you/net48-developer-pack-offline-installer) - -## Building the App - -### 1. Clone the Repository -Clone the repository to your local machine: - -```bash -git clone https://github.com/Xelvanta/roblox-multilauncher -cd -``` - -### 2. Install Dependencies -Ensure that you have the necessary .NET SDK and Framework installed. If they are missing, follow the installation instructions in the **Prerequisites** section. - -### 3. Build the App -To build the app, run the following command in the MultiBloxy directory of the project, where the .csproj and .cs files are located: - -```bash -dotnet build -``` - -This will compile the application and place the output in the `bin\Debug` or `bin\Release` folder, depending on the build configuration. - -## Running the App - -### 1. Run the App from the Command Line -After building the app, run it using the following command: - -```bash -dotnet run -``` - -Alternatively, navigate to the output directory (`bin\Debug` or `bin\Release`) and run the executable directly: - -```bash -cd bin\Debug -MultiBloxy.exe -``` - -### 2. Access the App via the System Tray -Once the app is running, it will minimize to the **system tray**. You will find an icon for **roblox-multilauncher** there. - -- **Right-click** on the icon to access the app's menu. - -## License - -This project is a **fork** of the original [MultiBloxy](https://github.com/Zgoly/MultiBloxy) and is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. - -## Troubleshooting - -- **Missing .NET SDK or Framework**: Ensure that both the .NET SDK 9 (or higher) and the .NET Framework 4.8 Developer Pack are installed. -- **Build Errors**: If you encounter build errors, verify that your environment meets all prerequisites. You may need to clean and rebuild the project using: - ```bash - dotnet clean - dotnet build - ``` - ---- - -By **Xelvanta Group Systems** -For support or inquiries, please contact us at [enquiry.information@proton.me](mailto:enquiry.information@proton.me). -GitHub: [https://github.com/Xelvanta](https://github.com/Xelvanta) \ No newline at end of file From bcc86906664ff7d1ce0d5d9983d77f75be25a0ff Mon Sep 17 00:00:00 2001 From: Xelvanta Date: Sun, 17 Nov 2024 00:04:12 -0800 Subject: [PATCH 7/9] Replace Program.cs with the updated version from main --- MultiBloxy/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MultiBloxy/Program.cs b/MultiBloxy/Program.cs index b39fb74..a110931 100644 --- a/MultiBloxy/Program.cs +++ b/MultiBloxy/Program.cs @@ -276,7 +276,7 @@ private static void ShowAppInExplorer() // Show error message when the application is already running private static void ShowSingletonError() { - MessageBox.Show(localization.GetTranslation("Error.SingletonError.Message")); + MessageBox.Show(localization.GetTranslation("Error.Singleton.Message")); } // Toggle pause/resume state From ad4dda61b062a2b03e83632d64ed2657690988c5 Mon Sep 17 00:00:00 2001 From: Xelvanta Date: Sun, 17 Nov 2024 00:07:22 -0800 Subject: [PATCH 8/9] Copy Localization.cs from main --- MultiBloxy/Localization.cs | 92 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 MultiBloxy/Localization.cs diff --git a/MultiBloxy/Localization.cs b/MultiBloxy/Localization.cs new file mode 100644 index 0000000..feabcf1 --- /dev/null +++ b/MultiBloxy/Localization.cs @@ -0,0 +1,92 @@ +using System.Collections.Generic; +using System.Globalization; + +namespace MultiBloxy +{ + public class Localization + { + private readonly Dictionary> _translations; + private string _currentLocale; + + public Localization() + { + _translations = new Dictionary>(); + _currentLocale = CultureInfo.CurrentCulture.Name; + LoadTranslations(); + } + + private void LoadTranslations() + { + _translations["en"] = new Dictionary + { + { "ContextMenu.StatusMenuItem.Running", "Status: Running" }, + { "ContextMenu.StatusMenuItem.Paused", "Status: Paused" }, + { "ContextMenu.StatusMenuItem.Error", "Status: Error creating Mutex" }, + { "ContextMenu.PauseMenuItem.Pause", "Pause" }, + { "ContextMenu.PauseMenuItem.Resume", "Resume" }, + { "ContextMenu.ReloadMenuItem.Reload", "Reload" }, + { "ContextMenu.StartNewInstanceMenuItem.StartNewInstance", "Start New Roblox Instance" }, + { "ContextMenu.StopAllInstancesMenuItem.StopAllInstances", "Stop All Roblox Instances" }, + { "ContextMenu.ShowInExplorerMenuItem.ShowInExplorer", "Show In Explorer" }, + { "ContextMenu.SettingsMenuItem.Settings", "Settings" }, + { "ContextMenu.SettingsMenuItem.PauseOnLaunchMenuItem.PauseOnLaunch", "Pause on Launch" }, + { "ContextMenu.SettingsMenuItem.ResetRememberedMenuItem.ResetRemembered", "Reset Remembered Options" }, + { "ContextMenu.ExitMenuItem.Exit", "Exit" }, + { "Error.Mutex.Caption", "Failed to Create Mutex" }, + { "Error.Mutex.Message", "An error occurred while creating the Mutex. This likely happened because when {0} was launched, Roblox was already running and had registered its handle. Try closing Roblox and try again." }, + { "Error.Singleton.Caption", "Singleton Error" }, + { "Error.Singleton.Message", "{0} is already running. Try looking in the system tray." } + }; + + _translations["ru"] = new Dictionary + { + { "ContextMenu.StatusMenuItem.Running", "Статус: Работает" }, + { "ContextMenu.StatusMenuItem.Paused", "Статус: Приостановлено" }, + { "ContextMenu.StatusMenuItem.Error", "Статус: Ошибка создания Mutex" }, + { "ContextMenu.PauseMenuItem.Pause", "Приостановить" }, + { "ContextMenu.PauseMenuItem.Resume", "Возобновить" }, + { "ContextMenu.ReloadMenuItem.Reload", "Перезагрузить" }, + { "ContextMenu.StartNewInstanceMenuItem.StartNewInstance", "Запустить новый экземпляр Roblox" }, + { "ContextMenu.StopAllInstancesMenuItem.StopAllInstances", "Закрыть все экземпляры Roblox" }, + { "ContextMenu.ShowInExplorerMenuItem.ShowInExplorer", "Показать в проводнике" }, + { "ContextMenu.SettingsMenuItem.Settings", "Настройки" }, + { "ContextMenu.SettingsMenuItem.PauseOnLaunchMenuItem.PauseOnLaunch", "Приостановить при запуске" }, + { "ContextMenu.SettingsMenuItem.ResetRememberedMenuItem.ResetRemembered", "Сбросить запомненные параметры" }, + { "ContextMenu.ExitMenuItem.Exit", "Выход" }, + { "Error.Mutex.Caption", "Не удалось создать Mutex" }, + { "Error.Mutex.Message", "Произошла ошибка при создании Mutex. Скорее всего, это связано с тем, что при запуске {0} Roblox уже был запущен и успел зарегистрировать свой дескриптор. Попробуйте закрыть Roblox и повторить попытку." }, + { "Error.Singleton.Caption", "Ошибка одиночного экземпляра" }, + { "Error.Singleton.Message", "{0} уже запущен. Попробуйте поискать в области уведомлений." } + }; + } + + public string GetTranslation(string key) + { + string locale = GetLocaleWithoutRegion(_currentLocale); + + if (_translations.ContainsKey(locale) && _translations[locale].ContainsKey(key)) + { + return _translations[locale][key]; + } + + // Fallback to English if the translation is not found + if (_translations.ContainsKey("en") && _translations["en"].ContainsKey(key)) + { + return _translations["en"][key]; + } + + // Fallback to the key if the translation is not found + return key; + } + + private string GetLocaleWithoutRegion(string locale) + { + int index = locale.IndexOf('-'); + if (index != -1) + { + return locale.Substring(0, index); + } + return locale; + } + } +} From 959bea925a679e225473d579c5ba60d85a1ccdd7 Mon Sep 17 00:00:00 2001 From: Xelvanta Date: Sun, 17 Nov 2024 00:58:36 -0800 Subject: [PATCH 9/9] Replace .gitignore with the updated version from main --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 39f36c3..2745bc7 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,6 @@ userconfig.txt Thumbs.db ehthumbs.db .DS_Store -.idea/ ScaffoldingReadMe.txt *.suo *.user