Command and settings changes.

Attribute for checking if command is for in-game only;
Added CommandException.cs and InvalidParametersException.cs
Added NecroSkeletonCount to GameServerConfig.cs, thus the amount of necro skeletons can now be set.
Added new quest command: !quest set [questId] [stepId] to fast-forward to a specific quest.
This commit is contained in:
Lucca Faria Ferri 2023-02-13 07:28:00 -08:00
parent aa05ae4988
commit f72ff60965
8 changed files with 120 additions and 58 deletions

View File

@ -24,12 +24,18 @@ namespace DiIiS_NA.GameServer.CommandManager
/// Minimum user level required to invoke the command. /// Minimum user level required to invoke the command.
/// </summary> /// </summary>
public Account.UserLevels MinUserLevel { get; private set; } public Account.UserLevels MinUserLevel { get; private set; }
/// <summary>
/// For InGame commands only. If true, the command will be available only in the game.
/// </summary>
public bool InGameOnly { get; }
public CommandGroupAttribute(string name, string help, Account.UserLevels minUserLevel = Account.UserLevels.Admin) public CommandGroupAttribute(string name, string help, Account.UserLevels minUserLevel = Account.UserLevels.Admin, bool inGameOnly = false)
{ {
Name = name.ToLower(); Name = name.ToLower();
Help = help; Help = help;
MinUserLevel = minUserLevel; MinUserLevel = minUserLevel;
InGameOnly = inGameOnly;
} }
} }
@ -49,21 +55,27 @@ namespace DiIiS_NA.GameServer.CommandManager
/// <summary> /// <summary>
/// Minimum user level required to invoke the command. /// Minimum user level required to invoke the command.
/// </summary> /// </summary>
public Account.UserLevels MinUserLevel { get; private set; } public Account.UserLevels MinUserLevel { get; }
/// <summary>
/// Whether the command is only for in-game command.
/// </summary>
public bool InGameOnly { get; }
public CommandAttribute(string command, string help, Account.UserLevels minUserLevel = Account.UserLevels.User) public CommandAttribute(string command, string help, Account.UserLevels minUserLevel = Account.UserLevels.User, bool inGameOnly = false)
{ {
Name = command.ToLower(); Name = command.ToLower();
Help = help; Help = help;
MinUserLevel = minUserLevel; MinUserLevel = minUserLevel;
InGameOnly = inGameOnly;
} }
} }
[AttributeUsage(AttributeTargets.Method)] [AttributeUsage(AttributeTargets.Method)]
public class DefaultCommand : CommandAttribute public class DefaultCommand : CommandAttribute
{ {
public DefaultCommand(Account.UserLevels minUserLevel = Account.UserLevels.User) public DefaultCommand(Account.UserLevels minUserLevel = Account.UserLevels.User, bool inGameOnly = false)
: base("", "", minUserLevel) : base("", "", minUserLevel, inGameOnly)
{ {
} }
} }

View File

@ -0,0 +1,9 @@
using System;
namespace DiIiS_NA.GameServer.CommandManager;
public class CommandException : Exception
{
public CommandException(string message) : base(message) {}
public CommandException(string message, Exception ex) : base(message, ex) {}
}

View File

