Skip to content

Commit b952417

Browse files
committed
Component Refactor & Optimization
1 parent 981c22a commit b952417

14 files changed

+615
-696
lines changed
Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,87 @@
11
using System.Diagnostics;
22
using Darkages.Network.Server;
3+
using Darkages.Sprites;
34

45
namespace Darkages.Network.Components;
56

67
public class BankInterestComponent(WorldServer server) : WorldServerComponent(server)
78
{
8-
private const int ComponentSpeed = 1800000;
9+
private const int ComponentSpeed = 1_800_000;
910

1011
protected internal override async Task Update()
1112
{
12-
var componentStopWatch = new Stopwatch();
13-
componentStopWatch.Start();
14-
var variableGameSpeed = ComponentSpeed;
13+
var sw = Stopwatch.StartNew();
14+
var target = ComponentSpeed;
1515

1616
while (ServerSetup.Instance.Running)
1717
{
18-
if (componentStopWatch.Elapsed.TotalMilliseconds < variableGameSpeed)
18+
var elapsed = sw.Elapsed.TotalMilliseconds;
19+
if (elapsed < target)
1920
{
20-
await Task.Delay(5000);
21+
var remaining = (int)(target - elapsed);
22+
23+
// This is a long-interval task; coarse sleep is fine.
24+
if (remaining > 0)
25+
await Task.Delay(Math.Min(remaining, 60_000));
26+
2127
continue;
2228
}
2329

2430
AccrueInterest();
25-
var awaiter = (int)(ComponentSpeed - componentStopWatch.Elapsed.TotalMilliseconds);
2631

27-
if (awaiter < 0)
28-
{
29-
variableGameSpeed = ComponentSpeed + awaiter;
30-
componentStopWatch.Restart();
31-
continue;
32-
}
32+
var postElapsed = sw.Elapsed.TotalMilliseconds;
33+
var overshoot = postElapsed - ComponentSpeed;
3334

34-
await Task.Delay(TimeSpan.FromMilliseconds(awaiter));
35-
variableGameSpeed = ComponentSpeed;
36-
componentStopWatch.Restart();
35+
target = (overshoot > 0 && overshoot < ComponentSpeed)
36+
? ComponentSpeed - (int)overshoot
37+
: ComponentSpeed;
38+
39+
sw.Restart();
3740
}
3841
}
3942

40-
private static void AccrueInterest()
43+
private void AccrueInterest()
4144
{
42-
if (!ServerSetup.Instance.Running || !Server.Aislings.Any()) return;
43-
44-
Parallel.ForEach(Server.Aislings, (player) =>
45+
foreach (var player in Server.Aislings)
4546
{
46-
if (player?.Client == null) return;
47-
if (!player.LoggedIn) return;
48-
if (player.BankManager == null) return;
49-
if (player.BankedGold <= 0)
47+
if (player?.Client == null) continue;
48+
if (!player.LoggedIn) continue;
49+
50+
try
5051
{
51-
player.BankedGold = 0;
52-
return;
52+
ApplyInterest(player);
5353
}
54-
55-
var interest = (uint)Math.Round(player.BankedGold * 0.00333);
56-
if (interest >= 1000000)
57-
interest = 1000000;
58-
if (player.BankedGold + interest >= ulong.MaxValue)
54+
catch (Exception ex)
5955
{
60-
player.Client.SendServerMessage(ServerMessageType.ActiveMessage, $"{{=uBank Cap - No interest gained -");
61-
return;
56+
SentrySdk.CaptureException(ex);
6257
}
58+
}
59+
}
60+
61+
private static void ApplyInterest(Aisling player)
62+
{
63+
if (player.BankedGold <= 0)
64+
{
65+
player.BankedGold = 0;
66+
return;
67+
}
68+
69+
// ~0.333% interest
70+
var interest = (uint)Math.Round(player.BankedGold * 0.00333);
71+
72+
// Cap interest per tick
73+
const uint maxInterest = 1_000_000;
74+
if (interest >= maxInterest)
75+
interest = maxInterest;
76+
77+
// Prevent overflow on BankedGold + interest
78+
if (player.BankedGold >= ulong.MaxValue - interest)
79+
{
80+
player.Client.SendServerMessage(ServerMessageType.ActiveMessage, "{=uBank Cap - No interest gained -");
81+
return;
82+
}
6383

64-
player.Client.SendServerMessage(ServerMessageType.ActiveMessage, $"{{=uInterest Accrued: {interest} coins");
65-
player.BankedGold += interest;
66-
});
84+
player.BankedGold += interest;
85+
player.Client.SendServerMessage(ServerMessageType.ActiveMessage, $"{{=uInterest Accrued: {interest} coins");
6786
}
6887
}
Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,66 @@
11
using System.Diagnostics;
2-
32
using Darkages.Network.Server;
43

