Skip to content

Commit 981c22a

Browse files
committed
Component Refactor
- Ping - Player Regen - Player Save - Player Cooldown - Player Status / Threat
1 parent 0221c18 commit 981c22a

File tree

5 files changed

+198
-178
lines changed

5 files changed

+198
-178
lines changed

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

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,38 +11,45 @@ public class PingComponent(WorldServer server) : WorldServerComponent(server)
1111

1212
protected internal override async Task Update()
1313
{
14-
var componentStopWatch = new Stopwatch();
15-
componentStopWatch.Start();
16-
var variableGameSpeed = ComponentSpeed;
14+
var sw = Stopwatch.StartNew();
15+
var target = ComponentSpeed;
1716

1817
while (ServerSetup.Instance.Running)
1918
{
20-
if (componentStopWatch.Elapsed.TotalMilliseconds < variableGameSpeed)
19+
var elapsed = sw.Elapsed.TotalMilliseconds;
20+
if (elapsed < target)
2121
{
22-
await Task.Delay(50);
22+
var remaining = (int)(target - elapsed);
23+
24+
// Clamp to avoid super tiny delays
25+
if (remaining > 0)
26+
await Task.Delay(Math.Min(remaining, 50));
2327
continue;
2428
}
2529

26-
var players = Server.Aislings;
27-
players.AsParallel()
28-
.WithDegreeOfParallelism(Environment.ProcessorCount)
29-
.ForAll(Ping);
30+
foreach (var player in Server.Aislings)
31+
Ping(player);
3032

31-
var awaiter = (int)(ComponentSpeed - componentStopWatch.Elapsed.TotalMilliseconds);
33+
var postElapsed = sw.Elapsed.TotalMilliseconds;
34+
var overshoot = postElapsed - ComponentSpeed;
3235

33-
if (awaiter < 0)
36+
if (overshoot > 0)
3437
{
35-
variableGameSpeed = ComponentSpeed + awaiter;
36-
componentStopWatch.Restart();
37-
continue;
38+
// Compensate next tick by firing slightly earlier
39+
target = ComponentSpeed - (int)overshoot;
40+
if (target < 0)
41+
target = 0;
42+
}
43+
else
44+
{
45+
target = ComponentSpeed;
3846
}
3947

40-
await Task.Delay(TimeSpan.FromMilliseconds(awaiter));
41-
variableGameSpeed = ComponentSpeed;
42-
componentStopWatch.Restart();
48+
sw.Restart();
4349
}
4450
}
4551

