8
8
using OWSShared . Options ;
9
9
using RabbitMQ . Client ;
10
10
using OWS . Interfaces ;
11
+ using Microsoft . AspNetCore . Mvc ;
12
+ using OWSData . Models . Composites ;
13
+ using Orleans . Runtime ;
14
+ using System . Text . RegularExpressions ;
11
15
12
16
namespace OWS . Grains
13
17
{
@@ -17,23 +21,136 @@ public class CharacterGrain : Grain, ICharacterGrain
17
21
private readonly IUsersRepository _usersRepository ;
18
22
private readonly ICharactersRepository _charactersRepository ;
19
23
private readonly IOptions < RabbitMQOptions > _rabbitMQOptions ;
24
+ private readonly ICustomCharacterDataSelector _customCharacterDataSelector ;
25
+ private readonly IGetReadOnlyPublicCharacterData _getReadOnlyPublicCharacterData ;
20
26
21
27
public CharacterGrain (
22
28
23
29
ILogger < CharacterGrain > logger ,
24
30
IUsersRepository usersRepository ,
25
31
ICharactersRepository charactersRepository ,
26
- IOptions < RabbitMQOptions > rabbitMQOptions )
32
+ IOptions < RabbitMQOptions > rabbitMQOptions ,
33
+ ICustomCharacterDataSelector customCharacterDataSelector ,
34
+ IGetReadOnlyPublicCharacterData getReadOnlyPublicCharacterData )
27
35
{
28
36
_logger = logger ;
29
37
_usersRepository = usersRepository ;
30
38
_charactersRepository = charactersRepository ;
31
39
_rabbitMQOptions = rabbitMQOptions ;
40
+ _customCharacterDataSelector = customCharacterDataSelector ;
41
+ _getReadOnlyPublicCharacterData = getReadOnlyPublicCharacterData ;
32
42
}
33
43
34
- public async Task < JoinMapByCharName > GetServerToConnectTo ( Guid customerGuid , string zoneName , int playerGroupType )
44
+ public async Task < CreateCharacter > Create ( Guid userSessionId , string className )
45
+ {
46
+ if ( Guid . TryParse ( RequestContext . Get ( "CustomerId" ) as string , out var customerGuid ) )
47
+ {
48
+ throw new ArgumentException ( "Invalid Customer ID" ) ;
49
+ }
50
+
51
+ var charName = this . GetPrimaryKeyString ( ) ;
52
+
53
+ string errorMessage = string . Empty ;
54
+ //Test for empty Character Names or Character Names that are shorter than the minimum Character name Length
55
+ if ( String . IsNullOrEmpty ( charName ) || charName . Length < 4 )
56
+ {
57
+ errorMessage = "Please enter a valid Character Name that is at least 4 characters in length." ;
58
+ }
59
+
60
+ Regex regex = new Regex ( @"^\w+$" ) ;
61
+ //Test for Character Names that use characters other than Letters (uppercase and lowercase) and Numbers.
62
+ if ( ! regex . IsMatch ( charName ) )
63
+ {
64
+ errorMessage = "Please enter a Character Name that only contains letters and numbers." ;
65
+ }
66
+
67
+ if ( ! String . IsNullOrEmpty ( errorMessage ) )
68
+ {
69
+ CreateCharacter createCharacterErrorMessage = new CreateCharacter ( ) ;
70
+ createCharacterErrorMessage . ErrorMessage = errorMessage ;
71
+ return createCharacterErrorMessage ;
72
+ }
73
+
74
+ var output = await _usersRepository . CreateCharacter ( customerGuid , userSessionId , charName , className ) ;
75
+
76
+ return output ;
77
+ }
78
+
79
+ public async Task < CharacterAndCustomData > PublicGetByNameRequest ( Guid userSessionId )
80
+ {
81
+ if ( Guid . TryParse ( RequestContext . Get ( "CustomerId" ) as string , out var customerGuid ) )
82
+ {
83
+ throw new ArgumentException ( "Invalid Customer ID" ) ;
84
+ }
85
+
86
+ CharacterAndCustomData Output = new CharacterAndCustomData ( ) ;
87
+
88
+ var characterName = this . GetPrimaryKeyString ( ) ;
89
+
90
+ //Get the User Session
91
+ GetUserSession userSession = await _usersRepository . GetUserSession ( customerGuid , userSessionId ) ;
92
+
93
+ //Make sure the User Session is valid
94
+ if ( userSession == null || ! userSession . UserGuid . HasValue )
95
+ {
96
+ //TODO: rework to be able to return success bool / error msg instead of throwing
97
+ throw new ArgumentException ( "User session is not valid" ) ;
98
+ }
99
+
100
+ //Get character data
101
+ GetCharByCharName characterData = await _charactersRepository . GetCharByCharName ( customerGuid , characterName ) ;
102
+
103
+ //Make sure the character data is valid and in the right User Session
104
+ if ( characterData == null || ! characterData . UserGuid . HasValue || characterData . UserGuid != userSession . UserGuid )
105
+ {
106
+ return Output ;
107
+ }
108
+
109
+ //Assign the character data to the output object
110
+ Output . CharacterData = characterData ;
111
+
112
+ //Get custom character data
113
+ IEnumerable < CustomCharacterData > customCharacterDataItems = await _charactersRepository . GetCustomCharacterData ( customerGuid , characterName ) ;
114
+
115
+ //Initialize the list
116
+ Output . CustomCharacterDataRows = new List < CustomCharacterDataDTO > ( ) ;
117
+
118
+ //Loop through all the CustomCharacterData rows
119
+ foreach ( CustomCharacterData currentCustomCharacterData in customCharacterDataItems )
120
+ {
121
+ //Use the ICustomCharacterDataSelector implementation to filter which fields are returned
122
+ if ( _customCharacterDataSelector . ShouldExportThisCustomCharacterDataField ( currentCustomCharacterData . CustomFieldName ) )
123
+ {
124
+ CustomCharacterDataDTO customCharacterDataDTO = new CustomCharacterDataDTO ( )
125
+ {
126
+ CustomFieldName = currentCustomCharacterData . CustomFieldName ,
127
+ FieldValue = currentCustomCharacterData . FieldValue
128
+ } ;
129
+
130
+ //Add the filtered Custom Character Data
131
+ Output . CustomCharacterDataRows . Add ( customCharacterDataDTO ) ;
132
+ }
133
+ }
134
+
135
+ //Add Read-only Character Data
136
+ CustomCharacterDataDTO readOnlyCharacterData = new CustomCharacterDataDTO ( )
137
+ {
138
+ CustomFieldName = "ReadOnlyCharacterData" ,
139
+ FieldValue = await _getReadOnlyPublicCharacterData . GetReadOnlyPublicCharacterData ( characterData . CharacterId )
140
+ } ;
141
+ Output . CustomCharacterDataRows . Add ( readOnlyCharacterData ) ;
142
+
143
+ return Output ;
144
+ }
145
+
146
+ public async Task < JoinMapByCharName > GetServerToConnectTo ( string zoneName , int playerGroupType )
35
147
{
36
148
var Output = new JoinMapByCharName ( ) ;
149
+
150
+ if ( Guid . TryParse ( RequestContext . Get ( "CustomerId" ) as string , out var customerGuid ) )
151
+ {
152
+ throw new ArgumentException ( "Invalid Customer ID" ) ;
153
+ }
37
154
38
155
//If ZoneName is empty, look it up from the character. This is used for the inital login.
39
156
if ( String . IsNullOrEmpty ( zoneName ) || zoneName == "GETLASTZONENAME" )
@@ -77,16 +194,6 @@ public async Task<JoinMapByCharName> GetServerToConnectTo(Guid customerGuid, str
77
194
//There is no zone server running that will accept our connection, so start up a new one
78
195
if ( joinMapByCharacterName . NeedToStartupMap )
79
196
{
80
-
81
- //var serverGrain = GrainFactory.GetGrain<IServerGrain>(joinMapByCharacterName.WorldServerID);
82
- //await serverGrain.RequestServerSpinUpAsync(
83
- // customerGuid: customerGuid,
84
- // zoneInstanceId: joinMapByCharacterName.MapInstanceID,
85
- // zoneName: joinMapByCharacterName.MapNameToStart,
86
- // port: joinMapByCharacterName.Port);
87
- //bool requestSuccess = await RequestServerSpinUp(joinMapByCharacterName.WorldServerID, joinMapByCharacterName.MapInstanceID, joinMapByCharacterName.MapNameToStart, joinMapByCharacterName.Port);\
88
-
89
-
90
197
ConnectionFactory factory = new ( )
91
198
{
92
199
HostName = _rabbitMQOptions . Value . RabbitMQHostName ,
@@ -120,15 +227,22 @@ public async Task<JoinMapByCharName> GetServerToConnectTo(Guid customerGuid, str
120
227
body : body ) ;
121
228
}
122
229
}
123
- //Wait OWSGeneralConfig.SecondsToWaitBeforeFirstPollForSpinUp seconds before the first CheckMapInstanceStatus to give it time to spin up
124
230
231
+ //TODO: it doesnt make any sense to delay before giving the client a server to connect to
232
+ //If we have begun to spin up a server, we should just give the client the ip and let them try to connect
233
+ //If it takes too long client-side then they should reach back out to the api to get a new server.
234
+ //i.e. why make the client _and_ the api server wait when we could just make the client wait.
235
+
236
+ //Wait OWSGeneralConfig.SecondsToWaitBeforeFirstPollForSpinUp seconds before the first CheckMapInstanceStatus to give it time to spin up
125
237
//Thread.Sleep(owsGeneralConfig.Value.SecondsToWaitBeforeFirstPollForSpinUp);
126
238
127
239
//readyForPlayersToConnect = await WaitForServerReadyToConnect(CustomerGUID, joinMapByCharacterName.MapInstanceID);
128
240
}
129
241
//We found a zone server we can connect to, but it is still spinning up. Wait until it is ready to connect to (up to OWSGeneralConfig.SecondsToWaitForServerSpinUp seconds).
130
242
else if ( joinMapByCharacterName . MapInstanceID > 0 && joinMapByCharacterName . MapInstanceStatus == 1 )
131
243
{
244
+ //see comment above for why this doesn't make sense.
245
+
132
246
//CheckMapInstanceStatus every OWSGeneralConfig.SecondsToWaitInBetweenSpinUpPolling seconds for up to OWSGeneralConfig.SecondsToWaitForServerSpinUp seconds
133
247
//readyForPlayersToConnect = await WaitForServerReadyToConnect(CustomerGUID, joinMapByCharacterName.MapInstanceID);
134
248
}
@@ -150,5 +264,72 @@ public async Task<JoinMapByCharName> GetServerToConnectTo(Guid customerGuid, str
150
264
Output . ErrorMessage = "" ;
151
265
return Output ;
152
266
}
267
+
268
+ public async Task AddOrUpdateCustomData ( AddOrUpdateCustomCharacterData request )
269
+ {
270
+ if ( Guid . TryParse ( RequestContext . Get ( "CustomerId" ) as string , out var customerGuid ) )
271
+ {
272
+ throw new ArgumentException ( "Invalid Customer ID" ) ;
273
+ }
274
+
275
+ await _charactersRepository . AddOrUpdateCustomCharacterData ( customerGuid , request ) ;
276
+ }
277
+
278
+ public async Task < GetCharByCharName > GetByName ( )
279
+ {
280
+ if ( Guid . TryParse ( RequestContext . Get ( "CustomerId" ) as string , out var customerGuid ) )
281
+ {
282
+ throw new ArgumentException ( "Invalid Customer ID" ) ;
283
+ }
284
+
285
+ return await _charactersRepository . GetCharByCharName ( customerGuid , this . GetPrimaryKeyString ( ) ) ;
286
+ }
287
+
288
+ public async Task < CustomCharacterDataRows > GetCustomData ( )
289
+ {
290
+ if ( Guid . TryParse ( RequestContext . Get ( "CustomerId" ) as string , out var customerGuid ) )
291
+ {
292
+ throw new ArgumentException ( "Invalid Customer ID" ) ;
293
+ }
294
+
295
+ CustomCharacterDataRows output = new CustomCharacterDataRows ( ) ;
296
+
297
+ output . Rows = await _charactersRepository . GetCustomCharacterData ( customerGuid , this . GetPrimaryKeyString ( ) ) ;
298
+
299
+ return output ;
300
+ }
301
+
302
+ public async Task UpdateCharacterPosition ( string mapName )
303
+ {
304
+ //TODO: UpdateAllPlayerPositions probably merits a MapGrain
305
+ }
306
+
307
+ public async Task < SuccessAndErrorMessage > UpdateCharacterStats ( UpdateCharacterStats stats )
308
+ {
309
+ SuccessAndErrorMessage successAndErrorMessage = new SuccessAndErrorMessage ( ) ;
310
+ successAndErrorMessage . Success = true ;
311
+
312
+ try
313
+ {
314
+ await _charactersRepository . UpdateCharacterStats ( stats ) ;
315
+ }
316
+ catch ( Exception ex )
317
+ {
318
+ successAndErrorMessage . ErrorMessage = ex . Message ;
319
+ successAndErrorMessage . Success = false ;
320
+ }
321
+
322
+ return successAndErrorMessage ;
323
+ }
324
+
325
+ public async Task Logout ( )
326
+ {
327
+ if ( Guid . TryParse ( RequestContext . Get ( "CustomerId" ) as string , out var customerGuid ) )
328
+ {
329
+ throw new ArgumentException ( "Invalid Customer ID" ) ;
330
+ }
331
+
332
+ await _charactersRepository . PlayerLogout ( customerGuid , this . GetPrimaryKeyString ( ) ) ;
333
+ }
153
334
}
154
335
}
0 commit comments