@ -6,6 +6,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentNHibernate.Utils;
namespace DiIiS_NA.GameServer.CommandManager namespace DiIiS_NA.GameServer.CommandManager
{ {
@ -13,7 +14,7 @@ namespace DiIiS_NA.GameServer.CommandManager
{ {
private static readonly Logger Logger = LogManager.CreateLogger("CmdGrp"); private static readonly Logger Logger = LogManager.CreateLogger("CmdGrp");
public CommandGroupAttribute Attributes { get; private set; } private CommandGroupAttribute Attributes { get; set; }
private readonly Dictionary<CommandAttribute, MethodInfo> _commands = new(); private readonly Dictionary<CommandAttribute, MethodInfo> _commands = new();
@ -37,7 +38,7 @@ namespace DiIiS_NA.GameServer.CommandManager
if (!_commands.ContainsKey(attribute)) if (!_commands.ContainsKey(attribute))
_commands.Add(attribute, method); _commands.Add(attribute, method);
else else
Logger.Fatal("Command '$[underline white]${0}$[/]$' already exists.", attribute.Name); Logger.Fatal($"$[red]$Command$[/]$ '$[underline white]${attribute.Name.SafeAnsi()}$[/]$' already exists.");
} }
} }
@ -67,6 +68,8 @@ namespace DiIiS_NA.GameServer.CommandManager
#else #else
return "You don't have enough privileges to invoke that command."; return "You don't have enough privileges to invoke that command.";
#endif #endif
if (invokerClient?.InGameClient == null && Attributes.InGameOnly)
return "You can only use this command in-game.";
string[] @params = null; string[] @params = null;
CommandAttribute target = null; CommandAttribute target = null;
@ -77,7 +80,7 @@ namespace DiIiS_NA.GameServer.CommandManager
@params = parameters.Split(' '); @params = parameters.Split(' ');
target = GetSubcommand(@params[0]) ?? GetDefaultSubcommand(); target = GetSubcommand(@params[0]) ?? GetDefaultSubcommand();
if (target != GetDefaultSubcommand()) if (!Equals(target, GetDefaultSubcommand()))
@params = @params.Skip(1).ToArray(); @params = @params.Skip(1).ToArray();
} }
@ -88,15 +91,28 @@ namespace DiIiS_NA.GameServer.CommandManager
#else #else
return "You don't have enough privileges to invoke that command."; return "You don't have enough privileges to invoke that command.";
#endif #endif
if (invokerClient?.InGameClient == null && target.InGameOnly)
return "This command can only be invoked in-game.";
return (string)_commands[target].Invoke(this, new object[] { @params, invokerClient }); try
{
return (string)_commands[target].Invoke(this, new object[] { @params, invokerClient });
}
catch (CommandException commandException)
{
return commandException.Message;
}
catch (Exception ex)
{
Logger.ErrorException(ex, "Command Handling Error");
return "An error occurred while executing the command.";
}
} }
public string GetHelp(string command) public string GetHelp(string command)
{ {
foreach (var pair in _commands) foreach (var pair in _commands.Where(pair => command == pair.Key.Name))
{ {
if (command != pair.Key.Name) continue;
return pair.Key.Help; return pair.Key.Help;
} }
@ -114,14 +130,8 @@ namespace DiIiS_NA.GameServer.CommandManager
return output.Substring(0, output.Length - 2) + "."; return output.Substring(0, output.Length - 2) + ".";
} }
protected CommandAttribute GetDefaultSubcommand() protected CommandAttribute GetDefaultSubcommand() => _commands.Keys.First();
{
return _commands.Keys.First();
}
protected CommandAttribute GetSubcommand(string name) protected CommandAttribute GetSubcommand(string name) => _commands.Keys.FirstOrDefault(command => command.Name == name);
{
return _commands.Keys.FirstOrDefault(command => command.Name == name);
}
} }
} }

View File

