Files

140 lines
8.4 KiB
C#
Raw Permalink Normal View History

2026-09-12 11:01:37 +02:00
using System;
using System.IO;
using System.IO.Compression;
using System.Drawing;
using System.Diagnostics;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Forms;
internal sealed class Setup : Form
{
Label status;
TextBox destination;
Button install;
ProgressBar progress;
CheckBox shortcut;
bool busy;
string installed;
static readonly Color Mint = Color.FromArgb(119, 223, 199);
[STAThread] static int Main(string[] args)
{
try
{
Application.EnableVisualStyles();
if (args.Length >= 2 && args[0] == "--test")
{
Install(args[1], Console.WriteLine, args.Length > 2 ? args[2] : null);
return 0;
}
using (var form = new Setup())
{
if (args.Length == 2 && args[0] == "--preview")
{
form.Show(); Application.DoEvents();
using (var bmp = new Bitmap(form.Width, form.Height)) { form.DrawToBitmap(bmp, new Rectangle(0, 0, form.Width, form.Height)); bmp.Save(args[1]); }
return 0;
}
Application.Run(form);
}
return 0;
}
catch (Exception ex) { Console.Error.WriteLine(ex); return 1; }
}
Setup()
{
Text = "BLOCKLINE / Setup"; ClientSize = new Size(780, 540);
BackColor = Color.FromArgb(10, 23, 31); ForeColor = Color.White;
Font = new Font("Segoe UI", 10); StartPosition = FormStartPosition.CenterScreen;
FormBorderStyle = FormBorderStyle.FixedSingle; MaximizeBox = false;
AddLabel("B / L • LOCAL OPERATIONS", 35, 28, 12, Mint);
AddLabel("DEIN NÄCHSTES MATCH.\nEINEN KLICK ENTFERNT.", 35, 72, 27, Color.White, 680, 95);
AddLabel("BLOCKLINE / RELAY DISTRICT", 35, 184, 13, Mint);
AddLabel("01 SPIELDATEIEN → 02 ENGINE → 03 SPIELBEREIT", 35, 222, 10, Color.Silver);
AddLabel("INSTALLATIONSORDNER", 35, 268, 9, Mint);
destination = new TextBox { Left = 35, Top = 293, Width = 600, Text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "BLOCKLINE"), BackColor = Color.FromArgb(31, 50, 59), ForeColor = Color.White, BorderStyle = BorderStyle.FixedSingle };
Controls.Add(destination);
var browse = new Button { Left = 645, Top = 290, Width = 100, Height = 30, Text = "Ändern" };
browse.Click += delegate { using (var dialog = new FolderBrowserDialog()) { if (dialog.ShowDialog() == DialogResult.OK) destination.Text = Path.Combine(dialog.SelectedPath, "BLOCKLINE"); } };
Controls.Add(browse);
shortcut = new CheckBox { Left = 35, Top = 333, Width = 500, Text = "Desktop-Verknüpfung erstellen", Checked = true }; Controls.Add(shortcut);
progress = new ProgressBar { Left = 35, Top = 383, Width = 710, Height = 5 }; Controls.Add(progress);
status = AddLabel("Godot wird automatisch geladen. Danach auch offline spielbar.", 35, 404, 10, Color.Silver);
install = new Button { Left = 475, Top = 455, Width = 270, Height = 48, Text = "INSTALLIEREN →", FlatStyle = FlatStyle.Flat, BackColor = Mint, ForeColor = Color.FromArgb(10, 23, 31) };
install.Click += async delegate
{
if (installed != null) { Process.Start(new ProcessStartInfo(Path.Combine(installed, "Start-Game.exe"), "--local") { WorkingDirectory = installed }); Close(); return; }
busy = true; install.Enabled = false; destination.Enabled = false; browse.Enabled = false; shortcut.Enabled = false;
progress.Style = ProgressBarStyle.Marquee;
try
{
string target = Path.GetFullPath(destination.Text);
await Task.Run(() => Install(target, message => BeginInvoke((Action)(() => status.Text = message)), null));
installed = target;
status.Text = "Bereit für Relay District. Installation erfolgreich.";
if (shortcut.Checked)
{
try { CreateShortcut(target); }
catch { status.Text = "Spiel installiert. Desktop-Link konnte nicht erstellt werden."; }
}
install.Text = "JETZT SPIELEN →"; progress.Style = ProgressBarStyle.Blocks; progress.Value = 100;
}
catch (Exception ex) { status.Text = "Installation fehlgeschlagen. Erneut versuchen."; MessageBox.Show(ex.Message, "BLOCKLINE Setup", MessageBoxButtons.OK, MessageBoxIcon.Error); destination.Enabled = true; browse.Enabled = true; shortcut.Enabled = true; }
finally { busy = false; install.Enabled = true; }
};
Controls.Add(install);
AddLabel("LAN / 28 SPIELER\nKeine Anmeldung erforderlich", 35, 457, 10, Color.Silver, 390, 55);
FormClosing += delegate(object sender, FormClosingEventArgs e) { if (busy) e.Cancel = true; };
}
Label AddLabel(string text, int x, int y, int size, Color color, int width = 710, int height = 32)
{
var label = new Label { Text = text, Left = x, Top = y, Width = width, Height = height, Font = new Font("Segoe UI", size), ForeColor = color }; Controls.Add(label); return label;
}
static void CreateShortcut(string target)
{
dynamic shell = Activator.CreateInstance(Type.GetTypeFromProgID("WScript.Shell"));
dynamic link = shell.CreateShortcut(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), "BLOCKLINE.lnk"));
link.TargetPath = Path.Combine(target, "Start-Game.exe"); link.Arguments = "--local"; link.WorkingDirectory = target; link.Save();
}
internal static void Install(string target, Action<string> status, string archive)
{
target = Path.GetFullPath(target).TrimEnd(Path.DirectorySeparatorChar);
if (Directory.Exists(target) || File.Exists(target)) throw new IOException("Ziel existiert bereits. Bitte einen neuen Installationsordner wählen. Bestehende Dateien bleiben erhalten.");
string parent = Path.GetDirectoryName(target);
Directory.CreateDirectory(parent);
string stage = Path.Combine(parent, ".blockline-setup-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(stage);
try
{
status("Spieldateien werden installiert …");
using (var resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("payload.zip"))
using (var zip = new ZipArchive(resource, ZipArchiveMode.Read))
foreach (var entry in zip.Entries)
{
string path = Path.GetFullPath(Path.Combine(stage, entry.FullName));
if (!path.StartsWith(stage + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) throw new InvalidDataException("Ungültiger Paketpfad.");
if (entry.FullName.EndsWith("/")) continue;
Directory.CreateDirectory(Path.GetDirectoryName(path)); entry.ExtractToFile(path);
}
EngineBootstrap.Install(stage, status, archive);
status("Texturen, Modelle und Spielcode werden vorbereitet …");
string logs = Path.Combine(stage, "logs"); Directory.CreateDirectory(logs);
string log = Path.Combine(logs, "setup-import.log");
var info = new ProcessStartInfo(Path.Combine(stage, "tools/godot/Godot_v4.5-stable_win64_console.exe"), "--headless --editor --import --quit --path \"" + stage + "\" --log-file \"" + log + "\"");
info.UseShellExecute = false; info.CreateNoWindow = true; info.WorkingDirectory = stage;
using (var process = Process.Start(info))
{
if (!process.WaitForExit(180000)) { process.Kill(); throw new TimeoutException("Import dauert zu lange."); }
if (process.ExitCode != 0 || !File.Exists(log) || File.ReadAllText(log).Contains("SCRIPT ERROR")) throw new IOException("Spielimport fehlgeschlagen. Installation wurde nicht aktiviert.");
}
Directory.Move(stage, target);
status("Installation abgeschlossen.");
}
finally
{
// Only remove the freshly created staging folder, never an existing install.
if (Path.GetDirectoryName(stage) == parent && Path.GetFileName(stage).StartsWith(".blockline-setup-") && Directory.Exists(stage)) Directory.Delete(stage, true);
}
}
}