Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of TokControl REPO Bridge v1.1.0
BepInEx/plugins/TokControlREPOBridge.dll
Decompiled 9 hours agousing System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Net.WebSockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using Microsoft.CodeAnalysis; using REPOLib.Modules; using TokControlREPOBridge.Commands; using TokControlREPOBridge.Logging; using TokControlREPOBridge.Network; using TokControlREPOBridge.Util; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("TokControlREPOBridge")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("TokControl WebSocket bridge for R.E.P.O. — spawn items and enemies from stream gifts")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0+1d47cf9520f4d8d4af7f2d50ca10714c95d509f0")] [assembly: AssemblyProduct("TokControlREPOBridge")] [assembly: AssemblyTitle("TokControlREPOBridge")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } internal static class IsExternalInit { } } namespace TokControlREPOBridge { [BepInPlugin("com.tokcontrol.repobridge", "TokControl REPO Bridge", "1.1.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { private ConfigEntry<int> _portConfig; private ConfigEntry<bool> _logToUnityConfig; private ConfigEntry<string> _defaultGhostEnemyConfig; private WebSocketServer? _server; private CommandProcessor? _processor; private GameObject? _dispatcherObject; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } private void Awake() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; BindConfig(); ModLog.Info("=== TokControl REPO Bridge v1.1.0 ==="); ModLog.Info("REPOLib dependency OK — initializing WebSocket bridge"); _dispatcherObject = new GameObject("TokControl_MainThreadDispatcher"); Object.DontDestroyOnLoad((Object)(object)_dispatcherObject); _dispatcherObject.AddComponent<MainThreadDispatcher>(); _processor = new CommandProcessor(_defaultGhostEnemyConfig.Value); SpawnRelay.Initialize(_processor.Actions); _server = new WebSocketServer(_portConfig.Value, _processor); _server.Start(); ModLog.Info($"WebSocket listening on ws://127.0.0.1:{_portConfig.Value}/"); ModLog.Info("Waiting for TokControl / Pandy App commands..."); } private void BindConfig() { _portConfig = ((BaseUnityPlugin)this).Config.Bind<int>("Server", "Port", 8080, "Local WebSocket port for TokControl commands (ws://127.0.0.1:PORT/)"); _logToUnityConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogToUnityConsole", true, "Mirror TokControl bridge logs to Unity debug console"); _defaultGhostEnemyConfig = ((BaseUnityPlugin)this).Config.Bind<string>("Gameplay", "DefaultGhostEnemy", "Hidden", "Enemy name used for spawn_ghost when no name is provided (e.g. Hidden, Robe, Hunter)"); } internal static bool ShouldLogToUnity() { return Instance?._logToUnityConfig?.Value ?? true; } private void OnDestroy() { _server?.Dispose(); if ((Object)(object)_dispatcherObject != (Object)null) { Object.Destroy((Object)(object)_dispatcherObject); } ModLog.Info("TokControl REPO Bridge shut down"); } } public static class PluginInfo { public const string PLUGIN_GUID = "com.tokcontrol.repobridge"; public const string PLUGIN_NAME = "TokControl REPO Bridge"; public const string PLUGIN_VERSION = "1.1.0"; } } namespace TokControlREPOBridge.Util { public class MainThreadDispatcher : MonoBehaviour { private static readonly ConcurrentQueue<Action> Queue = new ConcurrentQueue<Action>(); public static bool IsReady { get; private set; } public static void Enqueue(Action action) { if (action != null) { Queue.Enqueue(action); } } private void Awake() { IsReady = true; } private void OnDestroy() { IsReady = false; } private void Update() { Action result; while (Queue.TryDequeue(out result)) { try { result(); } catch (Exception ex) { ModLog.Error("MainThread action failed: " + ex.Message); } } } } } namespace TokControlREPOBridge.Network { public static class SpawnRelay { private sealed class SpawnRelayPayload { public string Cmd { get; set; } = ""; public string Name { get; set; } = ""; public int Count { get; set; } = 1; public string User { get; set; } = "viewer"; } private const string EventName = "TokControl_SpawnRelay_v1"; private static NetworkedEvent? _relayEvent; private static GameActions? _actions; public static void Initialize(GameActions actions) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown _actions = actions; _relayEvent = new NetworkedEvent("TokControl_SpawnRelay_v1", (Action<EventData>)OnRelayReceived); ModLog.Info("Spawn relay initialized (client → host, Chaos Tricks style)"); } public static CommandResult ExecuteSpawn(string cmd, string name, int count, string user) { if (_actions == null) { return CommandResult.Fail("relay_not_ready"); } if (!MainThreadDispatcher.IsReady) { return CommandResult.Fail("game_not_ready"); } if (SemiFunc.IsMasterClientOrSingleplayer()) { return ExecuteLocally(cmd, name, count, user); } if (!SemiFunc.IsMultiplayer()) { return ExecuteLocally(cmd, name, count, user); } return RelayToHost(cmd, name, count, user); } private static CommandResult RelayToHost(string cmd, string name, int count, string user) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (_relayEvent == null) { return CommandResult.Fail("relay_not_ready"); } try { string text = JsonSerializer.Serialize(new SpawnRelayPayload { Cmd = cmd, Name = name, Count = count, User = user }); _relayEvent.RaiseEvent((object)text, NetworkingEvents.RaiseMasterClient, SendOptions.SendReliable); ModLog.Info($"Relayed to host: {cmd} {name} x{count} for @{user}"); return CommandResult.Ok("relayed_to_host", "Spawn sent to lobby host — host must have this mod installed"); } catch (Exception ex) { ModLog.Error("Relay failed: " + ex.Message); return CommandResult.Fail("relay_failed:" + ex.Message); } } private static void OnRelayReceived(EventData eventData) { if (_actions == null) { return; } if (!SemiFunc.IsMasterClientOrSingleplayer()) { ModLog.Debug("Relay received on non-host — ignored"); return; } try { string text = eventData.CustomData as string; if (string.IsNullOrWhiteSpace(text)) { ModLog.Warn("Relay payload empty"); return; } SpawnRelayPayload payload = JsonSerializer.Deserialize<SpawnRelayPayload>(text); if (payload == null || string.IsNullOrWhiteSpace(payload.Cmd)) { ModLog.Warn("Relay payload invalid"); return; } ModLog.Info($"Host executing relay: {payload.Cmd} {payload.Name} x{payload.Count} for @{payload.User}"); MainThreadDispatcher.Enqueue(delegate { ExecuteLocally(payload.Cmd, payload.Name, payload.Count, payload.User ?? "viewer"); }); } catch (Exception ex) { ModLog.Error("Relay handler error: " + ex.Message); } } private static CommandResult ExecuteLocally(string cmd, string name, int count, string user) { if (_actions == null) { return CommandResult.Fail("actions_not_ready"); } count = Math.Max(1, Math.Min(count, 50)); cmd = cmd.Trim().ToLowerInvariant(); switch (cmd) { case "spawn_item": case "spawnitem": case "item": return _actions.SpawnItemLocal(name, count, user); case "spawnghost": case "spawn_ghost": case "ghost": return _actions.SpawnEnemyLocal(name, count, user); case "spawnenemy": case "spawn_enemy": case "enemy": return _actions.SpawnEnemyLocal(name, count, user); case "spawn_valuable": case "spawnvaluable": case "valuable": return _actions.SpawnValuableLocal(name, count, user); default: return CommandResult.Fail("unknown_spawn_cmd:" + cmd); } } } public sealed class WebSocketServer : IDisposable { private readonly int _port; private readonly CommandProcessor _processor; private readonly CancellationTokenSource _cts = new CancellationTokenSource(); private HttpListener? _listener; private Task? _acceptTask; public WebSocketServer(int port, CommandProcessor processor) { _port = port; _processor = processor; } public void Start() { _listener = new HttpListener(); _listener.Prefixes.Add($"http://127.0.0.1:{_port}/"); _listener.Prefixes.Add($"http://localhost:{_port}/"); _listener.Start(); _acceptTask = Task.Run(() => AcceptLoopAsync(_cts.Token)); ModLog.Info($"HTTP/WebSocket listener started on port {_port}"); } private async Task AcceptLoopAsync(CancellationToken ct) { while (!ct.IsCancellationRequested && _listener != null && _listener.IsListening) { HttpListenerContext context = null; try { context = await _listener.GetContextAsync().ConfigureAwait(continueOnCapturedContext: false); } catch (HttpListenerException) when (ct.IsCancellationRequested) { break; } catch (ObjectDisposedException) { break; } catch (Exception ex3) { ModLog.Warn("Accept error: " + ex3.Message); continue; } Task.Run(() => HandleContextAsync(context, ct), ct); } } private async Task HandleContextAsync(HttpListenerContext context, CancellationToken ct) { _ = 3; try { if (context.Request.IsWebSocketRequest) { await HandleWebSocketAsync((await context.AcceptWebSocketAsync(null).ConfigureAwait(continueOnCapturedContext: false)).WebSocket, ct).ConfigureAwait(continueOnCapturedContext: false); return; } string text = context.Request.Url?.AbsolutePath ?? "/"; string s; if (text.Equals("/health", StringComparison.OrdinalIgnoreCase)) { s = "{\"ok\":true,\"mod\":\"TokControlREPOBridge\"}"; } else if (context.Request.HttpMethod == "POST") { using StreamReader reader = new StreamReader(context.Request.InputStream, context.Request.ContentEncoding); string raw = await reader.ReadToEndAsync().ConfigureAwait(continueOnCapturedContext: false); CommandResult commandResult = _processor.Process(raw); s = commandResult.ToJson(); } else { s = "{\"ok\":true,\"hint\":\"Connect via WebSocket ws://127.0.0.1:" + _port + "/\"}"; } byte[] bytes = Encoding.UTF8.GetBytes(s); context.Response.StatusCode = 200; context.Response.ContentType = "application/json"; context.Response.ContentLength64 = bytes.Length; await context.Response.OutputStream.WriteAsync(bytes, 0, bytes.Length, ct).ConfigureAwait(continueOnCapturedContext: false); context.Response.Close(); } catch (Exception ex) { ModLog.Warn("Request handler error: " + ex.Message); try { context.Response.StatusCode = 500; context.Response.Close(); } catch { } } } private async Task HandleWebSocketAsync(WebSocket socket, CancellationToken ct) { byte[] buffer = new byte[8192]; ModLog.Info("WebSocket client connected"); try { _ = 1; try { while (socket.State == WebSocketState.Open && !ct.IsCancellationRequested) { WebSocketReceiveResult webSocketReceiveResult = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), ct).ConfigureAwait(continueOnCapturedContext: false); if (webSocketReceiveResult.MessageType != WebSocketMessageType.Close) { if (webSocketReceiveResult.MessageType == WebSocketMessageType.Text) { string text = Encoding.UTF8.GetString(buffer, 0, webSocketReceiveResult.Count); ModLog.Debug("WS recv: " + text); CommandResult commandResult = _processor.Process(text); byte[] bytes = Encoding.UTF8.GetBytes(commandResult.ToJson()); await socket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, endOfMessage: true, ct).ConfigureAwait(continueOnCapturedContext: false); } continue; } break; } } catch (WebSocketException ex) { ModLog.Debug("WebSocket closed: " + ex.Message); } catch (Exception ex2) { ModLog.Warn("WebSocket error: " + ex2.Message); } } finally { ModLog.Info("WebSocket client disconnected"); try { if (socket.State == WebSocketState.Open || socket.State == WebSocketState.CloseReceived) { await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "bye", CancellationToken.None).ConfigureAwait(continueOnCapturedContext: false); } } catch { } socket.Dispose(); } } public void Dispose() { _cts.Cancel(); try { _listener?.Stop(); } catch { } try { _listener?.Close(); } catch { } _listener = null; _cts.Dispose(); } } } namespace TokControlREPOBridge.Logging { public static class ModLog { public static void Info(string message) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)Format(message)); } if (Plugin.ShouldLogToUnity()) { Debug.Log((object)Format(message)); } } public static void Warn(string message) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)Format(message)); } if (Plugin.ShouldLogToUnity()) { Debug.LogWarning((object)Format(message)); } } public static void Error(string message) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)Format(message)); } if (Plugin.ShouldLogToUnity()) { Debug.LogError((object)Format(message)); } } public static void Debug(string message) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)Format(message)); } } private static string Format(string message) { return "[TokControl] " + message; } } } namespace TokControlREPOBridge.Commands { public sealed class CommandProcessor { private readonly string _defaultGhostEnemy; private readonly GameActions _actions = new GameActions(); internal GameActions Actions => _actions; public CommandProcessor(string defaultGhostEnemy) { _defaultGhostEnemy = (string.IsNullOrWhiteSpace(defaultGhostEnemy) ? "Hidden" : defaultGhostEnemy.Trim()); } public CommandResult Process(string raw) { if (string.IsNullOrWhiteSpace(raw)) { return CommandResult.Fail("empty_message"); } raw = raw.Trim(); ModLog.Info("Command received: " + raw); try { if (raw.StartsWith("{", StringComparison.Ordinal)) { return ProcessJson(raw); } return ProcessPlainText(raw); } catch (Exception ex) { ModLog.Error("Command parse error: " + ex.Message); return CommandResult.Fail(ex.Message); } } private CommandResult ProcessJson(string json) { using JsonDocument jsonDocument = JsonDocument.Parse(json); JsonElement rootElement = jsonDocument.RootElement; string text = GetString(rootElement, "cmd") ?? GetString(rootElement, "command") ?? GetString(rootElement, "action") ?? ""; text = text.Trim().ToLowerInvariant(); string name = GetString(rootElement, "name") ?? GetString(rootElement, "item") ?? GetString(rootElement, "enemy") ?? GetString(rootElement, "gift") ?? ""; int count = GetInt(rootElement, "count") ?? GetInt(rootElement, "amount") ?? 1; string user = GetString(rootElement, "user") ?? GetString(rootElement, "uniqueId") ?? "viewer"; if (string.IsNullOrEmpty(text) && rootElement.TryGetProperty("command", out var value)) { return ProcessPlainText(value.GetString() ?? ""); } return Dispatch(text, name, count, user); } private CommandResult ProcessPlainText(string text) { string[] array = text.Split('|'); if (array.Length >= 2) { string cmd = array[0].Trim().ToLowerInvariant(); string name = ((array.Length > 1) ? array[1].Trim() : ""); int result; int count = ((array.Length <= 2 || !int.TryParse(array[2], out result)) ? 1 : result); return Dispatch(cmd, name, count, "viewer"); } string[] array2 = text.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); if (array2.Length == 0) { return CommandResult.Fail("empty_command"); } string cmd2 = array2[0].ToLowerInvariant(); string name2 = ((array2.Length > 1) ? array2[1] : ""); int result2; int count2 = ((array2.Length <= 2 || !int.TryParse(array2[2], out result2)) ? 1 : result2); return Dispatch(cmd2, name2, count2, "viewer"); } private CommandResult Dispatch(string cmd, string name, int count, string user) { count = Math.Max(1, Math.Min(count, 50)); switch (cmd) { case "ping": case "health": return CommandResult.Ok("pong", "Server alive"); case "item": case "spawn_item": case "spawnitem": if (string.IsNullOrWhiteSpace(name)) { return CommandResult.Fail("spawn_item requires a name"); } return RunOnMainThread(() => SpawnRelay.ExecuteSpawn(cmd, name, count, user)); case "spawnghost": case "spawn_ghost": case "ghost": { string enemy = (string.IsNullOrWhiteSpace(name) ? _defaultGhostEnemy : name); return RunOnMainThread(() => SpawnRelay.ExecuteSpawn(cmd, enemy, count, user)); } case "spawnenemy": case "spawn_enemy": case "enemy": if (string.IsNullOrWhiteSpace(name)) { return CommandResult.Fail("spawn_enemy requires a name"); } return RunOnMainThread(() => SpawnRelay.ExecuteSpawn(cmd, name, count, user)); case "spawn_valuable": case "spawnvaluable": case "valuable": if (string.IsNullOrWhiteSpace(name)) { return CommandResult.Fail("spawn_valuable requires a name"); } return RunOnMainThread(() => SpawnRelay.ExecuteSpawn(cmd, name, count, user)); case "list_items": return RunOnMainThread(() => _actions.ListItems()); case "list_enemies": return RunOnMainThread(() => _actions.ListEnemies()); default: ModLog.Warn("Unknown command: " + cmd); return CommandResult.Fail("unknown_command:" + cmd); } } private static CommandResult RunOnMainThread(Func<CommandResult> action) { if (!MainThreadDispatcher.IsReady) { return CommandResult.Fail("game_not_ready"); } CommandResult result = null; ManualResetEventSlim wait = new ManualResetEventSlim(initialState: false); MainThreadDispatcher.Enqueue(delegate { try { result = action(); } catch (Exception ex) { result = CommandResult.Fail(ex.Message); } finally { wait.Set(); } }); if (!wait.Wait(TimeSpan.FromSeconds(10.0))) { return CommandResult.Fail("main_thread_timeout"); } return result ?? CommandResult.Fail("no_result"); } private static string? GetString(JsonElement el, string prop) { if (!el.TryGetProperty(prop, out var value)) { return null; } if (value.ValueKind != JsonValueKind.String) { return value.ToString(); } return value.GetString(); } private static int? GetInt(JsonElement el, string prop) { if (!el.TryGetProperty(prop, out var value)) { return null; } if (value.ValueKind == JsonValueKind.Number && value.TryGetInt32(out var value2)) { return value2; } if (value.ValueKind == JsonValueKind.String && int.TryParse(value.GetString(), out var result)) { return result; } return null; } } public sealed class CommandResult { public bool Success { get; init; } public string Message { get; init; } = ""; public string? Detail { get; init; } public static CommandResult Ok(string message, string? detail = null) { return new CommandResult { Success = true, Message = message, Detail = detail }; } public static CommandResult Fail(string message) { return new CommandResult { Success = false, Message = message }; } public string ToJson() { return JsonSerializer.Serialize(new { success = Success, message = Message, detail = Detail }); } } public sealed class GameActions { internal CommandResult SpawnItemLocal(string itemName, int count, string user) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) Item val = FindItem(itemName); if ((Object)(object)val == (Object)null) { ModLog.Warn("Item not found: " + itemName); return CommandResult.Fail("item_not_found:" + itemName); } int num = 0; for (int i = 0; i < count; i++) { Vector3 positionInFrontOfPlayer = SpawnHelper.GetPositionInFrontOfPlayer((float)i * 0.4f); Quaternion identity = Quaternion.identity; GameObject val2 = Items.SpawnItem(val, positionInFrontOfPlayer, identity); if ((Object)(object)val2 != (Object)null) { num++; } } ModLog.Info($"spawn_item '{val.itemName}' x{count} for @{user} (spawned {num})"); return CommandResult.Ok("spawned_item:" + val.itemName, $"count={num}"); } internal CommandResult SpawnEnemyLocal(string enemyName, int count, string user) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) EnemySetup val = FindEnemy(enemyName); if ((Object)(object)val == (Object)null) { ModLog.Warn("Enemy not found: " + enemyName); return CommandResult.Fail("enemy_not_found:" + enemyName); } string text = ((Object)val).name ?? enemyName; for (int i = 0; i < count; i++) { Vector3 positionInFrontOfPlayer = SpawnHelper.GetPositionInFrontOfPlayer(1.5f + (float)i * 0.8f); Enemies.SpawnEnemy(val, positionInFrontOfPlayer, Quaternion.identity, false); } ModLog.Info($"spawn_enemy '{text}' x{count} for @{user}"); return CommandResult.Ok("spawned_enemy:" + text, $"count={count}"); } internal CommandResult SpawnValuableLocal(string valuableName, int count, string user) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) try { PrefabRef val = ((IEnumerable<PrefabRef>)Valuables.AllValuables).FirstOrDefault((Func<PrefabRef, bool>)((PrefabRef v) => v != null && (string.Equals(((PrefabRef<GameObject>)(object)v).PrefabName, valuableName, StringComparison.OrdinalIgnoreCase) || (((PrefabRef<GameObject>)(object)v).PrefabName ?? "").IndexOf(valuableName, StringComparison.OrdinalIgnoreCase) >= 0))); if (val == null || !((PrefabRef<GameObject>)(object)val).IsValid()) { return CommandResult.Fail("valuable_not_found:" + valuableName); } for (int num = 0; num < count; num++) { Vector3 positionInFrontOfPlayer = SpawnHelper.GetPositionInFrontOfPlayer((float)num * 0.5f); Valuables.SpawnValuable(val, positionInFrontOfPlayer, Quaternion.identity); } ModLog.Info($"spawn_valuable '{((PrefabRef<GameObject>)(object)val).PrefabName}' x{count} for @{user}"); return CommandResult.Ok("spawned_valuable:" + ((PrefabRef<GameObject>)(object)val).PrefabName); } catch (Exception ex) { ModLog.Error("spawn_valuable error: " + ex.Message); return CommandResult.Fail(ex.Message); } } public CommandResult ListItems() { StringBuilder stringBuilder = new StringBuilder(); foreach (Item item in Items.AllItems.Take(80)) { if (!((Object)(object)item == (Object)null)) { stringBuilder.AppendLine(item.itemName); } } string text = stringBuilder.ToString(); ModLog.Info($"list_items ({Items.AllItems.Count} total)\n{text}"); return CommandResult.Ok("list_items", text); } public CommandResult ListEnemies() { StringBuilder stringBuilder = new StringBuilder(); foreach (EnemySetup item in Enemies.AllEnemies.Take(80)) { if (!((Object)(object)item == (Object)null)) { stringBuilder.AppendLine(((Object)item).name); } } string text = stringBuilder.ToString(); ModLog.Info($"list_enemies ({Enemies.AllEnemies.Count} total)\n{text}"); return CommandResult.Ok("list_enemies", text); } private static Item? FindItem(string name) { if (string.IsNullOrWhiteSpace(name)) { return null; } name = name.Trim(); Item val = ((IEnumerable<Item>)Items.AllItems).FirstOrDefault((Func<Item, bool>)((Item i) => (Object)(object)i != (Object)null && string.Equals(i.itemName, name, StringComparison.OrdinalIgnoreCase))); if ((Object)(object)val != (Object)null) { return val; } return ((IEnumerable<Item>)Items.AllItems).FirstOrDefault((Func<Item, bool>)((Item i) => (Object)(object)i != (Object)null && i.itemName.IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0)); } private static EnemySetup? FindEnemy(string name) { if (string.IsNullOrWhiteSpace(name)) { return null; } name = name.Trim(); foreach (EnemySetup allEnemy in Enemies.AllEnemies) { if (!((Object)(object)allEnemy == (Object)null)) { string text = ((Object)allEnemy).name ?? ""; if (string.Equals(text, name, StringComparison.OrdinalIgnoreCase)) { return allEnemy; } if (text.IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0) { return allEnemy; } } } return null; } } internal static class SpawnHelper { public static Vector3 GetPositionInFrontOfPlayer(float forwardOffset = 2f) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Camera.main != (Object)null) { Transform transform = ((Component)Camera.main).transform; return transform.position + transform.forward * forwardOffset + Vector3.up * 0.25f; } GameObject val = GameObject.FindGameObjectWithTag("Player"); if ((Object)(object)val != (Object)null) { return val.transform.position + val.transform.forward * forwardOffset + Vector3.up * 0.25f; } return Vector3.up * 2f; } } }