Skip to content

Commit e3aaa4d

Browse files
committed
Add customerId outgoing call filter. Migrate more endpoints to orleans
1 parent 7e87193 commit e3aaa4d

File tree

8 files changed

+276
-73
lines changed

8 files changed

+276
-73
lines changed

src/OWS.Grains/CharacterGrain.cs

+194-13
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
using OWSShared.Options;
99
using RabbitMQ.Client;
1010
using OWS.Interfaces;
11+
using Microsoft.AspNetCore.Mvc;
12+
using OWSData.Models.Composites;
13+
using Orleans.Runtime;
14+
using System.Text.RegularExpressions;
1115

1216
namespace OWS.Grains
1317
{
@@ -17,23 +21,136 @@ public class CharacterGrain : Grain, ICharacterGrain
1721
private readonly IUsersRepository _usersRepository;
1822
private readonly ICharactersRepository _charactersRepository;
1923
private readonly IOptions<RabbitMQOptions> _rabbitMQOptions;
24+
private readonly ICustomCharacterDataSelector _customCharacterDataSelector;
25+
private readonly IGetReadOnlyPublicCharacterData _getReadOnlyPublicCharacterData;
2026

2127
public CharacterGrain(
2228

2329
ILogger<CharacterGrain> logger,
2430
IUsersRepository usersRepository,
2531
ICharactersRepository charactersRepository,
26-
IOptions<RabbitMQOptions> rabbitMQOptions)
32+
IOptions<RabbitMQOptions> rabbitMQOptions,
33+
ICustomCharacterDataSelector customCharacterDataSelector,
34+
IGetReadOnlyPublicCharacterData getReadOnlyPublicCharacterData)
2735
{
2836
_logger = logger;
2937
_usersRepository = usersRepository;
3038
_charactersRepository = charactersRepository;
3139
_rabbitMQOptions = rabbitMQOptions;
40+
_customCharacterDataSelector = customCharacterDataSelector;
41+
_getReadOnlyPublicCharacterData = getReadOnlyPublicCharacterData;
3242
}
3343

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)
35147
{
36148
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+
}
37154

38155
//If ZoneName is empty, look it up from the character. This is used for the inital login.
39156
if (String.IsNullOrEmpty(zoneName) || zoneName == "GETLASTZONENAME")
@@ -77,16 +194,6 @@ public async Task<JoinMapByCharName> GetServerToConnectTo(Guid customerGuid, str
77194
//There is no zone server running that will accept our connection, so start up a new one
78195
if (joinMapByCharacterName.NeedToStartupMap)
79196
{
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-
90197
ConnectionFactory factory = new()
91198
{
92199
HostName = _rabbitMQOptions.Value.RabbitMQHostName,
@@ -120,15 +227,22 @@ public async Task<JoinMapByCharName> GetServerToConnectTo(Guid customerGuid, str
120227
body: body);
121228
}
122229
}
123-
//Wait OWSGeneralConfig.SecondsToWaitBeforeFirstPollForSpinUp seconds before the first CheckMapInstanceStatus to give it time to spin up
124230

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
125237
//Thread.Sleep(owsGeneralConfig.Value.SecondsToWaitBeforeFirstPollForSpinUp);
126238

127239
//readyForPlayersToConnect = await WaitForServerReadyToConnect(CustomerGUID, joinMapByCharacterName.MapInstanceID);
128240
}
129241
//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).
130242
else if (joinMapByCharacterName.MapInstanceID > 0 && joinMapByCharacterName.MapInstanceStatus == 1)
131243
{
244+
//see comment above for why this doesn't make sense.
245+
132246
//CheckMapInstanceStatus every OWSGeneralConfig.SecondsToWaitInBetweenSpinUpPolling seconds for up to OWSGeneralConfig.SecondsToWaitForServerSpinUp seconds
133247
//readyForPlayersToConnect = await WaitForServerReadyToConnect(CustomerGUID, joinMapByCharacterName.MapInstanceID);
134248
}
@@ -150,5 +264,72 @@ public async Task<JoinMapByCharName> GetServerToConnectTo(Guid customerGuid, str
150264
Output.ErrorMessage = "";
151265
return Output;
152266
}
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+
}
153334
}
154335
}

src/OWS.Grains/UserGrain.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using OWSData.Models.StoredProcs;
33
using OWSData.Repositories.Interfaces;
44
using OWS.Interfaces;
5+
using Orleans.Runtime;
56

67
namespace OWS.Grains
78
{
@@ -18,8 +19,13 @@ public UserGrain(
1819
_usersRepository = usersRepository;
1920
}
2021

21-
public async Task<GetUserSession> GetUserSessionAsync(Guid customerGuid)
22+
public async Task<GetUserSession> GetUserSessionAsync()
2223
{
24+
if (Guid.TryParse(RequestContext.Get("CustomerId") as string, out var customerGuid))
25+
{
26+
throw new ArgumentException("Invalid Customer ID");
27+
}
28+
2329
var session = await _usersRepository.GetUserSession(customerGuid, this.GetPrimaryKey());
2430
return session;
2531
}

src/OWS.Interfaces/ICharacterGrain.cs

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
1-
using OWSData.Models.StoredProcs;
1+
using OWSData.Models.Composites;
2+
using OWSData.Models.StoredProcs;
23

34
namespace OWS.Interfaces
45
{
56
public interface ICharacterGrain : IGrainWithStringKey
67
{
7-
Task<JoinMapByCharName> GetServerToConnectTo(Guid customerGuid, string zoneName, int playerGroupType);
8+
Task<CharacterAndCustomData> PublicGetByNameRequest(Guid userSessionId);
9+
Task<GetCharByCharName> GetByName();
10+
Task<JoinMapByCharName> GetServerToConnectTo(string zoneName, int playerGroupType);
11+
Task AddOrUpdateCustomData(AddOrUpdateCustomCharacterData request);
12+
Task<CustomCharacterDataRows> GetCustomData();
13+
Task<SuccessAndErrorMessage> UpdateCharacterStats(UpdateCharacterStats stats);
14+
Task Logout();
15+
Task<CreateCharacter> Create(Guid userSessionId, string className);
816
}
917
}

src/OWS.Interfaces/IServerGrain.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ namespace OWS.Interfaces
44
{
55
public interface IUserGrain : IGrainWithGuidKey
66
{
7-
Task<GetUserSession> GetUserSessionAsync(Guid customerGuid);
7+
Task<GetUserSession> GetUserSessionAsync();
88
}
99
}

0 commit comments

Comments
 (0)