diff --git a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs index c4af4d6fe..d8d751efd 100644 --- a/Barotrauma/BarotraumaClient/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaClient/Source/DebugConsole.cs @@ -42,17 +42,13 @@ namespace Barotrauma //listBox.Color = Color.Black * 0.7f; textBox = new GUITextBox(new Rectangle(0, 0, 0, 20), Color.Black, Color.White, Alignment.BottomLeft, Alignment.Left, "", frame); - //textBox.Color = Color.Black * 0.7f; - - //messages already added before initialization -> add them to the listbox - List unInitializedMessages = new List(Messages); - Messages.Clear(); - - foreach (ColoredText msg in unInitializedMessages) - { - NewMessage(msg.Text, msg.Color); - } + textBox.OnTextChanged += (textBox, text) => + { + ResetAutoComplete(); + return true; + }; + NewMessage("Press F3 to open/close the debug console", Color.Cyan); NewMessage("Enter \"help\" for a list of available console commands", Color.Cyan); @@ -105,6 +101,10 @@ namespace Barotrauma { SelectMessage(1); } + else if (PlayerInput.KeyHit(Keys.Tab)) + { + textBox.Text = AutoComplete(textBox.Text); + } if (PlayerInput.KeyHit(Keys.Enter)) { @@ -195,390 +195,285 @@ namespace Barotrauma } } - private static bool ExecProjSpecific(string[] commands) + private static void InitProjectSpecific() { - switch (commands[0].ToLowerInvariant()) + commands.Add(new Command("startclient", "", (string[] args) => { - case "help": - NewMessage("menu: go to main menu", Color.Cyan); - NewMessage("game: enter the \"game screen\"", Color.Cyan); - NewMessage("edit: switch to submarine editor", Color.Cyan); - NewMessage("edit [submarine name]: load a submarine and switch to submarine editor", Color.Cyan); - NewMessage("load [submarine name]: load a submarine", Color.Cyan); - NewMessage("save [submarine name]: save the current submarine using the specified name", Color.Cyan); + if (args.Length == 0) return; + + if (GameMain.Client == null) + { + GameMain.NetworkMember = new GameClient("Name"); + GameMain.Client.ConnectToServer(args[0]); + } + })); - NewMessage(" ", Color.Cyan); + commands.Add(new Command("mainmenuscreen|mainmenu|menu", "mainmenu/menu: Go to the main menu.", (string[] args) => + { + GameMain.GameSession = null; - NewMessage("spawn [creaturename] [near/inside/outside]: spawn a creature at a random spawnpoint (use the second parameter to only select spawnpoints near/inside/outside the submarine)", Color.Cyan); - NewMessage("spawnitem [itemname] [cursor/inventory]: spawn an item at the position of the cursor, in the inventory of the controlled character or at a random spawnpoint if the last parameter is omitted", Color.Cyan); + List characters = new List(Character.CharacterList); + foreach (Character c in characters) + { + c.Remove(); + } - NewMessage(" ", Color.Cyan); + GameMain.MainMenuScreen.Select(); + })); - NewMessage("lights: disable lighting", Color.Cyan); - NewMessage("los: disable the line of sight effect", Color.Cyan); - NewMessage("freecam: detach the camera from the controlled character", Color.Cyan); - NewMessage("control [character name]: start controlling the specified character", Color.Cyan); + commands.Add(new Command("gamescreen|game", "gamescreen/game: Go to the \"in-game\" view.", (string[] args) => + { - NewMessage(" ", Color.Cyan); + })); - NewMessage("water: allows adding water into rooms or removing it by holding the left/right mouse buttons", Color.Cyan); - NewMessage("fire: allows putting up fires by left clicking", Color.Cyan); + commands.Add(new Command("editsubscreen|editsub|subeditor", "editsub/subeditor: Switch to the submarine editor.", (string[] args) => + { + if (args.Length > 0) + { + Submarine.Load(string.Join(" ", args), true); + } + GameMain.EditMapScreen.Select(); + })); - NewMessage(" ", Color.Cyan); + commands.Add(new Command("editcharacter", "", (string[] args) => + { + GameMain.EditCharacterScreen.Select(); - NewMessage("teleport: teleport the controlled character to the position of the cursor", Color.Cyan); - NewMessage("teleport [character name]: teleport the specified character to the position of the cursor", Color.Cyan); - NewMessage("heal: restore the controlled character to full health", Color.Cyan); - NewMessage("heal [character name]: restore the specified character to full health", Color.Cyan); - NewMessage("revive: bring the controlled character back from the dead", Color.Cyan); - NewMessage("revive [character name]: bring the specified character back from the dead", Color.Cyan); - NewMessage("killmonsters: immediately kills all AI-controlled enemies in the level", Color.Cyan); + })); - NewMessage(" ", Color.Cyan); + commands.Add(new Command("control|controlcharacter", "control [character name]: Start controlling the specified character.", (string[] args) => + { + if (args.Length < 1) return; - NewMessage("fixwalls: fixes all the walls", Color.Cyan); - NewMessage("fixitems: fixes every item/device in the sub", Color.Cyan); - NewMessage("oxygen: replenishes the oxygen in every room to 100%", Color.Cyan); - NewMessage("power [amount]: immediately sets the temperature of the reactor to the specified value", Color.Cyan); + var character = FindMatchingCharacter(args, true); - NewMessage(" ", Color.Cyan); + if (character != null) + { + Character.Controlled = character; + } + })); - NewMessage("kick [name]: kick a player out from the server", Color.Cyan); - NewMessage("ban [name]: kick and ban the player from the server", Color.Cyan); - NewMessage("banip [IP address]: ban the IP address from the server", Color.Cyan); - NewMessage("debugdraw: toggles the \"debug draw mode\"", Color.Cyan); - NewMessage("netstats: toggles the visibility of the network statistics panel", Color.Cyan); + commands.Add(new Command("shake", "", (string[] args) => + { + GameMain.GameScreen.Cam.Shake = 10.0f; + })); - break; - case "startclient": - if (commands.Length == 1) return true; - if (GameMain.Client == null) + commands.Add(new Command("los", "los: Toggle the line of sight effect on/off.", (string[] args) => + { + GameMain.LightManager.LosEnabled = !GameMain.LightManager.LosEnabled; + NewMessage("Line of sight effect " + (GameMain.LightManager.LosEnabled ? "enabled" : "disabled"), Color.White); + })); + + commands.Add(new Command("lighting|lights", "Toggle lighting on/off.", (string[] args) => + { + GameMain.LightManager.LightingEnabled = !GameMain.LightManager.LightingEnabled; + NewMessage("Lighting " + (GameMain.LightManager.LightingEnabled ? "enabled" : "disabled"), Color.White); + })); + + commands.Add(new Command("tutorial", "", (string[] args) => + { + TutorialMode.StartTutorial(Tutorials.TutorialType.TutorialTypes[0]); + })); + + commands.Add(new Command("lobby|lobbyscreen", "", (string[] args) => + { + GameMain.LobbyScreen.Select(); + })); + + commands.Add(new Command("save|savesub", "save [submarine name]: Save the currently loaded submarine using the specified name.", (string[] args) => + { + if (args.Length < 1) return; + + if (GameMain.EditMapScreen.CharacterMode) + { + GameMain.EditMapScreen.ToggleCharacterMode(); + } + + string fileName = string.Join(" ", args); + if (fileName.Contains("../")) + { + ThrowError("Illegal symbols in filename (../)"); + return; + } + + if (Submarine.SaveCurrent(System.IO.Path.Combine(Submarine.SavePath, fileName + ".sub"))) + { + NewMessage("Sub saved", Color.Green); + } + })); + + commands.Add(new Command("load|loadsub", "load [submarine name]: Load a submarine.", (string[] args) => + { + if (args.Length == 0) return; + Submarine.Load(string.Join(" ", args), true); + })); + + commands.Add(new Command("cleansub", "", (string[] args) => + { + for (int i = MapEntity.mapEntityList.Count - 1; i >= 0; i--) + { + MapEntity me = MapEntity.mapEntityList[i]; + + if (me.SimPosition.Length() > 2000.0f) { - GameMain.NetworkMember = new GameClient("Name"); - GameMain.Client.ConnectToServer(commands[1]); + NewMessage("Removed " + me.Name + " (simposition " + me.SimPosition + ")", Color.Orange); + MapEntity.mapEntityList.RemoveAt(i); } - break; - case "mainmenuscreen": - case "mainmenu": - case "menu": - GameMain.GameSession = null; - - List characters = new List(Character.CharacterList); - foreach (Character c in characters) + else if (me.MoveWithLevel) { - c.Remove(); + NewMessage("Removed " + me.Name + " (MoveWithLevel==true)", Color.Orange); + MapEntity.mapEntityList.RemoveAt(i); } - - GameMain.MainMenuScreen.Select(); - break; - case "gamescreen": - case "game": - GameMain.GameScreen.Select(); - break; - case "editmapscreen": - case "editmap": - case "edit": - if (commands.Length > 1) + else if (me is Item) { - Submarine.Load(string.Join(" ", commands.Skip(1)), true); - } - GameMain.EditMapScreen.Select(); - break; - case "editcharacter": - case "editchar": - GameMain.EditCharacterScreen.Select(); - break; - case "controlcharacter": - case "control": - { - if (commands.Length < 2) break; + Item item = me as Item; + var wire = item.GetComponent(); + if (wire == null) continue; - var character = FindMatchingCharacter(commands, true); - - if (character != null) + if (wire.GetNodes().Count > 0 && !wire.Connections.Any(c => c != null)) { - Character.Controlled = character; + wire.Item.Drop(null); + NewMessage("Dropped wire (ID: " + wire.Item.ID + ") - attached on wall but no connections found", Color.Orange); } } - break; - case "setclientcharacter": - { - if (GameMain.Server == null) break; + } + })); - int separatorIndex = Array.IndexOf(commands, ";"); + commands.Add(new Command("messagebox", "", (string[] args) => + { + new GUIMessageBox("", string.Join(" ", args)); + })); - if (separatorIndex == -1 || commands.Length < 4) - { - ThrowError("Invalid parameters. The command should be formatted as \"setclientcharacter [client] ; [character]\""); - break; - } + commands.Add(new Command("debugdraw", "debugdraw: Toggle the debug drawing mode on/off.", (string[] args) => + { + GameMain.DebugDraw = !GameMain.DebugDraw; + NewMessage("Debug draw mode " + (GameMain.DebugDraw ? "enabled" : "disabled"), Color.White); - string[] commandsLeft = commands.Take(separatorIndex).ToArray(); - string[] commandsRight = commands.Skip(separatorIndex).ToArray(); + })); - string clientName = String.Join(" ", commandsLeft.Skip(1)); + commands.Add(new Command("togglehud|hud", "togglehud/hud: Toggle the character HUD (inventories, icons, buttons, etc) on/off.", (string[] args) => + { + GUI.DisableHUD = !GUI.DisableHUD; + GameMain.Instance.IsMouseVisible = !GameMain.Instance.IsMouseVisible; + NewMessage(GUI.DisableHUD ? "Disabled HUD" : "Enabled HUD", Color.White); + })); - var client = GameMain.Server.ConnectedClients.Find(c => c.name == clientName); - if (client == null) - { - ThrowError("Client \"" + clientName + "\" not found."); - } + commands.Add(new Command("followsub", "followsub: Toggle whether the ", (string[] args) => + { + Camera.FollowSub = !Camera.FollowSub; + NewMessage(Camera.FollowSub ? "Set the camera to follow the closest submarine" : "Disabled submarine following.", Color.White); + })); - var character = FindMatchingCharacter(commandsRight, false); - GameMain.Server.SetClientCharacter(client, character); - } - break; - case "test": - Submarine.Load("aegir mark ii", true); - GameMain.DebugDraw = true; - GameMain.LightManager.LosEnabled = false; - GameMain.EditMapScreen.Select(); - break; - case "shake": - GameMain.GameScreen.Cam.Shake = 10.0f; - break; - case "losenabled": - case "los": - case "drawlos": - GameMain.LightManager.LosEnabled = !GameMain.LightManager.LosEnabled; - break; - case "lighting": - case "lightingenabled": - case "light": - case "lights": - GameMain.LightManager.LightingEnabled = !GameMain.LightManager.LightingEnabled; - break; - case "tutorial": - TutorialMode.StartTutorial(Tutorials.TutorialType.TutorialTypes[0]); - break; - case "editortutorial": - GameMain.EditMapScreen.Select(); - GameMain.EditMapScreen.StartTutorial(); - break; - case "lobbyscreen": - case "lobby": - GameMain.LobbyScreen.Select(); - break; - case "savemap": - case "savesub": - case "save": - if (commands.Length < 2) break; - - if (GameMain.EditMapScreen.CharacterMode) - { - GameMain.EditMapScreen.ToggleCharacterMode(); - } - - string fileName = string.Join(" ", commands.Skip(1)); - if (fileName.Contains("../")) - { - DebugConsole.ThrowError("Illegal symbols in filename (../)"); - return true; - } - - if (Submarine.SaveCurrent(System.IO.Path.Combine(Submarine.SavePath, fileName + ".sub"))) - { - NewMessage("Sub saved", Color.Green); - //Submarine.Loaded.First().CheckForErrors(); - } - - break; - case "loadmap": - case "loadsub": - case "load": - if (commands.Length < 2) break; - - Submarine.Load(string.Join(" ", commands.Skip(1)), true); - break; - case "cleansub": - for (int i = MapEntity.mapEntityList.Count - 1; i >= 0; i--) - { - MapEntity me = MapEntity.mapEntityList[i]; - - if (me.SimPosition.Length() > 2000.0f) - { - DebugConsole.NewMessage("Removed " + me.Name + " (simposition " + me.SimPosition + ")", Color.Orange); - MapEntity.mapEntityList.RemoveAt(i); - } - else if (me.MoveWithLevel) - { - DebugConsole.NewMessage("Removed " + me.Name + " (MoveWithLevel==true)", Color.Orange); - MapEntity.mapEntityList.RemoveAt(i); - } - else if (me is Item) - { - Item item = me as Item; - var wire = item.GetComponent(); - if (wire == null) continue; - - if (wire.GetNodes().Count > 0 && !wire.Connections.Any(c => c != null)) - { - wire.Item.Drop(null); - DebugConsole.NewMessage("Dropped wire (ID: " + wire.Item.ID + ") - attached on wall but no connections found", Color.Orange); - } - } - - } - break; - case "messagebox": - if (commands.Length < 3) break; - new GUIMessageBox(commands[1], commands[2]); - break; - case "debugdraw": - GameMain.DebugDraw = !GameMain.DebugDraw; - break; - case "disablehud": - case "hud": - GUI.DisableHUD = !GUI.DisableHUD; - GameMain.Instance.IsMouseVisible = !GameMain.Instance.IsMouseVisible; - break; - case "followsub": - Camera.FollowSub = !Camera.FollowSub; - break; - case "drawaitargets": - case "showaitargets": - AITarget.ShowAITargets = !AITarget.ShowAITargets; - break; + commands.Add(new Command("toggleaitargets|aitargets", "toggleaitargets/aitargets: Toggle the visibility of AI targets (= targets that enemies can detect and attack/escape from).", (string[] args) => + { + AITarget.ShowAITargets = !AITarget.ShowAITargets; + NewMessage(AITarget.ShowAITargets ? "Enabled AI target drawing" : "Disabled AI target drawing", Color.White); + })); #if DEBUG - case "spamevents": - foreach (Item item in Item.ItemList) + commands.Add(new Command("spamchatmessages", "", (string[] args) => + { + int msgCount = 1000; + if (args.Length > 0) int.TryParse(args[0], out msgCount); + int msgLength = 50; + if (args.Length > 1) int.TryParse(args[1], out msgLength); + + for (int i = 0; i < msgCount; i++) + { + if (GameMain.Client != null) { - for (int i = 0; i(); - if (itemContainer != null) - { - GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.InventoryState }); - } - - GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.Status }); - - item.NeedsPositionUpdate = true; - } + GameMain.Server.SendChatMessage(ToolBox.RandomSeed(msgLength), ChatMessageType.Default); } - - foreach (Character c in Character.CharacterList) + else { - GameMain.Server.CreateEntityEvent(c, new object[] { NetEntityEvent.Type.Status }); + GameMain.Client.SendChatMessage(ToolBox.RandomSeed(msgLength)); } - - foreach (Structure wall in Structure.WallList) - { - GameMain.Server.CreateEntityEvent(wall); - } - break; - case "spamchatmessages": - int msgCount = 1000; - if (commands.Length > 1) int.TryParse(commands[1], out msgCount); - int msgLength = 50; - if (commands.Length > 2) int.TryParse(commands[2], out msgLength); - - for (int i = 0; i < msgCount; i++) - { - if (GameMain.Server != null) - { - GameMain.Server.SendChatMessage(ToolBox.RandomSeed(msgLength), ChatMessageType.Default); - } - else - { - GameMain.Client.SendChatMessage(ToolBox.RandomSeed(msgLength)); - } - } - break; + } + })); #endif - case "cleanbuild": - GameMain.Config.MusicVolume = 0.5f; - GameMain.Config.SoundVolume = 0.5f; - DebugConsole.NewMessage("Music and sound volume set to 0.5", Color.Green); + commands.Add(new Command("cleanbuild", "", (string[] args) => + { + GameMain.Config.MusicVolume = 0.5f; + GameMain.Config.SoundVolume = 0.5f; + NewMessage("Music and sound volume set to 0.5", Color.Green); - GameMain.Config.GraphicsWidth = 0; - GameMain.Config.GraphicsHeight = 0; - GameMain.Config.WindowMode = WindowMode.Fullscreen; - DebugConsole.NewMessage("Resolution set to 0 x 0 (screen resolution will be used)", Color.Green); - DebugConsole.NewMessage("Fullscreen enabled", Color.Green); + GameMain.Config.GraphicsWidth = 0; + GameMain.Config.GraphicsHeight = 0; + GameMain.Config.WindowMode = WindowMode.Fullscreen; + NewMessage("Resolution set to 0 x 0 (screen resolution will be used)", Color.Green); + NewMessage("Fullscreen enabled", Color.Green); - GameSettings.VerboseLogging = false; + GameSettings.VerboseLogging = false; - if (GameMain.Config.MasterServerUrl != "http://www.undertowgames.com/baromaster") + if (GameMain.Config.MasterServerUrl != "http://www.undertowgames.com/baromaster") + { + ThrowError("MasterServerUrl \"" + GameMain.Config.MasterServerUrl + "\"!"); + } + + GameMain.Config.Save("config.xml"); + + var saveFiles = System.IO.Directory.GetFiles(SaveUtil.SaveFolder); + + foreach (string saveFile in saveFiles) + { + System.IO.File.Delete(saveFile); + NewMessage("Deleted " + saveFile, Color.Green); + } + + if (System.IO.Directory.Exists(System.IO.Path.Combine(SaveUtil.SaveFolder, "temp"))) + { + System.IO.Directory.Delete(System.IO.Path.Combine(SaveUtil.SaveFolder, "temp"), true); + NewMessage("Deleted temp save folder", Color.Green); + } + + if (System.IO.Directory.Exists(ServerLog.SavePath)) + { + var logFiles = System.IO.Directory.GetFiles(ServerLog.SavePath); + + foreach (string logFile in logFiles) { - DebugConsole.ThrowError("MasterServerUrl \"" + GameMain.Config.MasterServerUrl + "\"!"); + System.IO.File.Delete(logFile); + NewMessage("Deleted " + logFile, Color.Green); } + } - GameMain.Config.Save("config.xml"); - - var saveFiles = System.IO.Directory.GetFiles(SaveUtil.SaveFolder); - - foreach (string saveFile in saveFiles) - { - System.IO.File.Delete(saveFile); - DebugConsole.NewMessage("Deleted " + saveFile, Color.Green); - } - - if (System.IO.Directory.Exists(System.IO.Path.Combine(SaveUtil.SaveFolder, "temp"))) - { - System.IO.Directory.Delete(System.IO.Path.Combine(SaveUtil.SaveFolder, "temp"), true); - DebugConsole.NewMessage("Deleted temp save folder", Color.Green); - } - - if (System.IO.Directory.Exists(ServerLog.SavePath)) - { - var logFiles = System.IO.Directory.GetFiles(ServerLog.SavePath); - - foreach (string logFile in logFiles) - { - System.IO.File.Delete(logFile); - DebugConsole.NewMessage("Deleted " + logFile, Color.Green); - } - } - - if (System.IO.File.Exists("filelist.xml")) - { - System.IO.File.Delete("filelist.xml"); - DebugConsole.NewMessage("Deleted filelist", Color.Green); - } + if (System.IO.File.Exists("filelist.xml")) + { + System.IO.File.Delete("filelist.xml"); + NewMessage("Deleted filelist", Color.Green); + } - if (System.IO.File.Exists("Submarines/TutorialSub.sub")) - { - System.IO.File.Delete("Submarines/TutorialSub.sub"); + if (System.IO.File.Exists("Submarines/TutorialSub.sub")) + { + System.IO.File.Delete("Submarines/TutorialSub.sub"); - DebugConsole.NewMessage("Deleted TutorialSub from the submarine folder", Color.Green); - } + NewMessage("Deleted TutorialSub from the submarine folder", Color.Green); + } - if (System.IO.File.Exists(GameServer.SettingsFile)) - { - System.IO.File.Delete(GameServer.SettingsFile); - DebugConsole.NewMessage("Deleted server settings", Color.Green); - } + if (System.IO.File.Exists(GameServer.SettingsFile)) + { + System.IO.File.Delete(GameServer.SettingsFile); + NewMessage("Deleted server settings", Color.Green); + } - if (System.IO.File.Exists(GameServer.ClientPermissionsFile)) - { - System.IO.File.Delete(GameServer.ClientPermissionsFile); - DebugConsole.NewMessage("Deleted client permission file", Color.Green); + if (System.IO.File.Exists(GameServer.ClientPermissionsFile)) + { + System.IO.File.Delete(GameServer.ClientPermissionsFile); + NewMessage("Deleted client permission file", Color.Green); + } - } + if (System.IO.File.Exists("crashreport.txt")) + { + System.IO.File.Delete("crashreport.txt"); + NewMessage("Deleted crashreport.txt", Color.Green); + } - if (System.IO.File.Exists("crashreport.txt")) - { - System.IO.File.Delete("crashreport.txt"); - DebugConsole.NewMessage("Deleted crashreport.txt", Color.Green); - } + if (!System.IO.File.Exists("Content/Map/TutorialSub.sub")) + { + ThrowError("TutorialSub.sub not found!"); + } + })); - if (!System.IO.File.Exists("Content/Map/TutorialSub.sub")) - { - DebugConsole.ThrowError("TutorialSub.sub not found!"); - } - - break; - default: - return false; //command not found - break; - } - return true; //command found } } } diff --git a/Barotrauma/BarotraumaClient/Source/GUI/GUITextBox.cs b/Barotrauma/BarotraumaClient/Source/GUI/GUITextBox.cs index 38dd5ac45..bc0185604 100644 --- a/Barotrauma/BarotraumaClient/Source/GUI/GUITextBox.cs +++ b/Barotrauma/BarotraumaClient/Source/GUI/GUITextBox.cs @@ -304,18 +304,10 @@ namespace Barotrauma { case '\b': //backspace if (Text.Length > 0) Text = Text.Substring(0, Text.Length - 1); + if (OnTextChanged != null) OnTextChanged(this, Text); break; - //case '\r': //return - // if (OnEnterPressed != null) - // OnEnterPressed(this); - // break; - //case '\t': //tab - // if (OnTabPressed != null) - // OnTabPressed(this); - // break; } - if (OnTextChanged != null) OnTextChanged(this, Text); } public void ReceiveSpecialInput(Keys key) diff --git a/Barotrauma/BarotraumaServer/Source/DebugConsole.cs b/Barotrauma/BarotraumaServer/Source/DebugConsole.cs index ec1df4a4d..84f035900 100644 --- a/Barotrauma/BarotraumaServer/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaServer/Source/DebugConsole.cs @@ -22,169 +22,148 @@ namespace Barotrauma } } - private static bool ExecProjSpecific(string[] commands) + private static void InitProjectSpecific() { - switch (commands[0].ToLower()) + commands.Add(new Command("restart|reset", "restart/reset: Close and restart the server.", (string[] args) => { - case "help": + NewMessage("*****************", Color.Lime); + NewMessage("RESTARTING SERVER", Color.Lime); + NewMessage("*****************", Color.Lime); + GameMain.Instance.CloseServer(); + GameMain.Instance.StartServer(); + })); - NewMessage("start: start a new round", Color.Cyan); - NewMessage("end: end the current round", Color.Cyan); - NewMessage("restart: restart the server", Color.Cyan); - NewMessage("quit: exit the game", Color.Cyan); + commands.Add(new Command("exit|quit|close", "exit/quit/close: Exit the application.", (string[] args) => + { + GameMain.ShouldRun = false; + })); - NewMessage(" ", Color.Cyan); + commands.Add(new Command("say", "say [message]: Send a chat message that displays \"HOST\" as the sender.", (string[] args) => + { + string text = string.Join(" ", args); + text = "HOST: " + text; + GameMain.Server.SendChatMessage(text, ChatMessageType.Server); + })); - NewMessage("say [chat message]: send a chat message", Color.Cyan); - NewMessage("clientlist: list the names and IPs of the connected clients", Color.Cyan); - NewMessage("kick [name]: kick a player out from the server", Color.Cyan); - NewMessage("ban [name]: kick and ban the player from the server", Color.Cyan); - NewMessage("banip [IP address]: ban the IP address from the server", Color.Cyan); - NewMessage("debugdraw: toggles the \"debug draw mode\"", Color.Cyan); - NewMessage("netstats: toggles the visibility of the network statistics panel", Color.Cyan); + commands.Add(new Command("msg", "msg [message]: Send a chat message with no sender specified.", (string[] args) => + { + string text = string.Join(" ", args); + GameMain.Server.SendChatMessage(text, ChatMessageType.Server); + })); - NewMessage(" ", Color.Cyan); + commands.Add(new Command("servername", "servername [name]: Change the name of the server.", (string[] args) => + { + GameMain.Server.Name = string.Join(" ", args); + GameMain.NetLobbyScreen.ChangeServerName(string.Join(" ", args)); + })); - NewMessage("servername [name]: change the name of the server", Color.Cyan); - NewMessage("servermsg [message]: change the message in the server lobby", Color.Cyan); - NewMessage("seed [seed]: changes the level seed for the next round", Color.Cyan); - NewMessage("gamemode [name]: select the specified game mode for the next round", Color.Cyan); - NewMessage("gamemode [index]: select the specified game mode (0 = sandbox, 1 = mission, etc)", Color.Cyan); - NewMessage("submarine [name]: select the specified game mode for the next round", Color.Cyan); - NewMessage("shuttle [name]: select the specified submarine as the respawn shuttle for the next round", Color.Cyan); + commands.Add(new Command("servermsg", "servermsg [message]: Change the message displayed in the server lobby.", (string[] args) => + { + GameMain.NetLobbyScreen.ChangeServerMessage(string.Join(" ", args)); + })); - NewMessage(" ", Color.Cyan); + commands.Add(new Command("seed|levelseed", "seed/levelseed: Changes the level seed for the next round.", (string[] args) => + { + GameMain.NetLobbyScreen.LevelSeed = string.Join(" ", args); + })); - NewMessage("spawn [creaturename] [near/inside/outside]: spawn a creature at a random spawnpoint (use the second parameter to only select spawnpoints near/inside/outside the submarine)", Color.Cyan); - - NewMessage(" ", Color.Cyan); - - NewMessage("heal [character name]: restore the specified character to full health", Color.Cyan); - NewMessage("revive [character name]: bring the specified character back from the dead", Color.Cyan); - NewMessage("killmonsters: immediately kills all AI-controlled enemies in the level", Color.Cyan); + commands.Add(new Command("gamemode", "gamemode [name]/[index]: Select the game mode for the next round. The parameter can either be the name or the index number of the game mode (0 = sandbox, 1 = mission, etc).", (string[] args) => + { + int index = -1; + if (int.TryParse(string.Join(" ", args), out index)) + { + GameMain.NetLobbyScreen.SelectedModeIndex = index; + } + else + { + GameMain.NetLobbyScreen.SelectedModeName = string.Join(" ", args); + } + NewMessage("Set gamemode to " + GameMain.NetLobbyScreen.SelectedModeName, Color.Cyan); + })); - NewMessage(" ", Color.Cyan); + commands.Add(new Command("mission", "mission [name]/[index]: Select the mission type for the next round. The parameter can either be the name or the index number of the mission type (0 = first mission type, 1 = second mission type, etc).", (string[] args) => + { + int index = -1; + if (int.TryParse(string.Join(" ", args), out index)) + { + GameMain.NetLobbyScreen.MissionTypeIndex = index; + } + else + { + GameMain.NetLobbyScreen.MissionTypeName = string.Join(" ", args); + } + NewMessage("Set mission to " + GameMain.NetLobbyScreen.MissionTypeName, Color.Cyan); + })); - NewMessage("fixwalls: fixes all the walls", Color.Cyan); - NewMessage("fixitems: fixes every item/device in the sub", Color.Cyan); - NewMessage("oxygen: replenishes the oxygen in every room to 100%", Color.Cyan); - NewMessage("power [amount]: immediately sets the temperature of the reactor to the specified value", Color.Cyan); - - break; - case "restart": - case "reset": - NewMessage("*****************", Color.Lime); - NewMessage("RESTARTING SERVER", Color.Lime); - NewMessage("*****************", Color.Lime); - GameMain.Instance.CloseServer(); - GameMain.Instance.StartServer(); - break; - case "exit": - case "close": - case "quit": - GameMain.ShouldRun = false; - break; - case "say": - case "msg": - string text = string.Join(" ", commands.Skip(1)); - if (commands[0].ToLower() == "say") text = "HOST: " + text; - GameMain.Server.SendChatMessage(text, ChatMessageType.Server); - break; - case "servername": - GameMain.Server.Name = string.Join(" ", commands.Skip(1)); - GameMain.NetLobbyScreen.ChangeServerName(string.Join(" ", commands.Skip(1))); - break; - case "servermsg": - GameMain.NetLobbyScreen.ChangeServerMessage(string.Join(" ", commands.Skip(1))); - break; - case "seed": - GameMain.NetLobbyScreen.LevelSeed = string.Join(" ", commands.Skip(1)); - break; - case "gamemode": - { - int index = -1; - if (int.TryParse(string.Join(" ", commands.Skip(1)), out index)) - { - GameMain.NetLobbyScreen.SelectedModeIndex = index; - } - else - { - GameMain.NetLobbyScreen.SelectedModeName = string.Join(" ", commands.Skip(1)); - } - NewMessage("Set gamemode to " + GameMain.NetLobbyScreen.SelectedModeName, Color.Cyan); - } - break; - case "mission": - { - int index = -1; - if (int.TryParse(string.Join(" ", commands.Skip(1)), out index)) - { - GameMain.NetLobbyScreen.MissionTypeIndex = index; - } - else - { - GameMain.NetLobbyScreen.MissionTypeName = string.Join(" ", commands.Skip(1)); - } - NewMessage("Set mission to " + GameMain.NetLobbyScreen.MissionTypeName, Color.Cyan); - } - break; - case "sub": - case "submarine": - { - Submarine sub = GameMain.NetLobbyScreen.GetSubList().Find(s => s.Name.ToLower() == string.Join(" ", commands.Skip(1)).ToLower()); + commands.Add(new Command("sub|submarine", "submarine [name]: Select the submarine for the next round.", (string[] args) => + { + Submarine sub = GameMain.NetLobbyScreen.GetSubList().Find(s => s.Name.ToLower() == string.Join(" ", args).ToLower()); - if (sub != null) - { - GameMain.NetLobbyScreen.SelectedSub = sub; - } - sub = GameMain.NetLobbyScreen.SelectedSub; - NewMessage("Selected sub: " + sub.Name + (sub.HasTag(SubmarineTag.Shuttle) ? " (shuttle)" : ""), Color.Cyan); - } - break; - case "shuttle": - { - Submarine shuttle = GameMain.NetLobbyScreen.GetSubList().Find(s => s.Name.ToLower() == string.Join(" ", commands.Skip(1)).ToLower()); + if (sub != null) + { + GameMain.NetLobbyScreen.SelectedSub = sub; + } + sub = GameMain.NetLobbyScreen.SelectedSub; + NewMessage("Selected sub: " + sub.Name + (sub.HasTag(SubmarineTag.Shuttle) ? " (shuttle)" : ""), Color.Cyan); + })); - if (shuttle != null) - { - GameMain.NetLobbyScreen.SelectedShuttle = shuttle; - } - shuttle = GameMain.NetLobbyScreen.SelectedShuttle; - NewMessage("Selected shuttle: " + shuttle.Name + (shuttle.HasTag(SubmarineTag.Shuttle) ? "" : " (not shuttle)"), Color.Cyan); - } - break; - case "startgame": - case "startround": - case "start": - if (Screen.Selected == GameMain.GameScreen) break; - if (!GameMain.Server.StartGame()) NewMessage("Failed to start a new round", Color.Yellow); - break; - case "endgame": - case "endround": - case "end": - if (Screen.Selected == GameMain.NetLobbyScreen) break; - GameMain.Server.EndGame(); - break; - case "entitydata": - Entity ent = Entity.FindEntityByID(Convert.ToUInt16(commands[1])); - if (ent != null) - { - NewMessage(ent.ToString(), Color.Lime); - } - break; + commands.Add(new Command("shuttle", "shuttle [name]: Select the specified submarine as the respawn shuttle for the next round.", (string[] args) => + { + Submarine shuttle = GameMain.NetLobbyScreen.GetSubList().Find(s => s.Name.ToLower() == string.Join(" ", args).ToLower()); + + if (shuttle != null) + { + GameMain.NetLobbyScreen.SelectedShuttle = shuttle; + } + shuttle = GameMain.NetLobbyScreen.SelectedShuttle; + NewMessage("Selected shuttle: " + shuttle.Name + (shuttle.HasTag(SubmarineTag.Shuttle) ? "" : " (not shuttle)"), Color.Cyan); + })); + + commands.Add(new Command("startgame|startround|start", "start/startgame/startround: Start a new round.", (string[] args) => + { + if (Screen.Selected == GameMain.GameScreen) return; + if (!GameMain.Server.StartGame()) NewMessage("Failed to start a new round", Color.Yellow); + })); + + commands.Add(new Command("endgame|endround|end", "end/endgame/endround: End the current round.", (string[] args) => + { + if (Screen.Selected == GameMain.NetLobbyScreen) return; + GameMain.Server.EndGame(); + })); + + commands.Add(new Command("entitydata", "", (string[] args) => + { + if (args.Length == 0) return; + Entity ent = Entity.FindEntityByID(Convert.ToUInt16(args[0])); + if (ent != null) + { + NewMessage(ent.ToString(), Color.Lime); + } + })); #if DEBUG - case "eventdata": - ServerEntityEvent ev = GameMain.Server.EntityEventManager.Events[Convert.ToUInt16(commands[1])]; - if (ev != null) - { - NewMessage(ev.StackTrace, Color.Lime); - } - break; + commands.Add(new Command("eventdata", "", (string[] args) => + { + if (args.Length == 0) return; + ServerEntityEvent ev = GameMain.Server.EntityEventManager.Events[Convert.ToUInt16(args[0])]; + if (ev != null) + { + NewMessage(ev.StackTrace, Color.Lime); + } + })); + + commands.Add(new Command("spamchatmessages", "", (string[] args) => + { + int msgCount = 1000; + if (args.Length > 0) int.TryParse(args[0], out msgCount); + int msgLength = 50; + if (args.Length > 1) int.TryParse(args[1], out msgLength); + + for (int i = 0; i < msgCount; i++) + { + GameMain.Server.SendChatMessage(ToolBox.RandomSeed(msgLength), ChatMessageType.Default); + } + })); #endif - default: - return false; - } - return true; //command found - } + } } } diff --git a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs index a27068b56..1b08f7a6e 100644 --- a/Barotrauma/BarotraumaShared/Source/DebugConsole.cs +++ b/Barotrauma/BarotraumaShared/Source/DebugConsole.cs @@ -26,6 +26,26 @@ namespace Barotrauma static partial class DebugConsole { + class Command + { + public readonly string[] names; + public readonly string help; + private Action onExecute; + + public Command(string name, string help, Action onExecute) + { + names = name.Split('|'); + this.help = help; + + this.onExecute = onExecute; + } + + public void Execute(string[] args) + { + onExecute(args); + } + } + const int MaxMessages = 200; public static List Messages = new List(); @@ -36,6 +56,586 @@ namespace Barotrauma private static GUIComponent activeQuestionText; #endif + private static List commands = new List(); + + static DebugConsole() + { + commands.Add(new Command("help", "", (string[] args) => + { + if (args.Length == 0) + { + foreach (Command c in commands) + { + if (string.IsNullOrEmpty(c.help)) continue; + NewMessage(c.help, Color.Cyan); + } + } + else + { + var matchingCommand = commands.Find(c => c.names.Any(name => name == args[0])); + if (matchingCommand == null) + { + NewMessage("Command " + args[0] + " not found.", Color.Red); + } + else + { + NewMessage(matchingCommand.help, Color.Cyan); + } + } + })); + + commands.Add(new Command("clientlist", "clientlist: List all the clients connected to the server.", (string[] args) => + { + if (GameMain.Server == null) return; + NewMessage("***************", Color.Cyan); + foreach (Client c in GameMain.Server.ConnectedClients) + { + NewMessage("- " + c.ID.ToString() + ": " + c.name + ", " + c.Connection.RemoteEndPoint.Address.ToString(), Color.Cyan); + } + NewMessage("***************", Color.Cyan); + })); + + + commands.Add(new Command("createfilelist", "", (string[] args) => + { + UpdaterUtil.SaveFileList("filelist.xml"); + })); + + commands.Add(new Command("spawn|spawncharacter", "spawn [creaturename] [near/inside/outside]: Spawn a creature at a random spawnpoint (use the second parameter to only select spawnpoints near/inside/outside the submarine).", (string[] args) => + { + if (args.Length == 0) return; + + Character spawnedCharacter = null; + + Vector2 spawnPosition = Vector2.Zero; + WayPoint spawnPoint = null; + + if (args.Length > 1) + { + switch (args[1].ToLowerInvariant()) + { + case "inside": + spawnPoint = WayPoint.GetRandom(SpawnType.Human, null, Submarine.MainSub); + break; + case "outside": + spawnPoint = WayPoint.GetRandom(SpawnType.Enemy); + break; + case "near": + case "close": + float closestDist = -1.0f; + foreach (WayPoint wp in WayPoint.WayPointList) + { + if (wp.Submarine != null) continue; + + //don't spawn inside hulls + if (Hull.FindHull(wp.WorldPosition, null) != null) continue; + + float dist = Vector2.Distance(wp.WorldPosition, GameMain.GameScreen.Cam.WorldViewCenter); + + if (closestDist < 0.0f || dist < closestDist) + { + spawnPoint = wp; + closestDist = dist; + } + } + break; + case "cursor": + spawnPosition = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); + break; + default: + spawnPoint = WayPoint.GetRandom(args[0].ToLowerInvariant() == "human" ? SpawnType.Human : SpawnType.Enemy); + break; + } + } + else + { + spawnPoint = WayPoint.GetRandom(args[0].ToLowerInvariant() == "human" ? SpawnType.Human : SpawnType.Enemy); + } + + if (string.IsNullOrWhiteSpace(args[0])) return; + + if (spawnPoint != null) spawnPosition = spawnPoint.WorldPosition; + + if (args[0].ToLowerInvariant() == "human") + { + spawnedCharacter = Character.Create(Character.HumanConfigFile, spawnPosition); + +#if CLIENT + if (GameMain.GameSession != null) + { + SinglePlayerMode mode = GameMain.GameSession.gameMode as SinglePlayerMode; + if (mode != null) + { + Character.Controlled = spawnedCharacter; + GameMain.GameSession.CrewManager.AddCharacter(Character.Controlled); + GameMain.GameSession.CrewManager.SelectCharacter(null, Character.Controlled); + } + } +#endif + } + else + { + spawnedCharacter = Character.Create( + "Content/Characters/" + + args[0].First().ToString().ToUpper() + args[0].Substring(1) + + "/" + args[0].ToLower() + ".xml", spawnPosition); + } + })); + + commands.Add(new Command("spawnitem", "spawnitem [itemname] [cursor/inventory]: Spawn an item at the position of the cursor, in the inventory of the controlled character or at a random spawnpoint if the last parameter is omitted.", (string[] args) => + { + if (args.Length < 1) return; + + Vector2? spawnPos = null; + Inventory spawnInventory = null; + + int extraParams = 0; + switch (args.Last()) + { + case "cursor": + extraParams = 1; + spawnPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); + break; + case "inventory": + extraParams = 1; + spawnInventory = Character.Controlled == null ? null : Character.Controlled.Inventory; + break; + default: + extraParams = 0; + break; + } + + string itemName = string.Join(" ", commands.Take(args.Length - extraParams - 1)).ToLowerInvariant(); + + var itemPrefab = MapEntityPrefab.list.Find(ip => ip.Name.ToLowerInvariant() == itemName) as ItemPrefab; + if (itemPrefab == null) + { + ThrowError("Item \"" + itemName + "\" not found!"); + return; + } + + if (spawnPos == null && spawnInventory == null) + { + var wp = WayPoint.GetRandom(SpawnType.Human, null, Submarine.MainSub); + spawnPos = wp == null ? Vector2.Zero : wp.WorldPosition; + } + + if (spawnPos != null) + { + Entity.Spawner.AddToSpawnQueue(itemPrefab, (Vector2)spawnPos); + + } + else if (spawnInventory != null) + { + Entity.Spawner.AddToSpawnQueue(itemPrefab, spawnInventory); + } + })); + + commands.Add(new Command("disablecrewai", "disablecrewai: Disable the AI of the NPCs in the crew.", (string[] args) => + { + HumanAIController.DisableCrewAI = true; + NewMessage("Crew AI disabled", Color.White); + })); + + commands.Add(new Command("enablecrewai", "enablecrewai: Enable the AI of the NPCs in the crew.", (string[] args) => + { + HumanAIController.DisableCrewAI = false; + NewMessage("Crew AI enabled", Color.White); + })); + + commands.Add(new Command("kick", "kick [name]: Kick a player out of the server.", (string[] args) => + { + if (GameMain.NetworkMember == null || args.Length == 0) return; + + string playerName = string.Join(" ", args); + + ShowQuestionPrompt("Reason for kicking \"" + playerName + "\"?", (reason) => + { + GameMain.NetworkMember.KickPlayer(playerName, reason); + }); + })); + + commands.Add(new Command("kickid", "kickid [id]: Kick the player with the specified client ID out of the server.", (string[] args) => + { + if (GameMain.Server == null || args.Length == 0) return; + + int id = 0; + int.TryParse(args[0], out id); + var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id); + if (client == null) + { + ThrowError("Client id \"" + id + "\" not found."); + return; + } + + ShowQuestionPrompt("Reason for kicking \"" + client.name + "\"?", (reason) => + { + GameMain.Server.KickPlayer(client.name, reason); + }); + })); + + commands.Add(new Command("ban", "ban [name]: Kick and ban the player from the server.", (string[] args) => + { + if (GameMain.NetworkMember == null || args.Length == 0) return; + + string clientName = string.Join(" ", args); + ShowQuestionPrompt("Reason for banning \"" + clientName + "\"?", (reason) => + { + ShowQuestionPrompt("Enter the duration of the ban (leave empty to ban permanently, or use the format \"[days] d [hours] h\")", (duration) => + { + TimeSpan? banDuration = null; + if (!string.IsNullOrWhiteSpace(duration)) + { + TimeSpan parsedBanDuration; + if (!TryParseTimeSpan(duration, out parsedBanDuration)) + { + ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\"."); + return; + } + banDuration = parsedBanDuration; + } + + GameMain.NetworkMember.BanPlayer(clientName, reason, false, banDuration); + }); + }); + })); + + commands.Add(new Command("banid", "banid [id]: Kick and ban the player with the specified client ID from the server.", (string[] args) => + { + if (GameMain.Server == null || args.Length == 0) return; + + int id = 0; + int.TryParse(args[0], out id); + var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id); + if (client == null) + { + ThrowError("Client id \"" + id + "\" not found."); + return; + } + + ShowQuestionPrompt("Reason for banning \"" + client.name + "\"?", (reason) => + { + ShowQuestionPrompt("Enter the duration of the ban (leave empty to ban permanently, or use the format \"[days] d [hours] h\")", (duration) => + { + TimeSpan? banDuration = null; + if (!string.IsNullOrWhiteSpace(duration)) + { + TimeSpan parsedBanDuration; + if (!TryParseTimeSpan(duration, out parsedBanDuration)) + { + ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\"."); + return; + } + banDuration = parsedBanDuration; + } + + GameMain.Server.BanPlayer(client.name, reason, false, banDuration); + }); + }); + })); + + + commands.Add(new Command("banip", "banip [ip]: Ban the IP address from the server.", (string[] args) => + { + if (GameMain.Server == null || args.Length == 0) return; + + ShowQuestionPrompt("Reason for banning the ip \"" + commands[1] + "\"?", (reason) => + { + ShowQuestionPrompt("Enter the duration of the ban (leave empty to ban permanently, or use the format \"[days] d [hours] h\")", (duration) => + { + TimeSpan? banDuration = null; + if (!string.IsNullOrWhiteSpace(duration)) + { + TimeSpan parsedBanDuration; + if (!TryParseTimeSpan(duration, out parsedBanDuration)) + { + ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\"."); + return; + } + banDuration = parsedBanDuration; + } + + var client = GameMain.Server.ConnectedClients.Find(c => c.Connection.RemoteEndPoint.Address.ToString() == args[0]); + if (client == null) + { + GameMain.Server.BanList.BanPlayer("Unnamed", args[0], reason, banDuration); + } + else + { + GameMain.Server.KickClient(client, reason); + } + }); + }); + + })); + + commands.Add(new Command("teleportcharacter|teleport", "teleport [character name]: Teleport the specified character to the position of the cursor. If the name parameter is omitted, the controlled character will be teleported.", (string[] args) => + { + Character tpCharacter = null; + + if (args.Length == 0) + { + tpCharacter = Character.Controlled; + } + else + { + FindMatchingCharacter(args, false); + } + + if (tpCharacter == null) return; + + var cam = GameMain.GameScreen.Cam; + tpCharacter.AnimController.CurrentHull = null; + tpCharacter.Submarine = null; + tpCharacter.AnimController.SetPosition(ConvertUnits.ToSimUnits(cam.ScreenToWorld(PlayerInput.MousePosition))); + tpCharacter.AnimController.FindHull(cam.ScreenToWorld(PlayerInput.MousePosition), true); + })); + + commands.Add(new Command("godmode", "godmode: Toggle submarine godmode. Makes the main submarine invulnerable to damage.", (string[] args) => + { + if (Submarine.MainSub == null) return; + + Submarine.MainSub.GodMode = !Submarine.MainSub.GodMode; + NewMessage(Submarine.MainSub.GodMode ? "Godmode on" : "Godmode off", Color.White); + })); + + commands.Add(new Command("lockx", "lockx: Lock horizontal movement of the main submarine.", (string[] args) => + { + Submarine.LockX = !Submarine.LockX; + })); + + commands.Add(new Command("locky", "loxky: Lock vertical movement of the main submarine.", (string[] args) => + { + Submarine.LockY = !Submarine.LockY; + })); + + commands.Add(new Command("dumpids", "", (string[] args) => + { + try + { + int count = args.Length == 0 ? 10 : int.Parse(args[0]); + Entity.DumpIds(count); + } + catch (Exception e) + { + ThrowError("Failed to dump ids", e); + } + })); + + commands.Add(new Command("heal", "heal [character name]: Restore the specified character to full health. If the name parameter is omitted, the controlled character will be healed.", (string[] args) => + { + Character healedCharacter = null; + if (args.Length == 0) + { + healedCharacter = Character.Controlled; + } + else + { + healedCharacter = FindMatchingCharacter(args); + } + + if (healedCharacter != null) + { + healedCharacter.AddDamage(CauseOfDeath.Damage, -healedCharacter.MaxHealth, null); + healedCharacter.Oxygen = 100.0f; + healedCharacter.Bleeding = 0.0f; + healedCharacter.SetStun(0.0f, true); + } + })); + + commands.Add(new Command("revive", "revive [character name]: Bring the specified character back from the dead. If the name parameter is omitted, the controlled character will be revived.", (string[] args) => + { + Character revivedCharacter = null; + if (args.Length == 0) + { + revivedCharacter = Character.Controlled; + } + else + { + revivedCharacter = FindMatchingCharacter(args); + } + + if (revivedCharacter == null) return; + + revivedCharacter.Revive(false); + if (GameMain.Server != null) + { + foreach (Client c in GameMain.Server.ConnectedClients) + { + if (c.Character != revivedCharacter) continue; + //clients stop controlling the character when it dies, force control back + GameMain.Server.SetClientCharacter(c, revivedCharacter); + break; + } + } + })); + + commands.Add(new Command("freeze", "", (string[] args) => + { + if (Character.Controlled != null) Character.Controlled.AnimController.Frozen = !Character.Controlled.AnimController.Frozen; + })); + + commands.Add(new Command("freecamera|freecam", "freecam: Detach the camera from the controlled character.", (string[] args) => + { + Character.Controlled = null; + GameMain.GameScreen.Cam.TargetPos = Vector2.Zero; + })); + + commands.Add(new Command("water|editwater", "water/editwater: Toggle water editing. Allows adding water into rooms by holding the left mouse button and removing it by holding the right mouse button.", (string[] args) => + { + if (GameMain.Client == null) + { + Hull.EditWater = !Hull.EditWater; + NewMessage(Hull.EditWater ? "Water editing on" : "Water editing off", Color.White); + } + })); + + commands.Add(new Command("fire|editfire", "fire/editfire: Allows putting up fires by left clicking.", (string[] args) => + { + if (GameMain.Client == null) + { + Hull.EditFire = !Hull.EditFire; + NewMessage(Hull.EditFire ? "Fire spawning on" : "Fire spawning off", Color.White); + } + })); + + commands.Add(new Command("explosion", "explosion [range] [force] [damage] [structuredamage]: Creates an explosion at the position of the cursor.", (string[] args) => + { + Vector2 explosionPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); + float range = 500, force = 10, damage = 50, structureDamage = 10; + if (args.Length > 0) float.TryParse(args[0], out range); + if (args.Length > 1) float.TryParse(args[1], out force); + if (args.Length > 2) float.TryParse(args[2], out damage); + if (args.Length > 3) float.TryParse(args[3], out structureDamage); + new Explosion(range, force, damage, structureDamage).Explode(explosionPos); + })); + + commands.Add(new Command("fixitems", "fixitems: Repairs all items and restores them to full condition.", (string[] args) => + { + foreach (Item it in Item.ItemList) + { + it.Condition = it.Prefab.Health; + } + })); + + commands.Add(new Command("fixhulls|fixwalls", "fixwalls/fixhulls: Fixes all walls.", (string[] args) => + { + foreach (Structure w in Structure.WallList) + { + for (int i = 0; i < w.SectionCount; i++) + { + w.AddDamage(i, -100000.0f); + } + } + })); + + commands.Add(new Command("power", "power [temperature]: Immediately sets the temperature of the nuclear reactor to the specified value.", (string[] args) => + { + Item reactorItem = Item.ItemList.Find(i => i.GetComponent() != null); + if (reactorItem == null) return; + + float power = 5000.0f; + if (args.Length > 0) float.TryParse(args[0], out power); + + var reactor = reactorItem.GetComponent(); + reactor.ShutDownTemp = power == 0 ? 0 : 7000.0f; + reactor.AutoTemp = true; + reactor.Temperature = power; + + if (GameMain.Server != null) + { + reactorItem.CreateServerEvent(reactor); + } + })); + + commands.Add(new Command("oxygen|air", "oxygen/air: Replenishes the oxygen levels in every room to 100%.", (string[] args) => + { + foreach (Hull hull in Hull.hullList) + { + hull.OxygenPercentage = 100.0f; + } + })); + + commands.Add(new Command("killmonsters", "killmonsters: Immediately kills all AI-controlled enemies in the level.", (string[] args) => + { + foreach (Character c in Character.CharacterList) + { + if (!(c.AIController is EnemyAIController)) continue; + c.AddDamage(CauseOfDeath.Damage, 10000.0f, null); + } + })); + + commands.Add(new Command("netstats", "netstats: Toggles the visibility of the network statistics UI.", (string[] args) => + { + if (GameMain.Server == null) return; + GameMain.Server.ShowNetStats = !GameMain.Server.ShowNetStats; + })); + + commands.Add(new Command("setclientcharacter", "setclientcharacter [client name] ; [character name]: Gives the client control of the specified character.", (string[] args) => + { + if (GameMain.Server == null) return; + + int separatorIndex = Array.IndexOf(args, ";"); + + if (separatorIndex == -1 || args.Length < 3) + { + ThrowError("Invalid parameters. The command should be formatted as \"setclientcharacter [client] ; [character]\""); + return; + } + + string[] argsLeft = args.Take(separatorIndex).ToArray(); + string[] argsRight = args.Skip(separatorIndex).ToArray(); + + string clientName = String.Join(" ", argsLeft); + + var client = GameMain.Server.ConnectedClients.Find(c => c.name == clientName); + if (client == null) + { + ThrowError("Client \"" + clientName + "\" not found."); + } + + var character = FindMatchingCharacter(argsRight, false); + GameMain.Server.SetClientCharacter(client, character); + })); +#if DEBUG + commands.Add(new Command("spamevents", "A debug command that immediately creates entity events for all items, characters and structures.", (string[] args) => + { + foreach (Item item in Item.ItemList) + { + for (int i = 0; i < item.components.Count; i++) + { + if (item.components[i] is IServerSerializable) + { + GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ComponentState, i }); + } + var itemContainer = item.GetComponent(); + if (itemContainer != null) + { + GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.InventoryState }); + } + + GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.Status }); + + item.NeedsPositionUpdate = true; + } + } + + foreach (Character c in Character.CharacterList) + { + GameMain.Server.CreateEntityEvent(c, new object[] { NetEntityEvent.Type.Status }); + } + + foreach (Structure wall in Structure.WallList) + { + GameMain.Server.CreateEntityEvent(wall); + } + })); +#endif + InitProjectSpecific(); + + commands.Sort((c1, c2) => c1.names[0].CompareTo(c2.names[0])); + } + private static string[] SplitCommand(string command) { command = command.Trim(); @@ -71,6 +671,41 @@ namespace Barotrauma return commands.ToArray(); } + + private static string currentAutoCompletedCommand; + private static int currentAutoCompletedIndex; + + public static string AutoComplete(string command) + { + if (string.IsNullOrWhiteSpace(currentAutoCompletedCommand)) + { + currentAutoCompletedCommand = command; + } + + List matchingCommands = new List(); + foreach (Command c in commands) + { + foreach (string name in c.names) + { + if (currentAutoCompletedCommand.Length > name.Length) continue; + if (currentAutoCompletedCommand == name.Substring(0, currentAutoCompletedCommand.Length)) + { + matchingCommands.Add(name); + } + } + } + + if (matchingCommands.Count == 0) return command; + + currentAutoCompletedIndex = currentAutoCompletedIndex % matchingCommands.Count; + return matchingCommands[currentAutoCompletedIndex++]; + } + + public static void ResetAutoComplete() + { + currentAutoCompletedCommand = ""; + currentAutoCompletedIndex = 0; + } public static void ExecuteCommand(string command, GameMain game) { @@ -89,487 +724,44 @@ namespace Barotrauma if (string.IsNullOrWhiteSpace(command)) return; - string[] commands = SplitCommand(command); + string[] splitCommand = SplitCommand(command); - if (!commands[0].ToLowerInvariant().Equals("admin")) + if (!splitCommand[0].ToLowerInvariant().Equals("admin")) { NewMessage(command, Color.White); } #if !DEBUG && CLIENT - if (GameMain.Client != null && !IsCommandPermitted(commands[0].ToLowerInvariant(), GameMain.Client)) + if (GameMain.Client != null && !IsCommandPermitted(args[0].ToLowerInvariant(), GameMain.Client)) { - ThrowError("You're not permitted to use the command \"" + commands[0].ToLowerInvariant()+"\"!"); + ThrowError("You're not permitted to use the command \"" + args[0].ToLowerInvariant()+"\"!"); return; } #endif - switch (commands[0].ToLowerInvariant()) + foreach (Command c in commands) { - case "clientlist": - if (GameMain.Server == null) break; - NewMessage("***************", Color.Cyan); - foreach (Client c in GameMain.Server.ConnectedClients) - { - NewMessage("- " + c.ID.ToString() + ": " + c.name + ", " + c.Connection.RemoteEndPoint.Address.ToString(), Color.Cyan); - } - NewMessage("***************", Color.Cyan); - break; - case "createfilelist": - UpdaterUtil.SaveFileList("filelist.xml"); - break; - case "spawn": - case "spawncharacter": - if (commands.Length == 1) return; - - Character spawnedCharacter = null; - - Vector2 spawnPosition = Vector2.Zero; - WayPoint spawnPoint = null; - - if (commands.Length > 2) - { - switch (commands[2].ToLowerInvariant()) - { - case "inside": - spawnPoint = WayPoint.GetRandom(SpawnType.Human, null, Submarine.MainSub); - break; - case "outside": - spawnPoint = WayPoint.GetRandom(SpawnType.Enemy); - break; - case "near": - case "close": - float closestDist = -1.0f; - foreach (WayPoint wp in WayPoint.WayPointList) - { - if (wp.Submarine != null) continue; - - //don't spawn inside hulls - if (Hull.FindHull(wp.WorldPosition, null) != null) continue; - - float dist = Vector2.Distance(wp.WorldPosition, GameMain.GameScreen.Cam.WorldViewCenter); - - if (closestDist < 0.0f || dist < closestDist) - { - spawnPoint = wp; - closestDist = dist; - } - } - break; - case "cursor": - spawnPosition = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); - break; - default: - spawnPoint = WayPoint.GetRandom(commands[1].ToLowerInvariant() == "human" ? SpawnType.Human : SpawnType.Enemy); - break; - } - } - else - { - spawnPoint = WayPoint.GetRandom(commands[1].ToLowerInvariant() == "human" ? SpawnType.Human : SpawnType.Enemy); - } - - if (string.IsNullOrWhiteSpace(commands[1])) return; - - if (spawnPoint != null) spawnPosition = spawnPoint.WorldPosition; - - if (commands[1].ToLowerInvariant() == "human") - { - spawnedCharacter = Character.Create(Character.HumanConfigFile, spawnPosition); - -#if CLIENT - if (GameMain.GameSession != null) - { - SinglePlayerMode mode = GameMain.GameSession.gameMode as SinglePlayerMode; - if (mode != null) - { - Character.Controlled = spawnedCharacter; - GameMain.GameSession.CrewManager.AddCharacter(Character.Controlled); - GameMain.GameSession.CrewManager.SelectCharacter(null, Character.Controlled); - } - } -#endif - } - else - { - spawnedCharacter = Character.Create( - "Content/Characters/" - + commands[1].First().ToString().ToUpper() + commands[1].Substring(1) - + "/" + commands[1].ToLower() + ".xml", spawnPosition); - } - - break; - case "spawnitem": - if (commands.Length < 2) return; - - Vector2? spawnPos = null; - Inventory spawnInventory = null; - - int extraParams = 0; - switch (commands.Last()) - { - case "cursor": - extraParams = 1; - spawnPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); - break; - case "inventory": - extraParams = 1; - spawnInventory = Character.Controlled == null ? null : Character.Controlled.Inventory; - break; - default: - extraParams = 0; - break; - } - - string itemName = string.Join(" ", commands.Skip(1).Take(commands.Length - extraParams - 1)).ToLowerInvariant(); - - var itemPrefab = MapEntityPrefab.list.Find(ip => ip.Name.ToLowerInvariant() == itemName) as ItemPrefab; - if (itemPrefab == null) - { - ThrowError("Item \"" + itemName + "\" not found!"); - return; - } - - if (spawnPos == null && spawnInventory == null) - { - var wp = WayPoint.GetRandom(SpawnType.Human, null, Submarine.MainSub); - spawnPos = wp == null ? Vector2.Zero : wp.WorldPosition; - } - - if (spawnPos != null) - { - Item.Spawner.AddToSpawnQueue(itemPrefab, (Vector2)spawnPos); - - } - else if (spawnInventory != null) - { - Item.Spawner.AddToSpawnQueue(itemPrefab, spawnInventory); - } - - break; - case "disablecrewai": - HumanAIController.DisableCrewAI = !HumanAIController.DisableCrewAI; - break; - case "enablecrewai": - HumanAIController.DisableCrewAI = false; - break; - /*case "admin": - if (commands.Length < 2) break; - - if (GameMain.Server != null) - { - GameMain.Server.AdminAuthPass = commands[1]; - - } - else if (GameMain.Client != null) - { - GameMain.Client.RequestAdminAuth(commands[1]); - } - break;*/ - case "kick": - if (GameMain.NetworkMember != null && commands.Length >= 2) - { - string playerName = string.Join(" ", commands.Skip(1)); - - ShowQuestionPrompt("Reason for kicking \"" + playerName + "\"?", (reason) => - { - GameMain.NetworkMember.KickPlayer(playerName, reason); - }); - } - break; - case "kickid": - case "banid": - if (GameMain.Server != null && commands.Length >= 2) - { - bool ban = commands[0].ToLowerInvariant() == "banid"; - - int id = 0; - int.TryParse(commands[1], out id); - var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id); - if (client == null) - { - ThrowError("Client id \"" + id + "\" not found."); - return; - } - - ShowQuestionPrompt(ban ? "Reason for banning \"" + client.name + "\"?" : "Reason for kicking \"" + client.name + "\"?", (reason) => - { - if (ban) - { - ShowQuestionPrompt("Enter the duration of the ban (leave empty to ban permanently, or use the format \"[days] d [hours] h\")", (duration) => - { - TimeSpan? banDuration = null; - if (!string.IsNullOrWhiteSpace(duration)) - { - TimeSpan parsedBanDuration; - if (!TryParseTimeSpan(duration, out parsedBanDuration)) - { - ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\"."); - return; - } - banDuration = parsedBanDuration; - } - - GameMain.Server.BanPlayer(client.name, reason, false, banDuration); - }); - } - else - { - GameMain.Server.KickPlayer(client.name, reason); - } - }); - } - break; - case "ban": - if (GameMain.NetworkMember != null || commands.Length >= 2) - { - string clientName = string.Join(" ", commands.Skip(1)); - ShowQuestionPrompt("Reason for banning \"" + clientName + "\"?", (reason) => - { - ShowQuestionPrompt("Enter the duration of the ban (leave empty to ban permanently, or use the format \"[days] d [hours] h\")", (duration) => - { - TimeSpan? banDuration = null; - if (!string.IsNullOrWhiteSpace(duration)) - { - TimeSpan parsedBanDuration; - if (!TryParseTimeSpan(duration, out parsedBanDuration)) - { - ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\"."); - return; - } - banDuration = parsedBanDuration; - } - - GameMain.NetworkMember.BanPlayer(clientName, reason, false, banDuration); - }); - }); - } - break; - case "banip": - if (GameMain.Server != null || commands.Length >= 2) - { - ShowQuestionPrompt("Reason for banning the ip \"" + commands[1] + "\"?", (reason) => - { - ShowQuestionPrompt("Enter the duration of the ban (leave empty to ban permanently, or use the format \"[days] d [hours] h\")", (duration) => - { - TimeSpan? banDuration = null; - if (!string.IsNullOrWhiteSpace(duration)) - { - TimeSpan parsedBanDuration; - if (!TryParseTimeSpan(duration, out parsedBanDuration)) - { - ThrowError("\""+duration+ "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\"."); - return; - } - banDuration = parsedBanDuration; - } - - var client = GameMain.Server.ConnectedClients.Find(c => c.Connection.RemoteEndPoint.Address.ToString() == commands[1]); - if (client == null) - { - GameMain.Server.BanList.BanPlayer("Unnamed", commands[1], reason, banDuration); - } - else - { - GameMain.Server.KickClient(client, reason); - } - }); - }); - } - break; - case "teleportcharacter": - case "teleport": - var tpCharacter = FindMatchingCharacter(commands, false); - - if (commands.Length < 2) - { - tpCharacter = Character.Controlled; - } - - if (tpCharacter != null) - { - var cam = GameMain.GameScreen.Cam; - tpCharacter.AnimController.CurrentHull = null; - tpCharacter.Submarine = null; - tpCharacter.AnimController.SetPosition(ConvertUnits.ToSimUnits(cam.ScreenToWorld(PlayerInput.MousePosition))); - tpCharacter.AnimController.FindHull(cam.ScreenToWorld(PlayerInput.MousePosition), true); - } - break; - case "godmode": - if (Submarine.MainSub == null) return; - - Submarine.MainSub.GodMode = !Submarine.MainSub.GodMode; - NewMessage(Submarine.MainSub.GodMode ? "Godmode on" : "Godmode off", Color.White); - break; - case "lockx": - Submarine.LockX = !Submarine.LockX; - break; - case "locky": - Submarine.LockY = !Submarine.LockY; - break; - case "dumpids": - try - { - int count = commands.Length < 2 ? 10 : int.Parse(commands[1]); - Entity.DumpIds(count); - } - catch - { - return; - } - break; - case "heal": - Character healedCharacter = null; - if (commands.Length == 1) - { - healedCharacter = Character.Controlled; - } - else - { - healedCharacter = FindMatchingCharacter(commands); - } - - if (healedCharacter != null) - { - healedCharacter.AddDamage(CauseOfDeath.Damage, -healedCharacter.MaxHealth, null); - healedCharacter.Oxygen = 100.0f; - healedCharacter.Bleeding = 0.0f; - healedCharacter.SetStun(0.0f, true); - } - - break; - case "revive": - Character revivedCharacter = null; - if (commands.Length == 1) - { - revivedCharacter = Character.Controlled; - } - else - { - revivedCharacter = FindMatchingCharacter(commands); - } - - if (revivedCharacter != null) - { - revivedCharacter.Revive(false); - if (GameMain.Server != null) - { - foreach (Client c in GameMain.Server.ConnectedClients) - { - if (c.Character != revivedCharacter) continue; - //clients stop controlling the character when it dies, force control back - GameMain.Server.SetClientCharacter(c, revivedCharacter); - break; - } - } - } - break; - case "freeze": - if (Character.Controlled != null) Character.Controlled.AnimController.Frozen = !Character.Controlled.AnimController.Frozen; - break; - case "freecamera": - case "freecam": - Character.Controlled = null; - GameMain.GameScreen.Cam.TargetPos = Vector2.Zero; - break; - case "editwater": - case "water": - if (GameMain.Client == null) - { - Hull.EditWater = !Hull.EditWater; - NewMessage(Hull.EditWater ? "Water editing on" : "Water editing off", Color.White); - } - - break; - case "explosion": - Vector2 explosionPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); - float range = 500, force = 10, damage=50; - if (commands.Length > 1) float.TryParse(commands[1], out range); - if (commands.Length > 2) float.TryParse(commands[2], out force); - if (commands.Length > 3) float.TryParse(commands[3], out damage); - new Explosion(range, force, damage, damage).Explode(explosionPos); - break; - case "fire": - if (GameMain.Client == null) - { - Hull.EditFire = !Hull.EditFire; - NewMessage(Hull.EditFire ? "Fire spawning on" : "Fire spawning off", Color.White); - } - - break; - case "fixitems": - foreach (Item it in Item.ItemList) - { - it.Condition = it.Prefab.Health; - } - break; - case "fixhull": - case "fixwalls": - foreach (Structure w in Structure.WallList) - { - for (int i = 0 ; i < w.SectionCount; i++) - { - w.AddDamage(i, -100000.0f); - } - } - break; - case "power": - Item reactorItem = Item.ItemList.Find(i => i.GetComponent() != null); - if (reactorItem == null) return; - - float power = 5000.0f; - if (commands.Length>1) float.TryParse(commands[1], out power); - - var reactor = reactorItem.GetComponent(); - reactor.ShutDownTemp = power == 0 ? 0 : 7000.0f; - reactor.AutoTemp = true; - reactor.Temperature = power; - - if (GameMain.Server != null) - { - reactorItem.CreateServerEvent(reactor); - } - break; - case "oxygen": - case "air": - foreach (Hull hull in Hull.hullList) - { - hull.OxygenPercentage = 100.0f; - } - break; - - case "killmonsters": - foreach (Character c in Character.CharacterList) - { - if (!(c.AIController is EnemyAIController)) continue; - c.AddDamage(CauseOfDeath.Damage, 10000.0f, null); - } - break; - case "netstats": - if (GameMain.Server == null) return; - - GameMain.Server.ShowNetStats = !GameMain.Server.ShowNetStats; - break; - - default: - if (!ExecProjSpecific(commands)) NewMessage("Command not found", Color.Red); + if (c.names.Contains(splitCommand[0].ToLowerInvariant())) + { + c.Execute(splitCommand.Skip(1).ToArray()); break; + } } } - private static Character FindMatchingCharacter(string[] commands, bool ignoreRemotePlayers = false) + private static Character FindMatchingCharacter(string[] args, bool ignoreRemotePlayers = false) { - if (commands.Length < 2) return null; + if (args.Length == 0) return null; int characterIndex; string characterName; - if (int.TryParse(commands.Last(), out characterIndex) && commands.Length > 2) + if (int.TryParse(args.Last(), out characterIndex) && args.Length > 2) { - characterName = string.Join(" ", commands.Skip(1).Take(commands.Length - 2)).ToLowerInvariant(); + characterName = string.Join(" ", args.Take(args.Length - 2)).ToLowerInvariant(); } else { - characterName = string.Join(" ", commands.Skip(1)).ToLowerInvariant(); + characterName = string.Join(" ", args).ToLowerInvariant(); characterIndex = -1; } @@ -587,7 +779,7 @@ namespace Barotrauma { NewMessage( "Found multiple matching characters. " + - "Use \"" + commands[0] + " [charactername] [0-" + (matchingCharacters.Count - 1) + "]\" to choose a specific character.", + "Use \"[charactername] [0-" + (matchingCharacters.Count - 1) + "]\" to choose a specific character.", Color.LightGray); } return matchingCharacters[0];