@ -1,25 +1,17 @@
using System; using System;
using DiIiS_NA.LoginServer.AccountsSystem;
using DiIiS_NA.LoginServer.Battle; using DiIiS_NA.LoginServer.Battle;
using FluentNHibernate.Utils;
namespace DiIiS_NA.GameServer.CommandManager; namespace DiIiS_NA.GameServer.CommandManager;
[CommandGroup("quest", [CommandGroup("quest",
"Retrieves information about quest states and manipulates quest progress.\n Usage: quest [triggers | trigger eventType eventValue | advance snoQuest]")] "Retrieves information about quest states and manipulates quest progress.\n" +
"Usage: quest [triggers | trigger eventType eventValue | advance snoQuest]",
Account.UserLevels.Tester, inGameOnly: true)]
public class QuestCommand : CommandGroup public class QuestCommand : CommandGroup
{ {
[DefaultCommand] [Command("advance", "Advances a quest by a single step\n Usage: advance", inGameOnly: true)]
public string Quest(string[] @params, BattleClient invokerClient)
{
if (invokerClient == null)
return "You cannot invoke this command from console.";
if (invokerClient.InGameClient == null)
return "You can only invoke this command while in-game.";
return Info(@params, invokerClient);
}
[Command("advance", "Advances a quest by a single step\n Usage: advance")]
public string Advance(string[] @params, BattleClient invokerClient) public string Advance(string[] @params, BattleClient invokerClient)
{ {
try try
@ -33,7 +25,7 @@ public class QuestCommand : CommandGroup
} }
} }
[Command("sideadvance", "Advances a side-quest by a single step\n Usage: sideadvance")] [Command("sideadvance", "Advances a side-quest by a single step\n Usage: sideadvance", inGameOnly: true)]
public string SideAdvance(string[] @params, BattleClient invokerClient) public string SideAdvance(string[] @params, BattleClient invokerClient)
{ {
try try
@ -69,7 +61,7 @@ public class QuestCommand : CommandGroup
} }
} }
[Command("timer", "Send broadcasted text message.\n Usage: public 'message'")] [Command("timer", "Send broadcast text message.\n Usage: public 'message'")]
public string Timer(string[] @params, BattleClient invokerClient) public string Timer(string[] @params, BattleClient invokerClient)
{ {
if (@params == null) if (@params == null)
@ -78,20 +70,42 @@ public class QuestCommand : CommandGroup
if (@params.Length != 2) if (@params.Length != 2)
return "Invalid arguments. Type 'help text public' to get help."; return "Invalid arguments. Type 'help text public' to get help.";
var eventId = int.Parse(@params[0]); if (!int.TryParse(@params[0], out var eventId) || !int.TryParse(@params[1], out var duration))
var duration = int.Parse(@params[1]); return "Invalid arguments. Type 'help text public' to get help.";
invokerClient.InGameClient.Game.QuestManager.LaunchQuestTimer(eventId, (float)duration, invokerClient.InGameClient.Game.QuestManager.LaunchQuestTimer(eventId, (float)duration, (_) => { });
new Action<int>((q) => { }));
return "Message sent."; return "Message sent.";
} }
[Command("set", "Advance to a specific quest step.\n Usage: quest to [questId] [step]")]
public string Set(string[] @params, BattleClient invokerClient)
{
if (@params == null)
return Fallback();
[Command("info", "Retrieves information about quest states.\n Usage: info")] if (@params.Length != 2)
return "Invalid arguments. Type 'help quest to' to get help.";
if (!int.TryParse(@params[0], out var questId) || !int.TryParse(@params[1], out var step))
return "Invalid arguments. Type 'help quest to' to get help.";
try
{
invokerClient.InGameClient.Game.QuestManager.AdvanceTo(questId, step);
return $"Advancing to quest {questId} step {step}";
}
catch (Exception e)
{
return e.Message;
}
}
[Command("info", "Retrieves information about quest states.\n Usage: info", inGameOnly: true)]
public string Info(string[] @params, BattleClient invokerClient) public string Info(string[] @params, BattleClient invokerClient)
{ {
if (invokerClient?.InGameClient?.Game?.QuestManager is not {} questManager) if (invokerClient.InGameClient.Game?.QuestManager is not {} questManager)
return "You can only invoke this command while in-game."; return "No quests found.";
var act = questManager.CurrentAct; var act = questManager.CurrentAct;
var quest = questManager.Game.CurrentQuest; var quest = questManager.Game.CurrentQuest;

View File

@ -0,0 +1,9 @@
using System;
namespace DiIiS_NA.GameServer.CommandManager;
public class InvalidParametersException : CommandException
{
public InvalidParametersException(string message) : base(message) {}
public InvalidParametersException(string message, Exception ex) : base(message, ex) {}
}

View File

@ -31,12 +31,11 @@ namespace DiIiS_NA.GameServer.GSSystem.ObjectsSystem
if (Contains(name)) if (Contains(name))
{ {
_attributeMap[name] += action; _attributeMap[name] = action;
_logger.Warn($"Fixed attribute {name} already exists. Action will be added."); _logger.Warn($"Overwrite attribute {name}");
return;
} }
else
_attributeMap.Add(name, action); _attributeMap.Add(name, action);
} }
public void Remove(FixedAttribute name) public void Remove(FixedAttribute name)

View File

