From b408be7559a6e9b04658e587c335a4cdcb528faa Mon Sep 17 00:00:00 2001 From: MrMaho Date: Sat, 26 Sep 2026 21:56:17 +0200 Subject: [PATCH] Fixed master address, admin cheats (money, needs, goto/bring), docs page Co-Authored-By: Claude Opus 5.5 --- Cheats.cs | 55 +++++++++++++++++++++++++++++++++ Commands.cs | 7 +---- MSCO.csproj | 1 + MultiplayerManager.cs | 71 +++++++++++++++++++++++++++++++++++++++++++ NetSession.cs | 3 +- Overlays.cs | 25 ++++++++++++++- README.md | 4 +-- ServerBrowser.cs | 11 ++----- 8 files changed, 158 insertions(+), 19 deletions(-) create mode 100644 Cheats.cs diff --git a/Cheats.cs b/Cheats.cs new file mode 100644 index 0000000..c9633bf --- /dev/null +++ b/Cheats.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using HutongGames.PlayMaker; +using UnityEngine; + +namespace MSCO +{ + /// + /// Admin-Cheats für den eigenen Spieler (Geld, Bedürfnisse). Nur aus dem Admin-Menü erreichbar, also nur für + /// Server-Admins bzw. den P2P-Host. Geld wird über MoneySync automatisch an alle weitergegeben. + /// + public static class Cheats + { + // Globale PlayMaker-Variablen des Spielers: Wert, auf den "auffüllen" sie setzt + static readonly KeyValuePair[] Needs = + { + new KeyValuePair("PlayerHunger", 0f), + new KeyValuePair("PlayerThirst", 0f), + new KeyValuePair("PlayerStress", 0f), + new KeyValuePair("PlayerUrine", 0f), + new KeyValuePair("PlayerFatigue", 0f), + new KeyValuePair("PlayerDirtiness", 0f), + }; + + public static string AddMoney(float amount) + { + FsmFloat money = FsmVariables.GlobalVariables.FindFsmFloat("PlayerMoney"); + if (money == null) return "Money not found (only works in the game)"; + money.Value += amount; + return "+" + amount.ToString("0") + " MK (now " + money.Value.ToString("0") + " MK)"; + } + + public static string FillNeeds() + { + int done = 0; + foreach (KeyValuePair need in Needs) + { + FsmFloat v = FsmVariables.GlobalVariables.FindFsmFloat(need.Key); + if (v == null) continue; + v.Value = need.Value; + done++; + } + return done > 0 ? "Hunger, thirst, stress, fatigue, bladder and dirt reset" : "Player needs not found (only works in the game)"; + } + + /// Eigenen Spieler an eine Position setzen (etwas darüber, damit er nicht im Boden steckt). + public static bool Teleport(Transform player, Vector3 position) + { + if (player == null) return false; + player.position = position + Vector3.up * 0.3f; + Rigidbody rb = player.GetComponent(); + if (rb != null && !rb.isKinematic) rb.velocity = Vector3.zero; + return true; + } + } +} diff --git a/Commands.cs b/Commands.cs index 6309839..32a05ed 100644 --- a/Commands.cs +++ b/Commands.cs @@ -13,10 +13,9 @@ namespace MSCO "disconnect - leave the server / game\n" + "host [port] / join [port] - direct LAN game (no Steam)\n" + "save - save the game (host / singleplayer)\n" + - "/admin - admin menu (kick / ban)\n" + + "/admin - admin menu (kick / ban / teleport / cheats)\n" + "vehicles | rig - debug info about vehicles / player models\n" + "debug - toggle sync debug output\n" + - "master [url] - show / change the server list address\n" + "clear - clear this console"; public static void Execute(string[] args, Action print) @@ -59,10 +58,6 @@ namespace MSCO mp.JoinServer(host, serverPort); break; } - case "master": - if (args.Length > 1) ServerBrowser.MasterUrl = args[1]; - print("Master server: " + ServerBrowser.MasterUrl); - break; case "save": mp.SaveGame(); break; diff --git a/MSCO.csproj b/MSCO.csproj index 4740fc0..6b90015 100644 --- a/MSCO.csproj +++ b/MSCO.csproj @@ -75,6 +75,7 @@ + diff --git a/MultiplayerManager.cs b/MultiplayerManager.cs index a8108ed..62a58bd 100644 --- a/MultiplayerManager.cs +++ b/MultiplayerManager.cs @@ -221,6 +221,66 @@ namespace MSCO return list; } + // ---------------------------------------------------------------- Cheats (Admin-Menü) + + /// Cheats nur im Spiel und nur für Server-Admins bzw. den P2P-Host. + public bool CanCheat { get { return session != null && MenuHelper.InGame && (dedicated ? isAdmin : session.IsHost); } } + + public void CheatMoney(float amount) + { + if (!CanCheat) return; + overlays.Log(Cheats.AddMoney(amount)); + AnnounceCheat("gave themselves " + amount.ToString("0") + " MK"); + } + + public void CheatNeeds() + { + if (!CanCheat) return; + overlays.Log(Cheats.FillNeeds()); + AnnounceCheat("refilled their needs"); + } + + /// Zu einem Mitspieler teleportieren. + public void GotoPlayer(int playerId) + { + PlayerInfo p; + if (!CanCheat || !players.TryGetValue(playerId, out p) || p.Avatar == null) { overlays.Log("Player position unknown"); return; } + passenger.Exit(localPlayer, playerCamera); + PlayerState s = p.Avatar.LastState; + Vector3 side = Quaternion.Euler(0f, s.RotationY, 0f) * Vector3.right; + if (Cheats.Teleport(localPlayer, s.Position + side * TeleportSideOffset)) overlays.Log("Teleported to " + p.Name); + } + + /// Mitspieler zu sich holen (Server prüft, ob man Admin ist). + public void BringPlayer(int playerId) + { + if (!CanCheat || localPlayer == null || playerId == localId) return; + Vector3 pos = localPlayer.position + localPlayer.forward * TeleportSideOffset; + if (dedicated) + { + session.SendTo(NetSession.HostId, MsgType.AdminAction, NetSession.Build(w => + { + w.Write((byte)3); + w.Write(playerId); + w.Write(""); + w.Write(pos.x); w.Write(pos.y); w.Write(pos.z); + })); + } + else if (players.ContainsKey(playerId)) + { + session.SendTo(playerId, MsgType.Teleport, NetSession.Build(w => { w.Write(pos.x); w.Write(pos.y); w.Write(pos.z); })); + overlays.Log("Brought " + players[playerId].Name + " to you"); + } + } + + /// Cheats für alle sichtbar im Chat melden. + void AnnounceCheat(string what) + { + string text = OwnName() + " " + what + " (admin)"; + overlays.AddChat("", text, true); + SendGame(MsgType.Chat, NetSession.Build(w => w.Write("* " + text))); + } + public void AdminAction(bool ban, int playerId, string reason) { if (session == null) return; @@ -835,6 +895,17 @@ namespace MSCO SaveSync.WriteFile(file, data); break; + case MsgType.Teleport: + { + // Ein Admin holt uns zu sich + var pos = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle()); + if (!MenuHelper.InGame || localPlayer == null) break; + passenger.Exit(localPlayer, playerCamera); + Cheats.Teleport(localPlayer, pos); + overlays.AddChat("", "An admin teleported you", true); + break; + } + case MsgType.AdminStatus: isAdmin = r.ReadBoolean(); if (isAdmin) overlays.Log("You are an admin on this server - type /admin in chat or 'admin' here."); diff --git a/NetSession.cs b/NetSession.cs index 1988ba1..f6893e4 100644 --- a/NetSession.cs +++ b/NetSession.cs @@ -37,7 +37,8 @@ namespace MSCO 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 struct Message diff --git a/Overlays.cs b/Overlays.cs index f27364c..8071e10 100644 --- a/Overlays.cs +++ b/Overlays.cs @@ -263,7 +263,7 @@ namespace MSCO void DrawAdmin(MultiplayerManager mp) { - float width = Mathf.Min(600, Screen.width - 40), height = Mathf.Min(460, Screen.height - 40); + float width = Mathf.Min(680, Screen.width - 40), height = Mathf.Min(560, Screen.height - 40); var rect = new Rect((Screen.width - width) / 2f, (Screen.height - height) / 2f, width, height); GUI.Box(rect, GUIContent.none, panel); @@ -284,6 +284,21 @@ namespace MSCO GUILayout.Label(mp.IsDedicated ? "Players on this server. Banned players cannot join again." : "Players in your game.", small); GUILayout.Space(10); + // Cheats für den eigenen Spieler (nur im Spiel) + if (mp.CanCheat) + { + GUILayout.Label("CHEATS", caption); + GUILayout.BeginHorizontal(); + if (GUILayout.Button("+1.000 MK", primary, GUILayout.Height(30))) mp.CheatMoney(1000f); + GUILayout.Space(6); + if (GUILayout.Button("+10.000 MK", primary, GUILayout.Height(30))) mp.CheatMoney(10000f); + GUILayout.Space(6); + if (GUILayout.Button("REFILL NEEDS", primary, GUILayout.Height(30))) mp.CheatNeeds(); + GUILayout.EndHorizontal(); + GUILayout.Label("Cheats are announced in the chat. GOTO / BRING teleport you to a player or a player to you.", small); + GUILayout.Space(10); + } + GUILayout.Label("REASON (optional)", caption); adminReason = GUILayout.TextField(adminReason, 100, field, GUILayout.Height(32)); GUILayout.Space(12); @@ -304,6 +319,14 @@ namespace MSCO GUILayout.FlexibleSpace(); bool self = p.Id == mp.LocalId; + if (mp.CanCheat) + { + GUI.enabled = !self; + if (GUILayout.Button("GOTO", secondary, GUILayout.Height(30), GUILayout.Width(70))) mp.GotoPlayer(p.Id); + GUILayout.Space(6); + if (GUILayout.Button("BRING", secondary, GUILayout.Height(30), GUILayout.Width(70))) mp.BringPlayer(p.Id); + GUILayout.Space(12); + } GUI.enabled = !self && !p.IsAdmin; if (GUILayout.Button("KICK", secondary, GUILayout.Height(30), GUILayout.Width(80))) mp.AdminAction(false, p.Id, adminReason); diff --git a/README.md b/README.md index e427976..1d0f799 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,8 @@ Multiplayer-Mod für **My Summer Car** (MSCLoader). Spiel mit bis zu 3 Spielern **Im Spiel** - **T** – Chat - **F8** – MSCO-Konsole mit allen Befehlen -- **/admin** – Admin-Menü (Spieler kicken/bannen) für Server-Admins bzw. den P2P-Host +- **/admin** – Admin-Menü für Server-Admins bzw. den P2P-Host: Spieler kicken/bannen, GOTO/BRING (teleportieren), + Cheats (+1.000 / +10.000 MK, Bedürfnisse auffüllen) – Cheats werden im Chat angezeigt - **P** – als Beifahrer ein-/aussteigen - Alle Tasten sind in den MSCLoader-Einstellungen der Mod änderbar - Discord Rich Presence („In the Menus“, Server, Spielerzahl) @@ -71,7 +72,6 @@ In der F8-Konsole direkt, in der MSCLoader-Konsole mit `mp` davor (z.B. `mp conn | `admin` bzw. `/admin` im Chat | Admin-Menü | | `vehicles`, `rig` | Diagnose: Fahrzeuge / Spielermodelle | | `debug` | Sync-Debugausgaben an/aus | -| `master [url]` | Adresse der Serverliste anzeigen/ändern | | `clear` | Konsole leeren | ## Selbst bauen diff --git a/ServerBrowser.cs b/ServerBrowser.cs index 76bf099..e979f60 100644 --- a/ServerBrowser.cs +++ b/ServerBrowser.cs @@ -37,8 +37,8 @@ namespace MSCO /// public class ServerBrowser { - public const string DefaultMasterUrl = "https://msco.mbm.wtf"; - const string PrefMasterUrl = "MSCO_MasterUrl"; + /// Offizieller Master (Serverliste) - fest eingebaut. + public const string MasterUrl = "https://msco.mbm.wtf"; const int QueryTimeoutMs = 3000; // Ein Server auf diesem PC erscheint automatisch oben in der Liste @@ -54,13 +54,6 @@ namespace MSCO /// Zeitpunkt der letzten Aktualisierung (Time.realtimeSinceStartup), für das Auto-Refresh. public float LastRefresh { get; private set; } - /// Adresse des Master-Servers (änderbar mit "mp master <url>", z.B. zum lokalen Testen). - public static string MasterUrl - { - get { return PlayerPrefs.GetString(PrefMasterUrl, DefaultMasterUrl).TrimEnd('/'); } - set { PlayerPrefs.SetString(PrefMasterUrl, string.IsNullOrEmpty(value) ? DefaultMasterUrl : value.TrimEnd('/')); } - } - /// Lokale Server zuerst, dann die öffentliche Liste. public List Servers {