using 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 _portConfig; private ConfigEntry _logToUnityConfig; private ConfigEntry _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(); _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("Server", "Port", 8080, "Local WebSocket port for TokControl commands (ws://127.0.0.1:PORT/)"); _logToUnityConfig = ((BaseUnityPlugin)this).Config.Bind("Debug", "LogToUnityConsole", true, "Mirror TokControl bridge logs to Unity debug console"); _defaultGhostEnemyConfig = ((BaseUnityPlugin)this).Config.Bind("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 Queue = new ConcurrentQueue(); 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)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(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(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(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 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)Valuables.AllValuables).FirstOrDefault((Func)((PrefabRef v) => v != null && (string.Equals(((PrefabRef)(object)v).PrefabName, valuableName, StringComparison.OrdinalIgnoreCase) || (((PrefabRef)(object)v).PrefabName ?? "").IndexOf(valuableName, StringComparison.OrdinalIgnoreCase) >= 0))); if (val == null || !((PrefabRef)(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)(object)val).PrefabName}' x{count} for @{user}"); return CommandResult.Ok("spawned_valuable:" + ((PrefabRef)(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)Items.AllItems).FirstOrDefault((Func)((Item i) => (Object)(object)i != (Object)null && string.Equals(i.itemName, name, StringComparison.OrdinalIgnoreCase))); if ((Object)(object)val != (Object)null) { return val; } return ((IEnumerable)Items.AllItems).FirstOrDefault((Func)((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; } } }