Skip to content

Commit 48f299e

Browse files
committed
timer based PW charges
1 parent b66412c commit 48f299e

11 files changed

Lines changed: 222 additions & 28 deletions

File tree

Shared/LobbyClient/Protocol/Messages.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -788,8 +788,8 @@ public class PwStatus
788788
public class PwAttackCharges
789789
{
790790
public int Current { get; set; }
791-
/// <summary>Absolute galaxy turn on which the next charge will be granted. Null when at max or charges are disabled.</summary>
792-
public int? NextRechargeTurn { get; set; }
791+
/// <summary>UTC time at which the next charge will be granted, rounded up to a full minute. Null when at max or charges are disabled.</summary>
792+
public DateTime? NextRechargeTime { get; set; }
793793
}
794794

795795

Shared/PlasmaShared/Utils.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ public static void SafeDispose(this IDisposable o)
3434
if (o != null) o.Dispose();
3535
}
3636

37+
public static DateTime CeilingToMinute(this DateTime d)
38+
{
39+
var floored = new DateTime(d.Year, d.Month, d.Day, d.Hour, d.Minute, 0, d.Kind);
40+
return d == floored ? d : floored.AddMinutes(1);
41+
}
42+
3743
public static IEnumerable<Indexed<T>> ToIndexedList<T>(this IEnumerable<T> enumeration)
3844
{
3945
return enumeration.Select((x, i) => new Indexed<T>(x, i));

Zero-K.info/Controllers/PlanetwarsAdminController.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ public ActionResult StartGalaxy(int galaxyID)
370370
db.Accounts.Where(x => x.FactionID != null).Update(x => new Account()
371371
{
372372
PwAttackCharges = maxCharges,
373-
PwLastChargeChangeTurn = null,
373+
PwLastChargeChange = null,
374374
});
375375
}
376376

