More work on commands, still needs some work but most should be working now.

This commit is contained in:
hondacrx
2022-08-28 15:48:56 -04:00
parent 89b92985c5
commit 0c0e588f18
13 changed files with 495 additions and 483 deletions
+322 -349
View File
@@ -15,6 +15,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Game.DataStorage;
using Game.Entities;
using Game.Spells;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -24,375 +28,344 @@ namespace Game.Chat
{
class CommandArgs
{
static Dictionary<Type, Func<CommandArguments, ParseStringResult>> Parsers = new()
public static ChatCommandResult ConsumeFromOffset(dynamic[] tuple, int offset, ParameterInfo[] parameterInfos, CommandHandler handler, string args)
{
{ typeof(sbyte), args => args.NextSByte() },
{ typeof(short), args => args.NextInt16() },
{ typeof(int), args => args.NextInt32() },
{ typeof(long), args => args.NextInt64() },
{ typeof(byte), args => args.NextByte() },
{ typeof(ushort), args => args.NextUInt16() },
{ typeof(uint), args => args.NextUInt32() },
{ typeof(ulong), args => args.NextUInt64() },
{ typeof(float), args => args.NextSingle() },
{ typeof(string), args => args.NextString() },
{ typeof(bool), args => args.NextBoolean() },
{ typeof(PlayerIdentifier), args => PlayerIdentifier.ParseFromString(args) },
{ typeof(AccountIdentifier), args => AccountIdentifier.ParseFromString(args) },
{ typeof(Tail), args => Tail.ParseFromString(args) },
};
public static bool Parse(out dynamic[] parsedArgs, CommandHandler handler, ParameterInfo[] parameterInfos, string tailCmd)
{
parsedArgs = new dynamic[parameterInfos.Length];
parsedArgs[0] = handler;
CommandArguments args = new CommandArguments(tailCmd);
for (var i = 1; i < parameterInfos.Length; i++)
{
if (!ParseArgument(out dynamic value, parameterInfos[i], args))
return false;
parsedArgs[i] = value;
}
return true;
}
static bool ParseArgument(out dynamic value, ParameterInfo parameterInfo, CommandArguments args)
{
var parameterType = parameterInfo.ParameterType;
if (Hyperlink.TryParse(out value, parameterType, args))
return true;
bool isOptional = false;
var optionalArgAttribute = parameterInfo.GetCustomAttribute<OptionalArgAttribute>(true);
if (optionalArgAttribute != null)
isOptional = true;
if (parameterType.IsGenericType && parameterType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
parameterType = Nullable.GetUnderlyingType(parameterType);
isOptional = true;
}
if (parameterType.IsEnum)
parameterType = parameterType.GetEnumUnderlyingType();
var pos = args.GetCurrentPosition();
var result = Parsers[parameterType](args);
if (result != ParseResult.Ok)
{
if (!isOptional)
return false;
if (offset < tuple.Length)
return TryConsumeTo(tuple, offset, parameterInfos, handler, args);
else if (!args.IsEmpty()) /* the entire string must be consumed */
return default;
else
args.SetCurrentPosition(pos);
return new ChatCommandResult(args);
}
value = result.GetValue();
return true;
}
}
public enum ParseResult
static ChatCommandResult TryConsumeTo(dynamic[] tuple, int offset, ParameterInfo[] parameterInfos, CommandHandler handler, string args)
{
Ok,
Error,
EndOfString
var optionalArgAttribute = parameterInfos[offset].GetCustomAttribute<OptionalArgAttribute>(true);
if (optionalArgAttribute != null || parameterInfos[offset].ParameterType.IsGenericType && parameterInfos[offset].ParameterType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
// try with the argument
Type myArg = Nullable.GetUnderlyingType(parameterInfos[offset].ParameterType) ?? parameterInfos[offset].ParameterType;
ChatCommandResult result1 = TryConsume(out tuple[offset], myArg, handler, args);
if (result1.IsSuccessful())
if ((result1 = ConsumeFromOffset(tuple, offset + 1, parameterInfos, handler, result1)).IsSuccessful())
return result1;
// try again omitting the argument
tuple[offset] = default;
ChatCommandResult result2 = ConsumeFromOffset(tuple, offset + 1, parameterInfos, handler, args);
if (result2.IsSuccessful())
return result2;
if (result1.HasErrorMessage() && result2.HasErrorMessage())
{
return ChatCommandResult.FromErrorMessage($"{handler.GetCypherString(CypherStrings.CmdparserEither)} \"{result2.GetErrorMessage()}\"\n{handler.GetCypherString(CypherStrings.CmdparserOr)} \"{result1.GetErrorMessage()}\"");
}
else if (result1.HasErrorMessage())
return result1;
else
return result2;
}
else
{
ChatCommandResult next;
var variantArgAttribute = parameterInfos[offset].GetCustomAttribute<VariantArgAttribute>(true);
if (variantArgAttribute != null)
next = TryConsumeVariant(out tuple[offset], variantArgAttribute.Types, handler, args);
else
next = TryConsume(out tuple[offset], parameterInfos[offset].ParameterType, handler, args);
if (next.IsSuccessful())
return ConsumeFromOffset(tuple, offset + 1, parameterInfos, handler, next);
else
return next;
}
}
public struct ParseStringResult
public static ChatCommandResult TryConsume(out dynamic val, Type type, CommandHandler handler, string args)
{
ParseResult result;
val = default;
var hyperlinkResult = Hyperlink.TryParse(out val, type, handler, args);
if (hyperlinkResult.IsSuccessful())
return hyperlinkResult;
if (type.IsEnum)
type = type.GetEnumUnderlyingType();
var (token, tail) = Tokenize(args);
switch (Type.GetTypeCode(type))
{
case TypeCode.SByte:
{
if (token.IsEmpty())
return default;
if (sbyte.TryParse(token, out sbyte tempValue))
val = tempValue;
else
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserStringValueInvalid, token, Type.GetTypeCode(type)));
return new ChatCommandResult(tail);
}
case TypeCode.Int16:
{
if (token.IsEmpty())
return default;
if (short.TryParse(token, out short tempValue))
val = tempValue;
else
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserStringValueInvalid, token, Type.GetTypeCode(type)));
return new ChatCommandResult(tail);
}
case TypeCode.Int32:
{
if (token.IsEmpty())
return default;
if (int.TryParse(token, out int tempValue))
val = tempValue;
else
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserStringValueInvalid, token, Type.GetTypeCode(type)));
return new ChatCommandResult(tail);
}
case TypeCode.Int64:
{
if (token.IsEmpty())
return default;
if (long.TryParse(token, out long tempValue))
val = tempValue;
else
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserStringValueInvalid, token, Type.GetTypeCode(type)));
return new ChatCommandResult(tail);
}
case TypeCode.Byte:
{
if (token.IsEmpty())
return default;
if (byte.TryParse(token, out byte tempValue))
val = tempValue;
else
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserStringValueInvalid, token, Type.GetTypeCode(type)));
return new ChatCommandResult(tail);
}
case TypeCode.UInt16:
{
if (token.IsEmpty())
return default;
if (ushort.TryParse(token, out ushort tempValue))
val = tempValue;
else
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserStringValueInvalid, token, Type.GetTypeCode(type)));
return new ChatCommandResult(tail);
}
case TypeCode.UInt32:
{
if (token.IsEmpty())
return default;
if (uint.TryParse(token, out uint tempValue))
val = tempValue;
else
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserStringValueInvalid, token, Type.GetTypeCode(type)));
return new ChatCommandResult(tail);
}
case TypeCode.UInt64:
{
if (token.IsEmpty())
return default;
if (ulong.TryParse(token, out ulong tempValue))
val = tempValue;
else
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserStringValueInvalid, token, Type.GetTypeCode(type)));
return new ChatCommandResult(tail);
}
case TypeCode.Single:
{
if (token.IsEmpty())
return default;
if (float.TryParse(token, out float tempValue))
val = tempValue;
else
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserStringValueInvalid, token, Type.GetTypeCode(type)));
if (!float.IsFinite(val))
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserStringValueInvalid, token, Type.GetTypeCode(type)));
return new ChatCommandResult(tail);
}
case TypeCode.String:
{
if (token.IsEmpty())
return default;
val = token;
return new ChatCommandResult(tail);
}
case TypeCode.Object:
{
switch (type.Name)
{
case nameof(Tail):
val = new Tail();
return val.TryConsume(handler, args);
case nameof(PlayerIdentifier):
val = new PlayerIdentifier();
return val.TryConsume(handler, args);
case nameof(AccountIdentifier):
val = new AccountIdentifier();
return val.TryConsume(handler, args);
case nameof(AchievementRecord):
{
ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(uint), handler, args);
if (!result.IsSuccessful() || (val = CliDB.AchievementStorage.LookupByKey((uint)tempVal)) != null)
return result;
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserAchievementNoExist, tempVal));
}
case nameof(CurrencyTypesRecord):
{
ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(uint), handler, args);
if (!result.IsSuccessful() || (val = CliDB.CurrencyTypesStorage.LookupByKey((uint)tempVal)) != null)
return result;
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserCurrencyNoExist, tempVal));
}
case nameof(GameTele):
{
ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(uint), handler, args);
if (!result.IsSuccessful())
result = TryConsume(out tempVal, typeof(string), handler, args);
if (!result.IsSuccessful() || (val = Global.ObjectMgr.GetGameTele(tempVal)) != null)
return result;
if (tempVal is uint)
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserGameTeleIdNoExist, tempVal));
else
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserGameTeleNoExist, tempVal));
}
case nameof(ItemTemplate):
{
ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(uint), handler, args);
if (!result.IsSuccessful() || (val = Global.ObjectMgr.GetItemTemplate(tempVal)))
return result;
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserItemNoExist, tempVal));
}
case nameof(SpellInfo):
{
ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(uint), handler, args);
if (!result.IsSuccessful() || (val = Global.SpellMgr.GetSpellInfo(tempVal, Difficulty.None)) != null)
return result;
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserSpellNoExist, tempVal));
}
}
break;
}
}
return default;
}
public static ChatCommandResult TryConsumeVariant(out dynamic val, Type[] types, CommandHandler handler, string args)
{
ChatCommandResult result = TryAtIndex(out val, types, 0, handler, args);
if (result.HasErrorMessage() && (result.GetErrorMessage().IndexOf('\n') != -1))
return ChatCommandResult.FromErrorMessage($"{handler.GetCypherString(CypherStrings.CmdparserEither)} {result.GetErrorMessage()}");
return result;
}
static ChatCommandResult TryAtIndex(out dynamic val, Type[] types, int index, CommandHandler handler, string args)
{
val = default;
if (index < types.Length)
{
ChatCommandResult thisResult = TryConsume(out val, types[index], handler, args);
if (thisResult.IsSuccessful())
return thisResult;
else
{
ChatCommandResult nestedResult = TryAtIndex(out val, types, index+1, handler, args);
if (nestedResult.IsSuccessful() || !thisResult.HasErrorMessage())
return nestedResult;
if (!nestedResult.HasErrorMessage())
return thisResult;
if (nestedResult.GetErrorMessage().StartsWith("\""))
return ChatCommandResult.FromErrorMessage($"\"{thisResult.GetErrorMessage()}\"\n{handler.GetCypherString(CypherStrings.CmdparserOr)} {nestedResult.GetErrorMessage()}");
else
return ChatCommandResult.FromErrorMessage($"\"{thisResult.GetErrorMessage()}\"\n{handler.GetCypherString(CypherStrings.CmdparserOr)} \"{nestedResult.GetErrorMessage()}\"");
}
}
else
return default;
}
public static (string token, string tail) Tokenize(string args)
{
(string token, string tail) result = new ("", "");
int delimPos = args.IndexOf(' ');
if (delimPos != -1)
{
result.token = args.Substring(0, delimPos);
int tailPos = args.FindFirstNotOf(" ", delimPos);
if (tailPos != -1)
result.tail = args.Substring(tailPos);
}
else
result.token = args;
return result;
}
}
public struct ChatCommandResult
{
bool result;
dynamic value;
string errorMessage;
public ParseStringResult(ParseResult _result, dynamic _value = default)
public ChatCommandResult(string _value = "")
{
result = _result;
result = true;
value = _value;
errorMessage = null;
}
public dynamic GetValue() { return value; }
public bool IsSuccessful() { return result; }
public static implicit operator ParseResult(ParseStringResult stringResult)
public bool HasErrorMessage() { return !errorMessage.IsEmpty(); }
public string GetErrorMessage() { return errorMessage; }
public void SetErrorMessage(string _value)
{
return stringResult.result;
result = false;
errorMessage = _value;
}
public static implicit operator string(ParseStringResult stringResult)
public static ChatCommandResult FromErrorMessage(string str)
{
var result = new ChatCommandResult();
result.SetErrorMessage(str);
return result;
}
public static implicit operator string(ChatCommandResult stringResult)
{
return stringResult.value;
}
}
public sealed class CommandArguments
{
static ParseStringResult ErrorResult = new(ParseResult.Error);
static ParseStringResult EndOfStringResult = new(ParseResult.EndOfString);
public CommandArguments(string args)
{
if (!args.IsEmpty())
activestring = args.TrimStart(' ');
activeposition = -1;
}
public CommandArguments(CommandArguments args)
{
activestring = args.activestring;
activeposition = args.activeposition;
Current = args.Current;
}
public bool Empty()
{
return activestring.IsEmpty();
}
public void MoveToNextChar(char c)
{
for (var i = activeposition; i < activestring.Length; ++i)
if (activestring[i] == c)
break;
}
public ParseStringResult NextString(string delimiters = " ")
{
if (!MoveNext(delimiters))
return new ParseStringResult(ParseResult.EndOfString, "");
if (Current.Any(c => char.IsLetter(c)))
return new ParseStringResult(ParseResult.Ok, Current);
return new ParseStringResult(ParseResult.Error, "");
}
public ParseStringResult NextBoolean(string delimiters = " ")
{
if (!MoveNext(delimiters))
return EndOfStringResult;
bool value;
if (bool.TryParse(Current, out value))
return new ParseStringResult(ParseResult.Ok, value);
if ((Current == "1") || Current.Equals("y", StringComparison.OrdinalIgnoreCase) || Current.Equals("on", StringComparison.OrdinalIgnoreCase) || Current.Equals("yes", StringComparison.OrdinalIgnoreCase) || Current.Equals("true", StringComparison.OrdinalIgnoreCase))
return new ParseStringResult(ParseResult.Ok, true);
if ((Current == "0") || Current.Equals("n", StringComparison.OrdinalIgnoreCase) || Current.Equals("off", StringComparison.OrdinalIgnoreCase) || Current.Equals("no", StringComparison.OrdinalIgnoreCase) || Current.Equals("false", StringComparison.OrdinalIgnoreCase))
return new ParseStringResult(ParseResult.Ok, false);
return ErrorResult;
}
public ParseStringResult NextChar(string delimiters = " ")
{
if (!MoveNext(delimiters))
return EndOfStringResult;
if (char.TryParse(Current, out char value))
return new ParseStringResult(ParseResult.Ok, value);
return ErrorResult;
}
public ParseStringResult NextByte(string delimiters = " ")
{
if (!MoveNext(delimiters))
return EndOfStringResult;
if (byte.TryParse(Current, out byte value))
return new ParseStringResult(ParseResult.Ok, value);
return ErrorResult;
}
public ParseStringResult NextSByte(string delimiters = " ")
{
if (!MoveNext(delimiters))
return EndOfStringResult;
if (sbyte.TryParse(Current, out sbyte value))
return new ParseStringResult(ParseResult.Ok, value);
return ErrorResult;
}
public ParseStringResult NextUInt16(string delimiters = " ")
{
if (!MoveNext(delimiters))
return EndOfStringResult;
if (ushort.TryParse(Current, out ushort value))
return new ParseStringResult(ParseResult.Ok, value);
return ErrorResult;
}
public ParseStringResult NextInt16(string delimiters = " ")
{
if (!MoveNext(delimiters))
return EndOfStringResult;
if (short.TryParse(Current, out short value))
return new ParseStringResult(ParseResult.Ok, value);
return ErrorResult;
}
public ParseStringResult NextUInt32(string delimiters = " ")
{
if (!MoveNext(delimiters))
return EndOfStringResult;
if (uint.TryParse(Current, out uint value))
return new ParseStringResult(ParseResult.Ok, value);
return ErrorResult;
}
public ParseStringResult NextInt32(string delimiters = " ")
{
if (!MoveNext(delimiters))
return EndOfStringResult;
if (int.TryParse(Current, out int value))
return new ParseStringResult(ParseResult.Ok, value);
return ErrorResult;
}
public ParseStringResult NextUInt64(string delimiters = " ")
{
if (!MoveNext(delimiters))
return EndOfStringResult;
if (ulong.TryParse(Current, out ulong value))
return new ParseStringResult(ParseResult.Ok, value);
return ErrorResult;
}
public ParseStringResult NextInt64(string delimiters = " ")
{
if (!MoveNext(delimiters))
return EndOfStringResult;
if (long.TryParse(Current, out long value))
return new ParseStringResult(ParseResult.Ok, value);
return ErrorResult;
}
public ParseStringResult NextSingle(string delimiters = " ")
{
if (!MoveNext(delimiters))
return EndOfStringResult;
if (float.TryParse(Current, out float value))
return new ParseStringResult(ParseResult.Ok, value);
return ErrorResult;
}
public ParseStringResult NextDouble(string delimiters = " ")
{
if (!MoveNext(delimiters))
return EndOfStringResult;
if (double.TryParse(Current, out double value))
return new ParseStringResult(ParseResult.Ok, value);
return ErrorResult;
}
public ParseStringResult NextDecimal(string delimiters = " ")
{
if (!MoveNext(delimiters))
return EndOfStringResult;
if (decimal.TryParse(Current, out decimal value))
return new ParseStringResult(ParseResult.Ok, value);
return ErrorResult;
}
public void AlignToNextChar()
{
while (activeposition < activestring.Length && activestring[activeposition] != ' ')
activeposition++;
}
public char this[int index]
{
get { return activestring[index]; }
}
public string GetString()
{
return activestring;
}
public void Reset()
{
activeposition = -1;
Current = null;
}
public bool IsAtEnd()
{
return activestring.IsEmpty() || activeposition == activestring.Length;
}
public int GetCurrentPosition()
{
return activeposition;
}
public void SetCurrentPosition(int currentPosition)
{
activeposition = currentPosition;
}
bool MoveNext(string delimiters)
{
//the stringtotokenize was never set:
if (activestring == null)
return false;
//all tokens have already been extracted:
if (activeposition == activestring.Length)
return false;
//bypass delimiters:
activeposition++;
while (activeposition < activestring.Length && delimiters.IndexOf(activestring[activeposition]) > -1)
{
activeposition++;
}
//only delimiters were left, so return null:
if (activeposition == activestring.Length)
return false;
//get starting position of string to return:
int startingposition = activeposition;
//read until next delimiter:
do
{
activeposition++;
} while (activeposition < activestring.Length && delimiters.IndexOf(activestring[activeposition]) == -1);
Current = activestring.Substring(startingposition, activeposition - startingposition);
return true;
}
private string activestring;
private int activeposition;
private string Current;
}
}
+11
View File
@@ -79,4 +79,15 @@ namespace Game.Chat
[AttributeUsage(AttributeTargets.Parameter)]
public class OptionalArgAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Parameter)]
public class VariantArgAttribute : Attribute
{
public Type[] Types { get; set; }
public VariantArgAttribute(params Type[] types)
{
Types = types;
}
}
}
+6 -7
View File
@@ -417,9 +417,9 @@ namespace Game.Chat
return searcher.GetTarget();
}
public string PlayerLink(string name, bool console = false)
public string PlayerLink(string name)
{
return console ? name : "|cffffffff|Hplayer:" + name + "|h[" + name + "]|h|r";
return _session != null ? "|cffffffff|Hplayer:" + name + "|h[" + name + "]|h|r" : name;
}
public virtual string GetNameLink()
{
@@ -622,8 +622,9 @@ namespace Game.Chat
}
public bool HasSentErrorMessage() { return _sentErrorMessage; }
public void SetSentErrorMessage(bool val) { _sentErrorMessage = val; }
internal bool _sentErrorMessage;
bool _sentErrorMessage;
WorldSession _session;
}
@@ -739,8 +740,7 @@ namespace Game.Chat
public override void SendSysMessage(string str, bool escapeCharacters)
{
_sentErrorMessage = true;
SetSentErrorMessage(true);
Log.outInfo(LogFilter.Server, str);
}
@@ -798,8 +798,7 @@ namespace Game.Chat
public override void SendSysMessage(string str, bool escapeCharacters)
{
_sentErrorMessage = true;
SetSentErrorMessage(true);
_reportToRA(str);
}
+13 -3
View File
@@ -223,7 +223,7 @@ namespace Game.Chat
if (cmd != null)
{ /* if we matched a command at some point, invoke it */
handler._sentErrorMessage = false;
handler.SetSentErrorMessage(false);
if (cmd.IsInvokerVisible(handler) && cmd.Invoke(handler, oldTail))
{ /* invocation succeeded, log this */
if (!handler.IsConsole())
@@ -413,13 +413,23 @@ namespace Game.Chat
return (bool)_methodInfo.Invoke(null, new object[] { handler, new StringArguments(args) });
else
{
if (CommandArgs.Parse(out dynamic[] parseArgs, handler, parameters, args))
var parseArgs = new dynamic[parameters.Length];
parseArgs[0] = handler;
var result = CommandArgs.ConsumeFromOffset(parseArgs, 1, parameters, handler, args);
if (result.IsSuccessful())
return (bool)_methodInfo.Invoke(null, parseArgs);
else
{
if (result.HasErrorMessage())
{
handler.SendSysMessage(result.GetErrorMessage());
handler.SetSentErrorMessage(true);
}
return false;
}
}
}
}
public struct CommandPermissions
{
+50 -56
View File
@@ -15,6 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using Game.Entities;
using System;
@@ -26,11 +27,7 @@ namespace Game.Chat
ObjectGuid _guid;
Player _player;
public PlayerIdentifier(string name, ObjectGuid guid)
{
_name = name;
_guid = guid;
}
public PlayerIdentifier() { }
public PlayerIdentifier(Player player)
{
@@ -75,40 +72,37 @@ namespace Game.Chat
return FromSelf(handler);
}
public static ParseStringResult ParseFromString(CommandArguments args)
public ChatCommandResult TryConsume(CommandHandler handler, string args)
{
var result = args.NextString();
if (result != ParseResult.Ok)
return new ParseStringResult(ParseResult.Error);
ChatCommandResult next = CommandArgs.TryConsume(out dynamic tempVal, typeof(ulong), handler, args);
if (!next.IsSuccessful())
next = CommandArgs.TryConsume(out tempVal, typeof(string), handler, args);
if (!next.IsSuccessful())
return next;
string arg = result.GetValue();
ulong guid = 0;
string name;
if (!Hyperlink.TryParse(out name, arg) || !ulong.TryParse(arg, out guid))
name = arg;
if (!name.IsEmpty())
if (tempVal is ulong)
{
ObjectManager.NormalizePlayerName(ref name);
var player = Global.ObjAccessor.FindPlayerByName(name);
if (player != null)
return new ParseStringResult(ParseResult.Ok, new PlayerIdentifier(player));
_guid = ObjectGuid.Create(HighGuid.Player, tempVal);
if ((_player = Global.ObjAccessor.FindPlayerByLowGUID(_guid.GetCounter())))
_name = _player.GetName();
else
if (!Global.CharacterCacheStorage.GetCharacterNameByGuid(_guid, out _name))
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserCharGuidNoExist, _guid.ToString()));
return next;
}
else
{
var objectGuid = Global.CharacterCacheStorage.GetCharacterGuidByName(name);
if (objectGuid.IsEmpty())
return new ParseStringResult(ParseResult.Ok, new PlayerIdentifier(name, objectGuid));
}
}
else if (guid != 0)
{
var player = Global.ObjAccessor.FindPlayerByLowGUID(guid);
if (player != null)
return new ParseStringResult(ParseResult.Ok, new PlayerIdentifier(player));
}
_name = tempVal;
return new ParseStringResult(ParseResult.Error);
if (!ObjectManager.NormalizePlayerName(ref _name))
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserCharNameInvalid, _name));
if ((_player = Global.ObjAccessor.FindPlayerByName(_name)))
_guid = _player.GetGUID();
else if ((_guid = Global.CharacterCacheStorage.GetCharacterGuidByName(_name)).IsEmpty())
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserCharNameNoExist, _name));
return next;
}
}
}
@@ -118,6 +112,7 @@ namespace Game.Chat
string _name;
WorldSession _session;
public AccountIdentifier() { }
public AccountIdentifier(WorldSession session)
{
_id = session.GetAccountId();
@@ -130,26 +125,29 @@ namespace Game.Chat
public bool IsConnected() { return _session != null; }
public WorldSession GetConnectedSession() { return _session; }
public static ParseStringResult ParseFromString(CommandArguments args)
public ChatCommandResult TryConsume(CommandHandler handler, string args)
{
var result = args.NextString();
if (result != ParseResult.Ok)
return new ParseStringResult(ParseResult.Error);
ChatCommandResult next = CommandArgs.TryConsume(out dynamic text, typeof(string), handler, args);
if (!next.IsSuccessful())
return next;
// try parsing as account name
var session = Global.WorldMgr.FindSession(Global.AccountMgr.GetId(result.GetValue()));
if (session != null) // account with name exists, we are done
return new ParseStringResult(ParseResult.Ok, new AccountIdentifier(session));
// first try parsing as account name
_name = text;
_id = Global.AccountMgr.GetId(_name);
_session = Global.WorldMgr.FindSession(_id);
if (_id != 0) // account with name exists, we are done
return next;
// try parsing as account id
if (!uint.TryParse(result.GetValue(), out uint id))
return new ParseStringResult(ParseResult.Error);
// try parsing as account id instead
if (uint.TryParse(text, out uint id))
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserAccountNameNoExist, _name));
_id = id;
_session = Global.WorldMgr.FindSession(_id);
session = Global.WorldMgr.FindSession(id);
if (session != null)
return new ParseStringResult(ParseResult.Ok, new AccountIdentifier(session));
return new ParseStringResult(ParseResult.Error);
if (Global.AccountMgr.GetName(_id, out _name))
return next;
else
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserAccountIdNoExist, _id));
}
public static AccountIdentifier FromTarget(CommandHandler handler)
@@ -174,11 +172,6 @@ namespace Game.Chat
{
string str;
public Tail(CommandArguments args)
{
str = args.NextString("").GetValue();
}
public bool IsEmpty() { return str.IsEmpty(); }
public static implicit operator string(Tail tail)
@@ -186,9 +179,10 @@ namespace Game.Chat
return tail.str;
}
public static ParseStringResult ParseFromString(CommandArguments args)
public ChatCommandResult TryConsume(CommandHandler handler, string args)
{
return new ParseStringResult(ParseResult.Ok, new Tail(args));
str = args;
return new ChatCommandResult(str);
}
}
}
+2 -2
View File
@@ -95,7 +95,7 @@ namespace Game.Chat
return false;
}
AccountOpResult result = Global.AccountMgr.CreateAccount(accountName, password, email);
AccountOpResult result = Global.AccountMgr.CreateAccount(accountName, password, email ?? "");
switch (result)
{
case AccountOpResult.Ok:
@@ -105,7 +105,7 @@ namespace Game.Chat
Log.outInfo(LogFilter.Player, "Account: {0} (IP: {1}) Character:[{2}] (GUID: {3}) created Account {4} (Email: '{5}')",
handler.GetSession().GetAccountId(), handler.GetSession().GetRemoteAddress(),
handler.GetSession().GetPlayer().GetName(), handler.GetSession().GetPlayer().GetGUID().ToString(),
accountName, email);
accountName, email ?? "");
}
break;
case AccountOpResult.NameTooLong:
@@ -27,12 +27,8 @@ namespace Game.Chat.Commands
class AchievementCommand
{
[Command("add", CypherStrings.CommandAchievementAddHelp, RBACPermissions.CommandAchievementAdd)]
static bool HandleAchievementAddCommand(CommandHandler handler, uint achievemntId)
static bool HandleAchievementAddCommand(CommandHandler handler, AchievementRecord achievementEntry)
{
AchievementRecord achievementEntry = CliDB.AchievementStorage.LookupByKey(achievemntId);
if (achievementEntry == null)
return false;
Player target = handler.GetSelectedPlayer();
if (!target)
{
+33 -3
View File
@@ -453,7 +453,7 @@ namespace Game.Chat
}
[Command("instancespawn", RBACPermissions.CommandDebug)]
static bool HandleDebugInstanceSpawns(CommandHandler handler, string optArg)
static bool HandleDebugInstanceSpawns(CommandHandler handler, [VariantArg(typeof(uint), typeof(string))] object optArg)
{
Player player = handler.GetPlayer();
if (player == null)
@@ -461,10 +461,10 @@ namespace Game.Chat
bool explain = false;
uint groupID = 0;
if (optArg.Equals("explain", StringComparison.OrdinalIgnoreCase))
if (optArg is string && (optArg as string).Equals("explain", StringComparison.OrdinalIgnoreCase))
explain = true;
else
groupID = uint.Parse(optArg);
groupID = (uint)optArg;
if (groupID != 0 && Global.ObjectMgr.GetSpawnGroupData(groupID) == null)
{
@@ -786,6 +786,36 @@ namespace Game.Chat
return true;
}
[Command("pvp warmode", RBACPermissions.CommandDebug, true)]
static bool HandleDebugWarModeBalanceCommand(CommandHandler handler, string command, int? rewardValue)
{
// USAGE: .debug pvp fb <alliance|horde|neutral|off> [pct]
// neutral Sets faction balance off.
// alliance Set faction balance to alliance.
// horde Set faction balance to horde.
// off Reset the faction balance and use the calculated value of it
switch (command)
{
case "alliance":
Global.WorldMgr.SetForcedWarModeFactionBalanceState(TeamId.Alliance, rewardValue.GetValueOrDefault(0));
break;
case "horde":
Global.WorldMgr.SetForcedWarModeFactionBalanceState(TeamId.Horde, rewardValue.GetValueOrDefault(0));
break;
case "neutral":
Global.WorldMgr.SetForcedWarModeFactionBalanceState(TeamId.Neutral);
break;
case "off":
Global.WorldMgr.DisableForcedWarModeFactionBalanceState();
break;
default:
handler.SendSysMessage(CypherStrings.BadValue);
return false;
}
return true;
}
[Command("questreset", RBACPermissions.CommandDebug)]
static bool HandleDebugQuestResetCommand(CommandHandler handler, string arg)
{
@@ -115,7 +115,7 @@ namespace Game.Chat
}
[Command("info", RBACPermissions.CommandGobjectInfo)]
static bool HandleGameObjectInfoCommand(CommandHandler handler, string isGuid, uint data)
static bool HandleGameObjectInfoCommand(CommandHandler handler, [OptionalArg] string isGuid, ulong data)
{
GameObject thisGO = null;
GameObjectData spawnData = null;
@@ -137,7 +137,7 @@ namespace Game.Chat
}
else
{
entry = data;
entry = (uint)data;
}
GameObjectTemplate gameObjectInfo = Global.ObjectMgr.GetGameObjectTemplate(entry);
+8 -8
View File
@@ -442,7 +442,7 @@ namespace Game.Chat.Commands
}
[Command("respawns", RBACPermissions.CommandListRespawns)]
static bool HandleListRespawnsCommand(CommandHandler handler, uint range)
static bool HandleListRespawnsCommand(CommandHandler handler, uint? range)
{
Player player = handler.GetSession().GetPlayer();
Map map = player.GetMap();
@@ -454,8 +454,8 @@ namespace Game.Chat.Commands
string zoneName = GetZoneName(zoneId, locale);
for (SpawnObjectType type = 0; type < SpawnObjectType.NumSpawnTypes; type++)
{
if (range != 0)
handler.SendSysMessage(CypherStrings.ListRespawnsRange, type, range);
if (range.HasValue)
handler.SendSysMessage(CypherStrings.ListRespawnsRange, type, range.Value);
else
handler.SendSysMessage(CypherStrings.ListRespawnsZone, type, zoneName, zoneId);
@@ -473,9 +473,9 @@ namespace Game.Chat.Commands
if (edata != null)
{
respawnZoneId = map.GetZoneId(PhasingHandler.EmptyPhaseShift, edata.SpawnPoint);
if (range != 0)
if (range.HasValue)
{
if (!player.IsInDist(edata.SpawnPoint, range))
if (!player.IsInDist(edata.SpawnPoint, range.Value))
continue;
}
else
@@ -615,9 +615,9 @@ namespace Game.Chat.Commands
aura.GetCasterGUID().ToString());
}
for (ushort i = 0; i < (int)AuraType.Total; ++i)
for (AuraType auraType = 0; auraType < AuraType.Total; ++auraType)
{
var auraList = unit.GetAuraEffectsByType((AuraType)i);
var auraList = unit.GetAuraEffectsByType(auraType);
if (auraList.Empty())
continue;
@@ -631,7 +631,7 @@ namespace Game.Chat.Commands
if (!sizeLogged)
{
sizeLogged = true;
handler.SendSysMessage(CypherStrings.CommandTargetListauratype, auraList.Count, i);
handler.SendSysMessage(CypherStrings.CommandTargetListauratype, auraList.Count, auraType);
}
handler.SendSysMessage(CypherStrings.CommandTargetAurasimple, effect.GetId(), effect.GetEffIndex(), effect.GetAmount());
+7 -13
View File
@@ -31,9 +31,8 @@ namespace Game.Chat
class TeleCommands
{
[Command("", RBACPermissions.CommandTele)]
static bool HandleTeleCommand(CommandHandler handler, string name)
static bool HandleTeleCommand(CommandHandler handler, GameTele tele)
{
var tele = Global.ObjectMgr.GetGameTele(name);
if (tele == null)
{
handler.SendSysMessage(CypherStrings.CommandTeleNotfound);
@@ -71,7 +70,7 @@ namespace Game.Chat
if (player == null)
return false;
if (Global.ObjectMgr.GetGameTele(name) != null)
if (Global.ObjectMgr.GetGameTeleExactName(name) != null)
{
handler.SendSysMessage(CypherStrings.CommandTpAlreadyexist);
return false;
@@ -100,9 +99,8 @@ namespace Game.Chat
}
[Command("del", RBACPermissions.CommandTeleDel, true)]
static bool HandleTeleDelCommand(CommandHandler handler, string name)
static bool HandleTeleDelCommand(CommandHandler handler, GameTele tele)
{
var tele = Global.ObjectMgr.GetGameTele(name);
if (tele == null)
{
handler.SendSysMessage(CypherStrings.CommandTeleNotfound);
@@ -115,9 +113,8 @@ namespace Game.Chat
}
[Command("group", RBACPermissions.CommandTeleGroup)]
static bool HandleTeleGroupCommand(CommandHandler handler, string name)
static bool HandleTeleGroupCommand(CommandHandler handler, GameTele tele)
{
var tele = Global.ObjectMgr.GetGameTele(name);
if (tele == null)
{
handler.SendSysMessage(CypherStrings.CommandTeleNotfound);
@@ -240,7 +237,7 @@ namespace Game.Chat
class TeleNameCommands
{
[Command("", RBACPermissions.CommandTeleName, true)]
static bool HandleTeleNameCommand(CommandHandler handler, PlayerIdentifier player, string where)
static bool HandleTeleNameCommand(CommandHandler handler, [OptionalArg] PlayerIdentifier player, [VariantArg(typeof(GameTele), typeof(string))] object where)
{
if (player == null)
player = PlayerIdentifier.FromTargetOrSelf(handler);
@@ -249,7 +246,7 @@ namespace Game.Chat
Player target = player.GetConnectedPlayer();
if (where.Equals("home", StringComparison.OrdinalIgnoreCase)) // References target's homebind
if (where is string && where.Equals("$home")) // References target's homebind
{
if (target)
target.TeleportTo(target.GetHomebind());
@@ -272,10 +269,7 @@ namespace Game.Chat
}
// id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
GameTele tele = Global.ObjectMgr.GetGameTele(where);
if (tele == null)
return false;
GameTele tele = where as GameTele;
return DoNameTeleport(handler, player, tele.mapId, new Position(tele.posX, tele.posY, tele.posZ, tele.orientation), tele.name);
}
+21 -39
View File
@@ -15,33 +15,23 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Framework.Constants;
using System;
namespace Game.Chat
{
class Hyperlink
{
public static bool TryParse(out string value, string arg)
public static ChatCommandResult TryParse(out dynamic value, Type type, CommandHandler handler, string arg)
{
value = null;
value = default;
HyperlinkInfo info = ParseHyperlink(arg);
// invalid hyperlinks cannot be consumed
if (info == null)
return false;
return default;
value = info.Data;
return true;
}
public static bool TryParse(out dynamic value, Type type, CommandArguments args)
{
value = default;
HyperlinkInfo info = ParseHyperlink(args.GetString());
// invalid hyperlinks cannot be consumed
if (info == null)
return false;
ChatCommandResult errorResult = ChatCommandResult.FromErrorMessage(handler.GetCypherString(CypherStrings.CmdparserLinkdataInvalid));
// store value
switch (Type.GetTypeCode(type))
@@ -49,42 +39,34 @@ namespace Game.Chat
case TypeCode.UInt32:
{
if (!uint.TryParse(info.Data, out uint tempValue))
return false;
return errorResult;
value = tempValue;
return true;
break;
}
case TypeCode.UInt64:
{
if (!ulong.TryParse(info.Data, out ulong tempValue))
return false;
return errorResult;
value = tempValue;
return true;
break;
}
case TypeCode.String:
{
value = info.Data;
return true;
break;
}
case TypeCode.Object:
{
switch (type.Name)
{
case nameof(PlayerIdentifier):
value = PlayerIdentifier.ParseFromString(args);
break;
case nameof(AccountIdentifier):
value = AccountIdentifier.ParseFromString(args);
break;
default:
return false;
}
return true;
}
return errorResult;
}
return false;
// finally, skip any potential delimiters
var (token, next) = CommandArgs.Tokenize(info.Tail);
if (token.IsEmpty()) /* empty token = first character is delimiter, skip past it */
return new ChatCommandResult(next);
else
return new ChatCommandResult(info.Tail);
}
public static bool CheckAllLinks(string str)
@@ -123,7 +105,7 @@ namespace Game.Chat
return false;
// tag is fine, find the next one
pos = str.Length - info.next.Length;
pos = str.Length - info.Tail.Length;
}
}
@@ -204,16 +186,16 @@ namespace Game.Chat
class HyperlinkInfo
{
public HyperlinkInfo(string n = null, uint c = 0, string tag = null, string data = null, string text = null)
public HyperlinkInfo(string t = null, uint c = 0, string tag = null, string data = null, string text = null)
{
next = n;
Tail = t;
color = new(c);
Tag = tag;
Data = data;
Text = text;
}
public string next;
public string Tail;
public HyperlinkColor color;
public string Tag;
public string Data;
+24 -1
View File
@@ -5766,7 +5766,30 @@ namespace Game
}
public GameTele GetGameTele(string name)
{
return gameTeleStorage.Values.FirstOrDefault(p => p.nameLow == name.ToLower());
name = name.ToLower();
// Alternative first GameTele what contains wnameLow as substring in case no GameTele location found
GameTele alt = null;
foreach (var (_, tele) in gameTeleStorage)
{
if (tele.nameLow == name)
return tele;
else if (alt == null && tele.nameLow.Contains(name))
alt = tele;
}
return alt;
}
public GameTele GetGameTeleExactName(string name)
{
name = name.ToLower();
foreach (var (_, tele) in gameTeleStorage)
{
if (tele.nameLow == name)
return tele;
}
return null;
}
public bool AddGameTele(GameTele tele)
{