mirror of
https://github.com/ClassiCube/MCGalaxy.git
synced 2025-09-24 05:03:34 -04:00
Add support for wildcard in /search. /search blocks now adds custom blocks. Allow using /gr [keyword] (and wildcards) to only randomly go to certain maps
This commit is contained in:
parent
01d59f6923
commit
5231ae8a0d
@ -16,9 +16,11 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using MCGalaxy.Blocks;
|
||||||
|
|
||||||
namespace MCGalaxy.Commands.Info {
|
namespace MCGalaxy.Commands.Info {
|
||||||
public class CmdSearch : Command {
|
public class CmdSearch : Command {
|
||||||
public override string name { get { return "Search"; } }
|
public override string name { get { return "Search"; } }
|
||||||
public override string type { get { return CommandTypes.Information; } }
|
public override string type { get { return CommandTypes.Information; } }
|
||||||
public override LevelPermission defaultRank { get { return LevelPermission.Builder; } }
|
public override LevelPermission defaultRank { get { return LevelPermission.Builder; } }
|
||||||
@ -34,111 +36,125 @@ namespace MCGalaxy.Commands.Info {
|
|||||||
SearchBlocks(p, keyword, modifier);
|
SearchBlocks(p, keyword, modifier);
|
||||||
} else if (args[0] == "rank" || args[0] == "ranks") {
|
} else if (args[0] == "rank" || args[0] == "ranks") {
|
||||||
SearchRanks(p, keyword, modifier);
|
SearchRanks(p, keyword, modifier);
|
||||||
} else if (args[0] == "command" || args[0] == "commands" || args[0] == "cmd" || args[0] == "cmds") {
|
} else if (args[0] == "command" || args[0] == "commands") {
|
||||||
SearchCommands(p, keyword, modifier);
|
SearchCommands(p, keyword, modifier);
|
||||||
} else if (args[0] == "user" || args[0] == "users" || args[0] == "player" || args[0] == "players") {
|
} else if (args[0] == "player" || args[0] == "players") {
|
||||||
SearchPlayers(p, keyword, modifier);
|
SearchPlayers(p, keyword, modifier);
|
||||||
} else if (args[0] == "loaded") {
|
} else if (args[0] == "loaded") {
|
||||||
SearchLoaded(p, keyword, modifier);
|
SearchLoaded(p, keyword, modifier);
|
||||||
} else if (args[0] == "level" || args[0] == "levels") {
|
} else if (args[0] == "level" || args[0] == "levels" || args[0] == "maps") {
|
||||||
SearchUnloaded(p, keyword, modifier);
|
SearchMaps(p, keyword, modifier);
|
||||||
} else {
|
} else {
|
||||||
Help(p);
|
Help(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static string CoreBlockName(ExtBlock block) { return Block.Name(block.BlockID); }
|
||||||
static void SearchBlocks(Player p, string keyword, string modifier) {
|
static void SearchBlocks(Player p, string keyword, string modifier) {
|
||||||
List<string> blocks = new List<string>();
|
List<ExtBlock> blockIDs = new List<ExtBlock>();
|
||||||
for (byte id = 0; id < Block.Invalid; id++) {
|
for (byte id = 0; id < Block.Invalid; id++) {
|
||||||
string name = Block.Name(id);
|
if (!Block.Name(id).CaselessEq("unknown")) {
|
||||||
if (name.CaselessContains(keyword) && !name.CaselessEq("unknown"))
|
blockIDs.Add(new ExtBlock(id, 0));
|
||||||
blocks.Add(name);
|
}
|
||||||
}
|
}
|
||||||
|
StringFormatter<ExtBlock> getName;
|
||||||
|
|
||||||
OutputList(p, keyword, "search blocks", "blocks",
|
if (!Player.IsSuper(p)) {
|
||||||
modifier, blocks, CmdBlocks.FormatBlockName);
|
for (int id = Block.CpeCount; id < Block.Count; id++) {
|
||||||
|
if (p.level.CustomBlockDefs[id] == null) continue;
|
||||||
|
blockIDs.Add(new ExtBlock(Block.custom_block, (byte)id));
|
||||||
|
}
|
||||||
|
getName = p.level.BlockName;
|
||||||
|
} else {
|
||||||
|
getName = CoreBlockName;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<string> blocks = FilterList(blockIDs, keyword, getName, null,
|
||||||
|
b => Group.GetColor(BlockPerms.List[b.BlockID].MinRank) + getName(b));
|
||||||
|
OutputList(p, keyword, "search blocks", "blocks", modifier, blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SearchCommands(Player p, string keyword, string modifier) {
|
static void SearchCommands(Player p, string keyword, string modifier) {
|
||||||
List<Command> cmds = new List<Command>();
|
List<string> commands = FilterList(Command.all.commands, keyword, cmd => cmd.name,
|
||||||
foreach (Command cmd in Command.all.commands) {
|
null, cmd => CmdHelp.GetColor(cmd) + cmd.name);
|
||||||
if (cmd.name.CaselessContains(keyword)) {
|
List<string> shortcuts = FilterList(Command.all.commands, keyword, cmd => cmd.shortcut,
|
||||||
cmds.Add(cmd); continue;
|
cmd => !String.IsNullOrEmpty(cmd.shortcut),
|
||||||
}
|
cmd => CmdHelp.GetColor(cmd) + cmd.name);
|
||||||
|
|
||||||
if (String.IsNullOrEmpty(cmd.shortcut)) continue;
|
|
||||||
if (cmd.shortcut.CaselessContains(keyword))
|
|
||||||
cmds.Add(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputList(p, keyword, "search commands", "commands",
|
// Match both names and shortcuts
|
||||||
modifier, cmds, (cmd) => CmdHelp.GetColor(cmd) + cmd.name);
|
foreach (string shortcutCmd in shortcuts) {
|
||||||
|
if (commands.CaselessContains(shortcutCmd)) continue;
|
||||||
|
commands.Add(shortcutCmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputList(p, keyword, "search commands", "commands", modifier, commands);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SearchRanks(Player p, string keyword, string modifier) {
|
static void SearchRanks(Player p, string keyword, string modifier) {
|
||||||
List<string> ranks = new List<string>();
|
List<string> ranks = FilterList(Group.GroupList, keyword, grp => grp.Name,
|
||||||
foreach (Group g in Group.GroupList) {
|
null, grp => grp.ColoredName);
|
||||||
if (g.Name.CaselessContains(keyword)) {
|
OutputList(p, keyword, "search ranks", "ranks", modifier, ranks);
|
||||||
ranks.Add(g.ColoredName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputList(p, keyword, "search ranks", "ranks",
|
|
||||||
modifier, ranks, (name) => name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SearchPlayers(Player p, string keyword, string modifier) {
|
static void SearchPlayers(Player p, string keyword, string modifier) {
|
||||||
List<string> players = new List<string>();
|
|
||||||
Player[] online = PlayerInfo.Online.Items;
|
Player[] online = PlayerInfo.Online.Items;
|
||||||
foreach (Player who in online) {
|
List<string> players = FilterList(online, keyword, pl => pl.name,
|
||||||
if (who.name.CaselessContains(keyword) && Entities.CanSee(p, who))
|
pl => Entities.CanSee(p, pl), pl => pl.ColoredName);
|
||||||
players.Add(who.ColoredName);
|
OutputList(p, keyword, "search players", "players", modifier, players);
|
||||||
}
|
|
||||||
|
|
||||||
OutputList(p, keyword, "search players", "players",
|
|
||||||
modifier, players, (name) => name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SearchLoaded(Player p, string keyword, string modifier) {
|
static void SearchLoaded(Player p, string keyword, string modifier) {
|
||||||
List<string> levels = new List<string>();
|
|
||||||
Level[] loaded = LevelInfo.Loaded.Items;
|
Level[] loaded = LevelInfo.Loaded.Items;
|
||||||
foreach (Level level in loaded) {
|
List<string> levels = FilterList(loaded, keyword, level => level.name);
|
||||||
if (level.name.CaselessContains(level.name))
|
OutputList(p, keyword, "search loaded", "loaded levels", modifier, levels);
|
||||||
levels.Add(level.name);
|
}
|
||||||
|
|
||||||
|
static void SearchMaps(Player p, string keyword, string modifier) {
|
||||||
|
string[] files = LevelInfo.AllMapFiles();
|
||||||
|
List<string> maps = FilterList(files, keyword,
|
||||||
|
map => Path.GetFileNameWithoutExtension(map));
|
||||||
|
OutputList(p, keyword, "search levels", "maps", modifier, maps);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static List<string> FilterList<T>(IList<T> input, string keyword, StringFormatter<T> formatter,
|
||||||
|
Predicate<T> filter = null, StringFormatter<T> listFormatter = null) {
|
||||||
|
List<string> matches = new List<string>();
|
||||||
|
Regex regex = null;
|
||||||
|
// wildcard matching
|
||||||
|
if (keyword.Contains("*") || keyword.Contains("?")) {
|
||||||
|
string pattern = "^" + Regex.Escape(keyword).Replace("\\?", ".").Replace("\\*", ".*") + "$";
|
||||||
|
regex = new Regex(pattern, RegexOptions.IgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputList(p, keyword, "search loaded", "loaded levels",
|
foreach (T item in input) {
|
||||||
modifier, levels, (level) => level);
|
if (filter != null && !filter(item)) continue;
|
||||||
}
|
string name = formatter(item);
|
||||||
|
|
||||||
static void SearchUnloaded(Player p, string keyword, string modifier) {
|
if (regex != null) { if (!regex.IsMatch(name)) continue; }
|
||||||
List<string> maps = new List<string>();
|
else { if (!name.CaselessContains(keyword)) continue; }
|
||||||
string[] files = LevelInfo.AllMapFiles();
|
|
||||||
foreach (string file in files) {
|
// format this item for display
|
||||||
string map = Path.GetFileNameWithoutExtension(file);
|
if (listFormatter != null) name = listFormatter(item);
|
||||||
if (map.CaselessContains(keyword)) maps.Add(map);
|
matches.Add(name);
|
||||||
}
|
}
|
||||||
|
return matches;
|
||||||
OutputList(p, keyword, "search levels", "maps",
|
|
||||||
modifier, maps, (map) => map);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OutputList<T>(Player p, string keyword, string cmd, string type, string modifier,
|
static void OutputList(Player p, string keyword, string cmd, string type, string modifier, List<string> items) {
|
||||||
List<T> items, StringFormatter<T> formatter) {
|
|
||||||
if (items.Count == 0) {
|
if (items.Count == 0) {
|
||||||
Player.Message(p, "No {0} found containing \"{1}\"", type, keyword);
|
Player.Message(p, "No {0} found containing \"{1}\"", type, keyword);
|
||||||
} else {
|
} else {
|
||||||
MultiPageOutput.Output(p, items, formatter, cmd + " " + keyword, type, modifier, false);
|
MultiPageOutput.Output(p, items, item => item, cmd + " " + keyword, type, modifier, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Help(Player p) {
|
public override void Help(Player p) {
|
||||||
Player.Message(p, "%T/Search blocks [keyword] %H- finds blocks with that keyword");
|
Player.Message(p, "%T/Search [list] [keyword]");
|
||||||
Player.Message(p, "%T/Search commands [keyword] %H- finds commands with that keyword");
|
Player.Message(p, "%HFinds entries in a list that match the given keyword");
|
||||||
Player.Message(p, "%T/Search ranks [keyword] %H- finds ranks with that keyword");
|
Player.Message(p, "%H keyword can also include wildcard characters:");
|
||||||
Player.Message(p, "%T/Search players [keyword] %H- find players with that keyword");
|
Player.Message(p, "%H * - placeholder for zero or more characters");
|
||||||
Player.Message(p, "%T/Search loaded [keyword] %H- finds loaded levels with that keyword");
|
Player.Message(p, "%H ? - placeholder for exactly one character");
|
||||||
Player.Message(p, "%T/Search levels [keyword] %H- find all levels with that keyword");
|
Player.Message(p, "%HLists available: &fblocks/commands/ranks/players/loaded/maps");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,9 @@
|
|||||||
permissions and limitations under the Licenses.
|
permissions and limitations under the Licenses.
|
||||||
*/
|
*/
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using MCGalaxy.Commands.Info;
|
||||||
|
|
||||||
namespace MCGalaxy.Commands.World {
|
namespace MCGalaxy.Commands.World {
|
||||||
public sealed class CmdGoto : Command {
|
public sealed class CmdGoto : Command {
|
||||||
@ -32,11 +34,25 @@ namespace MCGalaxy.Commands.World {
|
|||||||
public override void Use(Player p, string message) {
|
public override void Use(Player p, string message) {
|
||||||
if (message.Length == 0) { Help(p); return; }
|
if (message.Length == 0) { Help(p); return; }
|
||||||
|
|
||||||
if (message.CaselessEq("-random")) {
|
if (message.CaselessStarts("-random")) {
|
||||||
string[] files = LevelInfo.AllMapFiles();
|
string[] files = LevelInfo.AllMapFiles();
|
||||||
string map = files[new Random().Next(files.Length)];
|
string[] args = message.SplitSpaces(2);
|
||||||
|
string map;
|
||||||
|
|
||||||
map = Path.GetFileNameWithoutExtension(map);
|
// randomly only visit certain number of maps
|
||||||
|
if (args.Length > 1) {
|
||||||
|
List<string> maps = CmdSearch.FilterList(files, args[1],
|
||||||
|
mapFile => Path.GetFileNameWithoutExtension(mapFile));
|
||||||
|
if (maps.Count == 0) {
|
||||||
|
Player.Message(p, "No maps found containing \"{0}\"", args[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
map = maps[new Random().Next(maps.Count)];
|
||||||
|
} else {
|
||||||
|
map = files[new Random().Next(files.Length)];
|
||||||
|
map = Path.GetFileNameWithoutExtension(map);
|
||||||
|
}
|
||||||
|
|
||||||
PlayerActions.ChangeMap(p, map);
|
PlayerActions.ChangeMap(p, map);
|
||||||
} else if (Formatter.ValidName(p, message, "level")) {
|
} else if (Formatter.ValidName(p, message, "level")) {
|
||||||
PlayerActions.ChangeMap(p, message);
|
PlayerActions.ChangeMap(p, message);
|
||||||
|
@ -100,10 +100,11 @@ namespace MCGalaxy.Commands.Building {
|
|||||||
public override void Help(Player p) {
|
public override void Help(Player p) {
|
||||||
Player.Message(p, "%T/Mark <x y z> %H- Places a marker for selections, e.g for %T/z");
|
Player.Message(p, "%T/Mark <x y z> %H- Places a marker for selections, e.g for %T/z");
|
||||||
Player.Message(p, "%HUse ~ before a coordinate to mark relative to current position");
|
Player.Message(p, "%HUse ~ before a coordinate to mark relative to current position");
|
||||||
Player.Message(p, "%HIf <x y z> is not given, marks at where you are standing");
|
Player.Message(p, "%HIf no coordinates are given, marks at where you are standing");
|
||||||
|
Player.Message(p, "%HIf only x coordinate is given, it is used for y and z too");
|
||||||
Player.Message(p, " %He.g. /mark 30 y 20 will mark at (30, last y, 20)");
|
Player.Message(p, " %He.g. /mark 30 y 20 will mark at (30, last y, 20)");
|
||||||
Player.Message(p, "%HActivates the block (e.g. door) if no selection is in progress");
|
|
||||||
Player.Message(p, "%T/Mark all %H- Places markers at min and max corners of the map");
|
Player.Message(p, "%T/Mark all %H- Places markers at min and max corners of the map");
|
||||||
|
Player.Message(p, "%HActivates the block (e.g. door) if no selection is in progress");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,11 +50,16 @@ namespace MCGalaxy.Levels.IO {
|
|||||||
lvl.roty = header[offset + 11];
|
lvl.roty = header[offset + 11];
|
||||||
|
|
||||||
gs.Read(lvl.blocks, 0, lvl.blocks.Length);
|
gs.Read(lvl.blocks, 0, lvl.blocks.Length);
|
||||||
ReadCustomBlocksSection(lvl, gs);
|
ReadCustomBlocksSection(lvl, gs);
|
||||||
|
|
||||||
if (!metadata) return lvl;
|
if (!metadata) return lvl;
|
||||||
ReadPhysicsSection(lvl, gs);
|
|
||||||
return lvl;
|
for (;;) {
|
||||||
|
int section = gs.ReadByte();
|
||||||
|
if (section == 0xFC) {
|
||||||
|
ReadPhysicsSection(lvl, gs); continue;
|
||||||
|
}
|
||||||
|
return lvl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +103,6 @@ namespace MCGalaxy.Levels.IO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe static void ReadPhysicsSection(Level lvl, Stream gs) {
|
unsafe static void ReadPhysicsSection(Level lvl, Stream gs) {
|
||||||
if (gs.ReadByte() != 0xFC) return;
|
|
||||||
byte[] buffer = new byte[sizeof(int)];
|
byte[] buffer = new byte[sizeof(int)];
|
||||||
int read = gs.Read(buffer, 0, sizeof(int));
|
int read = gs.Read(buffer, 0, sizeof(int));
|
||||||
if (read < sizeof(int)) return;
|
if (read < sizeof(int)) return;
|
||||||
|
@ -25,8 +25,7 @@ namespace MCGalaxy {
|
|||||||
public static class PlayerActions {
|
public static class PlayerActions {
|
||||||
|
|
||||||
/// <summary> Moves the player to the specified block coordinates. (bY is treated as player feet) </summary>
|
/// <summary> Moves the player to the specified block coordinates. (bY is treated as player feet) </summary>
|
||||||
public static void MoveCoords(Player p, int bX, int bY, int bZ,
|
public static void MoveCoords(Player p, int bX, int bY, int bZ, byte rotX, byte rotY) {
|
||||||
byte rotX, byte rotY) {
|
|
||||||
Position pos = Position.FromFeet(16 + bX * 32, bY * 32, 16 + bZ * 32);
|
Position pos = Position.FromFeet(16 + bX * 32, bY * 32, 16 + bZ * 32);
|
||||||
p.SendPos(Entities.SelfID, pos, new Orientation(rotX, rotY));
|
p.SendPos(Entities.SelfID, pos, new Orientation(rotX, rotY));
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ namespace MCGalaxy {
|
|||||||
public static int BackupInterval = 300;
|
public static int BackupInterval = 300;
|
||||||
public static int BlockDBSaveInterval = 60;
|
public static int BlockDBSaveInterval = 60;
|
||||||
[ConfigString("backup-location", "Backup", "")]
|
[ConfigString("backup-location", "Backup", "")]
|
||||||
public static string BackupDirectory = Path.Combine(Utils.FolderPath, "levels/backups");
|
public static string BackupDirectory = Path.Combine(Utils.FolderPath, "levels/backups");
|
||||||
|
|
||||||
[ConfigInt("afk-minutes", "Other", 10)]
|
[ConfigInt("afk-minutes", "Other", 10)]
|
||||||
public static int AutoAfkMins = 10;
|
public static int AutoAfkMins = 10;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user