Server 2.10.0: distribute MSCO mods from data/mods
- ModStore: mods in data/mods (id, name, version, SHA-256), console "mods [reload]" - ModList is sent before Welcome, files on request in 60 KB parts (once per connection) - ModMessage relayed between players (max 64 KB) - Heartbeat reports the mods to the keymaster (automatic "modded" tag) - Web panel page "Mods": upload, remove, reload (DE/EN) - Tests for list, download, checksum, relay and limits Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -497,6 +497,48 @@ public sealed class AdminPanel
|
||||
return await RunText(s => s.RunCommandCaptured(command, by));
|
||||
});
|
||||
|
||||
// --- Mods (data/mods): bekommen die Spieler beim Beitreten vom Server
|
||||
app.MapGet("/api/mods", async (HttpContext ctx) =>
|
||||
{
|
||||
var user = Require(ctx, PanelRole.Admin, out var denied); if (user == null) return denied;
|
||||
return await Run(s => s.PanelMods(), () => ModStore.Load(dataDir, null)
|
||||
.Select(m => new { id = m.Id, name = m.Name, version = m.Version, sha256 = m.Sha256, size = m.Size }).ToList());
|
||||
});
|
||||
|
||||
app.MapPost("/api/mods/upload", async (HttpContext ctx) =>
|
||||
{
|
||||
var user = Require(ctx, PanelRole.Admin, out var denied); if (user == null) return denied;
|
||||
var sizeFeature = ctx.Features.Get<Microsoft.AspNetCore.Http.Features.IHttpMaxRequestBodySizeFeature>();
|
||||
if (sizeFeature != null && !sizeFeature.IsReadOnly) sizeFeature.MaxRequestBodySize = ModStore.MaxModBytes + 1024;
|
||||
byte[] data;
|
||||
try
|
||||
{
|
||||
using var ms = new MemoryStream();
|
||||
await ctx.Request.Body.CopyToAsync(ms);
|
||||
data = ms.ToArray();
|
||||
}
|
||||
catch (Exception e) { return Error(400, "Upload failed: " + e.Message); }
|
||||
string name = ctx.Request.Query["name"].ToString();
|
||||
string error = ModStore.Add(dataDir, name, data);
|
||||
if (error != null) return Error(400, error);
|
||||
GameServer.Log($"[panel] {By(user)} uploaded the mod {Path.GetFileName(name)}");
|
||||
return await RunText(s => s.ReloadMods(), () => "Mod uploaded - players get it when the server runs");
|
||||
});
|
||||
|
||||
app.MapPost("/api/mods/{id}/remove", async (HttpContext ctx, string id) =>
|
||||
{
|
||||
var user = Require(ctx, PanelRole.Admin, out var denied); if (user == null) return denied;
|
||||
if (!ModStore.Remove(dataDir, id)) return Error(404, "Mod not found");
|
||||
GameServer.Log($"[panel] {By(user)} removed the mod {id}");
|
||||
return await RunText(s => s.ReloadMods(), () => "Mod removed");
|
||||
});
|
||||
|
||||
app.MapPost("/api/mods/reload", async (HttpContext ctx) =>
|
||||
{
|
||||
var user = Require(ctx, PanelRole.Admin, out var denied); if (user == null) return denied;
|
||||
return await RunText(s => s.ReloadMods(), () => "The server is stopped - mods are loaded when it starts");
|
||||
});
|
||||
|
||||
// --- Welt / Backups
|
||||
app.MapGet("/api/backups", async (HttpContext ctx) =>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user