52+
4653
private static void Ping(Aisling player)
4754
{
4855
if (player?.Client == null) return;

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

Lines changed: 46 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Diagnostics;
2-
32
using Darkages.Enums;
43
using Darkages.Network.Client;
54
using Darkages.Network.Server;
@@ -9,79 +8,64 @@ namespace Darkages.Network.Components;
98

109
public class PlayerRegenerationComponent(WorldServer server) : WorldServerComponent(server)
1110
{
12-
private static readonly Stopwatch PlayerRegenControl = new();
1311
private const int ComponentSpeed = 1000;
1412

1513
protected internal override async Task Update()
1614
{
17-
var componentStopWatch = new Stopwatch();
18-
componentStopWatch.Start();
19-
var variableGameSpeed = ComponentSpeed;
15+
var sw = Stopwatch.StartNew();
16+
var target = ComponentSpeed;
2017

2118
while (ServerSetup.Instance.Running)
2219
{
23-
if (componentStopWatch.Elapsed.TotalMilliseconds < variableGameSpeed)
20+
var elapsed = sw.Elapsed.TotalMilliseconds;
21+
if (elapsed < target)
2422
{
25-
await Task.Delay(1);
23+
var remaining = (int)(target - elapsed);
24+
25+
if (remaining > 0)
26+
await Task.Delay(Math.Min(remaining, 50));
27+
2628
continue;
2729
}
2830

29-
_ = UpdatePlayerRegeneration();
30-
var awaiter = (int)(ComponentSpeed - componentStopWatch.Elapsed.TotalMilliseconds);
31+
UpdatePlayerRegeneration();
32+
33+
var postElapsed = sw.Elapsed.TotalMilliseconds;
34+
var overshoot = postElapsed - ComponentSpeed;
3135

32-
if (awaiter < 0)
36+
if (overshoot > 0 && overshoot < ComponentSpeed)
3337
{
34-
variableGameSpeed = ComponentSpeed + awaiter;
35-
componentStopWatch.Restart();
36-
continue;
38+
// Slightly compensate next tick if we ran late
39+
target = ComponentSpeed - (int)overshoot;
40+
}
41+
else
42+
{
43+
target = ComponentSpeed;
3744
}
3845

39-
await Task.Delay(TimeSpan.FromMilliseconds(awaiter));
40-
variableGameSpeed = ComponentSpeed;
41-
componentStopWatch.Restart();
46+
sw.Restart();
4247
}
4348
}
4449

45-
private static async Task UpdatePlayerRegeneration()
50+
private void UpdatePlayerRegeneration()
4651
{
47-
if (!ServerSetup.Instance.Running || !Server.Aislings.Any()) return;
48-
const int maxConcurrency = 10;
49-
var semaphore = new SemaphoreSlim(maxConcurrency);
50-
var playerUpdateTasks = new List<Task>();
52+
var players = Server.Aislings
53+
.Where(p => p?.Client != null && p.LoggedIn)
54+
.ToList();
5155

52-
if (!PlayerRegenControl.IsRunning)
53-
PlayerRegenControl.Start();
56+
if (players.Count == 0)
57+
return;
5458

55-
if (PlayerRegenControl.Elapsed.TotalMilliseconds < 1000) return;
56-
57-
try
58-
{
59-
var players = Server.Aislings.Where(player => player?.Client != null).ToList();
60-
61-
foreach (var player in players)
62-
{
63-
await semaphore.WaitAsync();
64-
var task = ProcessUpdates(player).ContinueWith(t =>
65-
{
66-
semaphore.Release();
67-
}, TaskScheduler.Default);
68-
69-
playerUpdateTasks.Add(task);
70-
}
71-
72-
await Task.WhenAll(playerUpdateTasks);
73-
PlayerRegenControl.Restart();
74-
}
75-
catch (Exception ex)
76-
{
77-
SentrySdk.CaptureException(ex);
78-
}
59+
foreach (var player in players)
60+
ProcessUpdates(player);
7961
}
8062

81-
private static Task ProcessUpdates(Aisling player)
63+
private static void ProcessUpdates(Aisling player)
8264
{
83-
if (player?.Client == null) return Task.CompletedTask;
84-
if (!player.LoggedIn) return Task.CompletedTask;
65+
if (player?.Client == null) return;
66+
if (!player.LoggedIn) return;
67+
68+
// Statuses that disable regen
8569
if (player.IsPoisoned || player.Skulled || player.IsDead())
8670
{
8771
player.RegenTimerDisabled = true;
@@ -91,28 +75,36 @@ private static Task ProcessUpdates(Aisling player)
9175
player.RegenTimerDisabled = false;
9276
}
9377

94-
if (player.RegenTimerDisabled) return Task.CompletedTask;
78+
if (player.RegenTimerDisabled) return;
79+
80+
// Already full
9581
if (player.CurrentHp == player.MaximumHp &&
96-
player.CurrentMp == player.MaximumMp) return Task.CompletedTask;
82+
player.CurrentMp == player.MaximumMp)
83+
return;
9784

85+
// Clamp overflows
9886
if (player.CurrentHp > player.MaximumHp || player.CurrentMp > player.MaximumMp)
9987
{
10088
player.CurrentHp = player.MaximumHp;
10189
player.CurrentMp = player.MaximumMp;
10290
player.Client.SendAttributes(StatUpdateType.Vitality);
10391
}
10492

105-
if (player.Path == Class.Peasant | player.GameMaster)
93+
// Special-case: peasants / GMs
94+
if (player.Path == Class.Peasant || player.GameMaster)
10695
{
10796
player.Recover();
10897
}
10998

110-
if (player.CurrentMp < 1) player.CurrentMp = 1;
99+
if (player.CurrentMp < 1)
100+
player.CurrentMp = 1;
111101

112102
var hpRegenSeed = HpRegenSoftCap(player.Client);
113103
var mpRegenSeed = MpRegenSoftCap(player.Client);
104+
114105
var hpHardCap = Math.Abs(player.BaseHp / 3.00);
115106
var mpHardCap = Math.Abs(player.BaseMp / 3.00);
107+
116108
var performedRegen = false;
117109

118110
if (player.CurrentHp < player.MaximumHp)
@@ -129,11 +121,8 @@ private static Task ProcessUpdates(Aisling player)
129121

130122
if (performedRegen)
131123
player.Client.SendAttributes(StatUpdateType.Vitality);
132-
133-
return Task.CompletedTask;
134124
}
135125

136-
137126
private static void RegenHpCalculator(WorldClient client, double seed, double cap)
138127
{
139128
var currentHp = client.Aisling.CurrentHp;
@@ -172,7 +161,6 @@ private static void RegenMpCalculator(WorldClient client, double seed, double ca
172161
}
173162
}
174163

175-
176164
private static double HpRegenSoftCap(WorldClient client)
177165
{
178166
var conMod = Math.Abs(client.Aisling.Con / 3.00);

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

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
using Darkages.Database;
44
using Darkages.Network.Server;
5+
using Microsoft.Extensions.Logging;
56

67
namespace Darkages.Network.Components;
78

@@ -11,39 +12,52 @@ public class PlayerSaveComponent(WorldServer server) : WorldServerComponent(serv
1112

1213
protected internal override async Task Update()
1314
{
14-
var componentStopWatch = new Stopwatch();
15-
componentStopWatch.Start();
16-
var variableGameSpeed = ComponentSpeed;
15+
var sw = Stopwatch.StartNew();
16+
var target = ComponentSpeed;
1717

1818
while (ServerSetup.Instance.Running)
1919
{
20-
if (componentStopWatch.Elapsed.TotalMilliseconds < variableGameSpeed)
20+
var elapsed = sw.Elapsed.TotalMilliseconds;
21+
if (elapsed < target)
2122
{
22-
await Task.Delay(500);
23+
var remaining = (int)(target - elapsed);
24+
25+
if (remaining > 0)
26+
await Task.Delay(Math.Min(remaining, 1000));
2327
continue;
2428
}
2529

26-
UpdatePlayerSave();
27-
var awaiter = (int)(ComponentSpeed - componentStopWatch.Elapsed.TotalMilliseconds);
30+
await UpdatePlayerSaveAsync();
31+
32+
var postElapsed = sw.Elapsed.TotalMilliseconds;
33+
var overshoot = postElapsed - ComponentSpeed;
2834

29-
if (awaiter < 0)
35+
if (overshoot > 0 && overshoot < ComponentSpeed)
3036
{
31-
variableGameSpeed = ComponentSpeed + awaiter;
32-
componentStopWatch.Restart();
33-
continue;
37+
target = ComponentSpeed - (int)overshoot;
38+
}
39+
else
40+
{
41+
target = ComponentSpeed;
3442
}
3543

36-
await Task.Delay(TimeSpan.FromMilliseconds(awaiter));
37-
variableGameSpeed = ComponentSpeed;
38-
componentStopWatch.Restart();
44+
sw.Restart();
3945
}
4046
}
4147

42-
private static void UpdatePlayerSave()
48+
private async Task UpdatePlayerSaveAsync()
4349
{
44-
if (!ServerSetup.Instance.Running || !Server.Aislings.Any()) return;
45-
4650
var playersList = Server.Aislings.ToList();
47-
_ = StorageManager.AislingBucket.ServerSave(playersList);
51+
if (playersList.Count == 0) return;
52+
53+
try
54+
{
55+
await StorageManager.AislingBucket.ServerSave(playersList);
56+
}
57+
catch (Exception e)
58+
{
59+
ServerSetup.EventsLogger($"PlayerSaveComponent failed to save {playersList.Count} players: {e}", LogLevel.Error);
60+
SentrySdk.CaptureException(e);
61+
}
4862
}
4963
}

0 commit comments

Comments
 (0)