77using Microsoft . EntityFrameworkCore ;
88using Orleans ;
99using Turbo . Database . Context ;
10+ using Turbo . Primitives . Messages . Outgoing . Collectibles ;
11+ using Turbo . Primitives . Messages . Outgoing . Inventory . Purse ;
12+ using Turbo . Primitives . Messages . Outgoing . Notifications ;
13+ using Turbo . Primitives . Orleans ;
1014using Turbo . Primitives . Orleans . Snapshots . Players ;
1115using Turbo . Primitives . Players . Enums . Wallet ;
1216using Turbo . Primitives . Players . Grains ;
@@ -18,11 +22,13 @@ namespace Turbo.Players.Grains;
1822
1923internal sealed class PlayerWalletGrain (
2024 IDbContextFactory < TurboDbContext > dbCtxFactory ,
21- ICurrencyTypeProvider currencyTypeProvider
25+ ICurrencyTypeProvider currencyTypeProvider ,
26+ IGrainFactory grainFactory
2227) : Grain , IPlayerWalletGrain
2328{
2429 private readonly IDbContextFactory < TurboDbContext > _dbCtxFactory = dbCtxFactory ;
2530 private readonly ICurrencyTypeProvider _currencyTypeProvider = currencyTypeProvider ;
31+ private readonly IGrainFactory _grainFactory = grainFactory ;
2632
2733 private readonly Dictionary < CurrencyKind , WalletCurrencySnapshot > _currenciesByKind = [ ] ;
2834
@@ -48,12 +54,31 @@ CancellationToken ct
4854 . Database . BeginTransactionAsync ( ct )
4955 . ConfigureAwait ( false ) ;
5056
57+ var updatedCurrencies = new List < CurrencyType > ( normalizedRequests . Count ) ;
58+
5159 foreach ( var request in normalizedRequests )
5260 {
5361 try
5462 {
63+ var prevAmount = _currenciesByKind . TryGetValue (
64+ request . CurrencyKind ,
65+ out var snapshot
66+ )
67+ ? snapshot . Amount
68+ : 0 ;
69+
5570 if ( ! await ProcessDebitRequestAsync ( dbCtx , request , ct ) )
5671 throw new Exception ( "Failed to process debit request" ) ;
72+
73+ var newAmount = _currenciesByKind . TryGetValue (
74+ request . CurrencyKind ,
75+ out snapshot
76+ )
77+ ? snapshot . Amount
78+ : 0 ;
79+
80+ if ( newAmount != prevAmount )
81+ updatedCurrencies . Add ( request . CurrencyKind . CurrencyType ) ;
5782 }
5883 catch
5984 {
@@ -70,6 +95,7 @@ CancellationToken ct
7095 }
7196
7297 await tx . CommitAsync ( ct ) ;
98+ await SyncCurrenciesAsync ( updatedCurrencies , ct ) ;
7399 }
74100
75101 return WalletDebitResult . Success ( ) ;
@@ -80,13 +106,10 @@ public Task RefundAsync(ImmutableArray<WalletDebitRequest> requests, Cancellatio
80106 return Task . CompletedTask ;
81107 }
82108
83- public async Task < int > GetAmountForCurrencyAsync ( CurrencyKind kind , CancellationToken ct )
84- {
85- if ( _currenciesByKind . TryGetValue ( kind , out var snapshot ) )
86- return snapshot . Amount ;
87-
88- return 0 ;
89- }
109+ public Task < int > GetAmountForCurrencyAsync ( CurrencyKind kind , CancellationToken ct ) =>
110+ Task . FromResult (
111+ _currenciesByKind . TryGetValue ( kind , out var snapshot ) ? snapshot . Amount : 0
112+ ) ;
90113
91114 public async Task < Dictionary < int , int > > GetActivityPointsAsync ( CancellationToken ct )
92115 {
@@ -166,8 +189,6 @@ private async Task<bool> ProcessDebitRequestAsync(
166189 CancellationToken ct
167190 )
168191 {
169- // TODO when a currency updates we need to send the packet
170-
171192 if ( request . Amount <= 0 )
172193 return true ;
173194
@@ -177,23 +198,79 @@ CancellationToken ct
177198 )
178199 return false ;
179200
201+ var newAmount = currency . Amount - request . Amount ;
202+
180203 var affectedRows = await dbCtx
181204 . PlayerCurrencies . Where ( x =>
182205 x . Id == currency . Id
183206 && x . PlayerEntityId == ( int ) this . GetPrimaryKeyLong ( )
184207 && x . Amount >= request . Amount
185208 )
186- . ExecuteUpdateAsync (
187- update => update . SetProperty ( x => x . Amount , x => x . Amount - request . Amount ) ,
188- ct
189- ) ;
209+ . ExecuteUpdateAsync ( update => update . SetProperty ( x => x . Amount , x => newAmount ) , ct ) ;
190210
191211 if ( affectedRows != 1 )
192212 return false ;
193213
214+ _currenciesByKind [ request . CurrencyKind ] = currency with { Amount = newAmount } ;
215+
194216 return true ;
195217 }
196218
219+ private async Task SyncCurrenciesAsync (
220+ List < CurrencyType > updatedCurrencies ,
221+ CancellationToken ct
222+ )
223+ {
224+ var playerPresence = _grainFactory . GetPlayerPresenceGrain ( ( int ) this . GetPrimaryKeyLong ( ) ) ;
225+
226+ foreach ( var currencyType in updatedCurrencies )
227+ {
228+ var kind = new CurrencyKind { CurrencyType = currencyType } ;
229+
230+ switch ( kind . CurrencyType )
231+ {
232+ case CurrencyType . Credits :
233+ await playerPresence . SendComposerAsync (
234+ new CreditBalanceEventMessageComposer
235+ {
236+ Balance = _currenciesByKind . TryGetValue ( kind , out var credits )
237+ ? credits . Amount . ToString ( )
238+ : "0" ,
239+ }
240+ ) ;
241+ break ;
242+ case CurrencyType . Emeralds :
243+ await playerPresence . SendComposerAsync (
244+ new EmeraldBalanceMessageComposer
245+ {
246+ EmeraldBalance = _currenciesByKind . TryGetValue ( kind , out var emeralds )
247+ ? emeralds . Amount
248+ : 0 ,
249+ }
250+ ) ;
251+ break ;
252+ case CurrencyType . Silver :
253+ await playerPresence . SendComposerAsync (
254+ new SilverBalanceMessageComposer
255+ {
256+ SilverBalance = _currenciesByKind . TryGetValue ( kind , out var silver )
257+ ? silver . Amount
258+ : 0 ,
259+ }
260+ ) ;
261+ break ;
262+ case CurrencyType . ActivityPoints :
263+ await playerPresence . SendComposerAsync (
264+ new ActivityPointsMessageComposer
265+ {
266+ PointsByCategoryId = await GetActivityPointsAsync ( ct ) ,
267+ }
268+ ) ;
269+ break ;
270+ }
271+ }
272+ }
273+
197274 private async Task HydrateAsync ( CancellationToken ct )
198275 {
199276 _currenciesByKind . Clear ( ) ;
0 commit comments