Files
CypherCore/Source/Game/Chat/CommandArgs.cs
T

363 lines
16 KiB
C#

/*
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* 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;
using System.Reflection;
namespace Game.Chat
{
class CommandArgs
{
public static ChatCommandResult ConsumeFromOffset(dynamic[] tuple, int offset, ParameterInfo[] parameterInfos, CommandHandler handler, string args)
{
if (offset < tuple.Length)
return TryConsumeTo(tuple, offset, parameterInfos, handler, args);
else if (!args.IsEmpty()) /* the entire string must be consumed */
return default;
else
return new ChatCommandResult(args);
}
static ChatCommandResult TryConsumeTo(dynamic[] tuple, int offset, ParameterInfo[] parameterInfos, CommandHandler handler, string args)
{
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 static ChatCommandResult TryConsume(out dynamic val, Type type, CommandHandler handler, string args)
{
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) = args.Tokenize();
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));
}
case nameof(Quest):
{
ChatCommandResult result = TryConsume(out dynamic tempVal, typeof(uint), handler, args);
if (!result.IsSuccessful() || (val = Global.ObjectMgr.GetQuestTemplate(tempVal)))
return result;
return ChatCommandResult.FromErrorMessage(handler.GetParsedString(CypherStrings.CmdparserQuestNoExist, 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 struct ChatCommandResult
{
bool result;
dynamic value;
string errorMessage;
public ChatCommandResult(string _value = "")
{
result = true;
value = _value;
errorMessage = null;
}
public bool IsSuccessful() { return result; }
public bool HasErrorMessage() { return !errorMessage.IsEmpty(); }
public string GetErrorMessage() { return errorMessage; }
public void SetErrorMessage(string _value)
{
result = false;
errorMessage = _value;
}
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;
}
}
}