54
namespace Darkages.Network.Components;
65

76
public class ClientCreationLimit(WorldServer server) : WorldServerComponent(server)
87
{
9-
private const int ComponentSpeed = 3600000;
8+
private const int ComponentSpeed = 3_600_000;
109

1110
protected internal override async Task Update()
1211
{
13-
var componentStopWatch = new Stopwatch();
14-
componentStopWatch.Start();
15-
var variableGameSpeed = ComponentSpeed;
12+
var sw = Stopwatch.StartNew();
13+
var target = ComponentSpeed;
1614

1715
while (ServerSetup.Instance.Running)
1816
{
19-
if (componentStopWatch.Elapsed.TotalMilliseconds < variableGameSpeed)
17+
var elapsed = sw.Elapsed.TotalMilliseconds;
18+
19+
if (elapsed < target)
2020
{
21-
await Task.Delay(60000);
21+
var remaining = (int)(target - elapsed);
22+
if (remaining > 0)
23+
await Task.Delay(Math.Min(remaining, 60_000));
2224
continue;
2325
}
2426

25-
RemoveLimit();
26-
var awaiter = (int)(ComponentSpeed - componentStopWatch.Elapsed.TotalMilliseconds);
27+
CleanupCreationLimits();
2728

28-
if (awaiter < 0)
29-
{
30-
variableGameSpeed = ComponentSpeed + awaiter;
31-
componentStopWatch.Restart();
32-
continue;
33-
}
29+
var postElapsed = sw.Elapsed.TotalMilliseconds;
30+
var overshoot = postElapsed - ComponentSpeed;
3431

35-
await Task.Delay(TimeSpan.FromMilliseconds(awaiter));
36-
variableGameSpeed = ComponentSpeed;
37-
componentStopWatch.Restart();
32+
target = (overshoot > 0 && overshoot < ComponentSpeed)
33+
? ComponentSpeed - (int)overshoot
34+
: ComponentSpeed;
35+
36+
sw.Restart();
3837
}
3938
}
4039

41-
private static void RemoveLimit()
40+
private static void CleanupCreationLimits()
4241
{
4342
if (!ServerSetup.Instance.Running) return;
4443

45-
foreach (var (ip, creationCount) in ServerSetup.Instance.GlobalCreationCount)
44+
var dict = ServerSetup.Instance.GlobalCreationCount;
45+
46+
foreach (var kvp in dict)
4647
{
47-
if (creationCount > 0)
48+
var ip = kvp.Key;
49+
var count = kvp.Value;
50+
51+
if (count == 0)
4852
{
49-
var countMod = creationCount;
50-
countMod--;
51-
ServerSetup.Instance.GlobalCreationCount.TryUpdate(ip, countMod, creationCount);
53+
dict.TryRemove(ip, out _);
54+
continue;
5255
}
5356

54-
if (creationCount == 0)
55-
ServerSetup.Instance.GlobalCreationCount.TryRemove(ip, out _);
57+
// Compute the decremented value safely
58+
byte newCount = (byte)(count - 1);
59+
dict.TryUpdate(ip, newCount, count);
60+
61+
// If we reached zero after decrement, remove the entry
62+
if (newCount == 0)
63+
dict.TryRemove(ip, out _);
5664
}
5765
}
5866
}

Zolian.Server.Base/Network/Components/DayLightComponent.cs

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,84 @@
11
using System.Diagnostics;
2-
32
using Darkages.Network.Server;
43

54
namespace Darkages.Network.Components;
65

