diff --git a/GameServer.cs b/GameServer.cs
index 5012fc9..16f08c0 100644
--- a/GameServer.cs
+++ b/GameServer.cs
@@ -286,6 +286,16 @@ public sealed class GameServer
string result;
if (!c.IsAdmin) result = "You are not an admin on this server";
else if (!clients.TryGetValue(targetId, out var target) || target.State != ClientState.Joined) result = "Player not found";
+ else if (action == 3)
+ {
+ // Spieler zum Admin holen: Position kommt vom Admin
+ if (r.BaseStream.Length - r.BaseStream.Position < 12) return;
+ float x = r.ReadSingle(), y = r.ReadSingle(), z = r.ReadSingle();
+ if (!float.IsFinite(x) || !float.IsFinite(y) || !float.IsFinite(z)) return;
+ target.Conn.Send(MsgType.Teleport, Wire.Build(w => { w.Write(x); w.Write(y); w.Write(z); }));
+ result = $"Brought {target.Name} to you";
+ Log($"{c.Name} teleported {target.Name} to themselves");
+ }
else if (target.IsAdmin && target != c) result = "You cannot kick or ban another admin";
else result = KickOrBan(target, action == 2, reason, c.Name);
c.Conn.Send(MsgType.AdminResult, Wire.Build(w => w.Write(result)));
diff --git a/MasterClient.cs b/MasterClient.cs
index 44d6f41..0412cf9 100644
--- a/MasterClient.cs
+++ b/MasterClient.cs
@@ -4,25 +4,13 @@ namespace MSCO.Server;
///
/// Alle Server hängen am offiziellen Master (Keymaster + Serverliste). Die Adresse ist fest eingebaut,
-/// damit niemand die Schlüsselprüfung über einen eigenen Master umgehen kann.
-/// Nur Debug-Builds (lokales Testen) dürfen sie mit MSCO_MASTER_URL umstellen.
+/// damit niemand die Schlüsselprüfung über einen eigenen Master umgehen kann - nicht über server.json,
+/// Umgebungsvariablen oder Startparameter änderbar.
///
public static class MasterServer
{
public const string OfficialUrl = "https://msco.mbm.wtf";
-
-#if DEBUG
- public static string Url
- {
- get
- {
- string custom = Environment.GetEnvironmentVariable("MSCO_MASTER_URL");
- return string.IsNullOrWhiteSpace(custom) ? OfficialUrl : custom.Trim().TrimEnd('/');
- }
- }
-#else
public static string Url => OfficialUrl;
-#endif
}
/// Spricht mit dem Master-Server: Lizenzschlüssel prüfen und Server in der Liste melden.
diff --git a/Protocol.cs b/Protocol.cs
index 7035735..d0802ed 100644
--- a/Protocol.cs
+++ b/Protocol.cs
@@ -33,7 +33,8 @@ public enum MsgType : byte
AdminAction = 26, // Client -> Server: byte (1 = kick, 2 = ban), int Spieler-ID, string Grund
AdminResult = 27, // Server -> Client: string Meldung
Chat = 28, // Chatnachricht (string)
- VarSync = 29 // Fahrzeugzustand: int n, je [int key, byte typ, wert]
+ VarSync = 29, // Fahrzeugzustand: int n, je [int key, byte typ, wert]
+ Teleport = 30 // Admin holt einen Spieler zu sich: 3 float Position
}
public readonly record struct Message(MsgType Type, byte[] Payload)
diff --git a/README.md b/README.md
index efe4027..f063f1d 100644
--- a/README.md
+++ b/README.md
@@ -132,7 +132,9 @@ Fehlende Einstellungen ergänzt der Server beim Start automatisch. Umgebungsvari
| `reload-save` | Spielstand-Dateien neu einlesen |
| `stop` | speichern und beenden |
-Im Spiel öffnen Admins mit `/admin` (Chat, Taste T) oder `admin` (F8-Konsole) ein Kick/Ban-Menü.
+Im Spiel öffnen Admins mit `/admin` (Chat, Taste T) oder `admin` (F8-Konsole) das Admin-Menü: Kick/Ban,
+GOTO/BRING (zu einem Spieler / Spieler zu sich teleportieren) und Cheats (Geld, Bedürfnisse auffüllen).
+Cheats werden allen im Chat angezeigt, Teleports stehen im Server-Log.
Der Chat landet auch im Server-Log.
## Was der Server speichert
@@ -181,7 +183,7 @@ einen aktuellen Spielstand nach `data/save/` legen und `msco reset-world` ausfü
## Sicherheit
- Die Adresse des Masters (`https://msco.mbm.wtf`) ist fest eingebaut, damit niemand die Schlüsselprüfung mit
- einem eigenen Master umgehen kann. Nur Debug-Builds akzeptieren `MSCO_MASTER_URL`.
+ einem eigenen Master umgehen kann. Sie lässt sich weder in `server.json` noch per Umgebungsvariable ändern.
- `data/` enthält Lizenzschlüssel und API-Keys – nicht veröffentlichen (steht in `.gitignore`).
- Der Container läuft nicht als root. Spielerdaten (Nachrichten, Objektzustände) werden in der Größe begrenzt,
unbekannte oder zu große Nachrichten verworfen.