mirror of
https://github.com/OxyZin/LegacyConsoleLauncher.git
synced 2026-07-18 00:30:47 +00:00
add per-account skins, skin preview and code refactor
- add per-account skin system - add automatic 64x64 → 64x32 skin conversion - add built-in skin preview in launcher UI - apply selected skin automatically on game launch - add original skin backup and restore system - improve launcher layout with integrated skin preview - refactor Form1 logic into multiple partial class files
This commit is contained in:
110
Form1.Accounts.cs
Normal file
110
Form1.Accounts.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LegacyConsoleLauncher
|
||||
{
|
||||
public partial class Form1
|
||||
{
|
||||
private void LoadAccounts()
|
||||
{
|
||||
usernameComboBox.Items.Clear();
|
||||
playtimeData.Clear();
|
||||
|
||||
if (!File.Exists(accountsFile))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string[] lines = File.ReadAllLines(accountsFile);
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string[] parts = line.Split('|');
|
||||
string username = parts[0].Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int seconds = 0;
|
||||
|
||||
if (parts.Length > 1)
|
||||
{
|
||||
int.TryParse(parts[1], out seconds);
|
||||
}
|
||||
|
||||
if (!playtimeData.ContainsKey(username))
|
||||
{
|
||||
playtimeData[username] = seconds;
|
||||
usernameComboBox.Items.Add(username);
|
||||
}
|
||||
}
|
||||
|
||||
if (usernameComboBox.Items.Count > 0)
|
||||
{
|
||||
usernameComboBox.SelectedIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveAccounts()
|
||||
{
|
||||
List<string> lines = new List<string>();
|
||||
|
||||
foreach (var entry in playtimeData)
|
||||
{
|
||||
lines.Add(entry.Key + "|" + entry.Value);
|
||||
}
|
||||
|
||||
File.WriteAllLines(accountsFile, lines);
|
||||
}
|
||||
|
||||
private void AddAccount(string username)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!playtimeData.ContainsKey(username))
|
||||
{
|
||||
playtimeData[username] = 0;
|
||||
usernameComboBox.Items.Add(username);
|
||||
}
|
||||
|
||||
usernameComboBox.SelectedItem = username;
|
||||
SaveAccounts();
|
||||
}
|
||||
|
||||
private string FormatPlaytime(int totalSeconds)
|
||||
{
|
||||
TimeSpan time = TimeSpan.FromSeconds(totalSeconds);
|
||||
return ((int)time.TotalHours) + "h " + time.Minutes + "m";
|
||||
}
|
||||
|
||||
private void UpdatePlaytimeLabel()
|
||||
{
|
||||
string username = usernameComboBox.Text.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
playtimeLabel.Text = "Playtime: 0h 0m";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!playtimeData.ContainsKey(username))
|
||||
{
|
||||
playtimeData[username] = 0;
|
||||
}
|
||||
|
||||
playtimeLabel.Text = "Playtime: " + FormatPlaytime(playtimeData[username]);
|
||||
}
|
||||
}
|
||||
}
|
||||
67
Form1.Designer.cs
generated
67
Form1.Designer.cs
generated
@@ -37,17 +37,24 @@
|
||||
this.resetLink = new System.Windows.Forms.LinkLabel();
|
||||
this.playtimeLabel = new System.Windows.Forms.Label();
|
||||
this.checkforLink = new System.Windows.Forms.LinkLabel();
|
||||
this.button1 = new System.Windows.Forms.Button();
|
||||
this.button2 = new System.Windows.Forms.Button();
|
||||
this.button3 = new System.Windows.Forms.Button();
|
||||
this.skinPreviewPictureBox = new System.Windows.Forms.PictureBox();
|
||||
((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.skinPreviewPictureBox)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// launchButton
|
||||
//
|
||||
this.launchButton.BackColor = System.Drawing.SystemColors.GradientActiveCaption;
|
||||
this.launchButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
|
||||
this.launchButton.Location = new System.Drawing.Point(246, 224);
|
||||
this.launchButton.Name = "launchButton";
|
||||
this.launchButton.Size = new System.Drawing.Size(111, 32);
|
||||
this.launchButton.Size = new System.Drawing.Size(111, 31);
|
||||
this.launchButton.TabIndex = 1;
|
||||
this.launchButton.Text = "Launch Game";
|
||||
this.launchButton.UseVisualStyleBackColor = true;
|
||||
this.launchButton.UseVisualStyleBackColor = false;
|
||||
this.launchButton.Click += new System.EventHandler(this.launchButton_Click);
|
||||
//
|
||||
// fullscreenCheckBox
|
||||
@@ -93,9 +100,9 @@
|
||||
//
|
||||
// setFolderButton
|
||||
//
|
||||
this.setFolderButton.Location = new System.Drawing.Point(129, 224);
|
||||
this.setFolderButton.Location = new System.Drawing.Point(96, 198);
|
||||
this.setFolderButton.Name = "setFolderButton";
|
||||
this.setFolderButton.Size = new System.Drawing.Size(111, 32);
|
||||
this.setFolderButton.Size = new System.Drawing.Size(95, 21);
|
||||
this.setFolderButton.TabIndex = 6;
|
||||
this.setFolderButton.Text = "Set Game Folder";
|
||||
this.setFolderButton.UseVisualStyleBackColor = true;
|
||||
@@ -120,9 +127,9 @@
|
||||
//
|
||||
// openFolderButton
|
||||
//
|
||||
this.openFolderButton.Location = new System.Drawing.Point(12, 224);
|
||||
this.openFolderButton.Location = new System.Drawing.Point(12, 198);
|
||||
this.openFolderButton.Name = "openFolderButton";
|
||||
this.openFolderButton.Size = new System.Drawing.Size(111, 32);
|
||||
this.openFolderButton.Size = new System.Drawing.Size(78, 21);
|
||||
this.openFolderButton.TabIndex = 9;
|
||||
this.openFolderButton.Text = "Open Folder";
|
||||
this.openFolderButton.UseVisualStyleBackColor = true;
|
||||
@@ -159,11 +166,52 @@
|
||||
this.checkforLink.Text = "Check for updates";
|
||||
this.checkforLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.checkforLink_LinkClicked);
|
||||
//
|
||||
// button1
|
||||
//
|
||||
this.button1.Location = new System.Drawing.Point(129, 224);
|
||||
this.button1.Name = "button1";
|
||||
this.button1.Size = new System.Drawing.Size(111, 32);
|
||||
this.button1.TabIndex = 13;
|
||||
this.button1.Text = "Choose Skin";
|
||||
this.button1.UseVisualStyleBackColor = true;
|
||||
this.button1.Click += new System.EventHandler(this.button1_Click);
|
||||
//
|
||||
// button2
|
||||
//
|
||||
this.button2.Location = new System.Drawing.Point(12, 224);
|
||||
this.button2.Name = "button2";
|
||||
this.button2.Size = new System.Drawing.Size(111, 32);
|
||||
this.button2.TabIndex = 14;
|
||||
this.button2.Text = "Settings (WIP)";
|
||||
this.button2.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// button3
|
||||
//
|
||||
this.button3.Location = new System.Drawing.Point(262, 198);
|
||||
this.button3.Name = "button3";
|
||||
this.button3.Size = new System.Drawing.Size(95, 20);
|
||||
this.button3.TabIndex = 15;
|
||||
this.button3.Text = "Servers (WIP)";
|
||||
this.button3.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// skinPreviewPictureBox
|
||||
//
|
||||
this.skinPreviewPictureBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224)))));
|
||||
this.skinPreviewPictureBox.Location = new System.Drawing.Point(381, 12);
|
||||
this.skinPreviewPictureBox.Name = "skinPreviewPictureBox";
|
||||
this.skinPreviewPictureBox.Size = new System.Drawing.Size(138, 281);
|
||||
this.skinPreviewPictureBox.TabIndex = 16;
|
||||
this.skinPreviewPictureBox.TabStop = false;
|
||||
//
|
||||
// Form1
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(364, 302);
|
||||
this.ClientSize = new System.Drawing.Size(531, 302);
|
||||
this.Controls.Add(this.skinPreviewPictureBox);
|
||||
this.Controls.Add(this.button3);
|
||||
this.Controls.Add(this.button2);
|
||||
this.Controls.Add(this.button1);
|
||||
this.Controls.Add(this.checkforLink);
|
||||
this.Controls.Add(this.resetLink);
|
||||
this.Controls.Add(this.openFolderButton);
|
||||
@@ -183,6 +231,7 @@
|
||||
this.Text = "LegacyConsoleLauncher";
|
||||
this.Load += new System.EventHandler(this.Form1_Load);
|
||||
((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.skinPreviewPictureBox)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
@@ -201,5 +250,9 @@
|
||||
private System.Windows.Forms.LinkLabel resetLink;
|
||||
private System.Windows.Forms.Label playtimeLabel;
|
||||
private System.Windows.Forms.LinkLabel checkforLink;
|
||||
private System.Windows.Forms.Button button1;
|
||||
private System.Windows.Forms.Button button2;
|
||||
private System.Windows.Forms.Button button3;
|
||||
private System.Windows.Forms.PictureBox skinPreviewPictureBox;
|
||||
}
|
||||
}
|
||||
173
Form1.Events.cs
Normal file
173
Form1.Events.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LegacyConsoleLauncher
|
||||
{
|
||||
public partial class Form1
|
||||
{
|
||||
private void Form1_DragEnter(object sender, DragEventArgs e)
|
||||
{
|
||||
if (!e.Data.GetDataPresent(DataFormats.FileDrop))
|
||||
{
|
||||
e.Effect = DragDropEffects.None;
|
||||
return;
|
||||
}
|
||||
|
||||
string[] paths = (string[])e.Data.GetData(DataFormats.FileDrop);
|
||||
|
||||
if (paths.Length == 0)
|
||||
{
|
||||
e.Effect = DragDropEffects.None;
|
||||
return;
|
||||
}
|
||||
|
||||
string droppedPath = paths[0];
|
||||
|
||||
if (File.Exists(droppedPath))
|
||||
{
|
||||
if (Path.GetFileName(droppedPath).Equals("Minecraft.Client.exe", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
e.Effect = DragDropEffects.Copy;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Directory.Exists(droppedPath))
|
||||
{
|
||||
string possibleExe = Path.Combine(droppedPath, "Minecraft.Client.exe");
|
||||
|
||||
if (File.Exists(possibleExe))
|
||||
{
|
||||
e.Effect = DragDropEffects.Copy;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
e.Effect = DragDropEffects.None;
|
||||
}
|
||||
|
||||
private void Form1_DragDrop(object sender, DragEventArgs e)
|
||||
{
|
||||
string[] paths = (string[])e.Data.GetData(DataFormats.FileDrop);
|
||||
|
||||
if (paths.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string droppedPath = paths[0];
|
||||
|
||||
if (File.Exists(droppedPath))
|
||||
{
|
||||
if (Path.GetFileName(droppedPath).Equals("Minecraft.Client.exe", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
SetGamePath(droppedPath);
|
||||
|
||||
MessageBox.Show(
|
||||
"Minecraft.Client.exe loaded successfully.",
|
||||
"Game Selected",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Directory.Exists(droppedPath))
|
||||
{
|
||||
string possibleExe = Path.Combine(droppedPath, "Minecraft.Client.exe");
|
||||
|
||||
if (File.Exists(possibleExe))
|
||||
{
|
||||
SetGamePath(possibleExe);
|
||||
|
||||
MessageBox.Show(
|
||||
"Game folder loaded successfully.",
|
||||
"Game Selected",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MessageBox.Show(
|
||||
"Please drag and drop either Minecraft.Client.exe or the folder containing it.",
|
||||
"Invalid Drop",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
}
|
||||
|
||||
private void resetLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
|
||||
{
|
||||
DialogResult result = MessageBox.Show(
|
||||
"This will reset the saved accounts and game path. Continue?",
|
||||
"Reset Launcher Settings",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Question
|
||||
);
|
||||
|
||||
if (result != DialogResult.Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (File.Exists(accountsFile))
|
||||
{
|
||||
File.Delete(accountsFile);
|
||||
}
|
||||
|
||||
if (File.Exists(gamePathFile))
|
||||
{
|
||||
File.Delete(gamePathFile);
|
||||
}
|
||||
|
||||
if (File.Exists(releaseInfoFile))
|
||||
{
|
||||
File.Delete(releaseInfoFile);
|
||||
}
|
||||
|
||||
usernameComboBox.Items.Clear();
|
||||
usernameComboBox.Text = string.Empty;
|
||||
playtimeData.Clear();
|
||||
|
||||
exePath = Path.Combine(Application.StartupPath, "Minecraft.Client.exe");
|
||||
fullscreenCheckBox.Checked = false;
|
||||
|
||||
AutoDetectGame();
|
||||
UpdateGamePathDisplay();
|
||||
UpdatePlaytimeLabel();
|
||||
|
||||
MessageBox.Show(
|
||||
"Launcher settings have been reset.",
|
||||
"Done",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
}
|
||||
|
||||
private void usernameComboBox_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
UpdatePlaytimeLabel();
|
||||
UpdateSkinPreview();
|
||||
}
|
||||
|
||||
private void fullscreenCheckBox_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void usernameLabel_Click(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void logoPictureBox_Click(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void gamePathLabel_Click(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
281
Form1.Game.cs
Normal file
281
Form1.Game.cs
Normal file
@@ -0,0 +1,281 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LegacyConsoleLauncher
|
||||
{
|
||||
public partial class Form1
|
||||
{
|
||||
private void LoadSavedGamePath()
|
||||
{
|
||||
if (!File.Exists(gamePathFile))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string savedPath = File.ReadAllText(gamePathFile).Trim();
|
||||
|
||||
if (File.Exists(savedPath))
|
||||
{
|
||||
exePath = savedPath;
|
||||
}
|
||||
}
|
||||
|
||||
private void AutoDetectGame()
|
||||
{
|
||||
if (File.Exists(exePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string[] possiblePaths =
|
||||
{
|
||||
Path.Combine(Application.StartupPath, "Minecraft.Client.exe"),
|
||||
Path.Combine(Application.StartupPath, "build", "Minecraft.Client.exe"),
|
||||
Path.Combine(Application.StartupPath, "bin", "Minecraft.Client.exe"),
|
||||
Path.Combine(Application.StartupPath, "game", "Minecraft.Client.exe"),
|
||||
Path.Combine(gameInstallDir, "Minecraft.Client.exe")
|
||||
};
|
||||
|
||||
foreach (string path in possiblePaths)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
exePath = path;
|
||||
File.WriteAllText(gamePathFile, exePath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateGamePathDisplay()
|
||||
{
|
||||
if (File.Exists(exePath))
|
||||
{
|
||||
gamePathTextBox.Text = exePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
gamePathTextBox.Text = "Not set";
|
||||
}
|
||||
|
||||
bool gameFound = File.Exists(exePath);
|
||||
openFolderButton.Enabled = gameFound;
|
||||
launchButton.Enabled = gameFound;
|
||||
}
|
||||
|
||||
private void SetGamePath(string newPath)
|
||||
{
|
||||
exePath = newPath;
|
||||
File.WriteAllText(gamePathFile, exePath);
|
||||
UpdateGamePathDisplay();
|
||||
LoadFullscreenSetting();
|
||||
}
|
||||
|
||||
private string FindGameExe(string rootFolder)
|
||||
{
|
||||
if (!Directory.Exists(rootFolder))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string[] files = Directory.GetFiles(rootFolder, "Minecraft.Client.exe", SearchOption.AllDirectories);
|
||||
return files.Length > 0 ? files[0] : string.Empty;
|
||||
}
|
||||
|
||||
private void LoadFullscreenSetting()
|
||||
{
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string gameFolder = Path.GetDirectoryName(exePath);
|
||||
string optionsPath = Path.Combine(gameFolder, "options.txt");
|
||||
|
||||
if (!File.Exists(optionsPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string[] lines = File.ReadAllLines(optionsPath);
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
if (line.StartsWith("fullscreen="))
|
||||
{
|
||||
fullscreenCheckBox.Checked = line.Trim() == "fullscreen=1";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveFullscreenSetting()
|
||||
{
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string gameFolder = Path.GetDirectoryName(exePath);
|
||||
string optionsPath = Path.Combine(gameFolder, "options.txt");
|
||||
|
||||
if (!File.Exists(optionsPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string[] lines = File.ReadAllLines(optionsPath);
|
||||
bool foundFullscreen = false;
|
||||
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
if (lines[i].StartsWith("fullscreen="))
|
||||
{
|
||||
lines[i] = "fullscreen=" + (fullscreenCheckBox.Checked ? "1" : "0");
|
||||
foundFullscreen = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundFullscreen)
|
||||
{
|
||||
var updatedLines = new System.Collections.Generic.List<string>(lines);
|
||||
updatedLines.Add("fullscreen=" + (fullscreenCheckBox.Checked ? "1" : "0"));
|
||||
File.WriteAllLines(optionsPath, updatedLines.ToArray());
|
||||
return;
|
||||
}
|
||||
|
||||
File.WriteAllLines(optionsPath, lines);
|
||||
}
|
||||
|
||||
private void openFolderButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
MessageBox.Show(
|
||||
"Minecraft.Client.exe was not found.",
|
||||
"Game Not Found",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
string gameFolder = Path.GetDirectoryName(exePath);
|
||||
Process.Start("explorer.exe", gameFolder);
|
||||
}
|
||||
|
||||
private void setFolderButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (FolderBrowserDialog dialog = new FolderBrowserDialog())
|
||||
{
|
||||
dialog.Description = "Select the folder containing Minecraft.Client.exe";
|
||||
|
||||
if (dialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
string selectedFolder = dialog.SelectedPath;
|
||||
string possibleExe = Path.Combine(selectedFolder, "Minecraft.Client.exe");
|
||||
|
||||
if (File.Exists(possibleExe))
|
||||
{
|
||||
SetGamePath(possibleExe);
|
||||
|
||||
MessageBox.Show(
|
||||
"Game folder set successfully.",
|
||||
"Success",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show(
|
||||
"Minecraft.Client.exe was not found in that folder.",
|
||||
"Game Not Found",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void launchButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
MessageBox.Show(
|
||||
"Minecraft.Client.exe was not found.",
|
||||
"Error",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Error
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
string username = usernameComboBox.Text.Trim();
|
||||
|
||||
AddAccount(username);
|
||||
File.WriteAllText(gamePathFile, exePath);
|
||||
SaveFullscreenSetting();
|
||||
ApplySkinForAccount(username);
|
||||
|
||||
string args = string.Empty;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
args += "-name \"" + username + "\" ";
|
||||
}
|
||||
|
||||
gameProcess = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = exePath,
|
||||
Arguments = args.Trim(),
|
||||
WorkingDirectory = Path.GetDirectoryName(exePath),
|
||||
UseShellExecute = true
|
||||
});
|
||||
|
||||
if (gameProcess == null)
|
||||
{
|
||||
MessageBox.Show(
|
||||
"Failed to start the game.",
|
||||
"Error",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Error
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
sessionStart = DateTime.Now;
|
||||
gameProcess.EnableRaisingEvents = true;
|
||||
gameProcess.Exited += GameProcess_Exited;
|
||||
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void GameProcess_Exited(object sender, EventArgs e)
|
||||
{
|
||||
int sessionSeconds = (int)(DateTime.Now - sessionStart).TotalSeconds;
|
||||
|
||||
Invoke((MethodInvoker)delegate
|
||||
{
|
||||
string username = usernameComboBox.Text.Trim();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
if (!playtimeData.ContainsKey(username))
|
||||
{
|
||||
playtimeData[username] = 0;
|
||||
}
|
||||
|
||||
playtimeData[username] += sessionSeconds;
|
||||
SaveAccounts();
|
||||
}
|
||||
|
||||
Application.Exit();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
156
Form1.SkinPreview.cs
Normal file
156
Form1.SkinPreview.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LegacyConsoleLauncher
|
||||
{
|
||||
public partial class Form1
|
||||
{
|
||||
private void InitializeSkinPreview()
|
||||
{
|
||||
if (skinPreviewPictureBox == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
skinPreviewPictureBox.SizeMode = PictureBoxSizeMode.CenterImage;
|
||||
UpdateSkinPreview();
|
||||
}
|
||||
|
||||
private void UpdateSkinPreview()
|
||||
{
|
||||
if (skinPreviewPictureBox == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string username = usernameComboBox.Text.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
ClearSkinPreview();
|
||||
return;
|
||||
}
|
||||
|
||||
string skinPath = GetAccountSkinPath(username);
|
||||
|
||||
if (!File.Exists(skinPath))
|
||||
{
|
||||
ClearSkinPreview();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (Bitmap skin = new Bitmap(skinPath))
|
||||
{
|
||||
Bitmap preview = CreateSkinPreviewBitmap(skin);
|
||||
ReplaceSkinPreviewImage(preview);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
ClearSkinPreview();
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearSkinPreview()
|
||||
{
|
||||
if (skinPreviewPictureBox.Image != null)
|
||||
{
|
||||
skinPreviewPictureBox.Image.Dispose();
|
||||
skinPreviewPictureBox.Image = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReplaceSkinPreviewImage(Image newImage)
|
||||
{
|
||||
if (skinPreviewPictureBox.Image != null)
|
||||
{
|
||||
skinPreviewPictureBox.Image.Dispose();
|
||||
}
|
||||
|
||||
skinPreviewPictureBox.Image = newImage;
|
||||
}
|
||||
|
||||
private Bitmap CreateSkinPreviewBitmap(Bitmap skin)
|
||||
{
|
||||
// base paper doll canvas
|
||||
Bitmap preview = new Bitmap(16, 32, PixelFormat.Format32bppArgb);
|
||||
|
||||
using (Graphics g = Graphics.FromImage(preview))
|
||||
{
|
||||
g.Clear(Color.Transparent);
|
||||
g.InterpolationMode = InterpolationMode.NearestNeighbor;
|
||||
g.PixelOffsetMode = PixelOffsetMode.Half;
|
||||
g.SmoothingMode = SmoothingMode.None;
|
||||
|
||||
// Head front: source (8,8) size 8x8
|
||||
g.DrawImage(
|
||||
skin,
|
||||
new Rectangle(4, 0, 8, 8),
|
||||
new Rectangle(8, 8, 8, 8),
|
||||
GraphicsUnit.Pixel
|
||||
);
|
||||
|
||||
// Body front: source (20,20) size 8x12
|
||||
g.DrawImage(
|
||||
skin,
|
||||
new Rectangle(4, 8, 8, 12),
|
||||
new Rectangle(20, 20, 8, 12),
|
||||
GraphicsUnit.Pixel
|
||||
);
|
||||
|
||||
// Right arm front: source (44,20) size 4x12
|
||||
g.DrawImage(
|
||||
skin,
|
||||
new Rectangle(0, 8, 4, 12),
|
||||
new Rectangle(44, 20, 4, 12),
|
||||
GraphicsUnit.Pixel
|
||||
);
|
||||
|
||||
// Left arm front (legacy format reuses arm texture)
|
||||
g.DrawImage(
|
||||
skin,
|
||||
new Rectangle(12, 8, 4, 12),
|
||||
new Rectangle(44, 20, 4, 12),
|
||||
GraphicsUnit.Pixel
|
||||
);
|
||||
|
||||
// Right leg front: source (4,20) size 4x12
|
||||
g.DrawImage(
|
||||
skin,
|
||||
new Rectangle(4, 20, 4, 12),
|
||||
new Rectangle(4, 20, 4, 12),
|
||||
GraphicsUnit.Pixel
|
||||
);
|
||||
|
||||
// Left leg front (legacy format reuses leg texture)
|
||||
g.DrawImage(
|
||||
skin,
|
||||
new Rectangle(8, 20, 4, 12),
|
||||
new Rectangle(4, 20, 4, 12),
|
||||
GraphicsUnit.Pixel
|
||||
);
|
||||
}
|
||||
|
||||
// upscale for cleaner display
|
||||
Bitmap scaled = new Bitmap(96, 192, PixelFormat.Format32bppArgb);
|
||||
|
||||
using (Graphics g = Graphics.FromImage(scaled))
|
||||
{
|
||||
g.Clear(Color.Transparent);
|
||||
g.InterpolationMode = InterpolationMode.NearestNeighbor;
|
||||
g.PixelOffsetMode = PixelOffsetMode.Half;
|
||||
g.SmoothingMode = SmoothingMode.None;
|
||||
g.DrawImage(preview, new Rectangle(0, 0, scaled.Width, scaled.Height));
|
||||
}
|
||||
|
||||
preview.Dispose();
|
||||
return scaled;
|
||||
}
|
||||
}
|
||||
}
|
||||
235
Form1.Skins.cs
Normal file
235
Form1.Skins.cs
Normal file
@@ -0,0 +1,235 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LegacyConsoleLauncher
|
||||
{
|
||||
public partial class Form1
|
||||
{
|
||||
private string GetSkinFolder()
|
||||
{
|
||||
return skinsDir;
|
||||
}
|
||||
|
||||
private string GetAccountSkinPath(string username)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
foreach (char c in Path.GetInvalidFileNameChars())
|
||||
{
|
||||
username = username.Replace(c, '_');
|
||||
}
|
||||
|
||||
return Path.Combine(GetSkinFolder(), username + ".png");
|
||||
}
|
||||
|
||||
private string GetGameRootFolder()
|
||||
{
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return Path.GetDirectoryName(exePath);
|
||||
}
|
||||
|
||||
private string GetGameSkinFolder()
|
||||
{
|
||||
string gameRoot = GetGameRootFolder();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(gameRoot))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return Path.Combine(gameRoot, "Common", "res", "mob");
|
||||
}
|
||||
|
||||
private string GetGameSkinPath()
|
||||
{
|
||||
string skinFolder = GetGameSkinFolder();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(skinFolder))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return Path.Combine(skinFolder, "char.png");
|
||||
}
|
||||
|
||||
private string GetOriginalSkinBackupPath()
|
||||
{
|
||||
string skinFolder = GetGameSkinFolder();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(skinFolder))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
return Path.Combine(skinFolder, "char_original_backup.png");
|
||||
}
|
||||
|
||||
private void EnsureOriginalSkinBackup()
|
||||
{
|
||||
string gameSkinPath = GetGameSkinPath();
|
||||
string backupPath = GetOriginalSkinBackupPath();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(gameSkinPath) || string.IsNullOrWhiteSpace(backupPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(gameSkinPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(backupPath))
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(backupPath));
|
||||
File.Copy(gameSkinPath, backupPath);
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap ConvertSkinToLegacyFormat(Bitmap original)
|
||||
{
|
||||
if (original.Width != 64 || (original.Height != 32 && original.Height != 64))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (original.Height == 32)
|
||||
{
|
||||
return new Bitmap(original);
|
||||
}
|
||||
|
||||
Bitmap converted = new Bitmap(64, 32, PixelFormat.Format32bppArgb);
|
||||
|
||||
using (Graphics g = Graphics.FromImage(converted))
|
||||
{
|
||||
g.Clear(Color.Transparent);
|
||||
g.DrawImage(
|
||||
original,
|
||||
new Rectangle(0, 0, 64, 32),
|
||||
new Rectangle(0, 0, 64, 32),
|
||||
GraphicsUnit.Pixel
|
||||
);
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
private void SaveSkinForCurrentAccount()
|
||||
{
|
||||
string username = usernameComboBox.Text.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
MessageBox.Show(
|
||||
"Please enter or select an account first.",
|
||||
"Skin Manager",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
UpdateSkinPreview();
|
||||
return;
|
||||
}
|
||||
|
||||
using (OpenFileDialog dialog = new OpenFileDialog())
|
||||
{
|
||||
dialog.Filter = "PNG Files (*.png)|*.png";
|
||||
dialog.Title = "Select a Minecraft skin";
|
||||
|
||||
if (dialog.ShowDialog() != DialogResult.OK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (Bitmap original = new Bitmap(dialog.FileName))
|
||||
using (Bitmap converted = ConvertSkinToLegacyFormat(original))
|
||||
{
|
||||
if (converted == null)
|
||||
{
|
||||
MessageBox.Show(
|
||||
"Invalid skin format. Only 64x32 and 64x64 PNG skins are supported.",
|
||||
"Invalid Skin",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(GetSkinFolder());
|
||||
string accountSkinPath = GetAccountSkinPath(username);
|
||||
converted.Save(accountSkinPath, ImageFormat.Png);
|
||||
|
||||
string formatMessage = original.Height == 64
|
||||
? "Skin saved for this account.\n\n64x64 skin was converted to 64x32."
|
||||
: "Skin saved for this account.";
|
||||
|
||||
MessageBox.Show(
|
||||
formatMessage,
|
||||
"Skin Manager",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplySkinForAccount(string username)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
RestoreOriginalSkin();
|
||||
return;
|
||||
}
|
||||
|
||||
string accountSkinPath = GetAccountSkinPath(username);
|
||||
string gameSkinPath = GetGameSkinPath();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(gameSkinPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(accountSkinPath))
|
||||
{
|
||||
RestoreOriginalSkin();
|
||||
return;
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(gameSkinPath));
|
||||
EnsureOriginalSkinBackup();
|
||||
File.Copy(accountSkinPath, gameSkinPath, true);
|
||||
}
|
||||
|
||||
private void RestoreOriginalSkin()
|
||||
{
|
||||
string backupPath = GetOriginalSkinBackupPath();
|
||||
string gameSkinPath = GetGameSkinPath();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(backupPath) || string.IsNullOrWhiteSpace(gameSkinPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(backupPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
File.Copy(backupPath, gameSkinPath, true);
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
SaveSkinForCurrentAccount();
|
||||
}
|
||||
}
|
||||
}
|
||||
326
Form1.Updates.cs
Normal file
326
Form1.Updates.cs
Normal file
@@ -0,0 +1,326 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LegacyConsoleLauncher
|
||||
{
|
||||
public partial class Form1
|
||||
{
|
||||
private async Task<string> DownloadStringWithUserAgentAsync(string url)
|
||||
{
|
||||
using (WebClient client = new WebClient())
|
||||
{
|
||||
client.Headers.Add("User-Agent", "LegacyConsoleLauncher");
|
||||
return await client.DownloadStringTaskAsync(url);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DownloadFileWithProgressAsync(string url, string outputPath, string statusText)
|
||||
{
|
||||
ShowProgressForm(statusText);
|
||||
|
||||
using (WebClient client = new WebClient())
|
||||
{
|
||||
client.Headers.Add("User-Agent", "LegacyConsoleLauncher");
|
||||
|
||||
client.DownloadProgressChanged += (s, e) =>
|
||||
{
|
||||
if (progressForm != null && !progressForm.IsDisposed)
|
||||
{
|
||||
progressForm.SetStatus(statusText + " " + e.ProgressPercentage + "%");
|
||||
progressForm.SetProgress(e.ProgressPercentage);
|
||||
}
|
||||
};
|
||||
|
||||
await client.DownloadFileTaskAsync(new Uri(url), outputPath);
|
||||
}
|
||||
|
||||
if (progressForm != null && !progressForm.IsDisposed)
|
||||
{
|
||||
progressForm.SetProgress(100);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowProgressForm(string status)
|
||||
{
|
||||
if (progressForm == null || progressForm.IsDisposed)
|
||||
{
|
||||
progressForm = new Form2();
|
||||
}
|
||||
|
||||
progressForm.Show();
|
||||
progressForm.BringToFront();
|
||||
progressForm.SetStatus(status);
|
||||
progressForm.SetProgress(0);
|
||||
}
|
||||
|
||||
private void CloseProgressForm()
|
||||
{
|
||||
if (progressForm != null && !progressForm.IsDisposed)
|
||||
{
|
||||
progressForm.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> GetNightlyCommitAsync()
|
||||
{
|
||||
string html = await DownloadStringWithUserAgentAsync(nightlyReleaseUrl);
|
||||
|
||||
Match match = Regex.Match(
|
||||
html,
|
||||
@"\b[0-9a-f]{7,40}\b",
|
||||
RegexOptions.IgnoreCase
|
||||
);
|
||||
|
||||
return match.Success ? match.Value : string.Empty;
|
||||
}
|
||||
|
||||
private string GetInstalledCommit()
|
||||
{
|
||||
if (!File.Exists(releaseInfoFile))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
foreach (string line in File.ReadAllLines(releaseInfoFile))
|
||||
{
|
||||
if (line.StartsWith("commit="))
|
||||
{
|
||||
return line.Substring("commit=".Length).Trim();
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private void SaveInstalledCommit(string commit)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(commit))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
File.WriteAllText(releaseInfoFile, "commit=" + commit);
|
||||
}
|
||||
|
||||
private async Task InstallGameAsync()
|
||||
{
|
||||
string zipPath = Path.Combine(Application.StartupPath, "LCEWindows64.zip");
|
||||
string tempExtractDir = Path.Combine(Application.StartupPath, "Game_Temp");
|
||||
|
||||
try
|
||||
{
|
||||
await DownloadFileWithProgressAsync(nightlyZipUrl, zipPath, "Installing...");
|
||||
ShowProgressForm("Extracting...");
|
||||
|
||||
if (Directory.Exists(tempExtractDir))
|
||||
{
|
||||
Directory.Delete(tempExtractDir, true);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(tempExtractDir);
|
||||
ZipFile.ExtractToDirectory(zipPath, tempExtractDir);
|
||||
|
||||
if (File.Exists(zipPath))
|
||||
{
|
||||
File.Delete(zipPath);
|
||||
}
|
||||
|
||||
if (Directory.Exists(gameInstallDir))
|
||||
{
|
||||
Directory.Delete(gameInstallDir, true);
|
||||
}
|
||||
|
||||
Directory.Move(tempExtractDir, gameInstallDir);
|
||||
|
||||
string detectedExe = FindGameExe(gameInstallDir);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(detectedExe) || !File.Exists(detectedExe))
|
||||
{
|
||||
throw new FileNotFoundException("Minecraft.Client.exe could not be found after installation.");
|
||||
}
|
||||
|
||||
SetGamePath(detectedExe);
|
||||
|
||||
string latestCommit = await GetNightlyCommitAsync();
|
||||
SaveInstalledCommit(latestCommit);
|
||||
|
||||
CloseProgressForm();
|
||||
|
||||
MessageBox.Show(
|
||||
"Game installed successfully.",
|
||||
"Install Complete",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
}
|
||||
catch
|
||||
{
|
||||
CloseProgressForm();
|
||||
|
||||
if (Directory.Exists(tempExtractDir))
|
||||
{
|
||||
Directory.Delete(tempExtractDir, true);
|
||||
}
|
||||
|
||||
if (File.Exists(zipPath))
|
||||
{
|
||||
File.Delete(zipPath);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateGameExeAsync()
|
||||
{
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
throw new FileNotFoundException("Game executable was not found.");
|
||||
}
|
||||
|
||||
string tempExePath = Path.Combine(Application.StartupPath, "Minecraft.Client.new.exe");
|
||||
|
||||
try
|
||||
{
|
||||
await DownloadFileWithProgressAsync(nightlyExeUrl, tempExePath, "Updating...");
|
||||
File.Copy(tempExePath, exePath, true);
|
||||
|
||||
if (File.Exists(tempExePath))
|
||||
{
|
||||
File.Delete(tempExePath);
|
||||
}
|
||||
|
||||
string latestCommit = await GetNightlyCommitAsync();
|
||||
SaveInstalledCommit(latestCommit);
|
||||
|
||||
CloseProgressForm();
|
||||
|
||||
MessageBox.Show(
|
||||
"Game updated successfully.",
|
||||
"Update Complete",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
}
|
||||
catch
|
||||
{
|
||||
CloseProgressForm();
|
||||
|
||||
if (File.Exists(tempExePath))
|
||||
{
|
||||
File.Delete(tempExePath);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckForUpdatesOnStartupAsync()
|
||||
{
|
||||
checkforLink.Visible = false;
|
||||
|
||||
try
|
||||
{
|
||||
string latestCommit = await GetNightlyCommitAsync();
|
||||
string installedCommit = GetInstalledCommit();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(latestCommit))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
DialogResult installResult = MessageBox.Show(
|
||||
"Game not installed.\n\nDo you want to download and install it now?",
|
||||
"Install Game",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Question
|
||||
);
|
||||
|
||||
if (installResult == DialogResult.Yes)
|
||||
{
|
||||
await InstallGameAsync();
|
||||
}
|
||||
|
||||
checkforLink.Visible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.Equals(latestCommit, installedCommit, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
checkforLink.Visible = true;
|
||||
|
||||
DialogResult updateResult = MessageBox.Show(
|
||||
"A new nightly build is available.\n\nDo you want to update now?",
|
||||
"Update Available",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
|
||||
if (updateResult == DialogResult.Yes)
|
||||
{
|
||||
await UpdateGameExeAsync();
|
||||
checkforLink.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
checkforLink.Visible = true;
|
||||
|
||||
MessageBox.Show(
|
||||
"Failed to check for updates.\n\n" + ex.Message,
|
||||
"Update Check Error",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async void checkforLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
string latestCommit = await GetNightlyCommitAsync();
|
||||
string installedCommit = GetInstalledCommit();
|
||||
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
await InstallGameAsync();
|
||||
checkforLink.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.Equals(latestCommit, installedCommit, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await UpdateGameExeAsync();
|
||||
checkforLink.Visible = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show(
|
||||
"You are already using the latest nightly build.",
|
||||
"No Updates",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(
|
||||
"Failed to check for updates.\n\n" + ex.Message,
|
||||
"Error",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
855
Form1.cs
855
Form1.cs
@@ -1,10 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
@@ -16,6 +14,7 @@ namespace LegacyConsoleLauncher
|
||||
private readonly string gamePathFile = Path.Combine(Application.StartupPath, "gamepath.txt");
|
||||
private readonly string releaseInfoFile = Path.Combine(Application.StartupPath, "releaseinfo.txt");
|
||||
private readonly string gameInstallDir = Path.Combine(Application.StartupPath, "Game");
|
||||
private readonly string skinsDir = Path.Combine(Application.StartupPath, "skins");
|
||||
|
||||
private string exePath = Path.Combine(Application.StartupPath, "Minecraft.Client.exe");
|
||||
|
||||
@@ -38,6 +37,7 @@ namespace LegacyConsoleLauncher
|
||||
openFolderButton.Text = "Open Folder";
|
||||
launchButton.Text = "Launch Game";
|
||||
setFolderButton.Text = "Set Game Folder";
|
||||
button1.Text = "Choose Skin";
|
||||
|
||||
fullscreenCheckBox.Text = "Fullscreen";
|
||||
usernameLabel.Text = "Username:";
|
||||
@@ -47,12 +47,15 @@ namespace LegacyConsoleLauncher
|
||||
AcceptButton = launchButton;
|
||||
checkforLink.Visible = false;
|
||||
|
||||
Directory.CreateDirectory(skinsDir);
|
||||
|
||||
LoadAccounts();
|
||||
LoadSavedGamePath();
|
||||
AutoDetectGame();
|
||||
UpdateGamePathDisplay();
|
||||
LoadFullscreenSetting();
|
||||
UpdatePlaytimeLabel();
|
||||
InitializeSkinPreview();
|
||||
|
||||
AllowDrop = true;
|
||||
DragEnter += Form1_DragEnter;
|
||||
@@ -60,851 +63,5 @@ namespace LegacyConsoleLauncher
|
||||
|
||||
await CheckForUpdatesOnStartupAsync();
|
||||
}
|
||||
|
||||
private void LoadSavedGamePath()
|
||||
{
|
||||
if (!File.Exists(gamePathFile))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string savedPath = File.ReadAllText(gamePathFile).Trim();
|
||||
|
||||
if (File.Exists(savedPath))
|
||||
{
|
||||
exePath = savedPath;
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadAccounts()
|
||||
{
|
||||
usernameComboBox.Items.Clear();
|
||||
playtimeData.Clear();
|
||||
|
||||
if (!File.Exists(accountsFile))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string[] lines = File.ReadAllLines(accountsFile);
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string[] parts = line.Split('|');
|
||||
string username = parts[0].Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int seconds = 0;
|
||||
|
||||
if (parts.Length > 1)
|
||||
{
|
||||
int.TryParse(parts[1], out seconds);
|
||||
}
|
||||
|
||||
if (!playtimeData.ContainsKey(username))
|
||||
{
|
||||
playtimeData[username] = seconds;
|
||||
usernameComboBox.Items.Add(username);
|
||||
}
|
||||
}
|
||||
|
||||
if (usernameComboBox.Items.Count > 0)
|
||||
{
|
||||
usernameComboBox.SelectedIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveAccounts()
|
||||
{
|
||||
List<string> lines = new List<string>();
|
||||
|
||||
foreach (var entry in playtimeData)
|
||||
{
|
||||
lines.Add(entry.Key + "|" + entry.Value);
|
||||
}
|
||||
|
||||
File.WriteAllLines(accountsFile, lines);
|
||||
}
|
||||
|
||||
private void AddAccount(string username)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!playtimeData.ContainsKey(username))
|
||||
{
|
||||
playtimeData[username] = 0;
|
||||
usernameComboBox.Items.Add(username);
|
||||
}
|
||||
|
||||
usernameComboBox.SelectedItem = username;
|
||||
SaveAccounts();
|
||||
}
|
||||
|
||||
private void AutoDetectGame()
|
||||
{
|
||||
if (File.Exists(exePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string[] possiblePaths =
|
||||
{
|
||||
Path.Combine(Application.StartupPath, "Minecraft.Client.exe"),
|
||||
Path.Combine(Application.StartupPath, "build", "Minecraft.Client.exe"),
|
||||
Path.Combine(Application.StartupPath, "bin", "Minecraft.Client.exe"),
|
||||
Path.Combine(Application.StartupPath, "game", "Minecraft.Client.exe"),
|
||||
Path.Combine(gameInstallDir, "Minecraft.Client.exe")
|
||||
};
|
||||
|
||||
foreach (string path in possiblePaths)
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
exePath = path;
|
||||
File.WriteAllText(gamePathFile, exePath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateGamePathDisplay()
|
||||
{
|
||||
if (File.Exists(exePath))
|
||||
{
|
||||
gamePathTextBox.Text = exePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
gamePathTextBox.Text = "Not set";
|
||||
}
|
||||
|
||||
bool gameFound = File.Exists(exePath);
|
||||
openFolderButton.Enabled = gameFound;
|
||||
launchButton.Enabled = gameFound;
|
||||
}
|
||||
|
||||
private void SetGamePath(string newPath)
|
||||
{
|
||||
exePath = newPath;
|
||||
File.WriteAllText(gamePathFile, exePath);
|
||||
UpdateGamePathDisplay();
|
||||
LoadFullscreenSetting();
|
||||
}
|
||||
|
||||
private string FormatPlaytime(int totalSeconds)
|
||||
{
|
||||
TimeSpan time = TimeSpan.FromSeconds(totalSeconds);
|
||||
return ((int)time.TotalHours) + "h " + time.Minutes + "m";
|
||||
}
|
||||
|
||||
private void UpdatePlaytimeLabel()
|
||||
{
|
||||
string username = usernameComboBox.Text.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
playtimeLabel.Text = "Playtime: 0h 0m";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!playtimeData.ContainsKey(username))
|
||||
{
|
||||
playtimeData[username] = 0;
|
||||
}
|
||||
|
||||
playtimeLabel.Text = "Playtime: " + FormatPlaytime(playtimeData[username]);
|
||||
}
|
||||
|
||||
private void LoadFullscreenSetting()
|
||||
{
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string gameFolder = Path.GetDirectoryName(exePath);
|
||||
string optionsPath = Path.Combine(gameFolder, "options.txt");
|
||||
|
||||
if (!File.Exists(optionsPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string[] lines = File.ReadAllLines(optionsPath);
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
if (line.StartsWith("fullscreen="))
|
||||
{
|
||||
fullscreenCheckBox.Checked = line.Trim() == "fullscreen=1";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveFullscreenSetting()
|
||||
{
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string gameFolder = Path.GetDirectoryName(exePath);
|
||||
string optionsPath = Path.Combine(gameFolder, "options.txt");
|
||||
|
||||
if (!File.Exists(optionsPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string[] lines = File.ReadAllLines(optionsPath);
|
||||
bool foundFullscreen = false;
|
||||
|
||||
for (int i = 0; i < lines.Length; i++)
|
||||
{
|
||||
if (lines[i].StartsWith("fullscreen="))
|
||||
{
|
||||
lines[i] = "fullscreen=" + (fullscreenCheckBox.Checked ? "1" : "0");
|
||||
foundFullscreen = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundFullscreen)
|
||||
{
|
||||
List<string> updatedLines = new List<string>(lines);
|
||||
updatedLines.Add("fullscreen=" + (fullscreenCheckBox.Checked ? "1" : "0"));
|
||||
File.WriteAllLines(optionsPath, updatedLines.ToArray());
|
||||
return;
|
||||
}
|
||||
|
||||
File.WriteAllLines(optionsPath, lines);
|
||||
}
|
||||
|
||||
private string FindGameExe(string rootFolder)
|
||||
{
|
||||
if (!Directory.Exists(rootFolder))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
string[] files = Directory.GetFiles(rootFolder, "Minecraft.Client.exe", SearchOption.AllDirectories);
|
||||
return files.Length > 0 ? files[0] : string.Empty;
|
||||
}
|
||||
|
||||
private async Task<string> DownloadStringWithUserAgentAsync(string url)
|
||||
{
|
||||
using (WebClient client = new WebClient())
|
||||
{
|
||||
client.Headers.Add("User-Agent", "LegacyConsoleLauncher");
|
||||
return await client.DownloadStringTaskAsync(url);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DownloadFileWithProgressAsync(string url, string outputPath, string statusText)
|
||||
{
|
||||
ShowProgressForm(statusText);
|
||||
|
||||
using (WebClient client = new WebClient())
|
||||
{
|
||||
client.Headers.Add("User-Agent", "LegacyConsoleLauncher");
|
||||
|
||||
client.DownloadProgressChanged += (s, e) =>
|
||||
{
|
||||
if (progressForm != null && !progressForm.IsDisposed)
|
||||
{
|
||||
progressForm.SetStatus(statusText + " " + e.ProgressPercentage + "%");
|
||||
progressForm.SetProgress(e.ProgressPercentage);
|
||||
}
|
||||
};
|
||||
|
||||
await client.DownloadFileTaskAsync(new Uri(url), outputPath);
|
||||
}
|
||||
|
||||
if (progressForm != null && !progressForm.IsDisposed)
|
||||
{
|
||||
progressForm.SetProgress(100);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowProgressForm(string status)
|
||||
{
|
||||
if (progressForm == null || progressForm.IsDisposed)
|
||||
{
|
||||
progressForm = new Form2();
|
||||
}
|
||||
|
||||
progressForm.Show();
|
||||
progressForm.BringToFront();
|
||||
progressForm.SetStatus(status);
|
||||
progressForm.SetProgress(0);
|
||||
}
|
||||
|
||||
private void CloseProgressForm()
|
||||
{
|
||||
if (progressForm != null && !progressForm.IsDisposed)
|
||||
{
|
||||
progressForm.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> GetNightlyCommitAsync()
|
||||
{
|
||||
string html = await DownloadStringWithUserAgentAsync(nightlyReleaseUrl);
|
||||
|
||||
Match match = Regex.Match(
|
||||
html,
|
||||
@"\b[0-9a-f]{7,40}\b",
|
||||
RegexOptions.IgnoreCase
|
||||
);
|
||||
|
||||
return match.Success ? match.Value : string.Empty;
|
||||
}
|
||||
|
||||
private string GetInstalledCommit()
|
||||
{
|
||||
if (!File.Exists(releaseInfoFile))
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
foreach (string line in File.ReadAllLines(releaseInfoFile))
|
||||
{
|
||||
if (line.StartsWith("commit="))
|
||||
{
|
||||
return line.Substring("commit=".Length).Trim();
|
||||
}
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
private void SaveInstalledCommit(string commit)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(commit))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
File.WriteAllText(releaseInfoFile, "commit=" + commit);
|
||||
}
|
||||
|
||||
private async Task InstallGameAsync()
|
||||
{
|
||||
string zipPath = Path.Combine(Application.StartupPath, "LCEWindows64.zip");
|
||||
string tempExtractDir = Path.Combine(Application.StartupPath, "Game_Temp");
|
||||
|
||||
try
|
||||
{
|
||||
await DownloadFileWithProgressAsync(nightlyZipUrl, zipPath, "Installing...");
|
||||
ShowProgressForm("Extracting...");
|
||||
|
||||
if (Directory.Exists(tempExtractDir))
|
||||
{
|
||||
Directory.Delete(tempExtractDir, true);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(tempExtractDir);
|
||||
ZipFile.ExtractToDirectory(zipPath, tempExtractDir);
|
||||
|
||||
if (File.Exists(zipPath))
|
||||
{
|
||||
File.Delete(zipPath);
|
||||
}
|
||||
|
||||
if (Directory.Exists(gameInstallDir))
|
||||
{
|
||||
Directory.Delete(gameInstallDir, true);
|
||||
}
|
||||
|
||||
Directory.Move(tempExtractDir, gameInstallDir);
|
||||
|
||||
string detectedExe = FindGameExe(gameInstallDir);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(detectedExe) || !File.Exists(detectedExe))
|
||||
{
|
||||
throw new FileNotFoundException("Minecraft.Client.exe could not be found after installation.");
|
||||
}
|
||||
|
||||
SetGamePath(detectedExe);
|
||||
|
||||
string latestCommit = await GetNightlyCommitAsync();
|
||||
SaveInstalledCommit(latestCommit);
|
||||
|
||||
CloseProgressForm();
|
||||
|
||||
MessageBox.Show(
|
||||
"Game installed successfully.",
|
||||
"Install Complete",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
}
|
||||
catch
|
||||
{
|
||||
CloseProgressForm();
|
||||
|
||||
if (Directory.Exists(tempExtractDir))
|
||||
{
|
||||
Directory.Delete(tempExtractDir, true);
|
||||
}
|
||||
|
||||
if (File.Exists(zipPath))
|
||||
{
|
||||
File.Delete(zipPath);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateGameExeAsync()
|
||||
{
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
throw new FileNotFoundException("Game executable was not found.");
|
||||
}
|
||||
|
||||
string tempExePath = Path.Combine(Application.StartupPath, "Minecraft.Client.new.exe");
|
||||
|
||||
try
|
||||
{
|
||||
await DownloadFileWithProgressAsync(nightlyExeUrl, tempExePath, "Updating...");
|
||||
File.Copy(tempExePath, exePath, true);
|
||||
|
||||
if (File.Exists(tempExePath))
|
||||
{
|
||||
File.Delete(tempExePath);
|
||||
}
|
||||
|
||||
string latestCommit = await GetNightlyCommitAsync();
|
||||
SaveInstalledCommit(latestCommit);
|
||||
|
||||
CloseProgressForm();
|
||||
|
||||
MessageBox.Show(
|
||||
"Game updated successfully.",
|
||||
"Update Complete",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
}
|
||||
catch
|
||||
{
|
||||
CloseProgressForm();
|
||||
|
||||
if (File.Exists(tempExePath))
|
||||
{
|
||||
File.Delete(tempExePath);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CheckForUpdatesOnStartupAsync()
|
||||
{
|
||||
checkforLink.Visible = false;
|
||||
|
||||
try
|
||||
{
|
||||
string latestCommit = await GetNightlyCommitAsync();
|
||||
string installedCommit = GetInstalledCommit();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(latestCommit))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
DialogResult installResult = MessageBox.Show(
|
||||
"Game not installed.\n\nDo you want to download and install it now?",
|
||||
"Install Game",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Question
|
||||
);
|
||||
|
||||
if (installResult == DialogResult.Yes)
|
||||
{
|
||||
await InstallGameAsync();
|
||||
}
|
||||
|
||||
checkforLink.Visible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.Equals(latestCommit, installedCommit, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
checkforLink.Visible = true;
|
||||
|
||||
DialogResult updateResult = MessageBox.Show(
|
||||
"A new nightly build is available.\n\nDo you want to update now?",
|
||||
"Update Available",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
|
||||
if (updateResult == DialogResult.Yes)
|
||||
{
|
||||
await UpdateGameExeAsync();
|
||||
checkforLink.Visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
checkforLink.Visible = true;
|
||||
|
||||
MessageBox.Show(
|
||||
"Failed to check for updates.\n\n" + ex.Message,
|
||||
"Update Check Error",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void openFolderButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
MessageBox.Show(
|
||||
"Minecraft.Client.exe was not found.",
|
||||
"Game Not Found",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
string gameFolder = Path.GetDirectoryName(exePath);
|
||||
Process.Start("explorer.exe", gameFolder);
|
||||
}
|
||||
|
||||
private void launchButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
MessageBox.Show(
|
||||
"Minecraft.Client.exe was not found.",
|
||||
"Error",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Error
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
string username = usernameComboBox.Text.Trim();
|
||||
|
||||
AddAccount(username);
|
||||
File.WriteAllText(gamePathFile, exePath);
|
||||
SaveFullscreenSetting();
|
||||
|
||||
string args = string.Empty;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
args += "-name \"" + username + "\" ";
|
||||
}
|
||||
|
||||
gameProcess = Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = exePath,
|
||||
Arguments = args.Trim(),
|
||||
WorkingDirectory = Path.GetDirectoryName(exePath),
|
||||
UseShellExecute = true
|
||||
});
|
||||
|
||||
if (gameProcess == null)
|
||||
{
|
||||
MessageBox.Show(
|
||||
"Failed to start the game.",
|
||||
"Error",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Error
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
sessionStart = DateTime.Now;
|
||||
gameProcess.EnableRaisingEvents = true;
|
||||
gameProcess.Exited += GameProcess_Exited;
|
||||
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void GameProcess_Exited(object sender, EventArgs e)
|
||||
{
|
||||
int sessionSeconds = (int)(DateTime.Now - sessionStart).TotalSeconds;
|
||||
|
||||
Invoke((MethodInvoker)delegate
|
||||
{
|
||||
string username = usernameComboBox.Text.Trim();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
if (!playtimeData.ContainsKey(username))
|
||||
{
|
||||
playtimeData[username] = 0;
|
||||
}
|
||||
|
||||
playtimeData[username] += sessionSeconds;
|
||||
SaveAccounts();
|
||||
}
|
||||
|
||||
Application.Exit();
|
||||
});
|
||||
}
|
||||
|
||||
private void setFolderButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
using (FolderBrowserDialog dialog = new FolderBrowserDialog())
|
||||
{
|
||||
dialog.Description = "Select the folder containing Minecraft.Client.exe";
|
||||
|
||||
if (dialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
string selectedFolder = dialog.SelectedPath;
|
||||
string possibleExe = Path.Combine(selectedFolder, "Minecraft.Client.exe");
|
||||
|
||||
if (File.Exists(possibleExe))
|
||||
{
|
||||
SetGamePath(possibleExe);
|
||||
|
||||
MessageBox.Show(
|
||||
"Game folder set successfully.",
|
||||
"Success",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show(
|
||||
"Minecraft.Client.exe was not found in that folder.",
|
||||
"Game Not Found",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Form1_DragEnter(object sender, DragEventArgs e)
|
||||
{
|
||||
if (!e.Data.GetDataPresent(DataFormats.FileDrop))
|
||||
{
|
||||
e.Effect = DragDropEffects.None;
|
||||
return;
|
||||
}
|
||||
|
||||
string[] paths = (string[])e.Data.GetData(DataFormats.FileDrop);
|
||||
|
||||
if (paths.Length == 0)
|
||||
{
|
||||
e.Effect = DragDropEffects.None;
|
||||
return;
|
||||
}
|
||||
|
||||
string droppedPath = paths[0];
|
||||
|
||||
if (File.Exists(droppedPath))
|
||||
{
|
||||
if (Path.GetFileName(droppedPath).Equals("Minecraft.Client.exe", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
e.Effect = DragDropEffects.Copy;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Directory.Exists(droppedPath))
|
||||
{
|
||||
string possibleExe = Path.Combine(droppedPath, "Minecraft.Client.exe");
|
||||
|
||||
if (File.Exists(possibleExe))
|
||||
{
|
||||
e.Effect = DragDropEffects.Copy;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
e.Effect = DragDropEffects.None;
|
||||
}
|
||||
|
||||
private void Form1_DragDrop(object sender, DragEventArgs e)
|
||||
{
|
||||
string[] paths = (string[])e.Data.GetData(DataFormats.FileDrop);
|
||||
|
||||
if (paths.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string droppedPath = paths[0];
|
||||
|
||||
if (File.Exists(droppedPath))
|
||||
{
|
||||
if (Path.GetFileName(droppedPath).Equals("Minecraft.Client.exe", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
SetGamePath(droppedPath);
|
||||
|
||||
MessageBox.Show(
|
||||
"Minecraft.Client.exe loaded successfully.",
|
||||
"Game Selected",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Directory.Exists(droppedPath))
|
||||
{
|
||||
string possibleExe = Path.Combine(droppedPath, "Minecraft.Client.exe");
|
||||
|
||||
if (File.Exists(possibleExe))
|
||||
{
|
||||
SetGamePath(possibleExe);
|
||||
|
||||
MessageBox.Show(
|
||||
"Game folder loaded successfully.",
|
||||
"Game Selected",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MessageBox.Show(
|
||||
"Please drag and drop either Minecraft.Client.exe or the folder containing it.",
|
||||
"Invalid Drop",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
}
|
||||
|
||||
private void resetLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
|
||||
{
|
||||
DialogResult result = MessageBox.Show(
|
||||
"This will reset the saved accounts and game path. Continue?",
|
||||
"Reset Launcher Settings",
|
||||
MessageBoxButtons.YesNo,
|
||||
MessageBoxIcon.Question
|
||||
);
|
||||
|
||||
if (result != DialogResult.Yes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (File.Exists(accountsFile))
|
||||
{
|
||||
File.Delete(accountsFile);
|
||||
}
|
||||
|
||||
if (File.Exists(gamePathFile))
|
||||
{
|
||||
File.Delete(gamePathFile);
|
||||
}
|
||||
|
||||
if (File.Exists(releaseInfoFile))
|
||||
{
|
||||
File.Delete(releaseInfoFile);
|
||||
}
|
||||
|
||||
usernameComboBox.Items.Clear();
|
||||
usernameComboBox.Text = string.Empty;
|
||||
playtimeData.Clear();
|
||||
|
||||
exePath = Path.Combine(Application.StartupPath, "Minecraft.Client.exe");
|
||||
fullscreenCheckBox.Checked = false;
|
||||
|
||||
AutoDetectGame();
|
||||
UpdateGamePathDisplay();
|
||||
UpdatePlaytimeLabel();
|
||||
|
||||
MessageBox.Show(
|
||||
"Launcher settings have been reset.",
|
||||
"Done",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
}
|
||||
|
||||
private void usernameComboBox_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
UpdatePlaytimeLabel();
|
||||
}
|
||||
|
||||
private void fullscreenCheckBox_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void usernameLabel_Click(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void logoPictureBox_Click(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void gamePathLabel_Click(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private async void checkforLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
string latestCommit = await GetNightlyCommitAsync();
|
||||
string installedCommit = GetInstalledCommit();
|
||||
|
||||
if (!File.Exists(exePath))
|
||||
{
|
||||
await InstallGameAsync();
|
||||
checkforLink.Visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!string.Equals(latestCommit, installedCommit, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await UpdateGameExeAsync();
|
||||
checkforLink.Visible = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show(
|
||||
"You are already using the latest nightly build.",
|
||||
"No Updates",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show(
|
||||
"Failed to check for updates.\n\n" + ex.Message,
|
||||
"Error",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Warning
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
39
Form3.Designer.cs
generated
Normal file
39
Form3.Designer.cs
generated
Normal file
@@ -0,0 +1,39 @@
|
||||
namespace LegacyConsoleLauncher
|
||||
{
|
||||
partial class Form3
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(800, 450);
|
||||
this.Text = "Form3";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
20
Form3.cs
Normal file
20
Form3.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace LegacyConsoleLauncher
|
||||
{
|
||||
public partial class Form3 : Form
|
||||
{
|
||||
public Form3()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,9 @@
|
||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System">
|
||||
<HintPath>..\..\..\..\..\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.dll</HintPath>
|
||||
</Reference>
|
||||
@@ -53,18 +56,42 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Form1.Accounts.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Form1.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Form1.Designer.cs">
|
||||
<DependentUpon>Form1.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Form1.Events.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Form1.Game.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Form1.SkinPreview.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Form1.Skins.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Form1.Updates.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Form2.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Form2.Designer.cs">
|
||||
<DependentUpon>Form2.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Form3.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Form3.Designer.cs">
|
||||
<DependentUpon>Form3.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<EmbeddedResource Include="Form1.resx">
|
||||
@@ -82,6 +109,7 @@
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
|
||||
44
README.md
44
README.md
@@ -116,5 +116,49 @@ Stores the location of `Minecraft.Client.exe`.
|
||||
|
||||
## License
|
||||
|
||||
## Updates
|
||||
|
||||
This file summarizes the update-related changes added to the project (see `Form1.Updates.cs`).
|
||||
|
||||
What was added
|
||||
|
||||
- Automatic nightly-check helpers
|
||||
- `GetNightlyCommitAsync()` downloads the release page and extracts the commit hash.
|
||||
- `GetInstalledCommit()` / `SaveInstalledCommit()` read/write the installed commit from the release info file.
|
||||
|
||||
- Download helpers with custom User-Agent and progress
|
||||
- `DownloadStringWithUserAgentAsync(string url)` uses `WebClient` and sets a `User-Agent` header.
|
||||
- `DownloadFileWithProgressAsync(string url, string outputPath, string statusText)` shows a progress UI while downloading and reports percentage updates.
|
||||
|
||||
- Install and update flows
|
||||
- `InstallGameAsync()` downloads the nightly ZIP, extracts it to a temporary folder, moves it into the install folder, detects the game executable and saves the installed commit.
|
||||
- `UpdateGameExeAsync()` downloads the updated game executable, replaces the existing exe and saves the installed commit.
|
||||
|
||||
- UI integration
|
||||
- `ShowProgressForm()` / `CloseProgressForm()` manage a `Form2` progress dialog (`progressForm`).
|
||||
- `CheckForUpdatesOnStartupAsync()` checks for the latest nightly commit on startup and prompts the user to install/update (also toggles `checkforLink` visibility).
|
||||
- `checkforLink_LinkClicked` is a manual trigger bound to a `LinkLabel` that installs/updates when clicked.
|
||||
|
||||
Error handling and cleanup
|
||||
|
||||
- Both install and update flows perform cleanup of temporary files/directories and close the progress UI on exceptions. Exceptions are re-thrown to let calling UI show user-facing messages.
|
||||
|
||||
Notes for maintainers
|
||||
|
||||
- The update code expects several fields to be present elsewhere in `Form1` (declared in other partial files):
|
||||
- `nightlyReleaseUrl`, `nightlyZipUrl`, `nightlyExeUrl`
|
||||
- `releaseInfoFile`, `gameInstallDir`, `exePath`
|
||||
- `progressForm` (type `Form2`) and `checkforLink` (type `LinkLabel`)
|
||||
|
||||
- The project targets .NET Framework 4.7.2 and uses C# 7.3 language features.
|
||||
|
||||
How to test
|
||||
|
||||
1. Build the project in Visual Studio (target .NET Framework 4.7.2).
|
||||
2. Run the launcher and confirm the startup update check logic runs (or trigger the `checkforLink` link).
|
||||
3. Observe the progress dialog while downloads happen and verify that the game installs/updates and `releaseInfoFile` is written.
|
||||
|
||||
If you want, I can expand this README with examples of the `releaseInfoFile` format, how to configure nightly URLs, or include screenshots of the progress dialog.
|
||||
|
||||
MIT License
|
||||
© 2026 GatoWare
|
||||
|
||||
4
packages.config
Normal file
4
packages.config
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net472" />
|
||||
</packages>
|
||||
Reference in New Issue
Block a user