76
public class DayLightComponent(WorldServer server) : WorldServerComponent(server)
87
{
9-
private const int ComponentSpeed = 15000;
10-
private static readonly SortedDictionary<int, (byte start, byte end)> Routine = new()
8+
private const int ComponentSpeed = 15_000;
9+
10+
private static readonly (byte start, byte end)[] Routine =
1111
{
12-
{0, (0, 0)},
13-
{1, (0, 1)},
14-
{2, (1, 2)},
15-
{3, (2, 3)},
16-
{4, (3, 4)},
17-
{5, (4, 5)},
18-
{6, (5, 4)},
19-
{7, (4, 3)},
20-
{8, (3, 2)},
21-
{9, (2, 1)},
22-
{10, (1, 0)},
23-
{11, (0, 0)}
12+
(0, 0),
13+
(0, 1),
14+
(1, 2),
15+
(2, 3),
16+
(3, 4),
17+
(4, 5),
18+
(5, 4),
19+
(4, 3),
20+
(3, 2),
21+
(2, 1),
22+
(1, 0),
23+
(0, 0)
2424
};
2525

2626
protected internal override async Task Update()
2727
{
28-
var componentStopWatch = new Stopwatch();
29-
componentStopWatch.Start();
30-
var variableGameSpeed = ComponentSpeed;
28+
var sw = Stopwatch.StartNew();
29+
var target = ComponentSpeed;
3130

3231
while (ServerSetup.Instance.Running)
3332
{
34-
if (componentStopWatch.Elapsed.TotalMilliseconds < variableGameSpeed)
33+
var elapsed = sw.Elapsed.TotalMilliseconds;
34+
35+
if (elapsed < target)
3536
{
36-
await Task.Delay(500);
37+
var remaining = (int)(target - elapsed);
38+
if (remaining > 0)
39+
await Task.Delay(Math.Min(remaining, 500));
3740
continue;
3841
}
3942

4043
UpdateDayLight();
41-
var awaiter = (int)(ComponentSpeed - componentStopWatch.Elapsed.TotalMilliseconds);
4244

43-
if (awaiter < 0)
44-
{
45-
variableGameSpeed = ComponentSpeed + awaiter;
46-
componentStopWatch.Restart();
47-
continue;
48-
}
45+
var post = sw.Elapsed.TotalMilliseconds;
46+
var overshoot = post - ComponentSpeed;
47+
48+
target = (overshoot > 0 && overshoot < ComponentSpeed)
49+
? ComponentSpeed - (int)overshoot
50+
: ComponentSpeed;
4951

50-
await Task.Delay(TimeSpan.FromMilliseconds(awaiter));
51-
variableGameSpeed = ComponentSpeed;
52-
componentStopWatch.Restart();
52+
sw.Restart();
5353
}
5454
}
5555

5656
private static void UpdateDayLight()
5757
{
58+
// Wrap phase 0–11
5859
if (ServerSetup.Instance.LightPhase >= 12)
5960
ServerSetup.Instance.LightPhase = 0;
6061

61-
var (start, end) = Routine.First(x => ServerSetup.Instance.LightPhase == x.Key).Value;
62+
var (start, end) = Routine[ServerSetup.Instance.LightPhase];
6263

64+
// Only move to the end state if we're still at "start"
6365
if (ServerSetup.Instance.LightLevel == start)
6466
ServerSetup.Instance.LightLevel = end;
6567

66-
Parallel.ForEach(Server.Aislings, (player) =>
68+
foreach (var player in Server.Aislings)
6769
{
68-
if (player?.Client == null) return;
69-
if (!player.LoggedIn) return;
70-
player.Client.SendLightLevel((LightLevel)ServerSetup.Instance.LightLevel);
71-
});
70+
if (player?.Client == null) continue;
71+
if (!player.LoggedIn) continue;
72+
73+
try
74+
{
75+
player.Client.SendLightLevel((LightLevel)ServerSetup.Instance.LightLevel);
76+
}
77+
catch (Exception ex)
78+
{
79+
SentrySdk.CaptureException(ex);
80+
}
81+
}
7282

7383
ServerSetup.Instance.LightPhase++;
7484
}

0 commit comments

Comments
 (0)