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.
/// </summary>
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();
Help = help;
MinUserLevel = minUserLevel;
InGameOnly = inGameOnly;
}
}
@ -49,21 +55,27 @@ namespace DiIiS_NA.GameServer.CommandManager
/// <summary>
/// Minimum user level required to invoke the command.
/// </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();
Help = help;
MinUserLevel = minUserLevel;
InGameOnly = inGameOnly;
}
}
[AttributeUsage(AttributeTargets.Method)]
public class DefaultCommand : CommandAttribute
{
public DefaultCommand(Account.UserLevels minUserLevel = Account.UserLevels.User)
: base("", "", minUserLevel)
public DefaultCommand(Account.UserLevels minUserLevel = Account.UserLevels.User, bool inGameOnly = false)
: 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.Text;
using System.Threading.Tasks;
using FluentNHibernate.Utils;
namespace DiIiS_NA.GameServer.CommandManager
{
@ -13,7 +14,7 @@ namespace DiIiS_NA.GameServer.CommandManager
{
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();
@ -37,7 +38,7 @@ namespace DiIiS_NA.GameServer.CommandManager
if (!_commands.ContainsKey(attribute))
_commands.Add(attribute, method);
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
return "You don't have enough privileges to invoke that command.";
#endif
if (invokerClient?.InGameClient == null && Attributes.InGameOnly)
return "You can only use this command in-game.";
string[] @params = null;
CommandAttribute target = null;
@ -77,7 +80,7 @@ namespace DiIiS_NA.GameServer.CommandManager
@params = parameters.Split(' ');
target = GetSubcommand(@params[0]) ?? GetDefaultSubcommand();
if (target != GetDefaultSubcommand())
if (!Equals(target, GetDefaultSubcommand()))
@params = @params.Skip(1).ToArray();
}
@ -88,15 +91,28 @@ namespace DiIiS_NA.GameServer.CommandManager
#else
return "You don't have enough privileges to invoke that command.";
#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)
{
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;
}
@ -114,14 +130,8 @@ namespace DiIiS_NA.GameServer.CommandManager
return output.Substring(0, output.Length - 2) + ".";
}
protected CommandAttribute GetDefaultSubcommand()
{
return _commands.Keys.First();
}
protected CommandAttribute GetDefaultSubcommand() => _commands.Keys.First();
protected CommandAttribute GetSubcommand(string name)
{
return _commands.Keys.FirstOrDefault(command => command.Name == name);
}
protected CommandAttribute GetSubcommand(string name) => _commands.Keys.FirstOrDefault(command => command.Name == name);
}
}

View File

@ -1,25 +1,17 @@
using System;
using DiIiS_NA.LoginServer.AccountsSystem;
using DiIiS_NA.LoginServer.Battle;
using FluentNHibernate.Utils;
namespace DiIiS_NA.GameServer.CommandManager;
[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
{
[DefaultCommand]
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")]
[Command("advance", "Advances a quest by a single step\n Usage: advance", inGameOnly: true)]
public string Advance(string[] @params, BattleClient invokerClient)
{
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)
{
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)
{
if (@params == null)
@ -78,20 +70,42 @@ public class QuestCommand : CommandGroup
if (@params.Length != 2)
return "Invalid arguments. Type 'help text public' to get help.";
var eventId = int.Parse(@params[0]);
var duration = int.Parse(@params[1]);
invokerClient.InGameClient.Game.QuestManager.LaunchQuestTimer(eventId, (float)duration,
new Action<int>((q) => { }));
if (!int.TryParse(@params[0], out var eventId) || !int.TryParse(@params[1], out var duration))
return "Invalid arguments. Type 'help text public' to get help.";
invokerClient.InGameClient.Game.QuestManager.LaunchQuestTimer(eventId, (float)duration, (_) => { });
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)
{
if (invokerClient?.InGameClient?.Game?.QuestManager is not {} questManager)
return "You can only invoke this command while in-game.";
if (invokerClient.InGameClient.Game?.QuestManager is not {} questManager)
return "No quests found.";
var act = questManager.CurrentAct;
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))
{
_attributeMap[name] += action;
_logger.Warn($"Fixed attribute {name} already exists. Action will be added.");
return;
_attributeMap[name] = action;
_logger.Warn($"Overwrite attribute {name}");
}
_attributeMap.Add(name, action);
else
_attributeMap.Add(name, action);
}
public void Remove(FixedAttribute name)

View File

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

View File

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