Console command arguments can be autocompleted by hitting tab. Closes #162

This commit is contained in:
Joonas Rikkonen
2017-12-30 18:30:55 +02:00
parent 65d6071378
commit 343895d776
3 changed files with 196 additions and 17 deletions

View File

@@ -266,6 +266,13 @@ namespace Barotrauma
{
Character.Controlled = character;
}
},
() =>
{
return new string[][]
{
Character.CharacterList.Select(c => c.Name).Distinct().ToArray()
};
}));
commands.Add(new Command("shake", "", (string[] args) =>

View File

@@ -1,6 +1,7 @@
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Linq;
using System.Collections.Generic;
namespace Barotrauma
@@ -94,6 +95,13 @@ namespace Barotrauma
}
}
NewMessage("Set gamemode to " + GameMain.NetLobbyScreen.SelectedModeName, Color.Cyan);
},
() =>
{
return new string[][]
{
GameModePreset.list.Select(gm => gm.Name).ToArray()
};
}));
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) =>
@@ -108,6 +116,13 @@ namespace Barotrauma
GameMain.NetLobbyScreen.MissionTypeName = string.Join(" ", args);
}
NewMessage("Set mission to " + GameMain.NetLobbyScreen.MissionTypeName, Color.Cyan);
},
() =>
{
return new string[][]
{
Mission.MissionTypes.ToArray()
};
}));
commands.Add(new Command("sub|submarine", "submarine [name]: Select the submarine for the next round.", (string[] args) =>
@@ -120,6 +135,13 @@ namespace Barotrauma
}
sub = GameMain.NetLobbyScreen.SelectedSub;
NewMessage("Selected sub: " + sub.Name + (sub.HasTag(SubmarineTag.Shuttle) ? " (shuttle)" : ""), Color.Cyan);
},
() =>
{
return new string[][]
{
Submarine.Loaded.Select(s => s.Name).ToArray()
};
}));
commands.Add(new Command("shuttle", "shuttle [name]: Select the specified submarine as the respawn shuttle for the next round.", (string[] args) =>
@@ -132,6 +154,13 @@ namespace Barotrauma
}
shuttle = GameMain.NetLobbyScreen.SelectedShuttle;
NewMessage("Selected shuttle: " + shuttle.Name + (shuttle.HasTag(SubmarineTag.Shuttle) ? "" : " (not shuttle)"), Color.Cyan);
},
() =>
{
return new string[][]
{
Submarine.Loaded.Select(s => s.Name).ToArray()
};
}));
commands.Add(new Command("startgame|startround|start", "start/startgame/startround: Start a new round.", (string[] args) =>

View File

@@ -46,6 +46,8 @@ namespace Barotrauma
/// </summary>
private Action<Client, Vector2, string[]> onClientRequestExecute;
public Func<string[][]> GetValidArgs;
public bool RelayToServer
{
get { return onClientExecute == null; }
@@ -56,7 +58,7 @@ namespace Barotrauma
/// <param name="onExecute">The default action when executing the command.</param>
/// <param name="onClientExecute">The action when a client attempts to execute the command. If null, the command is relayed to the server as-is.</param>
/// <param name="onClientRequestExecute">The server-side action when a client requests executing the command. If null, the default action is executed.</param>
public Command(string name, string help, Action<string[]> onExecute, Action<string[]> onClientExecute, Action<Client, Vector2, string[]> onClientRequestExecute)
public Command(string name, string help, Action<string[]> onExecute, Action<string[]> onClientExecute, Action<Client, Vector2, string[]> onClientRequestExecute, Func<string[][]> getValidArgs = null)
{
names = name.Split('|');
this.help = help;
@@ -64,19 +66,23 @@ namespace Barotrauma
this.onExecute = onExecute;
this.onClientExecute = onClientExecute;
this.onClientRequestExecute = onClientRequestExecute;
this.GetValidArgs = getValidArgs;
}
/// <summary>
/// Use this constructor to create a command that executes the same action regardless of whether it's executed by a client or the server.
/// </summary>
public Command(string name, string help, Action<string[]> onExecute)
public Command(string name, string help, Action<string[]> onExecute, Func<string[][]> getValidArgs = null)
{
names = name.Split('|');
this.help = help;
this.onExecute = onExecute;
this.onClientExecute = onExecute;
this.GetValidArgs = getValidArgs;
}
public void Execute(string[] args)
@@ -196,7 +202,7 @@ namespace Barotrauma
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) =>
commands.Add(new Command("spawn|spawncharacter", "spawn [creaturename] [near/inside/outside/cursor]: Spawn a creature at a random spawnpoint (use the second parameter to only select spawnpoints near/inside/outside the submarine).", (string[] args) =>
{
string errorMsg;
SpawnCharacter(args, GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), out errorMsg);
@@ -214,6 +220,20 @@ namespace Barotrauma
{
ThrowError(errorMsg);
}
},
() =>
{
List<string> characterFiles = GameMain.Config.SelectedContentPackage.GetFilesOfType(ContentType.Character);
for (int i = 0; i < characterFiles.Count; i++)
{
characterFiles[i] = Path.GetFileNameWithoutExtension(characterFiles[i]).ToLowerInvariant();
}
return new string[][]
{
characterFiles.ToArray(),
new string[] { "near", "inside", "outside", "cursor" }
};
}));
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.",
@@ -235,6 +255,21 @@ namespace Barotrauma
{
ThrowError(errorMsg);
}
},
() =>
{
List<string> itemNames = new List<string>();
foreach (MapEntityPrefab prefab in MapEntityPrefab.List)
{
ItemPrefab itemPrefab = prefab as ItemPrefab;
if (itemPrefab != null) itemNames.Add(itemPrefab.Name);
}
return new string[][]
{
itemNames.ToArray(),
new string[] { "cursor", "inventory" }
};
}));
commands.Add(new Command("disablecrewai", "disablecrewai: Disable the AI of the NPCs in the crew.", (string[] args) =>
@@ -572,6 +607,15 @@ namespace Barotrauma
{
GameMain.NetworkMember.KickPlayer(playerName, reason);
});
},
() =>
{
if (GameMain.NetworkMember == null) return null;
return new string[][]
{
GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray()
};
}));
commands.Add(new Command("kickid", "kickid [id]: Kick the player with the specified client ID out of the server.", (string[] args) =>
@@ -617,6 +661,15 @@ namespace Barotrauma
GameMain.NetworkMember.BanPlayer(clientName, reason, false, banDuration);
});
});
},
() =>
{
if (GameMain.NetworkMember == null) return null;
return new string[][]
{
GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray()
};
}));
commands.Add(new Command("banid", "banid [id]: Kick and ban the player with the specified client ID from the server.", (string[] args) =>
@@ -730,6 +783,13 @@ namespace Barotrauma
tpCharacter.Submarine = null;
tpCharacter.AnimController.SetPosition(ConvertUnits.ToSimUnits(cursorWorldPos));
tpCharacter.AnimController.FindHull(cursorWorldPos, true);
},
() =>
{
return new string[][]
{
Character.CharacterList.Select(c => c.Name).Distinct().ToArray()
};
}));
commands.Add(new Command("godmode", "godmode: Toggle submarine godmode. Makes the main submarine invulnerable to damage.", (string[] args) =>
@@ -812,6 +872,13 @@ namespace Barotrauma
healedCharacter.Bleeding = 0.0f;
healedCharacter.SetStun(0.0f, true);
}
},
() =>
{
return new string[][]
{
Character.CharacterList.Select(c => c.Name).Distinct().ToArray()
};
}));
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) =>
@@ -868,6 +935,13 @@ namespace Barotrauma
break;
}
}
},
() =>
{
return new string[][]
{
Character.CharacterList.Select(c => c.Name).Distinct().ToArray()
};
}));
commands.Add(new Command("freeze", "", (string[] args) =>
@@ -896,6 +970,13 @@ namespace Barotrauma
{
ragdolledCharacter.IsForceRagdolled = !ragdolledCharacter.IsForceRagdolled;
}
},
() =>
{
return new string[][]
{
Character.CharacterList.Select(c => c.Name).Distinct().ToArray()
};
}));
commands.Add(new Command("freecamera|freecam", "freecam: Detach the camera from the controlled character.", (string[] args) =>
@@ -1069,6 +1150,16 @@ namespace Barotrauma
var character = FindMatchingCharacter(argsRight, false);
GameMain.Server.SetClientCharacter(client, character);
},
() =>
{
if (GameMain.NetworkMember == null) return null;
return new string[][]
{
GameMain.NetworkMember.ConnectedClients.Select(c => c.Name).ToArray(),
Character.CharacterList.Select(c => c.Name).Distinct().ToArray()
};
}));
commands.Add(new Command("campaigninfo|campaignstatus", "campaigninfo: Display information about the state of the currently active campaign.", (string[] args) =>
@@ -1269,28 +1360,80 @@ namespace Barotrauma
public static string AutoComplete(string command)
{
if (string.IsNullOrWhiteSpace(currentAutoCompletedCommand))
{
currentAutoCompletedCommand = command;
}
string[] splitCommand = SplitCommand(command);
string[] args = splitCommand.Skip(1).ToArray();
List<string> matchingCommands = new List<string>();
foreach (Command c in commands)
//if an argument is given or the last character is a space, attempt to autocomplete the argument
if (args.Length > 0 || (command.Length > 0 && command.Last() == ' '))
{
foreach (string name in c.names)
Command matchingCommand = commands.Find(c => c.names.Contains(splitCommand[0]));
if (matchingCommand == null || matchingCommand.GetValidArgs == null) return command;
int autoCompletedArgIndex = args.Length > 0 && command.Last() != ' ' ? args.Length - 1 : args.Length;
//get all valid arguments for the given command
string[][] allArgs = matchingCommand.GetValidArgs();
if (allArgs == null || allArgs.GetLength(0) < autoCompletedArgIndex + 1) return command;
if (string.IsNullOrEmpty(currentAutoCompletedCommand))
{
if (currentAutoCompletedCommand.Length > name.Length) continue;
if (currentAutoCompletedCommand == name.Substring(0, currentAutoCompletedCommand.Length))
currentAutoCompletedCommand = autoCompletedArgIndex > args.Length - 1 ? " " : args.Last();
}
//find all valid autocompletions for the given argument
string[] validArgs = allArgs[autoCompletedArgIndex].Where(arg =>
currentAutoCompletedCommand.Trim().Length <= arg.Length &&
arg.Substring(0, currentAutoCompletedCommand.Trim().Length).ToLower() == currentAutoCompletedCommand.Trim().ToLower()).ToArray();
if (validArgs.Length == 0) return command;
currentAutoCompletedIndex = currentAutoCompletedIndex % validArgs.Length;
string autoCompletedArg = validArgs[currentAutoCompletedIndex++];
//add quotation marks to args that contain spaces
if (autoCompletedArg.Contains(' ')) autoCompletedArg = '"' + autoCompletedArg + '"';
for (int i = 0; i < splitCommand.Length; i++)
{
if (splitCommand[i].Contains(' ')) splitCommand[i] = '"' + splitCommand[i] + '"';
}
return string.Join(" ", autoCompletedArgIndex >= args.Length ? splitCommand : splitCommand.Take(splitCommand.Length - 1)) + " " + autoCompletedArg;
}
else
{
if (string.IsNullOrWhiteSpace(currentAutoCompletedCommand))
{
currentAutoCompletedCommand = command;
}
List<string> matchingCommands = new List<string>();
foreach (Command c in commands)
{
foreach (string name in c.names)
{
matchingCommands.Add(name);
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++];
}
}
if (matchingCommands.Count == 0) return command;
currentAutoCompletedIndex = currentAutoCompletedIndex % matchingCommands.Count;
return matchingCommands[currentAutoCompletedIndex++];
private static string AutoCompleteStr(string str, IEnumerable<string> validStrings)
{
if (string.IsNullOrEmpty(str)) return str;
foreach (string validStr in validStrings)
{
if (validStr.Length > str.Length && validStr.Substring(0, str.Length) == str) return validStr;
}
return str;
}
public static void ResetAutoComplete()