@ -3203,14 +3203,14 @@ public class Player : Actor, IMessageConsumer, IUpdateable
NecroSkeletons.Clear(); NecroSkeletons.Clear();
} }
while (NecroSkeletons.Count < 7) while (NecroSkeletons.Count < GameServerConfig.Instance.NecroSkeletonCount)
{ {
var Skeleton = new NecromancerSkeleton_A(World, ActorSno._p6_necro_commandskeletons_a, this); var necroSkeleton = new NecromancerSkeleton_A(World, ActorSno._p6_necro_commandskeletons_a, this);
Skeleton.Brain.DeActivate(); necroSkeleton.Brain.DeActivate();
Skeleton.Scale = 1.2f; necroSkeleton.Scale = 1.2f;
Skeleton.EnterWorld(PowerContext.RandomDirection(Position, 3f, 8f)); necroSkeleton.EnterWorld(PowerContext.RandomDirection(Position, 3f, 8f));
NecroSkeletons.Add(Skeleton); NecroSkeletons.Add(necroSkeleton);
/*this.InGameClient.SendMessage(new PetMessage() /*this.InGameClient.SendMessage(new PetMessage()
{ {
Owner = this.PlayerIndex, Owner = this.PlayerIndex,
@ -3219,7 +3219,7 @@ public class Player : Actor, IMessageConsumer, IUpdateable
Type = 70, Type = 70,
}); });
//*/ //*/
Skeleton.Brain.Activate(); necroSkeleton.Brain.Activate();
} }
} }
else else
@ -3540,7 +3540,7 @@ public class Player : Actor, IMessageConsumer, IUpdateable
world.Reveal(this); world.Reveal(this);
Unreveal(this); Unreveal(this);
if (Math.Abs(_CurrentHPValue - (-1f)) < Globals.FLOAT_TOLERANCE) if (Math.Abs(_CurrentHPValue - -1f) < Globals.FLOAT_TOLERANCE)
DefaultQueryProximityRadius = 60; DefaultQueryProximityRadius = 60;
InGameClient.SendMessage(new EnterWorldMessage() InGameClient.SendMessage(new EnterWorldMessage()
@ -3571,7 +3571,7 @@ public class Player : Actor, IMessageConsumer, IUpdateable
break; break;
} }
if (Math.Abs(_CurrentHPValue - (-1f)) < Globals.FLOAT_TOLERANCE) if (Math.Abs(_CurrentHPValue - -1f) < Globals.FLOAT_TOLERANCE)
AddPercentageHP(100); AddPercentageHP(100);
DefaultQueryProximityRadius = 100; DefaultQueryProximityRadius = 100;
@ -3741,7 +3741,7 @@ public class Player : Actor, IMessageConsumer, IUpdateable
AllBuffs.Clear(); AllBuffs.Clear();
BetweenWorlds = false; BetweenWorlds = false;
if (Math.Abs(_CurrentHPValue - (-1)) > Globals.FLOAT_TOLERANCE) if (Math.Abs(_CurrentHPValue - -1) > Globals.FLOAT_TOLERANCE)
{ {
Attributes[GameAttributes.Hitpoints_Cur] = _CurrentHPValue; Attributes[GameAttributes.Hitpoints_Cur] = _CurrentHPValue;
Attributes[GameAttributes.Resource_Cur, (int)Toon.HeroTable.PrimaryResource + 1] = _CurrentResourceValue; Attributes[GameAttributes.Resource_Cur, (int)Toon.HeroTable.PrimaryResource + 1] = _CurrentResourceValue;

View File

@ -298,12 +298,21 @@ namespace DiIiS_NA.GameServer
set => Set(nameof(NephalemRiftOrbsChance), value); set => Set(nameof(NephalemRiftOrbsChance), value);
} }
/// <summary>
/// Forces the game to reveal all the map.
/// </summary>
public bool ForceMinimapVisibility public bool ForceMinimapVisibility
{ {
get => GetBoolean(nameof(ForceMinimapVisibility), false); get => GetBoolean(nameof(ForceMinimapVisibility), false);
set => Set(nameof(ForceMinimapVisibility), value); set => Set(nameof(ForceMinimapVisibility), value);
} }
public int NecroSkeletonCount
{
get => GetInt(nameof(NecroSkeletonCount), 7);
set => Set(nameof(NecroSkeletonCount), value);
}
#endregion #endregion
public static GameServerConfig Instance { get; } = new(); public static GameServerConfig Instance { get; } = new();