Skip to content

Commit fea6309

Browse files
committed
Refactor skill/spell script storage to single immutable binding
- Replaced ConcurrentDictionary-based skill/spell script mapping with a single script binding - Enforced invariant that each skill/spell may only have one associated script - Removed unnecessary concurrency and lookup overhead - Improved clarity and domain intent of skill/spell-to-script association
1 parent 2c3aa36 commit fea6309

File tree

8 files changed

+54
-45
lines changed

8 files changed

+54
-45
lines changed

Zolian.Server.Base/GameScripts/Items/Consumable.cs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -540,16 +540,15 @@ public override void OnUse(Sprite sprite, byte slot)
540540

541541
foreach (var skill in skills)
542542
{
543-
if (skill?.Scripts is null || skill.Scripts.IsEmpty) continue;
543+
if (skill?.ScriptRecord is null) continue;
544544
if (skill.Template.Cooldown == 0)
545545
if (!skill.CanUseZeroLineAbility) continue;
546546
if (!skill.CanUse()) continue;
547547
if (skill.InUse) continue;
548548

549549
skill.InUse = true;
550550

551-
var script = skill.Scripts.Values.FirstOrDefault();
552-
script?.OnUse(aisling);
551+
skill.ScriptRecord.Script?.OnUse(aisling);
553552
skill.CurrentCooldown = skill.Template.Cooldown;
554553
aisling.Client.SendCooldown(true, skill.Slot, skill.CurrentCooldown);
555554
skill.LastUsedSkill = DateTime.UtcNow;
@@ -612,16 +611,15 @@ public override void OnUse(Sprite sprite, byte slot)
612611

613612
foreach (var skill in skills)
614613
{
615-
if (skill?.Scripts is null || skill.Scripts.IsEmpty) continue;
614+
if (skill?.ScriptRecord is null) continue;
616615
if (skill.Template.Cooldown == 0)
617616
if (!skill.CanUseZeroLineAbility) continue;
618617
if (!skill.CanUse()) continue;
619618
if (skill.InUse) continue;
620619

621620
skill.InUse = true;
622621

623-
var script = skill.Scripts.Values.FirstOrDefault();
624-
script?.OnUse(aisling);
622+
skill.ScriptRecord.Script?.OnUse(aisling);
625623
skill.CurrentCooldown = skill.Template.Cooldown;
626624
aisling.Client.SendCooldown(true, skill.Slot, skill.CurrentCooldown);
627625
skill.LastUsedSkill = DateTime.UtcNow;
@@ -702,16 +700,15 @@ public override void OnUse(Sprite sprite, byte slot)
702700

703701
foreach (var skill in skills)
704702
{
705-
if (skill?.Scripts is null || skill.Scripts.IsEmpty) continue;
703+
if (skill?.ScriptRecord is null) continue;
706704
if (skill.Template.Cooldown == 0)
707705
if (!skill.CanUseZeroLineAbility) continue;
708706
if (!skill.CanUse()) continue;
709707
if (skill.InUse) continue;
710708

711709
skill.InUse = true;
712710

713-
var script = skill.Scripts.Values.FirstOrDefault();
714-
script?.OnUse(aisling);
711+
skill.ScriptRecord.Script?.OnUse(aisling);
715712
skill.CurrentCooldown = skill.Template.Cooldown;
716713
aisling.Client.SendCooldown(true, skill.Slot, skill.CurrentCooldown);
717714
skill.LastUsedSkill = DateTime.UtcNow;
@@ -819,16 +816,15 @@ public override void OnUse(Sprite sprite, byte slot)
819816

820817
foreach (var skill in skills)
821818
{
822-
if (skill?.Scripts is null || skill.Scripts.IsEmpty) continue;
819+
if (skill?.ScriptRecord is null) continue;
823820
if (skill.Template.Cooldown == 0)
824821
if (!skill.CanUseZeroLineAbility) continue;
825822
if (!skill.CanUse()) continue;
826823
if (skill.InUse) continue;
827824

828825
skill.InUse = true;
829826

830-
var script = skill.Scripts.Values.FirstOrDefault();
831-
script?.OnUse(aisling);
827+
skill.ScriptRecord.Script?.OnUse(aisling);
832828
skill.CurrentCooldown = skill.Template.Cooldown;
833829
aisling.Client.SendCooldown(true, skill.Slot, skill.CurrentCooldown);
834830
skill.LastUsedSkill = DateTime.UtcNow;

Zolian.Server.Base/Network/Server/WorldServer.cs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1405,17 +1405,15 @@ private static void AssailRoutine(IWorldClient lpClient)
14051405
{
14061406
// Skill exists check
14071407
if (skill?.Template == null) continue;
1408-
if (skill.Scripts == null) continue;
1409-
var script = skill.Scripts.Values.FirstOrDefault();
1410-
if (script == null) continue;
1408+
if (skill.ScriptRecord is null) continue;
14111409

14121410
// Skill can be used check
14131411
if (!skill.Ready && skill.InUse) continue;
14141412

14151413
skill.InUse = true;
14161414

14171415
// Skill & Weapon script
1418-
script.OnUse(lpClient.Aisling);
1416+
skill.ScriptRecord.Script?.OnUse(lpClient.Aisling);
14191417
ExecuteWeaponScriptsOnAssail(lpClient, skill);
14201418

14211419
skill.LastUsedSkill = DateTime.UtcNow;
@@ -2507,15 +2505,13 @@ static ValueTask InnerOnUseSkill(IWorldClient localClient, SkillUseArgs localArg
25072505
return default;
25082506
}
25092507

2510-
if (skill.Template == null || skill.Scripts == null) return default;
2508+
if (skill.Template is null || skill.ScriptRecord is null) return default;
25112509

25122510
if (skill.Template.Cooldown == 0)
25132511
if (!skill.CanUseZeroLineAbility) return default;
25142512
if (!skill.CanUse()) return default;
25152513
if (skill.InUse) return default;
25162514

2517-
var script = skill.Scripts.Values.FirstOrDefault();
2518-
25192515
// Execute Ability or Assail Logic
25202516
skill.InUse = true;
25212517

@@ -2526,7 +2522,7 @@ static ValueTask InnerOnUseSkill(IWorldClient localClient, SkillUseArgs localArg
25262522

25272523
if (skill.Template.SkillType != SkillScope.Assail)
25282524
{
2529-
script?.OnUse(localClient.Aisling);
2525+
skill.ScriptRecord.Script?.OnUse(localClient.Aisling);
25302526

25312527
skill.LastUsedSkill = DateTime.UtcNow;
25322528
skill.CurrentCooldown = skill.Template.Cooldown;
@@ -2541,7 +2537,7 @@ static ValueTask InnerOnUseSkill(IWorldClient localClient, SkillUseArgs localArg
25412537
return default;
25422538
}
25432539

2544-
script?.OnUse(localClient.Aisling);
2540+
skill.ScriptRecord.Script?.OnUse(localClient.Aisling);
25452541

25462542
skill.LastUsedSkill = DateTime.UtcNow;
25472543
skill.CurrentCooldown = skill.Template.Cooldown;
@@ -3270,6 +3266,7 @@ private async void OnDisconnect(object sender, EventArgs e)
32703266
private void DisplayRecentClientPacketLogs(IWorldClient client)
32713267
{
32723268
var logs = ClientPacketLogger.GetRecentLogs(client.RemoteIp).ToList();
3269+
if (logs.IsNullOrEmpty()) return;
32733270

32743271
foreach (var log in logs)
32753272
ServerSetup.PacketLogger(log);
@@ -3278,6 +3275,7 @@ private void DisplayRecentClientPacketLogs(IWorldClient client)
32783275
private void DisplayRecentServerPacketLogs(IWorldClient client)
32793276
{
32803277
var logs = ServerPacketLogger.GetRecentLogs(client.RemoteIp).ToList();
3278+
if (logs.IsNullOrEmpty()) return;
32813279

32823280
foreach (var log in logs)
32833281
ServerSetup.PacketLogger(log);

Zolian.Server.Base/ScriptingBase/ScriptManager.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,31 @@
11
using System.Collections.Concurrent;
2+
using System.Collections.Frozen;
23
using System.Reflection;
34

45
namespace Darkages.ScriptingBase;
56

67
public static class ScriptManager
78
{
8-
private static readonly Dictionary<string, Type> Scripts = [];
9+
private static readonly FrozenDictionary<string, Type> Scripts;
910

1011
static ScriptManager()
1112
{
1213
var assembly = Assembly.GetExecutingAssembly();
14+
var temp = new Dictionary<string, Type>(StringComparer.Ordinal);
1315

1416
foreach (var type in assembly.GetTypes())
1517
{
16-
var attribute = type.GetCustomAttributes(typeof(ScriptAttribute), false).Cast<ScriptAttribute>().FirstOrDefault();
18+
var attribute = type.GetCustomAttributes(typeof(ScriptAttribute), inherit: false)
19+
.Cast<ScriptAttribute>()
20+
.FirstOrDefault();
1721

18-
if (attribute == null) continue;
22+
if (attribute is null)
23+
continue;
1924

20-
Scripts.Add(attribute.Name, type);
25+
temp.Add(attribute.Name, type);
2126
}
27+
28+
Scripts = temp.ToFrozenDictionary(StringComparer.Ordinal);
2229
}
2330

2431
/// <summary>

Zolian.Server.Base/Sprites/Entity/Aisling.cs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -303,11 +303,10 @@ public void CastSpell(Spell spell, CastInfo info)
303303
{
304304
if (info != null)
305305
{
306-
if (!string.IsNullOrEmpty(info.Data))
307-
foreach (var script in spell.Scripts.Values)
308-
script.Arguments = info.Data;
306+
if (spell.ScriptRecord is not null && !string.IsNullOrEmpty(info.Data))
307+
spell.ScriptRecord.Script?.Arguments = info.Data;
309308

310-
if (spell.Scripts != null)
309+
if (spell.ScriptRecord != null)
311310
{
312311
if (info.Target != 0)
313312
{
@@ -334,9 +333,9 @@ public void CastSpell(Spell spell, CastInfo info)
334333
spell.InUse = true;
335334

336335
var target = ObjectManager.GetObject(Map, i => i.Serial == info.Target, ObjectManager.Get.Damageable) ?? this;
337-
SendArmorBodyAnimationNearby(CastBodyAnimation, spell.Template.Sound);
338-
var script = spell.Scripts.Values.FirstOrDefault();
339-
script?.OnUse(this, target);
336+
SendArmorBodyAnimationNearby(CastBodyAnimation, null);
337+
SendTargetedClientMethod(PlayerScope.NearbyAislings, c => c.SendSound(spell.Template.Sound, false));
338+
spell.ScriptRecord.Script?.OnUse(this, target);
340339
spell.CurrentCooldown = spell.Template.Cooldown > 0 ? spell.Template.Cooldown : 0;
341340
Client.SendCooldown(false, spell.Slot, spell.CurrentCooldown);
342341
spell.LastUsedSpell = DateTime.UtcNow;
@@ -367,7 +366,7 @@ public void CastSpell(Spell spell, CastInfo info)
367366
/// <summary>
368367
/// Displays player's body animation based on Armor
369368
/// </summary>
370-
private Aisling SendArmorBodyAnimationNearby(byte animation, byte sound, byte actionSpeed = 30)
369+
private Aisling SendArmorBodyAnimationNearby(byte animation, byte? sound = null, byte actionSpeed = 30)
371370
{
372371
SendTargetedClientMethod(PlayerScope.NearbyAislings, c => c.SendBodyAnimation(Serial, (BodyAnimation)animation, actionSpeed, sound));
373372
return this;
@@ -683,7 +682,7 @@ public async Task AutoRoutine()
683682
var skills = SkillBook.Skills.Values
684683
.Where(s => s != null && s.Level < s.Template.MaxLevel && s.Template.MaxLevel != 1)
685684
.Where(skill => skill.CanUse())
686-
.Where(skill => skill.Scripts is not null && !skill.Scripts.IsEmpty)
685+
.Where(skill => skill.ScriptRecord is not null)
687686
.ToList();
688687

689688
var tasks = new List<Task>(skills.Count);
@@ -711,7 +710,7 @@ private Task ProcessSkill(Skill skill, SemaphoreSlim semaphore)
711710
{
712711
skill.InUse = true;
713712

714-
var script = skill.Scripts.Values.FirstOrDefault();
713+
var script = skill.ScriptRecord.Script;
715714
if (script == null) return Task.CompletedTask;
716715

717716
script.OnUse(this);
@@ -758,7 +757,7 @@ public async Task AutoCastRoutine()
758757

759758
// Only select usable
760759
var validSpells = spells
761-
.Where(spell => spell != null && spell.CanUse() && spell.Scripts is not null && !spell.Scripts.IsEmpty)
760+
.Where(spell => spell != null && spell.CanUse() && spell.ScriptRecord is not null)
762761
.ToList();
763762

764763
var tasks = new List<Task>(validSpells.Count);
@@ -800,7 +799,7 @@ private Task ProcessSpellAsync(Spell spell, SemaphoreSlim semaphore)
800799
try
801800
{
802801
spell.InUse = true;
803-
var script = spell.Scripts.Values.FirstOrDefault();
802+
var script = spell.ScriptRecord.Script;
804803
if (script == null) return Task.CompletedTask;
805804

806805
script.OnUse(this, spell.Template.TargetType == SpellTemplate.SpellUseType.NoTarget ? this : Target);

Zolian.Server.Base/Sprites/Entity/Monster.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,7 @@ public void Walk()
514514
if (CantMove) return;
515515
if (ThrownBack) return;
516516

517-
if (Target != null)
517+
while (Target != null)
518518
{
519519
if (Target is not Aisling aisling)
520520
{

Zolian.Server.Base/Sprites/Entity/Mundane.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
using Darkages.Templates;
1010
using Darkages.Types;
1111

12-
using Microsoft.Extensions.Logging;
13-
1412
using ServiceStack;
1513

1614
namespace Darkages.Sprites.Entity;

Zolian.Server.Base/Types/Skill.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@
1010
using Microsoft.Data.SqlClient;
1111
using Microsoft.Extensions.Logging;
1212

13-
using System.Collections.Concurrent;
1413
using System.ComponentModel;
1514
using System.Data;
1615

1716
namespace Darkages.Types;
1817

18+
public sealed record SkillScriptRecord(string Name, SkillScript Script);
19+
1920
public class Skill
2021
{
2122
public byte Icon { get; init; }
@@ -29,7 +30,7 @@ public class Skill
2930
public bool Ready => CurrentCooldown <= 0;
3031
public bool Refreshed { get; set; }
3132

32-
public ConcurrentDictionary<string, SkillScript> Scripts { get; set; }
33+
public SkillScriptRecord ScriptRecord { get; set; }
3334

3435
public byte Slot { get; set; }
3536
public SkillTemplate Template { get; set; }
@@ -50,7 +51,11 @@ public bool CanUseZeroLineAbility
5051

5152
public bool CanUse() => Ready;
5253

53-
public static void AttachScript(Skill skill) => skill.Scripts = ScriptManager.Load<SkillScript>(skill.Template.ScriptName, skill);
54+
public static void AttachScript(Skill skill)
55+
{
56+
if (ScriptManager.TryCreate<SkillScript>(skill.Template.ScriptName, out var scriptInstance, skill))
57+
skill.ScriptRecord = new SkillScriptRecord(skill.Template.ScriptName, scriptInstance);
58+
}
5459

5560
public static Skill Create(int slot, SkillTemplate skillTemplate)
5661
{

Zolian.Server.Base/Types/Spell.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
namespace Darkages.Types;
1818

19+
public sealed record SpellScriptRecord(string Name, SpellScript Script);
20+
1921
public class Spell
2022
{
2123
public byte Icon { get; init; }
@@ -29,7 +31,7 @@ public class Spell
2931
private bool Ready => CurrentCooldown <= 0;
3032
public bool Refreshed { get; set; }
3133

32-
public ConcurrentDictionary<string, SpellScript> Scripts { get; set; }
34+
public SpellScriptRecord ScriptRecord { get; set; }
3335

3436
public byte Slot { get; set; }
3537
public SpellTemplate Template { get; set; }
@@ -38,7 +40,11 @@ public class Spell
3840

3941
public bool CanUse() => Ready;
4042

41-
public static void AttachScript(Spell spell) => spell.Scripts = ScriptManager.Load<SpellScript>(spell.Template.ScriptName, spell);
43+
public static void AttachScript(Spell spell)
44+
{
45+
if (ScriptManager.TryCreate<SpellScript>(spell.Template.ScriptName, out var scriptInstance, spell))
46+
spell.ScriptRecord = new SpellScriptRecord(spell.Template.ScriptName, scriptInstance);
47+
}
4248

4349
public static Spell Create(int slot, SpellTemplate spellTemplate)
4450
{

0 commit comments

Comments
 (0)