Files

136 lines
7.0 KiB
C#
Raw Permalink Normal View History

2026-09-12 11:01:37 +02:00
using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
using System.Threading.Tasks;
using System.Drawing;
internal static class Launcher
{
[STAThread]
private static int Main(string[] args)
{
string root = AppDomain.CurrentDomain.BaseDirectory;
string logs = Path.Combine(root, "logs");
bool verify = Array.IndexOf(args, "--verify") >= 0;
bool offline = Array.IndexOf(args, "--offline") >= 0;
bool updateOnly = Array.IndexOf(args, "--update-only") >= 0;
bool local = Array.IndexOf(args, "--local") >= 0;
bool repositoryCopy = Directory.Exists(Path.Combine(root, ".git")) || File.Exists(Path.Combine(root, ".git"));
bool useProject = local || (repositoryCopy && !updateOnly);
Form window = null;
Label label = null;
try
{
Directory.CreateDirectory(logs);
if (local && updateOnly) throw new ArgumentException("--local und --update-only können nicht zusammen verwendet werden.");
if (!verify && !updateOnly)
{
Application.EnableVisualStyles();
window = new Form { Text = "BLOCKLINE - Spielstart", ClientSize = new Size(960, 540),
BackColor = Color.FromArgb(12, 11, 10), ForeColor = Color.FromArgb(225, 218, 203),
StartPosition = FormStartPosition.CenterScreen, FormBorderStyle = FormBorderStyle.None,
MaximizeBox = false, MinimizeBox = false, ControlBox = false };
window.Controls.Add(new Label { Left = 60, Top = 125, Width = 840, Height = 70,
Text = "BLOCKLINE", Font = new Font("Bahnschrift", 38, FontStyle.Bold) });
window.Controls.Add(new Label { Left = 64, Top = 200, Width = 840, Height = 40,
Text = "RELAY DISTRICT / EINSATZ WIRD VORBEREITET", ForeColor = Color.FromArgb(241, 139, 40),
Font = new Font("Bahnschrift", 12) });
label = new Label { Left = 64, Top = 310, Width = 830, Height = 65,
Font = new Font("Bahnschrift", 13), Text = "Bereite Spielstart vor ..." };
window.Controls.Add(new ProgressBar { Left = 64, Top = 408, Width = 830, Height = 5,
Style = ProgressBarStyle.Marquee, MarqueeAnimationSpeed = 25 });
window.Controls.Add(label);
window.Show();
Application.DoEvents();
}
Action<string> status = delegate(string message)
{
if (label != null) label.BeginInvoke((Action)delegate { label.Text = message; });
};
string engine = Path.Combine(root, "tools", "godot", "Godot_v4.5-stable_win64.exe");
if (!File.Exists(engine))
throw new FileNotFoundException("Godot fehlt: " + engine + "\nBitte den kompletten Spielordner verwenden.");
string gameRoot = root;
var update = Task.Factory.StartNew(delegate
{
if (useProject) { status("Starte Dateien aus diesem Projektordner ..."); return root; }
return Updater.Select(root, engine, delegate(string executable, string project)
{
string updateLog = Path.Combine(logs, "update-import.log");
Run(executable, "--headless --editor --import --quit --path " + Quote(project) +
" --log-file " + Quote(updateLog), project, true);
CheckLog(updateLog);
string smokeLog = Path.Combine(logs, "update-verify.log");
Run(executable, "--headless --quit-after 5 --path " + Quote(project) +
" --log-file " + Quote(smokeLog), project, true);
CheckLog(smokeLog);
}, status, offline);
});
while (!update.IsCompleted) { Application.DoEvents(); System.Threading.Thread.Sleep(30); }
gameRoot = update.GetAwaiter().GetResult();
File.WriteAllText(Path.Combine(logs, "selected-project.txt"),
"Project: " + gameRoot + Environment.NewLine +
"Selection: " + (useProject ? "local project" : "updater") + Environment.NewLine);
if (updateOnly) return 0;
if (!File.Exists(Path.Combine(gameRoot, "project.godot")))
throw new FileNotFoundException("project.godot fehlt neben Start-Game.exe.");
// Import classes/resources even on a fresh copy without .godot cache.
string importLog = Path.Combine(logs, "import.log");
if (label != null) { label.Text = "Bereite Spielressourcen vor ..."; Application.DoEvents(); }
Run(engine, "--headless --editor --import --quit --path " + Quote(gameRoot.TrimEnd('\\')) +
" --log-file " + Quote(importLog), gameRoot, true);
CheckLog(importLog);
string gameLog = Path.Combine(logs, "game.log");
string arguments = "--path " + Quote(gameRoot.TrimEnd('\\')) + " --log-file " + Quote(gameLog);
if (verify) arguments += " --headless --max-fps 60 --quit-after 120";
Run(engine, arguments, gameRoot, verify);
if (verify) CheckLog(gameLog);
return 0;
}
catch (Exception error)
{
string details = error.Message + "\n\nDiagnose: " + logs;
try { File.WriteAllText(Path.Combine(logs, "launcher-error.txt"), error.ToString()); } catch { }
if (!verify && !updateOnly) MessageBox.Show(details, "BLOCKLINE konnte nicht starten", MessageBoxButtons.OK, MessageBoxIcon.Error);
return 1;
}
finally { if (window != null) window.Dispose(); }
}
private static string Quote(string value) { return "\"" + value + "\""; }
private static void Run(string file, string args, string directory, bool wait)
{
var info = new ProcessStartInfo(file, args);
info.WorkingDirectory = directory;
info.UseShellExecute = false;
info.CreateNoWindow = true;
using (var process = Process.Start(info))
{
if (!wait) return;
var elapsed = Stopwatch.StartNew();
while (!process.WaitForExit(50))
{
Application.DoEvents();
if (elapsed.ElapsedMilliseconds > 90000)
{
process.Kill();
process.WaitForExit();
throw new Exception("Godot hat innerhalb von 90 Sekunden nicht reagiert.");
}
}
if (process.ExitCode != 0)
throw new Exception("Godot wurde mit Fehlercode " + process.ExitCode + " beendet.");
}
}
private static void CheckLog(string path)
{
if (!File.Exists(path)) throw new Exception("Godot hat kein Startprotokoll erzeugt.");
string contents = File.ReadAllText(path);
if (contents.Contains("SCRIPT ERROR:") || contents.Contains("Failed to load"))
throw new Exception("Godot meldet einen Ressourcen- oder Scriptfehler. Details in " + path);
}
}