Perform partial name matching in /notes and /rankinfo

This commit is contained in:
UnknownShadow200 2017-02-09 13:48:32 +11:00
parent c5876ee307
commit c1f13c4a5a
23 changed files with 192 additions and 172 deletions

View File

@ -126,15 +126,16 @@ namespace MCGalaxy.Gui {
bool GetAutoload() {
foreach (string line in Server.AutoloadMaps.Find(lvl.name + "="))
return true;
return false;
return Server.AutoloadMaps.Find(lvl.name) != null;
}
void SetAutoload(bool value) {
Server.AutoloadMaps.DeleteStartsWith(lvl.name + "=");
if (value)
Server.AutoloadMaps.Append(lvl.name + "=" + lvl.physics);
if (value) {
Server.AutoloadMaps.AddOrReplace(lvl.name, lvl.physics.ToString());
} else {
Server.AutoloadMaps.Remove(lvl.name);
}
Server.AutoloadMaps.Save();
}
}
}

View File

@ -70,8 +70,7 @@ namespace MCGalaxy.Commands {
BlockDBChange.OutputMessageBlock(p, b, id, x, y, z);
BlockDBChange.OutputPortal(p, b, id, x, y, z);
GC.Collect();
GC.WaitForPendingFinalizers();
Server.DoGC();
}
static void ListFromDatabase(Player p, ref bool foundAny, Dictionary<int, string> names,

View File

@ -16,6 +16,7 @@
permissions and limitations under the Licenses.
*/
using System;
using System.Collections.Generic;
using System.IO;
namespace MCGalaxy.Commands {
@ -28,19 +29,17 @@ namespace MCGalaxy.Commands {
public CmdRankInfo() { }
public override void Use(Player p, string message) {
if (message == "") {
if (Player.IsSuper(p)) { SuperRequiresArgs(p, "player name"); return; }
message = p.name;
}
Player who = PlayerInfo.Find(message);
string target = who == null ? message : who.name;
Player.Message(p, " Rank information for {0}:",
PlayerInfo.GetColoredName(p, target));
bool found = false;
if (CheckSuper(p, message, "player name")) return;
if (message == "") message = p.name;
List<string> rankings = Server.RankInfo.FindMatches(p, message, "rankings");
if (rankings == null) return;
string target = PlayerMetaList.GetName(rankings[0]);
Player.Message(p, " Rankings for {0}:", PlayerInfo.GetColoredName(p, target));
DateTime now = DateTime.Now;
foreach (string line in Server.RankInfo.Find(target)) {
foreach (string line in rankings) {
string[] parts = line.Split(' ');
string newRank = Group.GetColoredName(parts[7]);
string oldRank = Group.GetColoredName(parts[8]);
@ -55,10 +54,7 @@ namespace MCGalaxy.Commands {
Player.Message(p, "&aFrom {0} &ato {1} &a{2} ago",
oldRank, newRank, delta.Shorten(true, false));
Player.Message(p, "&aBy %S{0}&a, reason: %S{1}", parts[1], reason);
found = true;
}
if (!found)
Player.Message(p, "&cPlayer has not been ranked yet.");
}
public override void Help(Player p) {

View File

@ -28,8 +28,7 @@ namespace MCGalaxy.Commands {
public override void Use(Player p, string message) {
Player.Message(p, "Forcing garbage collection...");
GC.Collect();
GC.WaitForPendingFinalizers();
Server.DoGC();
Player.Message(p, "Garbage collection completed!");
}

View File

@ -16,6 +16,7 @@
permissions and limitations under the Licenses.
*/
using System;
using System.Collections.Generic;
namespace MCGalaxy.Commands {
public class CmdNotes : Command {
@ -32,15 +33,13 @@ namespace MCGalaxy.Commands {
if (CheckSuper(p, message, "player name")) return;
if (message == "") message = p.name;
int matches = 1;
Player who = message == "" ? p : PlayerInfo.FindMatches(p, message, out matches);
if (matches > 1) return;
if (who != null) message = who.name;
List<string> notes = Server.Notes.FindMatches(p, message, "notes");
if (notes == null) return;
Player.Message(p, "Notes for " + message + ":");
bool foundAny = false;
foreach (string line in Server.Notes.Find(message)) {
foundAny = true;
string target = PlayerMetaList.GetName(notes[0]);
Player.Message(p, " Notes for {0}:", PlayerInfo.GetColoredName(p, target));
foreach (string line in notes) {
string[] args = line.Split(' ');
if (args.Length <= 3) continue;
@ -50,8 +49,6 @@ namespace MCGalaxy.Commands {
Player.Message(p, Action(args[1]) + " by " + args[2] + " on " + args[3]
+ " - " + args[4].Replace("%20", " "));
}
if (!foundAny)
Player.Message(p, "No notes found.");
}
static string Action(string arg) {

View File

@ -52,8 +52,7 @@ namespace MCGalaxy.Commands.World {
lvl.Save(true);
} finally {
lvl.Dispose();
GC.Collect();
GC.WaitForPendingFinalizers();
Server.DoGC();
}
} catch (Exception ex) {
Server.ErrorLog(ex);
@ -61,7 +60,6 @@ namespace MCGalaxy.Commands.World {
return;
}
Player.Message(p, "Converted map!");
//CmdLoad.LoadLevel(p, name); pls
}
enum FileType { Mcf, Fcm, Dat, Cw };

View File

@ -45,8 +45,7 @@ namespace MCGalaxy.Commands.World {
try {
return LoadLevelCore(p, name, phys, autoLoaded);
} finally {
GC.Collect();
GC.WaitForPendingFinalizers();
Server.DoGC();
}
}

View File

@ -79,8 +79,7 @@ namespace MCGalaxy.Commands.World {
Chat.MessageAll(format, pName, name, seed);
} finally {
if (p != null) Interlocked.Exchange(ref p.GeneratingMap, 0);
GC.Collect();
GC.WaitForPendingFinalizers();
Server.DoGC();
}
return true;
}

View File

@ -46,8 +46,7 @@ namespace MCGalaxy.Commands {
}
LevelActions.ReloadMap(p, who, true);
}
GC.Collect();
GC.WaitForPendingFinalizers();
Server.DoGC();
}
bool ReloadAll(Player p, string[] parts) {

View File

@ -85,8 +85,7 @@ namespace MCGalaxy.Commands.World {
}
}
GC.Collect();
GC.WaitForPendingFinalizers();
Server.DoGC();
return Level.Load(lvl.name);
}

View File

@ -26,7 +26,7 @@ namespace MCGalaxy.Commands.World {
public override void Use(Player p, string message) {
string name = message.ToLower();
if (name == "" && Player.IsSuper(p)) { SuperRequiresArgs(p, "level name"); return; }
if (CheckSuper(p, message, "level name")) return;
if (name == "") {
if (!p.level.Unload()) {

View File

@ -158,8 +158,7 @@ namespace MCGalaxy.Drawing.Ops {
if (pl.level.name.CaselessEq(lvl.name))
LevelActions.ReloadMap(p, pl, true);
}
GC.Collect();
GC.WaitForPendingFinalizers();
Server.DoGC();
}

View File

@ -56,11 +56,6 @@ namespace fNbt {
}
byte[] bytes = ZeroArray;
public byte this[int tagIndex] {
get { return Value[tagIndex]; }
set { Value[tagIndex] = value; }
}
internal override void ReadTag(NbtBinaryReader readStream) {
int length = readStream.ReadInt32();
if (length < 0)
@ -233,16 +228,14 @@ namespace fNbt {
public byte ByteValue {
get {
if (TagType == NbtTagType.Byte)
return ((NbtByte)this).Value;
if (TagType == NbtTagType.Byte) return ((NbtByte)this).Value;
throw new InvalidCastException("Cannot get ByteValue from " + TagType);
}
}
public float FloatValue {
get {
if (TagType == NbtTagType.Float)
return ((NbtFloat)this).Value;
if (TagType == NbtTagType.Float) return ((NbtFloat)this).Value;
throw new InvalidCastException("Cannot get FloatValue from " + TagType);
}
}
@ -250,10 +243,8 @@ namespace fNbt {
public short ShortValue {
get {
switch (TagType) {
case NbtTagType.Byte:
return ((NbtByte)this).Value;
case NbtTagType.Short:
return ((NbtShort)this).Value;
case NbtTagType.Byte: return ((NbtByte)this).Value;
case NbtTagType.Short: return ((NbtShort)this).Value;
default:
throw new InvalidCastException("Cannot get ShortValue from " + TagType);
}
@ -263,12 +254,9 @@ namespace fNbt {
public int IntValue {
get {
switch (TagType) {
case NbtTagType.Byte:
return ((NbtByte)this).Value;
case NbtTagType.Short:
return ((NbtShort)this).Value;
case NbtTagType.Int:
return ((NbtInt)this).Value;
case NbtTagType.Byte: return ((NbtByte)this).Value;
case NbtTagType.Short: return ((NbtShort)this).Value;
case NbtTagType.Int: return ((NbtInt)this).Value;
default:
throw new InvalidCastException("Cannot get IntValue from " + TagType);
}
@ -277,16 +265,14 @@ namespace fNbt {
public byte[] ByteArrayValue {
get {
if (TagType == NbtTagType.ByteArray)
return ((NbtByteArray)this).Value;
if (TagType == NbtTagType.ByteArray) return ((NbtByteArray)this).Value;
throw new InvalidCastException("Cannot get ByteArrayValue from " + TagType);
}
}
public string StringValue {
get {
if (TagType == NbtTagType.String)
return ((NbtString)this).Value;
if (TagType == NbtTagType.String) return ((NbtString)this).Value;
throw new InvalidCastException("Cannot get StringValue from " + TagType);
}
}
@ -331,25 +317,22 @@ namespace fNbt {
public override short ReadInt16() {
if (swapNeeded) {
return Swap(base.ReadInt16());
} else {
return base.ReadInt16();
}
return base.ReadInt16();
}
public override int ReadInt32() {
if (swapNeeded) {
return Swap(base.ReadInt32());
} else {
return base.ReadInt32();
}
return base.ReadInt32();
}
public override long ReadInt64() {
if (swapNeeded) {
return Swap(base.ReadInt64());
} else {
return base.ReadInt64();
}
return base.ReadInt64();
}
public override float ReadSingle() {
@ -357,9 +340,8 @@ namespace fNbt {
FillBuffer(sizeof(float));
Array.Reverse(buffer, 0, sizeof(float));
return BitConverter.ToSingle(buffer, 0);
} else {
return base.ReadSingle();
}
return base.ReadSingle();
}
public override double ReadDouble() {
@ -405,25 +387,19 @@ namespace fNbt {
}
static short Swap(short v) {
unchecked {
return (short)((v >> 8) & 0x00FF | (v << 8) & 0xFF00);
}
return (short)((v >> 8) & 0x00FF | (v << 8) & 0xFF00);
}
static int Swap(int v) {
unchecked {
uint v2 = (uint)v;
return
(int)
((v2 >> 24) & 0x000000FF | (v2 >> 8) & 0x0000FF00 | (v2 << 8) & 0x00FF0000 |
(v2 << 24) & 0xFF000000);
}
uint v2 = (uint)v;
return
(int)
((v2 >> 24) & 0x000000FF | (v2 >> 8) & 0x0000FF00 | (v2 << 8) & 0x00FF0000 |
(v2 << 24) & 0xFF000000);
}
static long Swap(long v) {
unchecked {
return (Swap((int)v) & uint.MaxValue) << 32 | Swap((int)(v >> 32)) & uint.MaxValue;
}
return (Swap((int)v) & uint.MaxValue) << 32 | Swap((int)(v >> 32)) & uint.MaxValue;
}
}

View File

@ -204,8 +204,7 @@ namespace MCGalaxy {
} catch {
} finally {
Dispose();
GC.Collect();
GC.WaitForPendingFinalizers();
Server.DoGC();
if (!silent) Chat.MessageOps(ColoredName + " %Swas unloaded.");
Server.s.Log(name + " was unloaded.");
@ -271,8 +270,7 @@ namespace MCGalaxy {
Chat.MessageAll("FAILED TO SAVE {0}", ColoredName);
Server.ErrorLog(e);
}
GC.Collect();
GC.WaitForPendingFinalizers();
Server.DoGC();
}
void SaveCore(string path) {

View File

@ -332,8 +332,7 @@ namespace MCGalaxy {
SendMessage("There was an error sending the map data, you have been sent to the main level.");
Server.ErrorLog(ex);
} finally {
GC.Collect();
GC.WaitForPendingFinalizers();
Server.DoGC();
}
return success;
}

View File

@ -23,49 +23,50 @@ using System.Text;
namespace MCGalaxy {
public sealed class PlayerExtList {
char separator = ' ';
string path;
List<string> players = new List<string>();
List<string> names = new List<string>();
public List<string> lines = new List<string>();
readonly object locker = new object(), saveLocker = new object();
public void Add(string p, string data) {
p = p.ToLower();
public void Add(string name, string data) {
name = name.ToLower();
lock (locker) {
players.Add(p); lines.Add(p + " " + data);
names.Add(name); lines.Add(name + separator + data);
}
}
public bool Remove(string p) {
public bool Remove(string name) {
lock (locker) {
int idx = players.IndexOf(p.ToLower());
int idx = names.IndexOf(name.ToLower());
if (idx == -1) return false;
players.RemoveAt(idx);
names.RemoveAt(idx);
lines.RemoveAt(idx);
return true;
}
}
public void AddOrReplace(string p, string data) {
p = p.ToLower();
public void AddOrReplace(string name, string data) {
name = name.ToLower();
lock (locker) {
int idx = players.IndexOf(p);
int idx = names.IndexOf(name);
if (idx == -1) {
players.Add(p); lines.Add(p + " " + data);
names.Add(name); lines.Add(name + separator + data);
} else {
lines[idx] = p + " " + data;
lines[idx] = name + separator + data;
}
}
}
public string Find(string p) {
public string Find(string name) {
lock (locker) {
int idx = players.IndexOf(p.ToLower());
int idx = names.IndexOf(name.ToLower());
return idx == -1 ? null : lines[idx];
}
}
public int Count { get { lock (locker) return players.Count; } }
public int Count { get { lock (locker) return names.Count; } }
public void Save() { Save(true); }
@ -84,9 +85,10 @@ namespace MCGalaxy {
}
}
public static PlayerExtList Load(string path) {
public static PlayerExtList Load(string path, char separator = ' ') {
PlayerExtList list = new PlayerExtList();
list.path = path;
list.separator = separator;
if (!File.Exists(path)) {
File.Create(path).Close();
@ -98,8 +100,8 @@ namespace MCGalaxy {
string line = null;
while ((line = r.ReadLine()) != null) {
list.lines.Add(line);
int space = line.IndexOf(' ');
string name = space >= 0 ? line.Substring(0, space) : line;
int sepIndex = line.IndexOf(separator);
string name = sepIndex >= 0 ? line.Substring(0, sepIndex) : line;
// Need to convert uppercase to lowercase, in case user added in entries.
bool anyUpper = false;
@ -108,7 +110,7 @@ namespace MCGalaxy {
anyUpper |= (c >= 'A' && c <= 'Z');
}
if (anyUpper) name = name.ToLower();
list.players.Add(name);
list.names.Add(name);
}
}
return list;

View File

@ -67,7 +67,7 @@ namespace MCGalaxy {
public string FindMatches(Player p, string name, string type, out int matches) {
lock (locker) {
return Matcher.Find<string>(p, name, out matches, players,
n => true, n => n, type, 20);
null, n => n, type, 20);
}
}

View File

@ -33,9 +33,18 @@ namespace MCGalaxy {
}
public void EnsureExists() {
if (!File.Exists(file))
if (!File.Exists(file))
File.Create(file).Dispose();
}
/// <summary> Adds the given line to the end of the file. </summary>
public void Append(string data) {
lock (locker) {
using (StreamWriter w = new StreamWriter(file, true))
w.WriteLine(data);
}
}
/// <summary> Finds all lines which caselessly start with the given name. </summary>
public IEnumerable<string> Find(string name) {
@ -50,36 +59,28 @@ namespace MCGalaxy {
}
yield break;
}
public List<string> FindMatches(Player p, string name, string group) {
int matches = 0;
return Matcher.FindMulti<string>(p, name, out matches, AllLines(),
null, GetName, group);
}
/// <summary> Deletes all lines which start with the given value. </summary>
public void DeleteStartsWith(string value) {
if (!File.Exists(file)) return;
List<string> lines = new List<string>();
IEnumerable<string> AllLines() {
if (!File.Exists(file)) yield break;
using (StreamReader r = new StreamReader(file)) {
string line;
while ((line = r.ReadLine()) != null) {
if (line.StartsWith(value)) continue;
lines.Add(line);
yield return line;
}
}
WriteLines(lines);
yield break;
}
void WriteLines(List<string> lines) {
lock (locker) {
using (StreamWriter w = new StreamWriter(file, false)) {
foreach (string line in lines)
w.WriteLine(line);
}
}
}
/// <summary> Adds the given line to the end of the file. </summary>
public void Append(string data) {
lock (locker) {
using (StreamWriter w = new StreamWriter(file, true))
w.WriteLine(data);
}
public static string GetName(string line) {
int index = line.IndexOf(' ');
return index == -1 ? line : line.Substring(0, index);
}
}
}

View File

@ -59,8 +59,7 @@ namespace MCGalaxy {
GotoLevel(p, lvl, ignorePerms) : GotoMap(p, name, ignorePerms);
} finally {
Interlocked.Exchange(ref p.UsingGoto, 0);
GC.Collect();
GC.WaitForPendingFinalizers();
Server.DoGC();
}
if (!didJoin) return false;

View File

@ -52,7 +52,7 @@ namespace MCGalaxy {
public static Thread locationChecker;
public static DateTime StartTime, StartTimeLocal;
public static PlayerMetaList AutoloadMaps = new PlayerMetaList("text/autoload.txt");
public static PlayerExtList AutoloadMaps;
public static PlayerMetaList RankInfo = new PlayerMetaList("text/rankinfo.txt");
public static PlayerMetaList TempRanks = new PlayerMetaList("text/tempranks.txt");
public static PlayerMetaList Notes = new PlayerMetaList("text/notes.txt");

View File

@ -94,17 +94,17 @@ namespace MCGalaxy {
whiteList = PlayerList.Load("whitelist.txt");
}
void LoadAutoloadCommands() {
if (File.Exists("text/autoload.txt")) {
PropertiesFile.Read("text/autoload.txt", AutoLoadLineProcessor);
GC.Collect();
GC.WaitForPendingFinalizers();
} else {
Log("autoload.txt does not exist");
void LoadAutoloadMaps() {
AutoloadMaps = PlayerExtList.Load("text/autoload.txt", '=');
foreach (string line in AutoloadMaps.lines) {
int sepIndex = line.IndexOf('=');
string name = sepIndex >= 0 ? line.Substring(0, sepIndex) : line;
string value = sepIndex >= 0 ? line.Substring(sepIndex + 1) : "";
AutoLoadMap(name, value);
}
}
static void AutoLoadLineProcessor(string name, string phys) {
static void AutoLoadMap(string name, string phys) {
name = name.ToLower();
if (phys == "") phys = "0";

View File

@ -108,7 +108,7 @@ namespace MCGalaxy {
Plugin.Load();
Background.QueueOnce(UpgradeTasks.UpgradeOldBlacklist);
Background.QueueOnce(LoadPlayerLists);
Background.QueueOnce(LoadAutoloadCommands);
Background.QueueOnce(LoadAutoloadMaps);
Background.QueueOnce(UpgradeTasks.MovePreviousLevelFiles);
Background.QueueOnce(UpgradeTasks.UpgradeOldLockdown);
@ -366,5 +366,15 @@ namespace MCGalaxy {
level = mapName;
return true;
}
public static void DoGC() {
long start = GC.GetTotalMemory(false);
GC.Collect();
GC.WaitForPendingFinalizers();
long end = GC.GetTotalMemory(false);
double delta = (start - end) / 1024.0;
Server.s.Log("GC performed (freed " + delta.ToString("F2") + " KB)", true);
}
}
}

View File

@ -17,6 +17,7 @@
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
@ -82,16 +83,8 @@ namespace MCGalaxy {
/// <summary> Finds partial matches of 'name' against the names of the items in the 'items' enumerable. </summary>
/// <param name="pl"> The player to output messages to. </param>
/// <param name="name"> The name to perform partial matching against. </param>
/// <param name="matches"> The number of found/outputted matches. </param>
/// <param name="items"> Enumerable of items that may be matched with. </param>
/// <param name="filter"> Selects which items from 'items' are actually matched. </param>
/// <param name="nameGetter"> Gets the name of a particular item. </param>
/// <param name="group"> The group/type of the items. (e.g. 'players', 'commands') </param>
/// <param name="limit"> The maximum number of matches that are outputted. </param>
/// <returns> If exactly one match, the matching item. </returns>
public static T Find<T>(Player pl, string name, out int matches, IEnumerable items,
public static T Find<T>(Player p, string name, out int matches, IEnumerable items,
Predicate<T> filter, Func<T, string> nameGetter, string group, int limit = 5) {
T match = default(T); matches = 0;
StringBuilder nameMatches = new StringBuilder();
@ -110,19 +103,76 @@ namespace MCGalaxy {
}
}
if (matches == 1) return match;
if (matches == 0) {
Player.Message(pl, "No " + group + " match \"" + name + "\".");
return default(T);
} else if (matches == 1) {
return match;
Player.Message(p, "No " + group + " match \"" + name + "\".");
} else {
string count = matches > limit ? limit + "+ " : matches + " ";
string names = nameMatches.ToString(0, nameMatches.Length - 2);
Player.Message(pl, count + group + " match \"" + name + "\":");
Player.Message(pl, names);
return default(T);
OutputMulti(p, name, nameMatches, matches, group, limit);
}
return default(T);
}
/// <summary> Finds partial matches of 'name' against the names of the items in the 'items' enumerable. </summary>
/// <remarks> Outputs multiple matching entries, as 'items' enumerable may have multiple entries. </remarks>
/// <returns> If exactly one match, the matching list of items. </returns>
public static List<T> FindMulti<T>(Player p, string name, out int matches, IEnumerable items,
Predicate<T> filter, Func<T, string> nameGetter, string group, int limit = 5) {
List<T> matchItems = null; matches = 0;
StringBuilder nameMatches = new StringBuilder();
List<string> outputtedNames = new List<string>(limit);
string match = null;
foreach (T item in items) {
if (filter != null && !filter(item)) continue;
string itemName = nameGetter(item);
// Found an exact name match - only output items now which exactly match
if (itemName.Equals(name, comp)) {
if (match == null || !name.Equals(match, comp))
matchItems = new List<T>();
matchItems.Add(item);
matches = 1; match = name;
continue;
}
if (itemName.IndexOf(name, comp) < 0) continue;
if (matches == 0) { // Found our first partial match - init the list
matchItems = new List<T>();
matchItems.Add(item);
match = itemName;
} else if (match != null && itemName.Equals(match, comp)) { // Found same partial match
matchItems.Add(item);
}
// We do not want to output the same name multiple times
if (outputtedNames.CaselessContains(itemName) || matches > (limit + 1)) continue;
matches++;
if (matches <= limit) {
nameMatches.Append(itemName).Append(", ");
} else if (matches == limit + 1) {
nameMatches.Append("(and more)").Append(", ");
}
outputtedNames.Add(itemName);
}
if (matches == 1) return matchItems;
if (matches == 0) {
Player.Message(p, "No " + group + " found for \"" + name + "\".");
} else {
OutputMulti(p, name, nameMatches, matches, "players", limit);
}
return null;
}
static void OutputMulti(Player p, string name, StringBuilder nameMatches,
int matches, string group, int limit = 5) {
string count = matches > limit ? limit + "+ " : matches + " ";
string names = nameMatches.ToString(0, nameMatches.Length - 2);
Player.Message(p, count + group + " match \"" + name + "\":");
Player.Message(p, names);
}
}
}