Game Mods - Json + Configuration migration

- Changed the Game Mods config to a json file
 - Created a .ini migration for the json file
 - Better ANSI readability
 - Improved some methods
This commit is contained in:
Lucca Faria Ferri 2023-06-19 07:41:22 -07:00
parent 56dd6194a1
commit f3ccab713e
21 changed files with 510 additions and 175 deletions

View File

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
@ -24,12 +26,12 @@ public class AnsiTarget : LogTarget
_table = new Table().Expand().ShowFooters().ShowHeaders().Border(TableBorder.Rounded);
if (IncludeTimeStamps)
_table.AddColumn("Time");
_table.AddColumn("Time").Centered();
_table
.AddColumn("Level")
.AddColumn("Logger")
.AddColumn("Message")
.AddColumn("Error");
.AddColumn("Level").RightAligned()
.AddColumn("Message").Centered()
.AddColumn("Logger").LeftAligned()
.AddColumn("Error").RightAligned();
AnsiConsole.Live(_table).StartAsync(async ctx =>
{
@ -101,6 +103,15 @@ public class AnsiTarget : LogTarget
}
private static Dictionary<string, string> _replacements = new ()
{
["["] = "[[",
["]"] = "]]",
["$[[/]]$"] = "[/]",
["$[["] = "[",
["]]$"] = "]"
};
/// <summary>
/// Performs a cleanup on the target.
/// All [ becomes [[, and ] becomes ]] (for ignoring ANSI codes)
@ -112,82 +123,58 @@ public class AnsiTarget : LogTarget
/// </summary>
/// <param name="x"></param>
/// <returns></returns>
public static string Cleanup(string x) => Beautify(x.Replace("[", "[[").Replace("]", "]]").Replace("$[[/]]$", "[/]").Replace("$[[", "[").Replace("]]$", "]"));
public static string Cleanup(string input)
{
if (string.IsNullOrEmpty(input)) return "";
return Beautify(_replacements.Aggregate(input, (current, replacement) => current.Replace(replacement.Key, replacement.Value)));
}
public override void LogMessage(Logger.Level level, string logger, string message)
{
if (CancellationTokenSource.IsCancellationRequested)
return;
if (CancellationTokenSource.IsCancellationRequested) return;
try
{
if (IncludeTimeStamps)
_table.AddRow(
new Markup(DateTime.Now.ToString(TimeStampFormat), GetStyleByLevel(level)),
new Markup(level.ToString(), GetStyleByLevel(level)).RightJustified(),
new Markup(logger, GetStyleByLevel(level)).LeftJustified(),
new Markup(Cleanup(message), GetStyleByLevel(level)).LeftJustified(),
new Markup("", new Style(foreground: Color.Green3_1)).Centered());
else
_table.AddRow(
new Markup(level.ToString()).RightJustified(),
new Markup(logger, GetStyleByLevel(level)).LeftJustified(),
new Markup(Cleanup(message), GetStyleByLevel(level)).LeftJustified(),
new Markup("", new Style(foreground: Color.Green3_1)).Centered());
AddRow(level, logger, message, "");
}
catch (Exception ex)
{
var regex = new Regex(@"\$\[.*?\]\$");
var matches = regex.Matches(message);
foreach (Match match in matches)
{
message = message.Replace(match.Value, "");
}
if (IncludeTimeStamps)
{
_table.AddRow(
new Markup(DateTime.Now.ToString(TimeStampFormat), GetStyleByLevel(level)),
new Markup(level.ToString(), GetStyleByLevel(level)).RightJustified(),
new Markup(logger, GetStyleByLevel(level)).LeftJustified(),
new Markup(Cleanup(message), GetStyleByLevel(level)).LeftJustified(),
new Markup(ex.Message, new Style(foreground: Color.Red3_1)).Centered());
}
else
{
_table.AddRow(
new Markup(level.ToString()).RightJustified(),
new Markup(logger, GetStyleByLevel(level)).LeftJustified(),
new Markup(Cleanup(message), GetStyleByLevel(level)).LeftJustified(),
new Markup(ex.Message, new Style(foreground: Color.Red3_1)).Centered());
}
AddRow(level, logger, Cleanup(StripMarkup(message)), ex.Message, true);
}
}
public override void LogException(Logger.Level level, string logger, string message, Exception exception)
{
if (CancellationTokenSource.IsCancellationRequested)
return;
if (CancellationTokenSource.IsCancellationRequested) return;
try
{
if (IncludeTimeStamps)
_table.AddRow(
new Markup(DateTime.Now.ToString(TimeStampFormat), GetStyleByLevel(level)),
new Markup(level.ToString(), GetStyleByLevel(level)).RightJustified(),
new Markup(logger, GetStyleByLevel(level)).LeftJustified(),
new Markup(Cleanup(message), GetStyleByLevel(level)).LeftJustified(),
new Markup(
$"[underline red3_1 on white]{exception.GetType().Name}[/]\n" + Cleanup(exception.Message),
new Style(foreground: Color.Red3_1)).Centered());
else
_table.AddRow(
new Markup(level.ToString()).RightJustified(),
new Markup(logger, GetStyleByLevel(level)).LeftJustified(),
new Markup(message, GetStyleByLevel(level)).LeftJustified(),
new Markup(
$"[underline red3_1 on white]{exception.GetType().Name}[/]\n" + Cleanup(exception.Message),
new Style(foreground: Color.Red3_1)).Centered());
AddRow(level, logger, message, $"[underline red3_1 on white]{exception.GetType().Name}[/]\n" + Cleanup(exception.Message), exFormat: true);
}
catch (Exception ex)
{
AddRow(level, logger, Cleanup(StripMarkup(message)), ex.Message, true);
}
}
private void AddRow(Logger.Level level, string logger, string message, string exMessage, bool isError = false,
bool exFormat = false)
{
Style messageStyle = GetStyleByLevel(level);
Style exStyle = exFormat ? new Style(foreground: Color.Red3_1) : new Style(foreground: Color.Green3_1);
var colTimestamp = new Markup(DateTime.Now.ToString(TimeStampFormat), messageStyle).Centered();
var colLevel = new Markup(level.ToString(), messageStyle).RightJustified();
var colMessage = new Markup(Cleanup(message), messageStyle).Centered();
var colLogger = new Markup(logger, new Style(messageStyle.Foreground, messageStyle.Background, messageStyle.Decoration
#if DEBUG
//, link = ...
#endif
)).LeftJustified();
var colError = new Markup(isError ? exMessage : "", exStyle).RightJustified();
if (IncludeTimeStamps) _table.AddRow(colTimestamp, colLevel, colMessage, colLogger, colError);
else _table.AddRow(colLevel, colMessage, colLogger, colError);
}
private string StripMarkup(string message)
{
var regex = new Regex(@"\$\[.*?\]\$");
var matches = regex.Matches(message);
@ -196,38 +183,21 @@ public class AnsiTarget : LogTarget
message = message.Replace(match.Value, "");
}
if (IncludeTimeStamps)
{
_table.AddRow(
new Markup(DateTime.Now.ToString(TimeStampFormat), GetStyleByLevel(level)),
new Markup(level.ToString(), GetStyleByLevel(level)).RightJustified(),
new Markup(logger, GetStyleByLevel(level)).LeftJustified(),
new Markup(Cleanup(message), GetStyleByLevel(level)).LeftJustified(),
new Markup(ex.Message, new Style(foreground: Color.Red3_1)).Centered());
}
else
{
_table.AddRow(
new Markup(level.ToString()).RightJustified(),
new Markup(logger, GetStyleByLevel(level)).LeftJustified(),
new Markup(Cleanup(message), GetStyleByLevel(level)).LeftJustified(),
new Markup(ex.Message, new Style(foreground: Color.Red3_1)).Centered());
}
}
return message;
}
private static Style GetStyleByLevel(Logger.Level level)
{
return level switch
{
Logger.Level.RenameAccountLog => new Style(Color.DarkSlateGray3),//
Logger.Level.ChatMessage => new Style(Color.DarkSlateGray2),//
Logger.Level.Debug => new Style(Color.Olive),//
Logger.Level.MethodTrace => new Style(Color.DarkOliveGreen1_1),//
Logger.Level.Trace => new Style(Color.BlueViolet),//
Logger.Level.Info => new Style(Color.White),
Logger.Level.Success => new Style(Color.Green3_1),
Logger.Level.Warn => new Style(Color.Yellow),//
Logger.Level.RenameAccountLog => new Style(Color.Gold1),//
Logger.Level.ChatMessage => new Style(Color.Plum2),//
Logger.Level.Debug => new Style(Color.Grey62),//
Logger.Level.MethodTrace => new Style(Color.Grey74, decoration: Decoration.Dim | Decoration.Italic),//
Logger.Level.Trace => new Style(Color.Grey82),//
Logger.Level.Info => new Style(Color.SteelBlue),
Logger.Level.Success => new Style(Color.DarkOliveGreen3_2),
Logger.Level.Warn => new Style(Color.DarkOrange),//
Logger.Level.Error => new Style(Color.IndianRed1),//
Logger.Level.Fatal => new Style(Color.Red3_1),//
Logger.Level.PacketDump => new Style(Color.Maroon),//

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace DiIiS_NA.Core.Logging
{
@ -25,7 +26,7 @@ namespace DiIiS_NA.Core.Logging
/// Creates and returns a logger named with declaring type.
/// </summary>
/// <returns>A <see cref="Logger"/> instance.</returns>
public static Logger CreateLogger()
public static Logger CreateLogger([CallerFilePath] string filePath = "")
{
var frame = new StackFrame(1, false); // read stack frame.
var name = frame.GetMethod().DeclaringType.Name; // get declaring type's name.
@ -33,7 +34,7 @@ namespace DiIiS_NA.Core.Logging
if (name == null) // see if we got a name.
throw new Exception("Error getting full name for declaring type.");
return CreateLogger(name); // return the newly created logger.
return CreateLogger(name, filePath); // return the newly created logger.
}
/// <summary>
@ -41,10 +42,10 @@ namespace DiIiS_NA.Core.Logging
/// </summary>
/// <param name="name"></param>
/// <returns>A <see cref="Logger"/> instance.</returns>
public static Logger CreateLogger(string name)
public static Logger CreateLogger(string name, [CallerFilePath] string filePath = "")
{
if (!Loggers.ContainsKey(name)) // see if we already have instance for the given name.
Loggers.Add(name, new Logger(name)); // add it to dictionary of loggers.
Loggers.Add(name, new Logger(name, filePath)); // add it to dictionary of loggers.
return Loggers[name]; // return the newly created logger.
}

View File

@ -13,15 +13,17 @@ namespace DiIiS_NA.Core.Logging
public class Logger
{
public string Name { get; protected set; }
public string FilePath { get; protected set; }
/// <summary>
/// A logger base type is used to create a logger instance.
/// E.g. ConsoleTarget, FileTarget, etc.
/// </summary>
/// <param name="name">Logger name</param>
public Logger(string name)
public Logger(string name, string filePath = null)
{
Name = name;
FilePath = filePath;
}
public enum Level

View File

@ -79,7 +79,7 @@ namespace DiIiS_NA.Core.MPQ
public void Init()
{
Logger.Info("Loading Diablo III Assets..");
Logger.Info("Loading Diablo III Assets...");
DictSNOAccolade = Dicts.LoadAccolade();
DictSNOAct = Dicts.LoadActs();
DictSNOActor = Dicts.LoadActors();

View File

@ -30,11 +30,11 @@ namespace DiIiS_NA.Core.MPQ
var mpqFile = MPQStorage.GetMPQFile(file);
if (mpqFile == null)
{
Logger.Error("Cannot find base MPQ file: {0}.", file);
Logger.Fatal("Cannot find base MPQ file: $[white on red]${0}$[/]$.", file);
return;
}
this.BaseMPQFiles.Add(mpqFile);
Logger.Trace("Added MPQ storage: {0}.", file);
Logger.Debug($"Added MPQ storage: $[white underline]${file}$[/]$.");
}
this.PatchPattern = patchPattern;
@ -45,7 +45,7 @@ namespace DiIiS_NA.Core.MPQ
this.Loaded = true;
else
{
Logger.Error("Required patch-chain version {0} is not satified (found version: {1}).", this.RequiredVersion, topMostMPQVersion);
Logger.Error("Required patch-chain version {0} is not satisfied (found version: {1}).", this.RequiredVersion, topMostMPQVersion);
}
}

View File

@ -0,0 +1,18 @@
using DiIiS_NA.Core.Logging;
using DiIiS_NA.D3_GameServer;
using DiIiS_NA.LoginServer.AccountsSystem;
using DiIiS_NA.LoginServer.Battle;
namespace DiIiS_NA.GameServer.CommandManager;
[CommandGroup("game", "Game Commands", Account.UserLevels.Admin, inGameOnly: false)]
public class GameCommand : CommandGroup
{
private static readonly Logger Logger = LogManager.CreateLogger();
[Command("reload-mods", "Reload all game mods file.", Account.UserLevels.Admin, inGameOnly: false)]
public void ReloadMods(string[] @params, BattleClient invokerClient)
{
GameModsConfig.ReloadSettings();
invokerClient.SendServerWhisper("Game mods updated successfully!");
}
}

View File

@ -1,4 +1,5 @@
using DiIiS_NA.GameServer.MessageSystem.Message.Definitions.Hireling;
using System;
using DiIiS_NA.GameServer.MessageSystem.Message.Definitions.Hireling;
using DiIiS_NA.LoginServer.AccountsSystem;
using DiIiS_NA.LoginServer.Battle;

View File

@ -4,6 +4,7 @@ using DiIiS_NA.GameServer.GSSystem.AISystem.Brains;
using DiIiS_NA.GameServer.MessageSystem;
using DiIiS_NA.Core.Logging;
using DiIiS_NA.Core.MPQ.FileFormats;
using DiIiS_NA.D3_GameServer;
namespace DiIiS_NA.GameServer.GSSystem.ActorSystem.Implementations
{
@ -73,9 +74,9 @@ namespace DiIiS_NA.GameServer.GSSystem.ActorSystem.Implementations
//this.Attributes[GameAttribute.Immune_To_Charm] = true;
Attributes[GameAttributes.using_Bossbar] = true;
Attributes[GameAttributes.InBossEncounter] = true;
Attributes[GameAttributes.Hitpoints_Max] *= GameServerConfig.Instance.BossHealthMultiplier;
Attributes[GameAttributes.Damage_Weapon_Min, 0] *= GameServerConfig.Instance.BossDamageMultiplier;
Attributes[GameAttributes.Damage_Weapon_Delta, 0] *= GameServerConfig.Instance.BossDamageMultiplier;
Attributes[GameAttributes.Hitpoints_Max] *= GameModsConfig.Instance.Boss.HealthMultiplier;
Attributes[GameAttributes.Damage_Weapon_Min, 0] *= GameModsConfig.Instance.Boss.DamageMultiplier;
Attributes[GameAttributes.Damage_Weapon_Delta, 0] *= GameModsConfig.Instance.Boss.DamageMultiplier;
Attributes[GameAttributes.Hitpoints_Cur] = Attributes[GameAttributes.Hitpoints_Max_Total];
Attributes[GameAttributes.TeamID] = 10;

View File

@ -6,6 +6,7 @@ using GameBalance = DiIiS_NA.Core.MPQ.FileFormats.GameBalance;
using DiIiS_NA.GameServer.GSSystem.ObjectsSystem;
using DiIiS_NA.Core.Logging;
using DiIiS_NA.Core.MPQ.FileFormats;
using DiIiS_NA.D3_GameServer;
using DiIiS_NA.GameServer.GSSystem.TickerSystem;
using DiIiS_NA.GameServer.MessageSystem;
using DiIiS_NA.GameServer.Core.Types.SNO;
@ -96,26 +97,26 @@ namespace DiIiS_NA.GameServer.GSSystem.ActorSystem
Attributes[GameAttributes.Hitpoints_Max] = (int)((int)monsterLevels.MonsterLevel[monsterLevel].HPMin + DiIiS_NA.Core.Helpers.Math.RandomHelper.Next(0, (int)monsterLevels.MonsterLevel[monsterLevel].HPDelta) * HpMultiplier * World.Game.HpModifier);
Attributes[GameAttributes.Hitpoints_Max_Percent_Bonus_Multiplicative] = ((int)World.Game.ConnectedPlayers.Length + 1) * 1.5f;
Attributes[GameAttributes.Hitpoints_Max_Percent_Bonus_Multiplicative] *= GameServerConfig.Instance.RateMonsterHP;
Attributes[GameAttributes.Hitpoints_Max_Percent_Bonus_Multiplicative] *= GameModsConfig.Instance.Monster.HealthMultiplier;
if (World.Game.ConnectedPlayers.Length > 1)
Attributes[GameAttributes.Hitpoints_Max_Percent_Bonus_Multiplicative] = Attributes[GameAttributes.Hitpoints_Max_Percent_Bonus_Multiplicative];// / 2f;
var hpMax = Attributes[GameAttributes.Hitpoints_Max];
var hpTotal = Attributes[GameAttributes.Hitpoints_Max_Total];
float damageMin = monsterLevels.MonsterLevel[World.Game.MonsterLevel].Dmg * DmgMultiplier;// * 0.5f;
float damageDelta = damageMin;
Attributes[GameAttributes.Damage_Weapon_Min, 0] = damageMin * World.Game.DmgModifier * GameServerConfig.Instance.RateMonsterDMG;
Attributes[GameAttributes.Damage_Weapon_Min, 0] = damageMin * World.Game.DmgModifier * GameModsConfig.Instance.Monster.DamageMultiplier;
Attributes[GameAttributes.Damage_Weapon_Delta, 0] = damageDelta;
if (monsterLevel > 30)
{
Attributes[GameAttributes.Hitpoints_Max_Percent_Bonus_Multiplicative] = Attributes[GameAttributes.Hitpoints_Max_Percent_Bonus_Multiplicative];// * 0.5f;
Attributes[GameAttributes.Damage_Weapon_Min, 0] = damageMin * World.Game.DmgModifier * GameServerConfig.Instance.RateMonsterDMG;// * 0.2f;
Attributes[GameAttributes.Damage_Weapon_Min, 0] = damageMin * World.Game.DmgModifier * GameModsConfig.Instance.Monster.DamageMultiplier;// * 0.2f;
Attributes[GameAttributes.Damage_Weapon_Delta, 0] = damageDelta;
}
if (monsterLevel > 60)
{
Attributes[GameAttributes.Hitpoints_Max_Percent_Bonus_Multiplicative] = Attributes[GameAttributes.Hitpoints_Max_Percent_Bonus_Multiplicative];// * 0.7f;
Attributes[GameAttributes.Damage_Weapon_Min, 0] = damageMin * World.Game.DmgModifier * GameServerConfig.Instance.RateMonsterDMG;// * 0.15f;
Attributes[GameAttributes.Damage_Weapon_Min, 0] = damageMin * World.Game.DmgModifier * GameModsConfig.Instance.Monster.DamageMultiplier;// * 0.15f;
//this.Attributes[GameAttribute.Damage_Weapon_Delta, 0] = DamageDelta * 0.5f;
}

View File

@ -270,7 +270,7 @@ namespace DiIiS_NA.D3_GameServer.GSSystem.GameSystem
if (!Game.Empty)
{
RevealQuestProgress();
if ((Game.CurrentActEnum != ActEnum.OpenWorld && GameServerConfig.Instance.AutoSaveQuests) ||
if ((Game.CurrentActEnum != ActEnum.OpenWorld && GameModsConfig.Instance.Quest.AutoSave) ||
Quests[Game.CurrentQuest].Steps[Game.CurrentStep].Saveable)
SaveQuestProgress(false);
}

View File

@ -18,6 +18,7 @@ using DiIiS_NA.GameServer.Core.Types.TagMap;
using DiIiS_NA.GameServer.MessageSystem;
using DiIiS_NA.LoginServer.Toons;
using DiIiS_NA.Core.Helpers.Math;
using DiIiS_NA.D3_GameServer;
using DiIiS_NA.GameServer.GSSystem.PlayerSystem;
namespace DiIiS_NA.GameServer.GSSystem.ItemsSystem
@ -1358,8 +1359,8 @@ namespace DiIiS_NA.GameServer.GSSystem.ItemsSystem
private static void RandomSetUnidentified(Item item) => item.Unidentified =
FastRandom.Instance.Chance(item.Name.Contains("unique", StringComparison.InvariantCultureIgnoreCase)
|| item.ItemDefinition.Quality is ItemTable.ItemQuality.Legendary or ItemTable.ItemQuality.Special or ItemTable.ItemQuality.Set
? GameServerConfig.Instance.ChanceHighQualityUnidentified
: GameServerConfig.Instance.ChanceNormalUnidentified);
? GameModsConfig.Instance.Items.UnidentifiedDropChances.HighQuality
: GameModsConfig.Instance.Items.UnidentifiedDropChances.NormalQuality);
// Allows cooking a custom item.
public static Item Cook(Player player, string name)

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using DiIiS_NA.D3_GameServer;
namespace DiIiS_NA.GameServer.GSSystem.ItemsSystem
{
@ -613,16 +614,16 @@ namespace DiIiS_NA.GameServer.GSSystem.ItemsSystem
switch (MonsterQuality)
{
case 0: //Normal
return new List<float> { 0.18f * GameServerConfig.Instance.RateChangeDrop };
return new List<float> { 0.18f * GameModsConfig.Instance.Rate.ChangeDrop };
case 1: //Champion
return new List<float> { 1f, 1f, 1f, 1f, 0.75f * GameServerConfig.Instance.RateChangeDrop };
return new List<float> { 1f, 1f, 1f, 1f, 0.75f * GameModsConfig.Instance.Rate.ChangeDrop };
case 2: //Rare (Elite)
case 4: //Unique
return new List<float> { 1f, 1f, 1f, 1f, 1f };
case 7: //Boss
return new List<float> { 1f, 1f, 1f, 1f, 1f, 0.75f * GameServerConfig.Instance.RateChangeDrop, 0.4f * GameServerConfig.Instance.RateChangeDrop };
return new List<float> { 1f, 1f, 1f, 1f, 1f, 0.75f * GameModsConfig.Instance.Rate.ChangeDrop, 0.4f * GameModsConfig.Instance.Rate.ChangeDrop };
default:
return new List<float> { 0.12f * GameServerConfig.Instance.RateChangeDrop };
return new List<float> { 0.12f * GameModsConfig.Instance.Rate.ChangeDrop };
}
}

View File

@ -24,6 +24,7 @@ using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using DiIiS_NA.D3_GameServer;
using Actor = DiIiS_NA.GameServer.GSSystem.ActorSystem.Actor;
namespace DiIiS_NA.GameServer.GSSystem.MapSystem
@ -549,7 +550,7 @@ namespace DiIiS_NA.GameServer.GSSystem.MapSystem
SceneSNO = SceneSNO.Id,
Transform = Transform,
WorldID = World.GlobalID,
MiniMapVisibility = GameServerConfig.Instance.ForceMinimapVisibility
MiniMapVisibility = GameModsConfig.Instance.Minimap.ForceVisibility
};
}

View File

@ -10,6 +10,7 @@ using DiIiS_NA.Core.Helpers.Math;
using DiIiS_NA.Core.Logging;
using DiIiS_NA.Core.MPQ;
using DiIiS_NA.Core.MPQ.FileFormats;
using DiIiS_NA.D3_GameServer;
using DiIiS_NA.D3_GameServer.Core.Types.SNO;
using DiIiS_NA.GameServer.Core.Types.Math;
using DiIiS_NA.GameServer.Core.Types.QuadTrees;
@ -930,7 +931,7 @@ namespace DiIiS_NA.GameServer.GSSystem.MapSystem
/// <param name="position">The position for drop.</param>
public void SpawnGold(Actor source, Player player, int Min = -1)
{
int amount = (int)(LootManager.GetGoldAmount(player.Attributes[GameAttributes.Level]) * Game.GoldModifier * GameServerConfig.Instance.RateMoney);
int amount = (int)(LootManager.GetGoldAmount(player.Attributes[GameAttributes.Level]) * Game.GoldModifier * GameModsConfig.Instance.Rate.Money);
if (Min != -1)
amount += Min;
var item = ItemGenerator.CreateGold(player, amount); // somehow the actual ammount is not shown on ground /raist.

View File

@ -56,6 +56,7 @@ using DiIiS_NA.GameServer.MessageSystem.Message.Definitions.Pet;
using DiIiS_NA.GameServer.MessageSystem.Message.Definitions.Game;
using DiIiS_NA.GameServer.MessageSystem.Message.Definitions.Hireling;
using DiIiS_NA.Core.Helpers.Hash;
using DiIiS_NA.D3_GameServer;
using DiIiS_NA.GameServer.MessageSystem.Message.Definitions.Encounter;
using DiIiS_NA.D3_GameServer.Core.Types.SNO;
using DiIiS_NA.D3_GameServer.GSSystem.ActorSystem.Implementations.Artisans;
@ -2481,6 +2482,21 @@ public class Player : Actor, IMessageConsumer, IUpdateable
public bool SpeedCheckDisabled = false;
public float StrengthMultiplier => ParagonLevel > 0
? GameModsConfig.Instance.Player.Multipliers.Strength.Paragon
: GameModsConfig.Instance.Player.Multipliers.Strength.Normal;
public float DexterityMultiplier => ParagonLevel > 0
? GameModsConfig.Instance.Player.Multipliers.Dexterity.Paragon
: GameModsConfig.Instance.Player.Multipliers.Dexterity.Normal;
public float IntelligenceMultiplier => ParagonLevel > 0
? GameModsConfig.Instance.Player.Multipliers.Intelligence.Paragon
: GameModsConfig.Instance.Player.Multipliers.Intelligence.Normal;
public float VitalityMultiplier => ParagonLevel > 0
? GameModsConfig.Instance.Player.Multipliers.Vitality.Paragon
: GameModsConfig.Instance.Player.Multipliers.Intelligence.Normal;
public static byte[] StringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
@ -2675,8 +2691,9 @@ public class Player : Actor, IMessageConsumer, IUpdateable
Logger.WarnException(e, "questEvent()");
}
}
// Reset resurrection charges on zone change - TODO: do not reset charges on reentering the same zone
Attributes[GameAttributes.Corpse_Resurrection_Charges] = GameServerConfig.Instance.ResurrectionCharges;
// Reset resurrection charges on zone change
// TODO: do not reset charges on reentering the same zone
Attributes[GameAttributes.Corpse_Resurrection_Charges] = GameModsConfig.Instance.Health.ResurrectionCharges;
#if DEBUG
Logger.Warn($"Player Location {Toon.Name}, Scene: {CurrentScene.SceneSNO.Name} SNO: {CurrentScene.SceneSNO.Id} LevelArea: {CurrentScene.Specification.SNOLevelAreas[0]}");
@ -3995,7 +4012,7 @@ public class Player : Actor, IMessageConsumer, IUpdateable
get
{
var baseStrength = 0.0f;
var multiplier = ParagonLevel > 0 ? GameServerConfig.Instance.StrengthParagonMultiplier : GameServerConfig.Instance.StrengthMultiplier;
var multiplier = StrengthMultiplier;
baseStrength = Toon.HeroTable.CoreAttribute == GameBalance.PrimaryAttribute.Strength
? Toon.HeroTable.Strength + (Level - 1) * 3
: Toon.HeroTable.Strength + (Level - 1);
@ -4011,8 +4028,7 @@ public class Player : Actor, IMessageConsumer, IUpdateable
{
get
{
var multiplier = ParagonLevel > 0 ? GameServerConfig.Instance.DexterityParagonMultiplier : GameServerConfig.Instance.DexterityMultiplier;
var multiplier = DexterityMultiplier;
return Toon.HeroTable.CoreAttribute == GameBalance.PrimaryAttribute.Dexterity
? Toon.HeroTable.Dexterity + (Level - 1) * 3 * multiplier
: Toon.HeroTable.Dexterity + (Level - 1) * multiplier;
@ -4022,7 +4038,7 @@ public class Player : Actor, IMessageConsumer, IUpdateable
public float TotalDexterity =>
Attributes[GameAttributes.Dexterity] + Inventory.GetItemBonus(GameAttributes.Dexterity_Item);
public float Vitality => Toon.HeroTable.Vitality + (Level - 1) * 2 * (ParagonLevel > 0 ? GameServerConfig.Instance.VitalityParagonMultiplier : GameServerConfig.Instance.VitalityMultiplier);
public float Vitality => Toon.HeroTable.Vitality + (Level - 1) * 2 * (VitalityMultiplier);
public float TotalVitality =>
Attributes[GameAttributes.Vitality] + Inventory.GetItemBonus(GameAttributes.Vitality_Item);
@ -4031,7 +4047,7 @@ public class Player : Actor, IMessageConsumer, IUpdateable
{
get
{
var multiplier = ParagonLevel > 0 ? GameServerConfig.Instance.IntelligenceParagonMultiplier : GameServerConfig.Instance.IntelligenceMultiplier;
var multiplier = IntelligenceMultiplier;
return Toon.HeroTable.CoreAttribute == GameBalance.PrimaryAttribute.Intelligence
? Toon.HeroTable.Intelligence + (Level - 1) * 3 * multiplier
: Toon.HeroTable.Intelligence + (Level - 1) * multiplier;
@ -4090,7 +4106,7 @@ public class Player : Actor, IMessageConsumer, IUpdateable
},
SkillSlotEverAssigned = 0x0F, //0xB4,
PlaytimeTotal = Toon.TimePlayed,
WaypointFlags = GameServerConfig.Instance.UnlockAllWaypoints ? 0x0000ffff : World.Game.WaypointFlags,
WaypointFlags = GameModsConfig.Instance.Quest.UnlockAllWaypoints ? 0x0000ffff : World.Game.WaypointFlags,
HirelingData = new HirelingSavedData()
{
HirelingInfos = HirelingInfo,
@ -5499,7 +5515,7 @@ public class Player : Actor, IMessageConsumer, IUpdateable
{
if (InGameClient.Game.ActiveNephalemTimer && InGameClient.Game.ActiveNephalemKilledMobs == false)
{
InGameClient.Game.ActiveNephalemProgress += 15f * GameServerConfig.Instance.NephalemRiftProgressMultiplier;
InGameClient.Game.ActiveNephalemProgress += 15f * GameModsConfig.Instance.NephalemRift.ProgressMultiplier;
foreach (var plr in InGameClient.Game.Players.Values)
{
plr.InGameClient.SendMessage(new FloatDataMessage(Opcodes.DunggeonFinderProgressGlyphPickUp)

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using DiIiS_NA.D3_GameServer;
using DiIiS_NA.GameServer.GSSystem.PlayerSystem;
using DiIiS_NA.GameServer.GSSystem.TickerSystem;
using DiIiS_NA.LoginServer;
@ -12,8 +13,8 @@ namespace DiIiS_NA.GameServer.GSSystem.PowerSystem.Implementations.General
public override IEnumerable<TickTimer> Run()
{
if (User is not Player player) yield break;
player.AddPercentageHP(GameServerConfig.Instance.HealthPotionRestorePercentage);
AddBuff(player, player, new CooldownBuff(30211, TickTimer.WaitSeconds(player.World.Game, GameServerConfig.Instance.HealthPotionCooldown)));
player.AddPercentageHP(GameModsConfig.Instance.Health.PotionRestorePercentage);
AddBuff(player, player, new CooldownBuff(30211, TickTimer.WaitSeconds(player.World.Game, GameModsConfig.Instance.Health.PotionCooldown)));
}
}
}

View File

@ -14,6 +14,7 @@ using DiIiS_NA.GameServer.GSSystem.PowerSystem.Implementations;
using DiIiS_NA.GameServer.MessageSystem.Message.Definitions.Effect;
using DiIiS_NA.GameServer.MessageSystem.Message.Definitions.Combat;
using DiIiS_NA.Core.Helpers.Math;
using DiIiS_NA.D3_GameServer;
using DiIiS_NA.LoginServer.Toons;
using DiIiS_NA.GameServer.Core.Types.TagMap;
using DiIiS_NA.GameServer.GSSystem.GeneratorsSystem;
@ -425,7 +426,7 @@ namespace DiIiS_NA.GameServer.GSSystem.PowerSystem.Payloads
{
grantedExp = (int)(grantedExp * rangedPlayer.World.Game.XpModifier);
float tempExp = grantedExp * GameServerConfig.Instance.RateExp;
float tempExp = grantedExp * GameModsConfig.Instance.Rate.Experience;
rangedPlayer.UpdateExp(Math.Max((int)tempExp, 1));
var a = (int)rangedPlayer.Attributes[GameAttributes.Experience_Bonus];
@ -636,13 +637,13 @@ namespace DiIiS_NA.GameServer.GSSystem.PowerSystem.Payloads
Target.World.Game.ActiveNephalemTimer && Target.World.Game.ActiveNephalemKilledMobs == false)
{
Target.World.Game.ActiveNephalemProgress +=
GameServerConfig.Instance.NephalemRiftProgressMultiplier * (Target.Quality + 1);
GameModsConfig.Instance.NephalemRift.ProgressMultiplier * (Target.Quality + 1);
Player master = null;
foreach (var plr3 in Target.World.Game.Players.Values)
{
if (plr3.PlayerIndex == 0)
master = plr3;
if (GameServerConfig.Instance.NephalemRiftAutoFinish && Target.World.Monsters.Count(s => !s.Dead) <= GameServerConfig.Instance.NephalemRiftAutoFinishThreshold) Target.World.Game.ActiveNephalemProgress = 651;
if (GameModsConfig.Instance.NephalemRift.AutoFinish && Target.World.Monsters.Count(s => !s.Dead) <= GameModsConfig.Instance.NephalemRift.AutoFinishThreshold) Target.World.Game.ActiveNephalemProgress = 651;
plr3.InGameClient.SendMessage(new SimpleMessage(Opcodes.KillCounterRefresh));
plr3.InGameClient.SendMessage(new FloatDataMessage(Opcodes.DungeonFinderProgressMessage)
{
@ -711,7 +712,7 @@ namespace DiIiS_NA.GameServer.GSSystem.PowerSystem.Payloads
}
if (Target.Quality > 1 || FastRandom.Instance.Chance(GameServerConfig.Instance.NephalemRiftOrbsChance))
if (Target.Quality > 1 || FastRandom.Instance.Chance(GameModsConfig.Instance.NephalemRift.OrbsChance))
{
//spawn spheres for mining indicator
for (int i = 0; i < Target.Quality + 1; i++)
@ -938,7 +939,7 @@ namespace DiIiS_NA.GameServer.GSSystem.PowerSystem.Payloads
// if seed is less than the drop rate, drop the item
if (seed < rate * (1f
+ lootSpawnPlayer.Attributes[GameAttributes.Magic_Find])
* GameServerConfig.Instance.RateDrop)
* GameModsConfig.Instance.Rate.Drop)
{
//Logger.Debug("rate: {0}", rate);
var lootQuality = Target.World.Game.IsHardcore

View File

@ -0,0 +1,257 @@
using System;
using System.Dynamic;
using System.IO;
using DiIiS_NA;
using DiIiS_NA.Core.Logging;
using DiIiS_NA.GameServer;
using Newtonsoft.Json;
namespace DiIiS_NA.D3_GameServer;
public class RateConfig
{
public float Experience { get; set; } = 1;
public float Money { get; set; } = 1;
public float Drop { get; set; } = 1;
public float ChangeDrop { get; set; } = 1;
}
public class HealthConfig
{
public float PotionRestorePercentage { get; set; } = 60f;
public float PotionCooldown { get; set; } = 30f;
public int ResurrectionCharges { get; set; } = 3;
}
public class HealthDamageMultiplier
{
public float HealthMultiplier { get; set; } = 1;
public float DamageMultiplier { get; set; } = 1;
}
public class QuestConfig
{
public bool AutoSave { get; set; } = false;
public bool UnlockAllWaypoints { get; set; } = false;
}
public class PlayerMultiplierConfig
{
public ParagonConfig<float> Strength { get; set; } = new(1f);
public ParagonConfig<float> Dexterity { get; set; } = new(1f);
public ParagonConfig<float> Intelligence { get; set; } = new(1f);
public ParagonConfig<float> Vitality { get; set; } = new(1f);
}
public class PlayerConfig
{
public PlayerMultiplierConfig Multipliers = new();
}
public class ItemsConfig
{
public UnidentifiedDrop UnidentifiedDropChances { get; set; } = new();
}
public class UnidentifiedDrop
{
public float HighQuality { get; set; } = 30f;
public float NormalQuality { get; set; } = 5f;
}
public class MinimapConfig
{
public bool ForceVisibility { get; set; } = false;
}
public class NephalemRiftConfig
{
public float ProgressMultiplier { get; set; } = 1f;
public bool AutoFinish { get; set; } = false;
public int AutoFinishThreshold { get; set; } = 2;
public float OrbsChance { get; set; } = 0f;
}
public class GameModsConfig
{
public RateConfig Rate { get; set; } = new();
public HealthConfig Health { get; set; } = new();
public HealthDamageMultiplier Monster { get; set; } = new();
public HealthDamageMultiplier Boss { get; set; } = new();
public QuestConfig Quest { get; set; } = new();
public PlayerConfig Player { get; set; } = new();
public ItemsConfig Items { get; set; } = new();
public MinimapConfig Minimap { get; set; } = new();
public NephalemRiftConfig NephalemRift { get; set; } = new();
private static readonly Logger Logger = LogManager.CreateLogger();
public GameModsConfig() {}
static GameModsConfig()
{
CreateInstance();
}
public static void ReloadSettings()
{
CreateInstance(reload: true);
}
private static readonly object InstanceCreationLock = new();
public static GameModsConfig Instance { get; private set; }
private static void CreateInstance(bool reload = false)
{
lock (InstanceCreationLock)
{
if (reload && File.Exists("config.mods.json")) File.Delete("config.mods.json");
if (reload || !File.Exists("config.mods.json"))
{
Instance = CreateDefaultFile();
}
else
{
var content = File.ReadAllText("config.mods.json");
if (content.TryFromJson(out GameModsConfig config, out Exception ex))
{
Logger.Success("Game mods loaded successfully!");
Instance = config;
return;
}
Logger.Fatal("An error occured whilst loading $[white on red]$config.mods.json$[/]$ file. Please verify if the file is correct. Delete the file and try again.");
Program.Shutdown(ex);
}
}
}
private static GameModsConfig CreateDefaultFile()
{
var migration = GameServerConfig.Instance;
Logger.Info("$[blue]$Migrating mods configuration file...$[/]$");
GameModsConfig content = new()
{
#pragma warning disable CS0618
Rate =
{
Experience = migration.RateExp,
Money = migration.RateMoney,
ChangeDrop = migration.RateChangeDrop,
Drop = migration.RateDrop
},
Health =
{
ResurrectionCharges = migration.ResurrectionCharges,
PotionCooldown = migration.HealthPotionCooldown,
PotionRestorePercentage = migration.HealthPotionRestorePercentage
},
Monster =
{
HealthMultiplier = migration.RateMonsterHP,
DamageMultiplier = migration.RateMonsterDMG
},
Boss =
{
HealthMultiplier = migration.BossHealthMultiplier,
DamageMultiplier = migration.BossDamageMultiplier
},
Quest =
{
AutoSave = migration.AutoSaveQuests,
UnlockAllWaypoints = migration.UnlockAllWaypoints
},
Player =
{
Multipliers =
{
Strength = new(migration.StrengthMultiplier, migration.StrengthParagonMultiplier),
Dexterity = new(migration.DexterityMultiplier, migration.DexterityParagonMultiplier),
Intelligence = new(migration.IntelligenceMultiplier, migration.IntelligenceParagonMultiplier),
Vitality = new(migration.VitalityMultiplier, migration.VitalityParagonMultiplier)
}
},
Items =
{
UnidentifiedDropChances =
{
HighQuality = migration.ChanceHighQualityUnidentified,
NormalQuality = migration.ChanceNormalUnidentified
}
},
Minimap =
{
ForceVisibility = migration.ForceMinimapVisibility
},
NephalemRift =
{
AutoFinish = migration.NephalemRiftAutoFinish,
AutoFinishThreshold = migration.NephalemRiftAutoFinishThreshold,
OrbsChance = migration.NephalemRiftAutoFinishThreshold,
ProgressMultiplier = migration.NephalemRiftProgressMultiplier
}
#pragma warning restore CS0618
};
File.WriteAllText("config.mods.json", content.ToJson());
if (Program.Build == 30 && Program.Stage < 6)
{
Logger.Success(
"$[underline]$Migration is complete!$[/]$ - All game mods migrated from $[white]$config.ini$[/]$ to $[white]$config.mods.json$[/]$.");
}
return content;
}
}
public static class JsonExtensions
{
private const bool Indented = true;
public static string ToJson(this object obj, Formatting? formatting = null)
{
return JsonConvert.SerializeObject(obj, formatting ?? (Indented ? Formatting.Indented : Formatting.None));
}
public static bool TryFromJson<T>(this string obj, out T value)
where T: class, new()
{
try
{
value = obj.FromJson<T>();
return true;
}
catch (Exception ex)
{
value = default;
return false;
}
}
public static bool TryFromJson<T>(this string obj, out T value, out Exception exception)
where T: class, new()
{
try
{
value = obj.FromJson<T>();
exception = null;
return true;
}
catch (Exception ex)
{
value = default;
exception = ex;
return false;
}
}
public static T FromJson<T>(this string obj)
where T: class, new()
{
return JsonConvert.DeserializeObject<T>(obj);
}
public static dynamic FromJsonDynamic(this string obj)
{
return obj.FromJson<ExpandoObject>();
}
}

View File

@ -71,6 +71,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Rate of experience gain.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float RateExp
{
get => GetFloat(nameof(RateExp), 1);
@ -80,6 +81,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Rate of gold gain.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float RateMoney
{
get => GetFloat(nameof(RateMoney), 1);
@ -89,12 +91,14 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Rate of item drop.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float RateDrop
{
get => GetFloat(nameof(RateDrop), 1);
set => Set(nameof(RateDrop), value);
}
[Obsolete("Use GameModsConfig instead.")]
public float RateChangeDrop
{
get => GetFloat(nameof(RateChangeDrop), 1);
@ -104,6 +108,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Rate of monster's HP.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float RateMonsterHP
{
get => GetFloat(nameof(RateMonsterHP), 1);
@ -113,6 +118,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Rate of monster's damage.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float RateMonsterDMG
{
get => GetFloat(nameof(RateMonsterDMG), 1);
@ -122,6 +128,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Percentage that a unique, legendary, set or special item created is unidentified
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float ChanceHighQualityUnidentified
{
get => GetFloat(nameof(ChanceHighQualityUnidentified), 30f);
@ -131,6 +138,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Percentage that a normal item created is unidentified
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float ChanceNormalUnidentified
{
get => GetFloat(nameof(ChanceNormalUnidentified), 5f);
@ -140,6 +148,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Resurrection charges on changing worlds
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public int ResurrectionCharges
{
get => GetInt(nameof(ResurrectionCharges), 3);
@ -149,6 +158,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Boss Health Multiplier
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float BossHealthMultiplier
{
get => GetFloat(nameof(BossHealthMultiplier), 6f);
@ -158,6 +168,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Boss Damage Multiplier
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float BossDamageMultiplier
{
get => GetFloat(nameof(BossDamageMultiplier), 3f);
@ -167,6 +178,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Whether to bypass the quest's settings of "Saveable" to TRUE (unless in OpenWorld)
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public bool AutoSaveQuests
{
get => GetBoolean(nameof(AutoSaveQuests), false);
@ -176,6 +188,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Progress gained when killing a monster in Nephalem Rifts
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float NephalemRiftProgressMultiplier
{
get => GetFloat(nameof(NephalemRiftProgressMultiplier), 1f);
@ -185,6 +198,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// How much a health potion heals in percentage
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float HealthPotionRestorePercentage
{
get => GetFloat(nameof(HealthPotionRestorePercentage), 60f);
@ -194,6 +208,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Cooldown (in seconds) to use a health potion again.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float HealthPotionCooldown
{
get => GetFloat(nameof(HealthPotionCooldown), 30f);
@ -203,6 +218,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Unlocks all waypoints in the campaign.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public bool UnlockAllWaypoints
{
get => GetBoolean(nameof(UnlockAllWaypoints), false);
@ -212,6 +228,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Strength multiplier when you're not a paragon.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float StrengthMultiplier
{
get => GetFloat(nameof(StrengthMultiplier), 1f);
@ -221,6 +238,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Strength multiplier when you're a paragon.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float StrengthParagonMultiplier
{
get => GetFloat(nameof(StrengthParagonMultiplier), 1f);
@ -230,6 +248,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Dexterity multiplier when you're not a paragon.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float DexterityMultiplier
{
get => GetFloat(nameof(DexterityMultiplier), 1f);
@ -239,6 +258,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Dexterity multiplier when you're a paragon.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float DexterityParagonMultiplier
{
get => GetFloat(nameof(DexterityParagonMultiplier), 1f);
@ -248,6 +268,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Intelligence multiplier when you're not a paragon.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float IntelligenceMultiplier
{
get => GetFloat(nameof(IntelligenceMultiplier), 1f);
@ -257,6 +278,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Intelligence multiplier when you're a paragon.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float IntelligenceParagonMultiplier
{
get => GetFloat(nameof(IntelligenceParagonMultiplier), 1f);
@ -266,6 +288,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Vitality multiplier when you're not a paragon.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float VitalityMultiplier
{
get => GetFloat(nameof(VitalityMultiplier), 1f);
@ -275,6 +298,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Vitality multiplier when you're a paragon.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float VitalityParagonMultiplier
{
get => GetFloat(nameof(VitalityParagonMultiplier), 1f);
@ -284,6 +308,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Auto finishes nephalem rift when there's <see cref="NephalemRiftAutoFinishThreshold"></see> or less monsters left.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public bool NephalemRiftAutoFinish
{
get => GetBoolean(nameof(NephalemRiftAutoFinish), false);
@ -293,6 +318,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// If <see cref="NephalemRiftAutoFinish"></see> is enabled, this is the threshold.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public int NephalemRiftAutoFinishThreshold
{
get => GetInt(nameof(NephalemRiftAutoFinishThreshold), 2);
@ -302,6 +328,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Nephalem Rifts chance of spawning a orb.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public float NephalemRiftOrbsChance
{
get => GetFloat(nameof(NephalemRiftOrbsChance), 0f);
@ -311,6 +338,7 @@ namespace DiIiS_NA.GameServer
/// <summary>
/// Forces the game to reveal all the map.
/// </summary>
[Obsolete("Use GameModsConfig instead.")]
public bool ForceMinimapVisibility
{
get => GetBoolean(nameof(ForceMinimapVisibility), false);

View File

@ -0,0 +1,16 @@
public class ParagonConfig<T>
{
public T Normal { get; set; }
public T Paragon { get; set; }
public ParagonConfig() {}
public ParagonConfig(T defaultValue) : this(defaultValue, defaultValue)
{
}
public ParagonConfig(T normal, T paragon)
{
Normal = normal;
Paragon = paragon;
}
}

View File

@ -33,6 +33,7 @@ using System.Security.Permissions;
using System.Threading;
using System.Threading.Tasks;
using DiIiS_NA.Core.Extensions;
using DiIiS_NA.D3_GameServer;
using Spectre.Console;
using Environment = System.Environment;
@ -65,11 +66,19 @@ namespace DiIiS_NA
public static string RestServerIp = RestConfig.Instance.IP;
public static string PublicGameServerIp = DiIiS_NA.GameServer.NATConfig.Instance.PublicIP;
public static int Build => 30;
public static int Stage => 2;
public const int Build = 30;
public const int Stage = 3;
public static TypeBuildEnum TypeBuild => TypeBuildEnum.Beta;
private static bool DiabloCoreEnabled = DiIiS_NA.GameServer.GameServerConfig.Instance.CoreActive;
private static readonly CancellationTokenSource CancellationTokenSource = new();
public static readonly CancellationToken Token = CancellationTokenSource.Token;
public static void Cancel() => CancellationTokenSource.Cancel();
public static void CancelAfter(TimeSpan span) => CancellationTokenSource.CancelAfter(span);
public static bool IsCancellationRequested() => CancellationTokenSource.IsCancellationRequested;
public void MergeCancellationWith(params CancellationToken[] tokens) =>
CancellationTokenSource.CreateLinkedTokenSource(tokens);
static void WriteBanner()
{
void RightTextRule(string text, string ruleStyle) => AnsiConsole.Write(new Rule(text).RuleStyle(ruleStyle));
@ -104,7 +113,7 @@ namespace DiIiS_NA
if (!DiabloCoreEnabled)
Logger.Warning("Diablo III Core is $[red]$disabled$[/]$.");
#endif
var mod = GameModsConfig.Instance;
#pragma warning disable CS4014
Task.Run(async () =>
#pragma warning restore CS4014
@ -126,6 +135,9 @@ namespace DiIiS_NA
$"Memory: {totalMemory:0.000} GB | " +
$"CPU Time: {cpuTime.ToSmallText()} | " +
$"Uptime: {uptime.ToSmallText()}";
if (IsCancellationRequested())
text = "SHUTTING DOWN: " + text;
if (SetTitle(text))
await Task.Delay(1000);
else
@ -242,15 +254,15 @@ namespace DiIiS_NA
IChannel boundChannel = await serverBootstrap.BindAsync(loginConfig.Port);
Logger.Info(
"$[bold red3_1]$Tip:$[/]$ graceful shutdown with $[red3_1]$CTRL+C$[/]$ or $[red3_1]$!q[uit]$[/]$ or $[red3_1]$!exit$[/]$.");
Logger.Info("$[bold red3_1]$" +
"Tip:$[/]$ SNO breakdown with $[red3_1]$!sno$[/]$ $[red3_1]$<fullSnoBreakdown(true:false)>$[/]$.");
while (true)
Logger.Info("$[bold deeppink4]$Gracefully$[/]$ shutdown with $[red3_1]$CTRL+C$[/]$ or $[deeppink4]$!q[uit]$[/]$.");
while (!IsCancellationRequested())
{
var line = Console.ReadLine();
if (line is null or "!q" or "!quit" or "!exit")
{
break;
}
if (line is "!cls" or "!clear" or "cls" or "clear")
{
AnsiConsole.Clear();
@ -272,9 +284,11 @@ namespace DiIiS_NA
if (PlayerManager.OnlinePlayers.Count > 0)
{
Logger.Success("Gracefully shutting down...");
Logger.Info(
$"Server is shutting down in 1 minute, $[blue]${PlayerManager.OnlinePlayers.Count} players$[/]$ are still online.");
PlayerManager.SendWhisper("Server is shutting down in 1 minute.");
await Task.Delay(TimeSpan.FromMinutes(1));
}
@ -292,16 +306,22 @@ namespace DiIiS_NA
}
}
private static void Shutdown(Exception exception = null)
{
// if (!IsTargetEnabled("ansi"))
private static bool _shuttingDown = false;
public static void Shutdown(Exception exception = null)
{
if (_shuttingDown) return;
_shuttingDown = true;
if (!IsCancellationRequested())
Cancel();
AnsiTarget.StopIfRunning(IsTargetEnabled("ansi"));
if (exception != null)
{
AnsiConsole.WriteLine("An unhandled exception occured at initialization. Please report this to the developers.");
AnsiConsole.WriteLine(
"An unhandled exception occured at initialization. Please report this to the developers.");
AnsiConsole.WriteException(exception);
}
AnsiConsole.Progress().Start(ctx =>
{
var task = ctx.AddTask("[darkred_1]Shutting down[/] [white]in[/] [red underline]10 seconds[/]");
@ -312,15 +332,13 @@ namespace DiIiS_NA
{
task.Increment(1);
Thread.Sleep(100);
}
}
task.Description = $"[darkred_1]Shutting down[/]";
task.Description = $"[darkred_1]Shutting down now.[/]";
task.StopTask();
});
}
Environment.Exit(exception is null ? 0 : -1);
}