ZkData/Ef/Account.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ public static double AdjustEloWeight(double currentWeight, double sumWeight, int
154154
public double PwWarpUsed { get; set; }
155155
public double PwAttackPoints { get; set; }
156156
public int PwAttackCharges { get; set; }
157-
public int? PwLastChargeChangeTurn { get; set; }
157+
public DateTime? PwLastChargeChange { get; set; }
158158
public bool HasVpnException { get; set; }
159159
public bool HasKudos { get; set; }
160160
public int ForumTotalUpvotes { get; set; }
@@ -532,18 +532,18 @@ public void ResetQuotas()
532532

533533

534534

535-
public void SpendPwAttackCharge(int currentTurn)
535+
public void SpendPwAttackCharge()
536536
{
537537
if (PwAttackCharges > 0) PwAttackCharges--;
538-
PwLastChargeChangeTurn = currentTurn;
538+
PwLastChargeChange = DateTime.UtcNow;
539539
}
540540

541-
public void GrantPwAttackCharge(int maxCharges, int currentTurn)
541+
public void GrantPwAttackCharge(int maxCharges)
542542
{
543543
if (maxCharges <= 0) return;
544544
if (PwAttackCharges >= maxCharges) return;
545545
PwAttackCharges++;
546-
PwLastChargeChangeTurn = currentTurn;
546+
PwLastChargeChange = DateTime.UtcNow;
547547
}
548548

549549

ZkData/Ef/DynamicConfig.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ public class DynamicConfig
4646
[Description("PlanetWars: maximum attack charges a player can hold. 0 disables the charge system.")]
4747
public int PwAttackChargesMax { get; set; } = 2;
4848

49-
[Description("PlanetWars: turns a player must be idle (no spend/gain) before their next +1 passive charge, up to max. Every charge gain or loss resets this clock, so the average regen rate is at most 1 charge per this many turns.")]
50-
public int PwAttackChargesCooldownTurns { get; set; } = 4;
49+
[Description("PlanetWars: minutes a player must be idle (no spend/gain) before their next +1 passive charge, up to max. Every charge gain or loss resets this clock, so the average regen rate is at most 1 charge per this many minutes.")]
50+
public int PwAttackChargesCooldownMinutes { get; set; } = 60;
5151

5252
[Description("PlanetWars: fraction of enemy bomber IP damage also applied to the bomber's own faction. 0 disables self-damage.")]
5353
public double PwBomberSelfIpRate { get; set; } = 0.5;

ZkData/Migrations/202604251056090_AddPwChargesWallclock.Designer.cs

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace ZkData.Migrations
2+
{
3+
using System;
4+
using System.Data.Entity.Migrations;
5+
6+
public partial class AddPwChargesWallclock : DbMigration
7+
{
8+
public override void Up()
9+
{
10+
DropColumn("dbo.Accounts", "PwLastChargeChangeTurn");
11+
AddColumn("dbo.Accounts", "PwLastChargeChange", c => c.DateTime());
12+
DropColumn("dbo.DynamicConfigs", "PwAttackChargesCooldownTurns");
13+
AddColumn("dbo.DynamicConfigs", "PwAttackChargesCooldownMinutes", c => c.Int(nullable: false, defaultValue: 60));
14+
}
15+
16+
public override void Down()
17+
{
18+
DropColumn("dbo.DynamicConfigs", "PwAttackChargesCooldownMinutes");
19+
AddColumn("dbo.DynamicConfigs", "PwAttackChargesCooldownTurns", c => c.Int(nullable: false, defaultValue: 4));
20+
DropColumn("dbo.Accounts", "PwLastChargeChange");
21+
AddColumn("dbo.Accounts", "PwLastChargeChangeTurn", c => c.Int());
22+
}
23+
}
24+
}

ZkData/Migrations/202604251056090_AddPwChargesWallclock.resx

Lines changed: 123 additions & 0 deletions
Large diffs are not rendered by default.

ZkData/ZkData.csproj

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,10 @@
511511
<Compile Include="Migrations\202604241242120_AddPwAttackCharges.Designer.cs">
512512
<DependentUpon>202604241242120_AddPwAttackCharges.cs</DependentUpon>
513513
</Compile>
514+
<Compile Include="Migrations\202604251056090_AddPwChargesWallclock.cs" />
515+
<Compile Include="Migrations\202604251056090_AddPwChargesWallclock.Designer.cs">
516+
<DependentUpon>202604251056090_AddPwChargesWallclock.cs</DependentUpon>
517+
</Compile>
514518
<Compile Include="SpringFilesUnitsyncAttempt.cs" />
515519
<Compile Include="SteamWebApi.cs" />
516520
<Compile Include="UserLanguageNoteAttribute.cs" />
@@ -974,6 +978,9 @@
974978
<EmbeddedResource Include="Migrations\202604241242120_AddPwAttackCharges.resx">
975979
<DependentUpon>202604241242120_AddPwAttackCharges.cs</DependentUpon>
976980
</EmbeddedResource>
981+
<EmbeddedResource Include="Migrations\202604251056090_AddPwChargesWallclock.resx">
982+
<DependentUpon>202604251056090_AddPwChargesWallclock.cs</DependentUpon>
983+
</EmbeddedResource>
977984
<EmbeddedResource Include="ZkDataResources.resx">
978985
<Generator>ResXFileCodeGenerator</Generator>
979986
<LastGenOutput>ZkDataResources.Designer.cs</LastGenOutput>

ZkLobbyServer/SpringieInterface/PlanetWarsMatchMaker.cs

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public class PlanetWarsMatchMaker : PlanetWarsMatchMakerState
2828
private DateTime? defendersFullTime; // set when every formed squad has enough defender volunteers
2929

3030
private Timer timer;
31+
private DateTime lastChargeRechargeCheck = DateTime.MinValue;
3132

3233
public PlanetWarsMatchMaker(ZkLobbyServer.ZkLobbyServer server)
3334
{
@@ -108,6 +109,12 @@ private async void TimerOnElapsed(object sender, ElapsedEventArgs elapsedEventAr
108109
lastPlanetWarsMode = MiscVar.PlanetWarsMode;
109110
}
110111

112+
if (DateTime.UtcNow - lastChargeRechargeCheck >= TimeSpan.FromMinutes(1))
113+
{
114+
lastChargeRechargeCheck = DateTime.UtcNow;
115+
await ProcessChargeRecharge();
116+
}
117+
111118
if (MiscVar.PlanetWarsMode != PlanetWarsModes.Running) return;
112119

113120
// clean up stale running battles (e.g. if Spring process crashed)
@@ -119,7 +126,6 @@ private async void TimerOnElapsed(object sender, ElapsedEventArgs elapsedEventAr
119126
case PwPhase.AttackCollect:
120127
if (DateTime.UtcNow > GetAttackDeadline())
121128
{
122-
await ApplyTurnEndChargeBump();
123129
RunSquadFormation();
124130
if (FormedSquads.Any())
125131
{
@@ -506,9 +512,8 @@ private async Task SpendAttackCharges(List<string> playerNames)
506512
List<Account> accounts;
507513
using (var db = new ZkDataContext())
508514
{
509-
var turn = db.Galaxies.First(g => g.IsDefault).Turn;
510515
accounts = db.Accounts.Where(a => playerNames.Contains(a.Name)).ToList();
511-
foreach (var acc in accounts) acc.SpendPwAttackCharge(turn);
516+
foreach (var acc in accounts) acc.SpendPwAttackCharge();
512517
db.SaveChanges();
513518
}
514519
await Task.WhenAll(accounts.Select(acc => SendPwAttackCharges(server, acc.Name, acc)));
@@ -1179,13 +1184,13 @@ public void RemoveFromRunningBattles(int battleID)
11791184
public static PwAttackCharges BuildPwAttackCharges(Account account)
11801185
{
11811186
var max = DynamicConfig.Instance.PwAttackChargesMax;
1182-
int? nextRechargeTurn = null;
1183-
if (max > 0 && account.PwAttackCharges < max && account.PwLastChargeChangeTurn != null)
1184-
nextRechargeTurn = account.PwLastChargeChangeTurn.Value + DynamicConfig.Instance.PwAttackChargesCooldownTurns;
1187+
DateTime? nextRechargeTime = null;
1188+
if (max > 0 && account.PwAttackCharges < max && account.PwLastChargeChange != null)
1189+
nextRechargeTime = account.PwLastChargeChange.Value.AddMinutes(DynamicConfig.Instance.PwAttackChargesCooldownMinutes).CeilingToMinute();
11851190
return new PwAttackCharges
11861191
{
11871192
Current = account.PwAttackCharges,
1188-
NextRechargeTurn = nextRechargeTurn,
1193+
NextRechargeTime = nextRechargeTime,
11891194
};
11901195
}
11911196

@@ -1208,28 +1213,28 @@ private async Task SendPwAttackChargesForUser(string userName)
12081213
}
12091214
}
12101215

1211-
private async Task ApplyTurnEndChargeBump()
1216+
private async Task ProcessChargeRecharge()
12121217
{
12131218
try
12141219
{
12151220
var max = DynamicConfig.Instance.PwAttackChargesMax;
12161221
if (max <= 0) return;
1217-
var cooldown = DynamicConfig.Instance.PwAttackChargesCooldownTurns;
1222+
var cooldownMinutes = DynamicConfig.Instance.PwAttackChargesCooldownMinutes;
1223+
// +35s offset: displayed nextRechargeTime is rounded up to a full minute. Bumping the
1224+
// eligibility window forward absorbs ≤1min jitter between the recharge check and the
1225+
// displayed minute boundary, so the user never sees the time pass without the grant.
1226+
var threshold = DateTime.UtcNow.AddSeconds(35).AddMinutes(-cooldownMinutes);
12181227

12191228
List<Account> bumped;
12201229
using (var db = new ZkDataContext())
12211230
{
1222-
var turn = db.Galaxies.First(g => g.IsDefault).Turn;
12231231
bumped = db.Accounts.Where(a =>
12241232
a.FactionID != null &&
12251233
a.PwAttackCharges < max &&
1226-
(a.PwLastChargeChangeTurn == null || turn - a.PwLastChargeChangeTurn >= cooldown)).ToList();
1234+
a.PwLastChargeChange != null &&
1235+
a.PwLastChargeChange <= threshold).ToList();
12271236

1228-
foreach (var acc in bumped)
1229-
{
1230-
acc.PwAttackCharges++;
1231-
acc.PwLastChargeChangeTurn = turn;
1232-
}
1237+
foreach (var acc in bumped) acc.GrantPwAttackCharge(max);
12331238

12341239
if (bumped.Count > 0) db.SaveChanges();
12351240
}
@@ -1238,7 +1243,7 @@ private async Task ApplyTurnEndChargeBump()
12381243
}
12391244
catch (Exception ex)
12401245
{
1241-
Trace.TraceError("PlanetWars turn-end charge bump error: {0}", ex);
1246+
Trace.TraceError("PlanetWars charge recharge tick error: {0}", ex);
12421247
}
12431248
}
12441249

0 commit comments

Comments
 (0)