From 76175f943fa4125b4f81fb1cd50917ad29451b54 Mon Sep 17 00:00:00 2001 From: wwevo Date: Tue, 14 Apr 2026 17:32:30 +0200 Subject: [PATCH] Release 2.0.2.0 --- .gitignore | 36 +++ CHRANIBotTNG/CHRANIBotTNG.dll | Bin 0 -> 13312 bytes CHRANIBotTNG/ModInfo.xml | 9 + Harmony/CHRANIBotTNG.cs | 454 ++++++++++++++++++++++++++++++++++ build.sh | 20 ++ release_bot.sh | 39 +++ 6 files changed, 558 insertions(+) create mode 100644 .gitignore create mode 100644 CHRANIBotTNG/CHRANIBotTNG.dll create mode 100644 CHRANIBotTNG/ModInfo.xml create mode 100644 Harmony/CHRANIBotTNG.cs create mode 100755 build.sh create mode 100755 release_bot.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f451f81 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf +.idea/**/aws.xml +.idea/**/contentModel.xml +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml +.idea/**/gradle.xml +.idea/**/libraries +cmake-build-*/ +.idea/**/mongoSettings.xml +*.iws +out/ +.idea_modules/ +atlassian-ide-plugin.xml +.idea/replstate.xml +.idea/sonarlint/ +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties +.idea/httpRequests +.idea/caches/build_file_checksums.ser +/.idea/.gitignore +/.idea/chrani-bot-tng-mod.iml +/.idea/misc.xml +/.idea/modules.xml +/.idea/vcs.xml +/CHRANIBotTNG/CHRANIBotTNG.dll diff --git a/CHRANIBotTNG/CHRANIBotTNG.dll b/CHRANIBotTNG/CHRANIBotTNG.dll new file mode 100644 index 0000000000000000000000000000000000000000..f5b2cc3d254313fbe0bdb14098c3e030164c6297 GIT binary patch literal 13312 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 + + + + + + + + \ No newline at end of file diff --git a/Harmony/CHRANIBotTNG.cs b/Harmony/CHRANIBotTNG.cs new file mode 100644 index 0000000..fd06229 --- /dev/null +++ b/Harmony/CHRANIBotTNG.cs @@ -0,0 +1,454 @@ +using HarmonyLib; +using System.Reflection; +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Text; +using System.Xml.Linq; + +public class CHRANIBotTNG : IModApi +{ + public static HashSet MutedPlayers = new HashSet(); + private static string modPath; + + public void InitMod(Mod _modInstance) + { + Log.Out("[CHRANIBotTNG] Loading"); + + modPath = _modInstance.Path; + + // Load persisted mute list + MuteStorage.Initialize(modPath); + MutedPlayers = MuteStorage.LoadMutedPlayers(); + + // Load admin list from serveradmin.xml + AdminManager.Initialize(); + + var harmony = new Harmony("com.chranibottng.mod"); + harmony.PatchAll(Assembly.GetExecutingAssembly()); + + Log.Out($"[CHRANIBotTNG] Loaded - {MutedPlayers.Count} muted players, {AdminManager.GetAdminCount()} admins"); + } +} + +// ==================== MUTE STORAGE (JSON Persistence) ==================== +public static class MuteStorage +{ + private static string muteFilePath; + private static readonly object fileLock = new object(); + + public static void Initialize(string modPath) + { + string dataDir = Path.Combine(modPath, "Data"); + if (!Directory.Exists(dataDir)) + { + Directory.CreateDirectory(dataDir); + } + muteFilePath = Path.Combine(dataDir, "muted_players.json"); + } + + public static HashSet LoadMutedPlayers() + { + lock (fileLock) + { + try + { + if (File.Exists(muteFilePath)) + { + string json = File.ReadAllText(muteFilePath); + var players = ParseJsonArray(json); + Log.Out($"[CHRANIBotTNG] Loaded {players.Count} muted players"); + return players; + } + } + catch (Exception e) + { + Console.WriteLine($"[MuteStorage] Error loading: {e.Message}"); + } + } + return new HashSet(); + } + + public static void SaveMutedPlayers(HashSet players) + { + lock (fileLock) + { + try + { + string json = ToJsonArray(players); + File.WriteAllText(muteFilePath, json); + Log.Out($"[CHRANIBotTNG] Saved {players.Count} muted players"); + } + catch (Exception e) + { + Console.WriteLine($"[MuteStorage] Error saving: {e.Message}"); + } + } + } + + private static string ToJsonArray(HashSet players) + { + StringBuilder sb = new StringBuilder(); + sb.AppendLine("{"); + sb.AppendLine(" \"MutedPlayers\": ["); + + var list = players.ToList(); + for (int i = 0; i < list.Count; i++) + { + sb.Append($" \"{EscapeJson(list[i])}\""); + if (i < list.Count - 1) sb.Append(","); + sb.AppendLine(); + } + + sb.AppendLine(" ]"); + sb.AppendLine("}"); + return sb.ToString(); + } + + private static HashSet ParseJsonArray(string json) + { + HashSet result = new HashSet(); + try + { + // Simple parser: extract strings between quotes in the array + bool inArray = false; + for (int i = 0; i < json.Length; i++) + { + if (json[i] == '[') inArray = true; + if (json[i] == ']') break; + + if (inArray && json[i] == '"') + { + int start = i + 1; + int end = json.IndexOf('"', start); + if (end > start) + { + string value = json.Substring(start, end - start); + if (!string.IsNullOrWhiteSpace(value)) + { + result.Add(value); + } + i = end; + } + } + } + } + catch (Exception e) + { + Console.WriteLine($"[MuteStorage] Parse error: {e.Message}"); + } + return result; + } + + private static string EscapeJson(string str) + { + if (string.IsNullOrEmpty(str)) return ""; + return str.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n").Replace("\r", "\\r"); + } +} + +public static class AdminManager +{ + private static HashSet adminSteamIDs = new HashSet(); + private static string serverAdminPath; + + public static void Initialize() + { + serverAdminPath = FindServerAdminXml(); + if (serverAdminPath != null) + { + LoadAdmins(); + } + else + { + Console.WriteLine("[AdminManager] serveradmin.xml not found - admin checks disabled"); + } + } + + private static string FindServerAdminXml() + { + List attemptedPaths = new List(); + + try + { + string[] possiblePaths = new[] + { + Path.Combine(GameIO.GetSaveGameDir(), "..", "..", "serveradmin.xml"), + Path.Combine(GameIO.GetSaveGameDir(), "..", "serveradmin.xml"), + Path.Combine(GameIO.GetSaveGameDir(), "serveradmin.xml"), + "serveradmin.xml" + }; + + foreach (string path in possiblePaths) + { + try + { + string fullPath = Path.GetFullPath(path); + attemptedPaths.Add(fullPath); + + if (File.Exists(fullPath)) + { + Console.WriteLine($"[AdminManager] Found serveradmin.xml: {fullPath}"); + return fullPath; + } + } + catch (Exception e) + { + attemptedPaths.Add($"{path} (Error: {e.Message})"); + } + } + } + catch (Exception e) + { + Console.WriteLine($"[AdminManager] Error finding serveradmin.xml: {e.Message}"); + } + + Console.WriteLine("[AdminManager] serveradmin.xml not found. Attempted paths:"); + foreach (var path in attemptedPaths) + { + Console.WriteLine($" - {path}"); + } + + return null; + } + + private static void LoadAdmins() + { + try + { + var permStr = ""; + XDocument doc = XDocument.Load(serverAdminPath); + + adminSteamIDs = doc.Descendants("user") + .Where(u => + { + permStr = u.Attribute("permission_level")?.Value; + return int.TryParse(permStr, out int perm) && perm < 1000; + }) + .Select(u => u.Attribute("userid")?.Value) + .Where(id => !string.IsNullOrEmpty(id)) + .ToHashSet(); + + Log.Out($"[CHRANIBotTNG] Loaded {adminSteamIDs.Count} admins (permission < 1000)"); + foreach (var id in adminSteamIDs) + { + Log.Out($"[CHRANIBotTNG] Admin: {id} ({permStr})"); + } + } + catch (Exception e) + { + Console.WriteLine($"[AdminManager] Error loading serveradmin.xml: {e.Message}"); + } + } + + public static bool IsAdmin(ClientInfo _cInfo) + { + if (_cInfo == null || _cInfo.PlatformId == null) return false; + + DumpObject(_cInfo); + string steamId = _cInfo.PlatformId.ReadablePlatformUserIdentifier; + + Log.Out($"[CHRANIBotTNG] Checking admin-permissions for user: {steamId}"); + + // Try exact match first + if (adminSteamIDs.Contains(steamId)) + return true; + + // Try without "Steam_" prefix (serveradmin.xml might not have the prefix) + if (steamId.StartsWith("Steam_")) + { + string steamIdWithoutPrefix = steamId.Substring(6); // Remove "Steam_" + if (adminSteamIDs.Contains(steamIdWithoutPrefix)) + return true; + } + + return false; + } + + public static int GetAdminCount() + { + return adminSteamIDs.Count; + } + + public static void Reload() + { + if (serverAdminPath != null) + { + Log.Out($"[CHRANIBotTNG] Reloading serveradmin.xml"); + LoadAdmins(); + } + } + + public static void DumpObject(object obj) + { + if (obj == null) + { + Console.WriteLine("Object is null"); + return; + } + + var type = obj.GetType(); + + Console.WriteLine($"Type: {type.Name}"); + + // Alle öffentlichen Eigenschaften + Console.WriteLine("Public Properties:"); + foreach (var prop in type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)) + { + try + { + var val = prop.GetValue(obj); + Console.WriteLine($" {prop.Name} = {val}"); + } + catch { } + } + + // Alle privaten und geschützten Eigenschaften + Console.WriteLine("All Properties (inkl. non-public):"); + foreach (var prop in type.GetProperties(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)) + { + try + { + var val = prop.GetValue(obj); + Console.WriteLine($" {prop.Name} = {val}"); + } + catch { } + } + + // Alle Felder (inkl. private) + Console.WriteLine("Fields:"); + foreach (var field in type.GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)) + { + try + { + var val = field.GetValue(obj); + Console.WriteLine($" {field.Name} = {val}"); + } + catch { } + } + } +} + +[HarmonyPatch(typeof(GameManager), "ChatMessageServer")] +public class ChatMessagePatch +{ + static void Prefix(ClientInfo _cInfo, string _msg, ref List _recipientEntityIds) + { + // Handle /bot commands + if (_msg != null && _msg.StartsWith("/bot ")) + { + string[] parts = _msg.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + + if (parts.Length >= 3 && parts[1].ToLower() == "mute") + { + // Check if user is admin + if (!AdminManager.IsAdmin(_cInfo)) + { + Log.Out($"[CHRANIBotTNG] Non-admin {_cInfo?.playerName} tried to mute - denied"); + } + else + { + string targetId = parts[2]; + CHRANIBotTNG.MutedPlayers.Add(targetId); + MuteStorage.SaveMutedPlayers(CHRANIBotTNG.MutedPlayers); + Log.Out($"[CHRANIBotTNG] Admin {_cInfo?.playerName} muted: {targetId}"); + } + } + else if (parts.Length >= 3 && parts[1].ToLower() == "unmute") + { + // Check if user is admin + if (!AdminManager.IsAdmin(_cInfo)) + { + Log.Out($"[CHRANIBotTNG] Non-admin {_cInfo?.playerName} tried to unmute - denied"); + } + else + { + string targetId = parts[2]; + if (CHRANIBotTNG.MutedPlayers.Remove(targetId)) + { + MuteStorage.SaveMutedPlayers(CHRANIBotTNG.MutedPlayers); + Log.Out($"[CHRANIBotTNG] Admin {_cInfo?.playerName} unmuted: {targetId}"); + } + else + { + Log.Out($"[CHRANIBotTNG] Player was not muted: {targetId}"); + } + } + } + else if (parts.Length >= 2 && parts[1].ToLower() == "mutelist") + { + // Show muted players list + if (!AdminManager.IsAdmin(_cInfo)) + { + Log.Out($"[CHRANIBotTNG] Non-admin {_cInfo?.playerName} tried to view mutelist - denied"); + } + else + { + Log.Out($"[CHRANIBotTNG] Muted players ({CHRANIBotTNG.MutedPlayers.Count}):"); + foreach (var muted in CHRANIBotTNG.MutedPlayers) + { + Log.Out($"[CHRANIBotTNG] {muted}"); + } + } + } + else if (parts.Length >= 2 && parts[1].ToLower() == "reload") + { + // Reload serveradmin.xml + if (!AdminManager.IsAdmin(_cInfo)) + { + Log.Out($"[CHRANIBotTNG] Non-admin {_cInfo?.playerName} tried to reload - denied"); + } + else + { + AdminManager.Reload(); + Log.Out($"[CHRANIBotTNG] Admin {_cInfo?.playerName} reloaded serveradmin.xml"); + } + } + + // Clear recipients so no players see /bot commands in-game + if (_recipientEntityIds == null) + { + _recipientEntityIds = new List(); + } + else + { + _recipientEntityIds.Clear(); + } + return; + } + + // Check if sender is muted + if (_cInfo != null && IsPlayerMuted(_cInfo)) + { + Console.WriteLine($"[CHRANIBotTNG] Blocked message from muted player: {_cInfo.playerName}"); + + // Clear recipients so no players see the message + if (_recipientEntityIds == null) + { + _recipientEntityIds = new List(); + } + else + { + _recipientEntityIds.Clear(); + } + } + } + + static bool IsPlayerMuted(ClientInfo _cInfo) + { + // Check EntityID + if (CHRANIBotTNG.MutedPlayers.Contains(_cInfo.entityId.ToString())) + return true; + + // Check player name + if (CHRANIBotTNG.MutedPlayers.Contains(_cInfo.playerName)) + return true; + + // Check SteamID (InternalId) + if (_cInfo.InternalId != null && CHRANIBotTNG.MutedPlayers.Contains(_cInfo.InternalId.ReadablePlatformUserIdentifier)) + return true; + + return false; + } +} diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..9d12bdf --- /dev/null +++ b/build.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Set your 7DTD installation path +GAME_DIR="${GAME_DIR:-$HOME/Software/SteamLibrary/steamapps/common/7 Days To Die}" + +# Use csc (Roslyn compiler) instead of mcs +csc -target:library \ + -out:CHRANIBotTNG/CHRANIBotTNG.dll \ + -nostdlib \ + -r:"$GAME_DIR/7DaysToDie_Data/Managed/mscorlib.dll" \ + -r:"$GAME_DIR/7DaysToDie_Data/Managed/LogLibrary.dll" \ + -r:"$GAME_DIR/7DaysToDie_Data/Managed/netstandard.dll" \ + -r:"$GAME_DIR/7DaysToDie_Data/Managed/System.dll" \ + -r:"$GAME_DIR/7DaysToDie_Data/Managed/System.Core.dll" \ + -r:"$GAME_DIR/7DaysToDie_Data/Managed/System.Xml.dll" \ + -r:"$GAME_DIR/7DaysToDie_Data/Managed/System.Xml.Linq.dll" \ + -r:"$GAME_DIR/7DaysToDie_Data/Managed/Assembly-CSharp.dll" \ + -r:"$GAME_DIR/7DaysToDie_Data/Managed/UnityEngine.CoreModule.dll" \ + -r:"/home/ecv/Software/SteamLibrary/steamapps/common/7 Days To Die/Mods/0_TFP_Harmony/0Harmony.dll" \ + Harmony/CHRANIBotTNG.cs diff --git a/release_bot.sh b/release_bot.sh new file mode 100755 index 0000000..9bbf6f4 --- /dev/null +++ b/release_bot.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# release-to-gitea.sh + +WORK_REMOTE="https://github.com/wwevo/chrani-bot-tng-mod.git" +GITEA_REMOTE="https://code.notjustfor.me/wwevo/chrani-bot-tng-mod.git" +RELEASE_TAG=$(grep -oP '(?<=