diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 2b92de6fac..0000000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Set the default line endings to LF -* text=auto \ No newline at end of file diff --git a/.gitignore b/.gitignore index fc8f35e055..56001f55aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,418 +1,36 @@ -/DebugTools/MinecraftClientProxy.suo -/DebugTools/MinecraftClientProxy.v11.suo -/MinecraftClient.v11.suo -/MinecraftClient.suo -/MinecraftClientGUI.v11.suo -/MinecraftClientGUI.suo -/MinecraftClient.userprefs -/Other/ -/.vs/ -SessionCache.ini -.* -!/.github -/packages - -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +# Build outputs +**/bin/ +**/obj/ +publish/ -# User-specific files -*.rsuser -*.suo +# Visual Studio +.vs/ *.user +*.suo *.userosscache *.sln.docstates -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Mono auto generated files -mono_crash.* - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Ww][Ii][Nn]32/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ -[Ll]ogs/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ - -# ASP.NET Scaffolding -ScaffoldingReadMe.txt - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta -*.obj -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.log -*.tlog -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Coverlet is a free, cross platform Code Coverage Tool -coverage*.json -coverage*.xml -coverage*.info - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# NuGet Symbol Packages -*.snupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Nuget personal access tokens and Credentials -nuget.config - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx -*.appxbundle -*.appxupload - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- [Bb]ackup.rdl -*- [Bb]ackup ([0-9]).rdl -*- [Bb]ackup ([0-9][0-9]).rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# CodeRush personal settings -.cr/personal - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# Local History for Visual Studio -.localhistory/ - -# BeatPulse healthcheck temp database -healthchecksdb - -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -MigrationBackup/ - -# Ionide (cross platform F# VS Code tools) working folder -.ionide/ - -# Fody - auto-generated XML schema -FodyWeavers.xsd - -# VS Code files for those working on multiple tools -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -*.code-workspace - -# Local History for Visual Studio Code -.history/ - -# Windows Installer files from build outputs -*.cab -*.msi -*.msix -*.msm -*.msp - # JetBrains Rider .idea/ -*.sln.iml -*.sln.iml -# docs -!/docs/.vuepress -/docs/.vuepress/dist +# Klucze tymczasowe +*.pfx -# translations -/MinecraftClient/Resources/Translations/Translations.*.resx -/MinecraftClient/Resources/AsciiArt/AsciiArt.*.resx -/MinecraftClient/Resources/ConfigComments/ConfigComments.*.resx +# Logi aplikacji (generowane automatycznie) +logs/ -/docs/.vuepress/translations/*.json -!/docs/.vuepress/translations/en.json +# Pliki konfiguracyjne użytkownika (generowane przy pierwszym uruchomieniu) +settings_v3.txt +macros.txt +language.txt +MinecraftClient.ini + +# Cache MCC - wrazliwe dane (tokeny sesji, klucze profilu) - NIGDY nie wrzucac! +SessionCache.ini +ProfileKeyCache.ini +*.cache -/docs/l10n/ -/docs/.vuepress/public/MCC-README/ +# System +Thumbs.db +Desktop.ini +.DS_Store diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 5ef4ed0f99..0000000000 --- a/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "ConsoleInteractive"] - path = ConsoleInteractive - url = https://github.com/breadbyte/ConsoleInteractive - branch = main diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index a05f1bccf8..0000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Default ignored files -/.idea.MinecraftClient/.idea/workspace.xml diff --git a/ConsoleInteractive b/ConsoleInteractive deleted file mode 160000 index db2be2a7f8..0000000000 --- a/ConsoleInteractive +++ /dev/null @@ -1 +0,0 @@ -Subproject commit db2be2a7f8ea71c734ebeff6314fd2fdec73f4fc diff --git a/MinecraftClientGUI.sln b/MinecraftClientGUI.sln index 9a2d7184f7..0985d471a0 100644 --- a/MinecraftClientGUI.sln +++ b/MinecraftClientGUI.sln @@ -1,6 +1,8 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.3.11505.172 d18.3 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MinecraftClientGUI", "MinecraftClientGUI\MinecraftClientGUI.csproj", "{B9E600B3-C8F0-4BF8-85E1-C164956B0B0D}" EndProject Global @@ -9,8 +11,8 @@ Global Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B9E600B3-C8F0-4BF8-85E1-C164956B0B0D}.Debug|x86.ActiveCfg = Debug|x86 - {B9E600B3-C8F0-4BF8-85E1-C164956B0B0D}.Debug|x86.Build.0 = Debug|x86 + {B9E600B3-C8F0-4BF8-85E1-C164956B0B0D}.Debug|x86.ActiveCfg = Release|x86 + {B9E600B3-C8F0-4BF8-85E1-C164956B0B0D}.Debug|x86.Build.0 = Release|x86 {B9E600B3-C8F0-4BF8-85E1-C164956B0B0D}.Release|x86.ActiveCfg = Release|x86 {B9E600B3-C8F0-4BF8-85E1-C164956B0B0D}.Release|x86.Build.0 = Release|x86 EndGlobalSection diff --git a/MinecraftClientGUI/Form1.Designer.cs b/MinecraftClientGUI/Form1.Designer.cs index ba2c948abd..abe8e0a5ba 100644 --- a/MinecraftClientGUI/Form1.Designer.cs +++ b/MinecraftClientGUI/Form1.Designer.cs @@ -3,14 +3,14 @@ partial class Form1 { /// - /// Required designer variable. + /// Wymagana zmienna projektanta. /// private System.ComponentModel.IContainer components = null; /// - /// Clean up any resources being used. + /// Wyczyść wszystkie używane zasoby. /// - /// true if managed resources should be disposed; otherwise, false. + /// prawda, jeżeli zarządzane zasoby powinny zostać zlikwidowane; Fałsz w przeciwnym wypadku. protected override void Dispose(bool disposing) { if (disposing && (components != null)) @@ -20,154 +20,28 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } - #region Windows Form Designer generated code + #region Kod generowany przez Projektanta formularzy systemu Windows /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. + /// Metoda wymagana do obsługi projektanta — nie należy modyfikować + /// jej zawartości w edytorze kodu. /// private void InitializeComponent() { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1)); - this.groupBox_Login = new System.Windows.Forms.GroupBox(); - this.btn_connect = new System.Windows.Forms.Button(); - this.box_ip = new System.Windows.Forms.TextBox(); - this.box_password = new System.Windows.Forms.TextBox(); - this.box_Login = new System.Windows.Forms.TextBox(); - this.box_output = new System.Windows.Forms.RichTextBox(); - this.box_input = new System.Windows.Forms.TextBox(); - this.btn_send = new System.Windows.Forms.Button(); - this.btn_about = new System.Windows.Forms.Button(); - this.groupBox_Login.SuspendLayout(); + this.components = new System.ComponentModel.Container(); this.SuspendLayout(); // - // groupBox_Login - // - this.groupBox_Login.BackColor = System.Drawing.Color.Transparent; - this.groupBox_Login.Controls.Add(this.btn_connect); - this.groupBox_Login.Controls.Add(this.box_ip); - this.groupBox_Login.Controls.Add(this.box_password); - this.groupBox_Login.Controls.Add(this.box_Login); - this.groupBox_Login.Location = new System.Drawing.Point(13, 11); - this.groupBox_Login.Name = "groupBox_Login"; - this.groupBox_Login.Size = new System.Drawing.Size(564, 46); - this.groupBox_Login.TabIndex = 0; - this.groupBox_Login.TabStop = false; - this.groupBox_Login.Text = " "; - // - // btn_connect - // - this.btn_connect.Location = new System.Drawing.Point(513, 15); - this.btn_connect.Name = "btn_connect"; - this.btn_connect.Size = new System.Drawing.Size(40, 23); - this.btn_connect.TabIndex = 6; - this.btn_connect.Text = "Go!"; - this.btn_connect.UseVisualStyleBackColor = true; - this.btn_connect.Click += new System.EventHandler(this.btn_connect_Click); - // - // box_ip - // - this.box_ip.Location = new System.Drawing.Point(400, 17); - this.box_ip.Name = "box_ip"; - this.box_ip.Size = new System.Drawing.Size(100, 20); - this.box_ip.TabIndex = 5; - this.box_ip.KeyUp += new System.Windows.Forms.KeyEventHandler(this.loginBox_KeyUp); - // - // box_password - // - this.box_password.Location = new System.Drawing.Point(235, 17); - this.box_password.Name = "box_password"; - this.box_password.PasswordChar = '•'; - this.box_password.Size = new System.Drawing.Size(100, 20); - this.box_password.TabIndex = 3; - this.box_password.KeyUp += new System.Windows.Forms.KeyEventHandler(this.loginBox_KeyUp); - // - // box_Login - // - this.box_Login.Location = new System.Drawing.Point(67, 17); - this.box_Login.Name = "box_Login"; - this.box_Login.Size = new System.Drawing.Size(100, 20); - this.box_Login.TabIndex = 1; - this.box_Login.KeyUp += new System.Windows.Forms.KeyEventHandler(this.loginBox_KeyUp); - // - // box_output - // - this.box_output.Location = new System.Drawing.Point(13, 66); - this.box_output.Name = "box_output"; - this.box_output.ReadOnly = true; - this.box_output.Size = new System.Drawing.Size(564, 292); - this.box_output.TabIndex = 1; - this.box_output.Text = ""; - this.box_output.LinkClicked += new System.Windows.Forms.LinkClickedEventHandler(this.LinkClicked); - // - // box_input - // - this.box_input.AcceptsTab = true; - this.box_input.Location = new System.Drawing.Point(13, 365); - this.box_input.MaxLength = 100; - this.box_input.Multiline = true; - this.box_input.Name = "box_input"; - this.box_input.Size = new System.Drawing.Size(490, 20); - this.box_input.TabIndex = 2; - this.box_input.KeyDown += new System.Windows.Forms.KeyEventHandler(this.inputBox_KeyDown); - // - // btn_send - // - this.btn_send.Location = new System.Drawing.Point(509, 364); - this.btn_send.Name = "btn_send"; - this.btn_send.Size = new System.Drawing.Size(40, 22); - this.btn_send.TabIndex = 3; - this.btn_send.Text = "Send"; - this.btn_send.UseVisualStyleBackColor = true; - this.btn_send.Click += new System.EventHandler(this.btn_send_Click); - // - // btn_about - // - this.btn_about.Location = new System.Drawing.Point(555, 364); - this.btn_about.Name = "btn_about"; - this.btn_about.Size = new System.Drawing.Size(22, 22); - this.btn_about.TabIndex = 4; - this.btn_about.Text = "?"; - this.btn_about.UseVisualStyleBackColor = true; - this.btn_about.Click += new System.EventHandler(this.btn_about_Click); - // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.BackColor = System.Drawing.SystemColors.Control; - this.ClientSize = new System.Drawing.Size(589, 398); - this.Controls.Add(this.btn_about); - this.Controls.Add(this.btn_send); - this.Controls.Add(this.box_input); - this.Controls.Add(this.box_output); - this.Controls.Add(this.groupBox_Login); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.MaximizeBox = false; + this.ClientSize = new System.Drawing.Size(1100, 700); this.Name = "Form1"; + this.Text = "MCC Multibox Commander"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Minecraft Console Client GUI"; - this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.onClose); - this.Load += new System.EventHandler(this.Form1_Load); - this.groupBox_Login.ResumeLayout(false); - this.groupBox_Login.PerformLayout(); this.ResumeLayout(false); - this.PerformLayout(); - } #endregion - - private System.Windows.Forms.GroupBox groupBox_Login; - private System.Windows.Forms.Button btn_connect; - private System.Windows.Forms.TextBox box_ip; - private System.Windows.Forms.TextBox box_password; - private System.Windows.Forms.TextBox box_Login; - private System.Windows.Forms.RichTextBox box_output; - private System.Windows.Forms.TextBox box_input; - private System.Windows.Forms.Button btn_send; - private System.Windows.Forms.Button btn_about; } } - diff --git a/MinecraftClientGUI/Form1.cs b/MinecraftClientGUI/Form1.cs index 189cab59e9..42eecc643c 100644 --- a/MinecraftClientGUI/Form1.cs +++ b/MinecraftClientGUI/Form1.cs @@ -1,337 +1,767 @@ using System; using System.Collections.Generic; -using System.ComponentModel; -using System.Data; +using System.Diagnostics; using System.Drawing; +using System.IO; using System.Linq; -using System.Text; -using System.Windows.Forms; +using System.Runtime.InteropServices; using System.Threading; +using System.Windows.Forms; namespace MinecraftClientGUI { - /// - /// The main graphical user interface - /// - - public partial class Form1 : Form + static class Theme { - private LinkedList previous = new LinkedList(); - private MinecraftClient Client; - private Thread t_clientread; - - #region Aero Glass Low-level Windows API + public static Color BgDark = Color.FromArgb(15, 15, 18); + public static Color BgPanel = Color.FromArgb(22, 22, 28); + public static Color BgHeader = Color.FromArgb(28, 28, 36); + public static Color BgCard = Color.FromArgb(32, 32, 42); + public static Color BgInput = Color.FromArgb(20, 20, 26); + public static Color TabActive = Color.FromArgb(38, 38, 52); + public static Color TabInactive = Color.FromArgb(22, 22, 28); + public static Color Accent = Color.FromArgb(82, 130, 255); + public static Color AccentHover = Color.FromArgb(110, 155, 255); + public static Color AccentRed = Color.FromArgb(220, 70, 70); + public static Color AccentGreen = Color.FromArgb(60, 200, 100); + public static Color Text = Color.FromArgb(220, 220, 230); + public static Color TextDim = Color.FromArgb(120, 120, 140); + public static Color TextMuted = Color.FromArgb(70, 70, 90); + public static Color Border = Color.FromArgb(40, 40, 55); + } - [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] - public struct MARGINS + class DarkComboBox : ComboBox + { + public DarkComboBox() { - public int Left; - public int Right; - public int Top; - public int Bottom; + DrawMode = DrawMode.OwnerDrawFixed; + FlatStyle = FlatStyle.Flat; + BackColor = Theme.BgInput; + ForeColor = Theme.Text; + Font = new Font("Segoe UI", 9f); } + protected override void OnDrawItem(DrawItemEventArgs e) + { + if (e.Index < 0) return; + e.Graphics.FillRectangle( + new SolidBrush((e.State & DrawItemState.Selected) != 0 ? Theme.TabActive : Theme.BgInput), + e.Bounds); + TextRenderer.DrawText(e.Graphics, Items[e.Index].ToString(), Font, e.Bounds, + Theme.Text, TextFormatFlags.VerticalCenter | TextFormatFlags.Left); + } + } - [System.Runtime.InteropServices.DllImport("dwmapi.dll")] - public static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMargins); + class FlatBtn : Button + { + private Color _back, _hover; + public FlatBtn(string text, Color back, Color? hover = null) + { + Text = text; + _back = back; + _hover = hover ?? ControlPaint.Light(back, 0.2f); + FlatStyle = FlatStyle.Flat; + FlatAppearance.BorderSize = 0; + BackColor = _back; + ForeColor = Theme.Text; + Font = new Font("Segoe UI", 9f, FontStyle.Bold); + Cursor = Cursors.Hand; + MouseEnter += (s, e) => BackColor = _hover; + MouseLeave += (s, e) => BackColor = _back; + } + } - #endregion + public partial class Form1 : Form + { + private const string SettingsFile = "settings_v3.txt"; + private const string MacrosFile = "macros.txt"; + private string currentLang = "en"; + + private Panel tabBar, contentArea, topPanel, bottomPanel, rightPanel; + private Label lblLogin, lblPass, lblIP, lblActive; + private DarkComboBox cmbLogin, cmbIP; + private TextBox txtPassword; + private FlatBtn btnAddBot; + private TextBox boxGlobalInput; + private FlatBtn btnGlobalSend; + private CheckBox chkSendToAll; + private Label lblMacrosTitle; + private FlowLayoutPanel macroPanel; + private FlatBtn btnEditMacros, btnRefreshMacros, btnLangSwitch; + + private List historyLogins = new List(); + private List historyIPs = new List(); + private List tabs = new List(); + private ConsoleTab activeTab = null; public Form1(string[] args) { InitializeComponent(); - if (args.Length > 0) { initClient(new MinecraftClient(args)); } + BuildUI(); + LoadSettings(); + LoadMacros(); + UpdateLanguage(); + if (args.Length > 0) AddNewTab("Auto-Bot", args); + this.FormClosing += (s, e) => { foreach (var t in tabs.ToList()) t.CloseTab(); }; } - /// - /// Define some element properties and init Aero Glass if using Vista or newer - /// - - private void Form1_Load(object sender, EventArgs e) + private void BuildUI() { - box_output.ScrollBars = RichTextBoxScrollBars.None; - box_output.Font = new Font("Consolas", 8); - box_output.BackColor = Color.White; - - if (Environment.OSVersion.Version.Major >= 6 && Environment.OSVersion.Version.Minor == 1) + this.Text = "MCC Multibox Commander"; + this.Size = new Size(1200, 780); + this.MinimumSize = new Size(900, 600); + this.BackColor = Theme.BgDark; + this.ForeColor = Theme.Text; + this.Font = new Font("Segoe UI", 9f); + this.StartPosition = FormStartPosition.CenterScreen; + + // GORNY PANEL + topPanel = new Panel { Dock = DockStyle.Top, Height = 60, BackColor = Theme.BgHeader, Padding = new Padding(12, 0, 12, 0) }; + topPanel.Paint += PaintBottomBorder; + this.Controls.Add(topPanel); + + int y = 17; + lblLogin = MkLabel("Username / Email:", 10, 2); topPanel.Controls.Add(lblLogin); + cmbLogin = new DarkComboBox { Location = new Point(10, y), Size = new Size(195, 26) }; topPanel.Controls.Add(cmbLogin); + + lblPass = MkLabel("Password:", 215, 2); topPanel.Controls.Add(lblPass); + txtPassword = new TextBox { Location = new Point(215, y), Size = new Size(155, 26), BackColor = Theme.BgInput, ForeColor = Theme.Text, BorderStyle = BorderStyle.FixedSingle, UseSystemPasswordChar = true, Font = new Font("Segoe UI", 9f) }; + topPanel.Controls.Add(txtPassword); + + lblIP = MkLabel("Server IP:", 380, 2); topPanel.Controls.Add(lblIP); + cmbIP = new DarkComboBox { Location = new Point(380, y), Size = new Size(215, 26) }; topPanel.Controls.Add(cmbIP); + + btnAddBot = new FlatBtn("+ Add Account", Theme.Accent, Theme.AccentHover) { Location = new Point(608, y - 1), Size = new Size(148, 28) }; + btnAddBot.Click += BtnAddBot_Click; + topPanel.Controls.Add(btnAddBot); + + lblActive = new Label { Location = new Point(770, y), AutoSize = true, ForeColor = Theme.AccentGreen, Font = new Font("Segoe UI", 9f, FontStyle.Bold) }; + topPanel.Controls.Add(lblActive); + + var timer = new System.Windows.Forms.Timer { Interval = 1000 }; + timer.Tick += (s, e) => lblActive.Text = (currentLang == "en" ? "Active accounts: " : "Aktywne konta: ") + tabs.Count; + timer.Start(); + + // PRAWY PANEL + rightPanel = new Panel { Dock = DockStyle.Right, Width = 185, BackColor = Theme.BgPanel }; + rightPanel.Paint += PaintLeftBorder; + this.Controls.Add(rightPanel); + + // Jezyk - duzy przycisk u gory + btnLangSwitch = new FlatBtn("PL", Color.FromArgb(45, 75, 145), Color.FromArgb(60, 100, 185)) { - this.BackColor = Color.DarkMagenta; this.TransparencyKey = Color.DarkMagenta; - MARGINS marg = new MARGINS() { Left = -1, Right = -1, Top = -1, Bottom = -1 }; - DwmExtendFrameIntoClientArea(this.Handle, ref marg); - } + Dock = DockStyle.Top, + Height = 36, + Font = new Font("Segoe UI", 11f, FontStyle.Bold), + ForeColor = Color.White + }; + btnLangSwitch.Click += (s, e) => { currentLang = currentLang == "en" ? "pl" : "en"; UpdateLanguage(); }; + rightPanel.Controls.Add(btnLangSwitch); + + // Naglowek Makr + var macroHeader = new Panel { Dock = DockStyle.Top, Height = 52, BackColor = Theme.BgPanel }; + macroHeader.Paint += PaintBottomBorder; + rightPanel.Controls.Add(macroHeader); + + lblMacrosTitle = new Label { Text = "Quick Actions", Location = new Point(8, 8), Size = new Size(169, 18), ForeColor = Theme.Text, Font = new Font("Segoe UI", 9f, FontStyle.Bold) }; + macroHeader.Controls.Add(lblMacrosTitle); + + btnEditMacros = new FlatBtn("Edit", Color.FromArgb(40, 40, 58)) { Location = new Point(8, 28), Size = new Size(76, 20), Font = new Font("Segoe UI", 8f, FontStyle.Bold) }; + btnEditMacros.Click += BtnEditMacros_Click; + macroHeader.Controls.Add(btnEditMacros); + + btnRefreshMacros = new FlatBtn("Reload", Color.FromArgb(40, 40, 58)) { Location = new Point(90, 28), Size = new Size(76, 20), Font = new Font("Segoe UI", 8f, FontStyle.Bold) }; + btnRefreshMacros.Click += (s, e) => LoadMacros(); + macroHeader.Controls.Add(btnRefreshMacros); + + macroPanel = new FlowLayoutPanel { Dock = DockStyle.Fill, FlowDirection = FlowDirection.TopDown, WrapContents = false, AutoScroll = true, BackColor = Theme.BgPanel, Padding = new Padding(8, 8, 0, 8) }; + rightPanel.Controls.Add(macroPanel); + + rightPanel.Controls.SetChildIndex(macroPanel, 0); + rightPanel.Controls.SetChildIndex(macroHeader, 1); + rightPanel.Controls.SetChildIndex(btnLangSwitch, 2); + + // DOLNY PANEL + bottomPanel = new Panel { Dock = DockStyle.Bottom, Height = 40, BackColor = Theme.BgHeader, Padding = new Padding(6, 6, 6, 0) }; + bottomPanel.Paint += PaintTopBorder; + this.Controls.Add(bottomPanel); + + btnGlobalSend = new FlatBtn("Send", Color.FromArgb(50, 90, 160), Theme.Accent) { Dock = DockStyle.Right, Width = 80 }; + btnGlobalSend.Click += BtnGlobalSend_Click; + bottomPanel.Controls.Add(btnGlobalSend); + + chkSendToAll = new CheckBox { Text = "Send to all", Dock = DockStyle.Right, Width = 120, ForeColor = Color.FromArgb(255, 160, 90), Padding = new Padding(8, 0, 0, 0), Font = new Font("Segoe UI", 9f, FontStyle.Bold) }; + bottomPanel.Controls.Add(chkSendToAll); + + boxGlobalInput = new TextBox { Dock = DockStyle.Fill, BackColor = Theme.BgInput, ForeColor = Theme.Text, BorderStyle = BorderStyle.FixedSingle, Font = new Font("Consolas", 11f) }; + boxGlobalInput.KeyDown += (s, e) => { if (e.KeyCode == Keys.Enter) { BtnGlobalSend_Click(s, e); e.SuppressKeyPress = true; } }; + bottomPanel.Controls.Add(boxGlobalInput); + + // PASEK ZAKLADEK + tabBar = new Panel { Dock = DockStyle.Top, Height = 38, BackColor = Theme.BgPanel }; + tabBar.Paint += PaintBottomBorder; + this.Controls.Add(tabBar); + + // OBSZAR ZAWARTOSCI + contentArea = new Panel { Dock = DockStyle.Fill, BackColor = Theme.BgDark }; + this.Controls.Add(contentArea); + + this.Controls.SetChildIndex(contentArea, 0); + this.Controls.SetChildIndex(tabBar, 1); + this.Controls.SetChildIndex(bottomPanel, 2); + this.Controls.SetChildIndex(rightPanel, 3); + this.Controls.SetChildIndex(topPanel, 4); } - /// - /// Launch the Minecraft Client by clicking the "Go!" button. - /// If a client is already running, it will be closed. - /// - - private void btn_connect_Click(object sender, EventArgs e) + private void AddNewTab(string title, string[] args) { - if (Client != null) - { - Client.Close(); - t_clientread.Abort(); - box_output.Text = ""; - } - string username = box_Login.Text; - string password = box_password.Text; - string serverip = box_ip.Text; - if (password == "") { password = "-"; } - if (username != "" && serverip != "") - { - initClient(new MinecraftClient(username, password, serverip)); - } + var tab = new ConsoleTab(title, args, currentLang) { Dock = DockStyle.Fill }; + tabs.Add(tab); + contentArea.Controls.Add(tab); + RebuildTabBar(); + ActivateTab(tab); } - /// - /// Handle a new Minecraft Client - /// - /// Client to handle - - private void initClient(MinecraftClient client) + private void ActivateTab(ConsoleTab tab) { - Client = client; - t_clientread = new Thread(new ThreadStart(t_clientread_loop)); - t_clientread.Start(); - box_input.Select(); + activeTab = tab; + foreach (Control c in contentArea.Controls) c.Visible = (c == tab); + RebuildTabBar(); } - /// - /// Thread reading output from the Minecraft Client - /// - - private void t_clientread_loop() + private void RebuildTabBar() { - while (true && !Client.Disconnected) + tabBar.Controls.Clear(); + int x = 4; + foreach (var tab in tabs) { - printstring(Client.ReadLine()); + var t = tab; + bool active = (t == activeTab); + + var btn = new Panel { Location = new Point(x, active ? 2 : 5), Size = new Size(148, active ? 32 : 27), BackColor = active ? Theme.TabActive : Theme.TabInactive, Cursor = Cursors.Hand }; + btn.Paint += (s, e) => { + if (t == activeTab) + e.Graphics.FillRectangle(new SolidBrush(Theme.Accent), 0, btn.Height - 2, btn.Width, 2); + }; + + var lbl = new Label + { + Text = t.TabTitle, + Location = new Point(8, 0), + Size = new Size(108, 30), + ForeColor = active ? Theme.Text : Theme.TextDim, + Font = new Font("Segoe UI", 9f, active ? FontStyle.Bold : FontStyle.Regular), + TextAlign = ContentAlignment.MiddleLeft, + Cursor = Cursors.Hand + }; + lbl.Click += (s, e) => ActivateTab(t); + btn.Click += (s, e) => ActivateTab(t); + btn.Controls.Add(lbl); + + var btnX = new Label + { + Text = "x", + Location = new Point(120, 0), + Size = new Size(25, 30), + ForeColor = Theme.TextMuted, + Font = new Font("Segoe UI", 9f), + TextAlign = ContentAlignment.MiddleCenter, + Cursor = Cursors.Hand + }; + btnX.MouseEnter += (s, e) => btnX.ForeColor = Theme.AccentRed; + btnX.MouseLeave += (s, e) => btnX.ForeColor = Theme.TextMuted; + btnX.Click += (s, e) => { + t.CloseTab(); + tabs.Remove(t); + contentArea.Controls.Remove(t); + if (activeTab == t) { activeTab = tabs.LastOrDefault(); if (activeTab != null) ActivateTab(activeTab); } + RebuildTabBar(); + }; + btn.Controls.Add(btnX); + tabBar.Controls.Add(btn); + x += 152; } } - /// - /// Print a Minecraft-Formatted string to the console area - /// - /// String to print + private void UpdateLanguage() + { + bool en = currentLang == "en"; + btnLangSwitch.Text = en ? "Switch to PL" : "Switch to EN"; + lblLogin.Text = en ? "Username / Email:" : "Login / Email:"; + lblPass.Text = en ? "Password:" : "Haslo:"; + lblIP.Text = en ? "Server IP:" : "IP Serwera:"; + btnAddBot.Text = en ? "+ Add Account" : "+ Dodaj Konto"; + lblMacrosTitle.Text = en ? "Quick Actions" : "Szybkie Akcje"; + btnEditMacros.Text = en ? "Edit" : "Edytuj"; + btnRefreshMacros.Text = en ? "Reload" : "Odswiez"; + btnGlobalSend.Text = en ? "Send" : "Wyslij"; + chkSendToAll.Text = en ? "Send to all" : "Wyslij do wszystkich"; + foreach (var tab in tabs) tab.UpdateLang(currentLang); + } + + private void BtnEditMacros_Click(object sender, EventArgs e) + { + if (!File.Exists(MacrosFile)) + File.WriteAllLines(MacrosFile, new[] { "Creative|/gamemode creative|Gold", "Survival|/gamemode survival|Gray", "Hello|Hello everyone!|Green", "Login|/login password123|Purple", "Spawn|/spawn|Blue" }); + Process.Start("notepad.exe", MacrosFile); + } - private void printstring(string str) + private void LoadMacros() { - if (!String.IsNullOrEmpty(str)) + macroPanel.Controls.Clear(); + if (!File.Exists(MacrosFile)) return; + try { - Color color = Color.Black; - FontStyle style = FontStyle.Regular; - string[] subs = str.Split('§'); - if (subs[0].Length > 0) { AppendTextBox(box_output, subs[0], Color.Black, FontStyle.Regular); } - for (int i = 1; i < subs.Length; i++) + foreach (var line in File.ReadAllLines(MacrosFile)) { - if (subs[i].Length > 0) + if (string.IsNullOrWhiteSpace(line)) continue; + var parts = line.Split('|'); + if (parts.Length >= 2) { - if (subs[i].Length > 1) - { - switch (subs[i][0]) - { - //Font colors - case '0': color = Color.Black; break; - case '1': color = Color.DarkBlue; break; - case '2': color = Color.DarkGreen; break; - case '3': color = Color.DarkCyan; break; - case '4': color = Color.DarkRed; break; - case '5': color = Color.DarkMagenta; break; - case '6': color = Color.DarkGoldenrod; break; - case '7': color = Color.DimGray; break; - case '8': color = Color.Gray; break; - case '9': color = Color.Blue; break; - case 'a': color = Color.Green; break; - case 'b': color = Color.CornflowerBlue; break; - case 'c': color = Color.Red; break; - case 'd': color = Color.Magenta; break; - case 'e': color = Color.Goldenrod; break; - - //White on white = invisible so use gray instead - case 'f': color = Color.DimGray; break; - - //Font styles. Can use several styles eg Bold + Underline - case 'l': style = style | FontStyle.Bold; break; - case 'm': style = style | FontStyle.Strikeout; break; - case 'n': style = style | FontStyle.Underline; break; - case 'o': style = style | FontStyle.Italic; break; - - //Reset font color & style - case 'r': color = Color.Black; style = FontStyle.Regular; break; - } - - AppendTextBox(box_output, subs[i].Substring(1, subs[i].Length - 1), color, style); - } + Color c = parts.Length > 2 ? Color.FromName(parts[2]) : Theme.Accent; + if (c.IsEmpty) c = Theme.Accent; + AddMacroBtn(parts[1], parts[0], c); } } - AppendTextBox(box_output, "\n", Color.Black, FontStyle.Regular); } - Console.ForegroundColor = ConsoleColor.Gray; + catch (Exception ex) { MessageBox.Show("Error loading macros: " + ex.Message); } } - /// - /// Append text to a RichTextBox with font customization - /// - /// Target RichTextBox - /// Text to add - /// Color of the text - /// Font style of the text + private void AddMacroBtn(string cmd, string label, Color color) + { + Color bg = Color.FromArgb(32, 32, 48); + Color hov = Color.FromArgb(44, 44, 64); + var btn = new Button + { + Text = " " + label, + Width = macroPanel.Width - 22, + Height = 34, + FlatStyle = FlatStyle.Flat, + BackColor = bg, + ForeColor = Theme.Text, + Font = new Font("Segoe UI", 9f, FontStyle.Bold), + Cursor = Cursors.Hand, + Margin = new Padding(0, 0, 0, 4), + TextAlign = ContentAlignment.MiddleLeft, + Tag = color + }; + btn.FlatAppearance.BorderSize = 0; + btn.MouseEnter += (s, e) => btn.BackColor = hov; + btn.MouseLeave += (s, e) => btn.BackColor = bg; + btn.Paint += (s, e) => e.Graphics.FillRectangle(new SolidBrush(color), 0, 0, 4, btn.Height); + btn.Click += (s, e) => { + if (chkSendToAll.Checked) foreach (var tab in tabs) tab.Send(cmd); + else activeTab?.Send(cmd); + }; + macroPanel.Controls.Add(btn); + } - private void AppendTextBox(RichTextBox box, string text, Color color, FontStyle style) + private void BtnAddBot_Click(object sender, EventArgs e) { - if (InvokeRequired) + string user = cmbLogin.Text.Trim(), pass = txtPassword.Text.Trim(), ip = cmbIP.Text.Trim(); + if (string.IsNullOrEmpty(user) || string.IsNullOrEmpty(ip)) { - this.Invoke(new Action(AppendTextBox), new object[] { box, text, color, style }); + MessageBox.Show(currentLang == "en" ? "Please enter username and IP!" : "Podaj login i IP serwera!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; } - else + SaveSettings(user, ip); + string tabTitle = user.Contains("@") ? user.Split('@')[0] : user; + AddNewTab(tabTitle, new[] { user, pass, ip }); + } + + private void BtnGlobalSend_Click(object sender, EventArgs e) + { + string cmd = boxGlobalInput.Text.Trim(); + if (string.IsNullOrEmpty(cmd)) return; + if (chkSendToAll.Checked) foreach (var tab in tabs) tab.Send(cmd); + else activeTab?.Send(cmd); + boxGlobalInput.Clear(); + } + + private void LoadSettings() + { + try { - box.SelectionStart = box.TextLength; - box.SelectionLength = 0; - box.SelectionColor = color; - box.SelectionFont = new Font(box.Font, style); - box.AppendText(text); - box.SelectionColor = box.ForeColor; - box.SelectionStart = box.Text.Length; - box.ScrollToCaret(); + if (!File.Exists(SettingsFile)) return; + var lines = File.ReadAllLines(SettingsFile).ToList(); + if (lines.Count > 0) txtPassword.Text = lines[0]; + if (lines.Count > 1) { historyLogins = lines[1].Split('|').ToList(); cmbLogin.Items.AddRange(historyLogins.ToArray()); if (cmbLogin.Items.Count > 0) cmbLogin.SelectedIndex = 0; } + if (lines.Count > 2) { historyIPs = lines[2].Split('|').ToList(); cmbIP.Items.AddRange(historyIPs.ToArray()); if (cmbIP.Items.Count > 0) cmbIP.SelectedIndex = 0; } } + catch { } } - /// - /// Properly disconnect the client when clicking the [X] close button - /// - - protected void onClose(object sender, EventArgs e) + private void SaveSettings(string user, string ip) { - if (t_clientread != null) { t_clientread.Abort(); } - if (Client != null) { new Thread(new ThreadStart(Client.Close)).Start(); } + historyLogins.Remove(user); historyLogins.Insert(0, user); if (historyLogins.Count > 10) historyLogins.RemoveAt(10); + historyIPs.Remove(ip); historyIPs.Insert(0, ip); if (historyIPs.Count > 10) historyIPs.RemoveAt(10); + cmbLogin.Items.Clear(); cmbLogin.Items.AddRange(historyLogins.ToArray()); cmbLogin.Text = user; + cmbIP.Items.Clear(); cmbIP.Items.AddRange(historyIPs.ToArray()); cmbIP.Text = ip; + try { File.WriteAllLines(SettingsFile, new[] { txtPassword.Text, string.Join("|", historyLogins), string.Join("|", historyIPs) }); } catch { } } - /// - /// Allows an Enter keypress in "Login", "Password" or "Server IP" box to be considered as a click on the "Go!" button - /// - /// - /// + private void PaintBottomBorder(object sender, PaintEventArgs e) { var c = (Control)sender; e.Graphics.DrawLine(new Pen(Theme.Border), 0, c.Height - 1, c.Width, c.Height - 1); } + private void PaintTopBorder(object sender, PaintEventArgs e) { var c = (Control)sender; e.Graphics.DrawLine(new Pen(Theme.Border), 0, 0, c.Width, 0); } + private void PaintLeftBorder(object sender, PaintEventArgs e) { var c = (Control)sender; e.Graphics.DrawLine(new Pen(Theme.Border), 0, 0, 0, c.Height); } + private Label MkLabel(string text, int x, int y) => new Label { Text = text, Location = new Point(x, y), AutoSize = true, ForeColor = Theme.TextDim, Font = new Font("Segoe UI", 8f) }; + } + + // ===================================================== + // TYPY LINII (do filtrowania) + // ===================================================== + enum LineType { Chat, System, Error } + + struct LogLine + { + public string Raw; + public string Display; + public LineType Type; + public DateTime Time; + } + + public class ConsoleTab : Panel + { + private MinecraftClient Client; + private Thread t_read; + private RichTextBox boxOutput; + private Button btnDisconnect; + private Label lblStatus; + private Label lblTimer; + private CheckBox chkAutoScroll; + private bool autoScroll = true; + + private Button btnFilterAll, btnFilterChat, btnFilterSystem, btnFilterError; + private LineType? activeFilter = null; + + private List allLines = new List(); + private object logLock = new object(); + + private StreamWriter logWriter; + private DateTime connectedAt; + private System.Windows.Forms.Timer timerClock; + private bool isConnected = false; - public void loginBox_KeyUp(object sender, KeyEventArgs e) + public string TabTitle { get; set; } + + [DllImport("user32.dll")] public static extern IntPtr SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam); + [DllImport("uxtheme.dll", ExactSpelling = true, CharSet = CharSet.Unicode)] + private static extern int SetWindowTheme(IntPtr hwnd, string pszSubAppName, string pszSubIdList); + private const int WM_VSCROLL = 0x115, SB_BOTTOM = 7; + + public ConsoleTab(string title, string[] args, string lang) + { + this.TabTitle = title; + this.BackColor = Theme.BgDark; + + InitLogFile(title); + + // Gorny pasek + var topBar = new Panel { Dock = DockStyle.Top, Height = 36, BackColor = Theme.BgCard }; + topBar.Paint += (s, e) => e.Graphics.DrawLine(new Pen(Theme.Border), 0, topBar.Height - 1, topBar.Width, topBar.Height - 1); + + btnDisconnect = new Button + { + Text = lang == "en" ? "Disconnect" : "Rozlacz", + Dock = DockStyle.Right, + Width = 105, + FlatStyle = FlatStyle.Flat, + BackColor = Color.FromArgb(160, 45, 45), + ForeColor = Color.White, + Font = new Font("Segoe UI", 9f, FontStyle.Bold), + Cursor = Cursors.Hand + }; + btnDisconnect.FlatAppearance.BorderSize = 0; + btnDisconnect.Click += (s, e) => CloseTab(); + topBar.Controls.Add(btnDisconnect); + + chkAutoScroll = new CheckBox + { + Text = "Auto-scroll", + Dock = DockStyle.Right, + Width = 95, + ForeColor = Theme.TextDim, + Font = new Font("Segoe UI", 8f), + Checked = true, + Padding = new Padding(0, 0, 12, 0) + }; + chkAutoScroll.CheckedChanged += (s, e) => autoScroll = chkAutoScroll.Checked; + topBar.Controls.Add(chkAutoScroll); + + lblTimer = new Label + { + Text = "00:00:00", + Dock = DockStyle.Right, + Width = 70, + ForeColor = Theme.TextDim, + Font = new Font("Consolas", 8f), + TextAlign = ContentAlignment.MiddleCenter + }; + topBar.Controls.Add(lblTimer); + + lblStatus = new Label + { + Text = " ● " + title, + Dock = DockStyle.Fill, + ForeColor = Theme.TextMuted, + Font = new Font("Segoe UI", 9f, FontStyle.Bold), + TextAlign = ContentAlignment.MiddleLeft + }; + topBar.Controls.Add(lblStatus); + this.Controls.Add(topBar); + + // Pasek filtrow + var filterBar = new Panel { Dock = DockStyle.Top, Height = 30, BackColor = Theme.BgPanel }; + filterBar.Paint += (s, e) => e.Graphics.DrawLine(new Pen(Theme.Border), 0, filterBar.Height - 1, filterBar.Width, filterBar.Height - 1); + + btnFilterAll = MakeFilterBtn("All", null, filterBar, 4); + btnFilterChat = MakeFilterBtn("Chat", LineType.Chat, filterBar, 54); + btnFilterSystem = MakeFilterBtn("System", LineType.System, filterBar, 118); + btnFilterError = MakeFilterBtn("Errors", LineType.Error, filterBar, 192); + SetFilterActive(btnFilterAll); + this.Controls.Add(filterBar); + + // Konsola + boxOutput = new RichTextBox + { + Dock = DockStyle.Fill, + BackColor = Theme.BgDark, + ForeColor = Color.FromArgb(200, 200, 215), + Font = new Font("Consolas", 10f), + BorderStyle = BorderStyle.None, + ReadOnly = true, + ScrollBars = RichTextBoxScrollBars.Vertical + }; + var ctx = new ContextMenuStrip { BackColor = Theme.BgCard, ForeColor = Theme.Text }; + ctx.Items.Add("Disconnect / Close", null, (s, e) => CloseTab()); + ctx.Items.Add("Clear Console", null, (s, e) => { boxOutput.Clear(); lock (logLock) allLines.Clear(); }); + boxOutput.ContextMenuStrip = ctx; + this.Controls.Add(boxOutput); + + try { SetWindowTheme(boxOutput.Handle, "DarkMode_Explorer", null); } catch { } + + timerClock = new System.Windows.Forms.Timer { Interval = 1000 }; + timerClock.Tick += (s, e) => { + if (isConnected && !lblTimer.IsDisposed) + { + var elapsed = DateTime.Now - connectedAt; + lblTimer.Text = elapsed.ToString(@"hh\:mm\:ss"); + } + }; + timerClock.Start(); + + PrintSystem("Initializing...", LineType.System); + if (args.Length == 3) new Thread(() => InitClient(new MinecraftClient(args[0], args[1], args[2]))).Start(); + else new Thread(() => InitClient(new MinecraftClient(args))).Start(); + } + + private void InitLogFile(string title) { - if (e.KeyCode == Keys.Enter) + try { - btn_connect_Click(sender, e); - e.Handled = true; + string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs"); + Directory.CreateDirectory(dir); + string safeName = string.Concat(title.Split(Path.GetInvalidFileNameChars())); + string date = DateTime.Now.ToString("yyyy-MM-dd_HH-mm"); + string path = Path.Combine(dir, safeName + "_" + date + ".txt"); + logWriter = new StreamWriter(path, append: true, encoding: System.Text.Encoding.UTF8) { AutoFlush = true }; + logWriter.WriteLine("=== Session started: " + DateTime.Now + " | Account: " + title + " ==="); } + catch { } } - /// - /// Handle special functions in the input box : send with Enter key, command history and tab-complete - /// - /// - /// + private void WriteLog(string text, LineType type) + { + try { logWriter?.WriteLine("[" + DateTime.Now.ToString("HH:mm:ss") + "][" + type + "] " + text); } catch { } + } - public void inputBox_KeyDown(object sender, KeyEventArgs e) + private Button MakeFilterBtn(string label, LineType? type, Panel parent, int x) { - if (e.KeyCode == Keys.Enter) + int w = label == "All" ? 44 : label == "System" ? 68 : 58; + var btn = new Button { - btn_send_Click(sender, e); - e.Handled = true; - } - else if (e.KeyCode == Keys.Down) + Text = label, + Location = new Point(x, 4), + Size = new Size(w, 22), + FlatStyle = FlatStyle.Flat, + BackColor = Theme.BgCard, + ForeColor = Theme.TextDim, + Font = new Font("Segoe UI", 8f, FontStyle.Bold), + Cursor = Cursors.Hand + }; + btn.FlatAppearance.BorderSize = 1; + btn.FlatAppearance.BorderColor = Theme.Border; + btn.Click += (s, e) => { activeFilter = type; SetFilterActive(btn); RedrawFiltered(); }; + parent.Controls.Add(btn); + return btn; + } + + private void SetFilterActive(Button active) + { + foreach (var b in new[] { btnFilterAll, btnFilterChat, btnFilterSystem, btnFilterError }) { - if (previous.Count > 0) - { - box_input.Text = previous.First.Value; - previous.AddLast(box_input.Text); - previous.RemoveFirst(); - box_input.Select(box_input.Text.Length, 0); - } - e.Handled = true; + if (b == null) continue; + b.BackColor = b == active ? Theme.Accent : Theme.BgCard; + b.ForeColor = b == active ? Color.White : Theme.TextDim; } - else if (e.KeyCode == Keys.Up) + } + + private void RedrawFiltered() + { + InvokeUI(() => { + boxOutput.Clear(); + List snapshot; + lock (logLock) snapshot = new List(allLines); + foreach (var line in snapshot) + if (activeFilter == null || line.Type == activeFilter) + RenderLine(line); + if (autoScroll) SendMessage(boxOutput.Handle, WM_VSCROLL, (IntPtr)SB_BOTTOM, IntPtr.Zero); + }); + } + + private void RenderLine(LogLine line) + { + if (line.Type == LineType.System || line.Type == LineType.Error) { - if (previous.Count > 0) - { - box_input.Text = previous.Last.Value; - previous.AddFirst(box_input.Text); - previous.RemoveLast(); - box_input.Select(box_input.Text.Length, 0); - } - e.Handled = true; + boxOutput.SelectionColor = line.Type == LineType.Error ? Color.FromArgb(220, 80, 80) : Color.FromArgb(85, 85, 105); + boxOutput.AppendText(line.Display + "\n"); } - else if (e.KeyCode == Keys.Tab) + else { - if (box_input.SelectionStart > 0) + string[] subs = line.Raw.Split('\u00a7'); + boxOutput.SelectionColor = Color.FromArgb(200, 200, 215); + if (subs.Length > 0) boxOutput.AppendText(subs[0]); + for (int i = 1; i < subs.Length; i++) { - string behind_cursor = box_input.Text.Substring(0, box_input.SelectionStart); - string after_cursor = box_input.Text.Substring(box_input.SelectionStart); - string[] behind_temp = behind_cursor.Split(' '); - string autocomplete = Client.tabAutoComplete(behind_temp[behind_temp.Length - 1]); - if (!String.IsNullOrEmpty(autocomplete)) + if (subs[i].Length > 1) { - behind_temp[behind_temp.Length - 1] = autocomplete; - behind_cursor = String.Join(" ", behind_temp); - box_input.Text = behind_cursor + after_cursor; - box_input.SelectionStart = behind_cursor.Length; + boxOutput.SelectionColor = GetColor(subs[i][0]); + boxOutput.SelectionFont = GetFont(subs[i][0], boxOutput.Font); + boxOutput.AppendText(subs[i].Substring(1)); } } - e.SuppressKeyPress = true; - e.Handled = true; + boxOutput.AppendText("\n"); } } - /// - /// Send the input in the input box, if any, by pressing the "Send" button. - /// Handle "/quit" command to properly disconnect and close the GUI. - /// + public void UpdateLang(string lang) { if (btnDisconnect != null) btnDisconnect.Text = lang == "en" ? "Disconnect" : "Rozlacz"; } + + private void InitClient(MinecraftClient client) + { + Client = client; + t_read = new Thread(ReadLoop) { IsBackground = true }; + t_read.Start(); + connectedAt = DateTime.Now; + isConnected = true; + InvokeUI(() => { + lblStatus.Text = " ● " + TabTitle; + lblStatus.ForeColor = Color.FromArgb(100, 210, 130); + PrintSystem("Connected.", LineType.System); + }); + } - private void btn_send_Click(object sender, EventArgs e) + private void ReadLoop() { - if (Client != null) + try { - if (box_input.Text.Trim().ToLower() == "/quit") + while (Client != null && !Client.Disconnected) { - Close(); - } - else - { - Client.SendText(box_input.Text); - previous.AddLast(box_input.Text); - box_input.Text = ""; + string line = Client.ReadLine(); + if (!string.IsNullOrEmpty(line)) PrintChat(line); } } + catch (ThreadAbortException) + { + } + catch (Exception ex) { InvokeUI(() => PrintSystem("Error: " + ex.Message, LineType.Error)); } + finally + { + isConnected = false; + InvokeUI(() => { + PrintSystem("Disconnected.", LineType.Error); + if (lblStatus != null) { lblStatus.Text = " ● " + TabTitle; lblStatus.ForeColor = Color.FromArgb(220, 80, 80); } + }); + } } - /// - /// Draw text on glass pane without ClearType, only black pixels - /// - - protected override void OnPaint(PaintEventArgs e) + public void Send(string text) { - e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit; - e.Graphics.DrawString("Login Details", this.Font, Brushes.Black, 20, 11); - e.Graphics.DrawString("Username:", this.Font, Brushes.Black, 20, 31); - e.Graphics.DrawString("Password:", this.Font, Brushes.Black, 191, 31); - e.Graphics.DrawString("Server IP:", this.Font, Brushes.Black, 355, 31); + if (Client != null && !Client.Disconnected) + { + Client.SendText(text); + InvokeUI(() => PrintSystem("> " + text, LineType.System, Color.FromArgb(100, 180, 255))); + } } - /// - /// Show the "About" message box, open the official topic in an internet browser if the user press OK. - /// - - private void btn_about_Click(object sender, EventArgs e) + public void CloseTab() { - if (MessageBox.Show("MCC GUI version 1.0 - (c) 2013 ORelio\nAllows to send commands to any Minecraft server\nand receive text messages in a fast and easy way.\n\nPress OK to visit the official topic on Minecraft Forums.", - "About Minecraft Console Client", MessageBoxButtons.OKCancel, MessageBoxIcon.Information) == DialogResult.OK) + try { - System.Diagnostics.Process.Start("http://www.minecraftforum.net/topic/1314800-/"); + isConnected = false; + timerClock?.Stop(); + logWriter?.WriteLine("=== Session ended: " + DateTime.Now + " ==="); + logWriter?.Close(); + if (Client != null) { Client.Close(); Client = null; } + if (t_read != null && t_read.IsAlive) { t_read.Abort(); t_read = null; } } + catch { } + } + + private void PrintSystem(string text, LineType type, Color? color = null) + { + string prefix = type == LineType.Error ? "[ERR] " : "[SYS] "; + Color c = color ?? (type == LineType.Error ? Color.FromArgb(220, 80, 80) : Color.FromArgb(85, 85, 105)); + var entry = new LogLine { Raw = prefix + text, Display = prefix + text, Type = type, Time = DateTime.Now }; + lock (logLock) allLines.Add(entry); + WriteLog(text, type); + InvokeUI(() => { + if (activeFilter == null || activeFilter == type) + { + boxOutput.SelectionColor = c; + boxOutput.AppendText(entry.Display + "\n"); + if (autoScroll) SendMessage(boxOutput.Handle, WM_VSCROLL, (IntPtr)SB_BOTTOM, IntPtr.Zero); + } + }); } - /// - /// Open a link located in the console window - /// + private void PrintChat(string raw) + { + string plain = System.Text.RegularExpressions.Regex.Replace(raw, @"§.", ""); + var entry = new LogLine { Raw = raw, Display = plain, Type = LineType.Chat, Time = DateTime.Now }; + lock (logLock) allLines.Add(entry); + WriteLog(plain, LineType.Chat); + InvokeUI(() => { + if (activeFilter == null || activeFilter == LineType.Chat) + { + boxOutput.SuspendLayout(); + RenderLine(entry); + if (autoScroll) SendMessage(boxOutput.Handle, WM_VSCROLL, (IntPtr)SB_BOTTOM, IntPtr.Zero); + boxOutput.ResumeLayout(); + } + }); + } - private void LinkClicked(object sender, LinkClickedEventArgs e) + private void InvokeUI(Action a) { if (!boxOutput.IsDisposed) { if (boxOutput.InvokeRequired) try { boxOutput.Invoke(a); } catch { } else a(); } } + private Font GetFont(char c, Font f) => c == 'l' ? new Font(f, FontStyle.Bold) : f; + private Color GetColor(char c) { - try { System.Diagnostics.Process.Start(e.LinkText); } - catch (Exception ex) { MessageBox.Show("An error occured while opening the link :\n" + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } + switch (c) + { + case '0': return Color.FromArgb(20, 20, 20); + case '1': return Color.FromArgb(85, 85, 255); + case '2': return Color.FromArgb(85, 200, 85); + case '3': return Color.FromArgb(85, 220, 220); + case '4': return Color.FromArgb(220, 85, 85); + case '5': return Color.FromArgb(200, 85, 200); + case '6': return Color.FromArgb(255, 180, 30); + case '7': return Color.Silver; + case '8': return Color.FromArgb(120, 120, 140); + case '9': return Color.FromArgb(100, 130, 255); + case 'a': return Color.FromArgb(85, 255, 85); + case 'b': return Color.FromArgb(85, 255, 255); + case 'c': return Color.FromArgb(255, 85, 85); + case 'd': return Color.FromArgb(255, 130, 255); + case 'e': return Color.FromArgb(255, 255, 85); + case 'f': return Color.White; + default: return Color.FromArgb(200, 200, 215); + } } } -} +} \ No newline at end of file diff --git a/MinecraftClientGUI/MinecraftClient.cs b/MinecraftClientGUI/MinecraftClient.cs index abf9b4ef22..8d0a84d976 100644 --- a/MinecraftClientGUI/MinecraftClient.cs +++ b/MinecraftClientGUI/MinecraftClient.cs @@ -24,33 +24,18 @@ class MinecraftClient private Process Client; private Thread Reader; - /// - /// Start a client using command-line arguments - /// - /// Arguments to pass - public MinecraftClient(string[] args) { initClient("\"" + String.Join("\" \"", args) + "\" BasicIO"); } - /// - /// Start the client using username, password and server IP - /// - /// Username or email - /// Password for the given username - /// Server IP to join - public MinecraftClient(string username, string password, string serverip) { + // Jeśli hasło jest puste, przesyłamy pusty ciąg, aby obsłużyć logowanie Microsoft/Browser + if (password == null) password = ""; initClient('"' + username + "\" \"" + password + "\" \"" + serverip + "\" BasicIO"); } - /// - /// Inner function for launching the external console application - /// - /// Arguments to pass - private void initClient(string arguments) { if (File.Exists(ExePath)) @@ -59,7 +44,10 @@ private void initClient(string arguments) Client.StartInfo.FileName = ExePath; Client.StartInfo.Arguments = arguments; Client.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; - Client.StartInfo.StandardOutputEncoding = Encoding.GetEncoding(System.Globalization.CultureInfo.CurrentCulture.TextInfo.ANSICodePage); + + // POPRAWKA: Wymuszenie UTF-8 naprawia polskie znaki i kolory + Client.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8; + Client.StartInfo.UseShellExecute = false; Client.StartInfo.RedirectStandardOutput = true; Client.StartInfo.RedirectStandardInput = true; @@ -69,66 +57,58 @@ private void initClient(string arguments) Reader = new Thread(new ThreadStart(t_reader)); Reader.Start(); } - else throw new FileNotFoundException("Cannot find Minecraft Client Executable!", ExePath); + else throw new FileNotFoundException("Nie znaleziono pliku MinecraftClient.exe!", ExePath); } - /// - /// Thread for reading output and app messages from the console - /// - private void t_reader() { while (true) { try { - string line = ""; - while (line.Trim() == "") + if (Client.HasExited) { disconnected = true; break; } + + string line = Client.StandardOutput.ReadLine(); + if (line != null) { - line = Client.StandardOutput.ReadLine() + Client.MainWindowTitle; - if (line.Length > 0) + if (line.Trim() != "") { - if (line == "Server was successfuly joined.") { disconnected = false; } - if (line == "You have left the server.") { disconnected = true; } - if (line[0] == (char)0x00) + if (line.Contains("Server was successfuly joined")) { disconnected = false; } + if (line.Contains("You have left the server")) { disconnected = true; } + + if (line.Length > 0 && line[0] == (char)0x00) { - //App message from the console string[] command = line.Substring(1).Split((char)0x00); - switch (command[0].ToLower()) + if (command[0].ToLower() == "autocomplete") { - case "autocomplete": - if (command.Length > 1) { tabAutoCompleteBuffer.AddLast(command[1]); } - else tabAutoCompleteBuffer.AddLast(""); - break; + if (command.Length > 1) { tabAutoCompleteBuffer.AddLast(command[1]); } + else tabAutoCompleteBuffer.AddLast(""); } } - else OutputBuffer.AddLast(line); + else + { + OutputBuffer.AddLast(line); + } } } + else { Thread.Sleep(10); } // Mała pauza, żeby nie obciążać CPU } - catch (NullReferenceException) { break; } + catch (Exception) { break; } } } - /// - /// Get the first queuing output line to print - /// - /// - public string ReadLine() { - while (OutputBuffer.Count < 1) { } + while (OutputBuffer.Count < 1) + { + if (disconnected) return ""; + Thread.Sleep(10); // Oszczędzamy procesor czekając na dane + } string line = OutputBuffer.First.Value; OutputBuffer.RemoveFirst(); return line; } - /// - /// Complete a playername or a command, usually by pressing the TAB key - /// - /// Text to complete - /// Returns an autocompletion for the provided text - public string tabAutoComplete(string text_behindcursor) { tabAutoCompleteBuffer.Clear(); @@ -136,7 +116,12 @@ public string tabAutoComplete(string text_behindcursor) { text_behindcursor = text_behindcursor.Trim(); SendText((char)0x00 + "autocomplete" + (char)0x00 + text_behindcursor); - int maxwait = 30; while (tabAutoCompleteBuffer.Count < 1 && maxwait > 0) { Thread.Sleep(100); maxwait--; } + int maxwait = 30; + while (tabAutoCompleteBuffer.Count < 1 && maxwait > 0) + { + Thread.Sleep(100); + maxwait--; + } if (tabAutoCompleteBuffer.Count > 0) { string text_completed = tabAutoCompleteBuffer.First.Value; @@ -148,14 +133,9 @@ public string tabAutoComplete(string text_behindcursor) else return ""; } - /// - /// Send a message or a command to the server - /// - /// Text to send - public void SendText(string text) { - if (text != null) + if (text != null && !Client.HasExited) { text = text.Replace("\t", ""); text = text.Replace("\r", ""); @@ -168,18 +148,17 @@ public void SendText(string text) } } - /// - /// Properly disconnect from the server and dispose the client - /// - public void Close() { - Client.StandardInput.WriteLine("/quit"); - if (Reader.IsAlive) { Reader.Abort(); } - if (!Client.WaitForExit(3000)) + if (!Client.HasExited) { - try { Client.Kill(); } catch { } + Client.StandardInput.WriteLine("/quit"); + if (Reader.IsAlive) { Reader.Abort(); } + if (!Client.WaitForExit(2000)) + { + try { Client.Kill(); } catch { } + } } } } -} +} \ No newline at end of file diff --git a/MinecraftClientGUI/MinecraftClient.exe b/MinecraftClientGUI/MinecraftClient.exe new file mode 100644 index 0000000000..51e28041dd Binary files /dev/null and b/MinecraftClientGUI/MinecraftClient.exe differ diff --git a/MinecraftClientGUI/MinecraftClientGUI.csproj b/MinecraftClientGUI/MinecraftClientGUI.csproj index ce8642c4e0..684de98884 100644 --- a/MinecraftClientGUI/MinecraftClientGUI.csproj +++ b/MinecraftClientGUI/MinecraftClientGUI.csproj @@ -1,5 +1,5 @@  - + Debug x86 @@ -10,9 +10,26 @@ Properties MinecraftClientGUI MinecraftClientGUI - v4.0 - Client + v4.8 + + 512 + false + C:\Users\Admin\Desktop\publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 3 + 1.0.0.%2a + false + true + true x86 @@ -23,6 +40,7 @@ DEBUG;TRACE prompt 4 + false x86 @@ -32,10 +50,23 @@ TRACE prompt 4 + false AppIcon.ico + + 4C76A63F11DB91E010AF3039521927BEB537A320 + + + MinecraftClientGUI_TemporaryKey.pfx + + + true + + + true + @@ -69,7 +100,13 @@ True Resources.resx + True + + + Always + + SettingsSingleFileGenerator Settings.Designer.cs @@ -82,6 +119,30 @@ + + Always + + + Always + + + Always + + + Always + + + + + False + Microsoft .NET Framework 4.8 %28x86 i x64%29 + true + + + False + .NET Framework 3.5 SP1 + false +