Skip to content

Commit

Permalink
Merge pull request #73 from cmu-sei/next
Browse files Browse the repository at this point in the history
3.6.6
  • Loading branch information
sei-mkaar authored Nov 17, 2022
2 parents a54215d + 2614e79 commit 734fa12
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 14 deletions.
1 change: 1 addition & 0 deletions src/Gameboard.Api/Features/UnityGames/IUnityGameService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ public interface IUnityGameService
{
Task<Data.Challenge> AddChallenge(NewUnityChallenge newChallenge, User actor);
Task<Data.ChallengeEvent> CreateMissionEvent(UnityMissionUpdate model, Api.User actor);
Task<Data.Challenge> HasChallengeData(NewUnityChallenge newUnityChallenge);
Task DeleteChallengeData(string gameId);
bool IsUnityGame(Game game);
bool IsUnityGame(Data.Game game);
Expand Down
53 changes: 44 additions & 9 deletions src/Gameboard.Api/Features/UnityGames/UnityGameController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Threading;
using System.Threading.Tasks;
using AutoMapper;
using Gameboard.Api.Data.Abstractions;
using Gameboard.Api.Features.UnityGames;
using Gameboard.Api.Hubs;
using Gameboard.Api.Services;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
Expand All @@ -25,11 +26,12 @@ namespace Gameboard.Api.Controllers;
[Authorize]
public class UnityGameController : _Controller
{
private static SemaphoreSlim SP_CHALLENGE_DATA = new SemaphoreSlim(1, 1);
private readonly IChallengeStore _challengeStore;
private readonly ConsoleActorMap _actorMap;
private readonly GameService _gameService;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IHubContext<AppHub, IAppHubEvent> _hub;
private readonly LinkGenerator _linkGenerator;
private readonly IMapper _mapper;
private readonly IUnityGameService _unityGameService;

Expand All @@ -41,18 +43,19 @@ public UnityGameController(
// other stuff
ConsoleActorMap actorMap,
GameService gameService,
PlayerService playerService,
IChallengeStore challengeStore,
IHttpClientFactory httpClientFactory,
IUnityGameService unityGameService,
IHubContext<AppHub, IAppHubEvent> hub,
LinkGenerator link,
IMapper mapper
) : base(logger, cache, validator)
{
_actorMap = actorMap;
_challengeStore = challengeStore;
_gameService = gameService;
_httpClientFactory = httpClientFactory;
_hub = hub;
_linkGenerator = link;
_mapper = mapper;
_unityGameService = unityGameService;
}
Expand Down Expand Up @@ -127,15 +130,43 @@ public async Task<string> UndeployUnitySpace([FromQuery] string gid, [FromRoute]
);

await Validate(model);
var result = await _unityGameService.AddChallenge(model, Actor);

// each _team_ will only get one copy of the challenge, and by rule, that challenge must have the id
// of the topo gamespace ID. If it's already in the DB, send them on their way with the challenge we've already got
//
// semaphore locking because, if i don't, may not sleep during the competition
Data.Challenge challengeData = null;
try
{
Console.Write("Entering the Unity challenge data semaphore");
await SP_CHALLENGE_DATA.WaitAsync();

challengeData = await _unityGameService.HasChallengeData(model);
if (challengeData != null)
{
return challengeData;
}

// otherwise, add new challenge data and send gamebrain the ids of the consoles (which are based on the challenge id)
challengeData = await _unityGameService.AddChallenge(model, Actor);
}
catch (Exception ex)
{
Console.WriteLine("Error inside the Unity challenge data semaphore:", ex);
throw;
}
finally
{
SP_CHALLENGE_DATA.Release();
}

// now that we have challenge IDs, we can update gamebrain's console urls
var gamebrainClient = await CreateGamebrain();

var vmData = model.Vms.Select(vm =>
{
var consoleHost = new UriBuilder(Request.Scheme, Request.Host.Host, Request.Host.Port ?? -1, $"{Request.PathBase}/mks");
consoleHost.Query = $"f=1&s={result.Id}&v={vm.Name}";
consoleHost.Query = $"f=1&s={challengeData.Id}&v={vm.Name}";
return new UnityGameVm
{
Expand All @@ -152,14 +183,15 @@ public async Task<string> UndeployUnitySpace([FromQuery] string gid, [FromRoute]
catch (Exception ex)
{
Console.Write("Calling gamebrain failed with", ex);
throw;
}

// notify the hub (if there is one)
await _hub.Clients
.Group(model.TeamId)
.ChallengeEvent(new HubEvent<Challenge>(_mapper.Map<Challenge>(result), EventAction.Updated));
.ChallengeEvent(new HubEvent<Challenge>(_mapper.Map<Challenge>(challengeData), EventAction.Updated));

return result;
return challengeData;
}

[HttpPost("api/unity/mission-update")]
Expand All @@ -179,7 +211,10 @@ public async Task<IActionResult> CreateMissionEvent([FromBody] UnityMissionUpdat
return Accepted();
}

// this means we actually created an event
// this means we actually created an event, so also update player scores
await _challengeStore.UpdateTeam(model.TeamId);

// call back with the event
return Ok(challengeEvent);
}

Expand Down
18 changes: 13 additions & 5 deletions src/Gameboard.Api/Features/UnityGames/UnityGameService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,13 @@ ConsoleActorMap actorMap

public async Task<Data.Challenge> AddChallenge(NewUnityChallenge newChallenge, User actor)
{
// each _team_ should only get one copy of the challenge. If anyone else tries, send them on their way
// each player should only create their challenge data once, so if they call again, just return what they've already
// got
// each _team_ should only get one copy of the challenge, and by rule, that challenge must have the id
// of the topo gamespace ID. If it's already in the DB, send them on their way with the challenge we've already got
var existingChallenge = await Store.DbContext
.Challenges
.AsNoTracking()
.Include(c => c.Events)
.Where(c => c.GameId == newChallenge.GameId && c.TeamId == newChallenge.TeamId)
.FirstOrDefaultAsync();
.FirstOrDefaultAsync(c => c.Id == newChallenge.GamespaceId);

if (existingChallenge != null)
{
Expand Down Expand Up @@ -157,6 +155,15 @@ ConsoleActorMap actorMap
return newChallengeEntity;
}

public async Task<Data.Challenge> HasChallengeData(NewUnityChallenge model)
{
return await Store.DbContext
.Challenges
.AsNoTracking()
.Include(c => c.Events)
.FirstOrDefaultAsync(c => c.Id == model.GamespaceId);
}

public async Task DeleteChallengeData(string gameId)
{
var challenges = await Store
Expand Down Expand Up @@ -215,6 +222,7 @@ public async Task DeleteChallengeData(string gameId)
challenge.Events.Add(challengeEvent);

// also update the score of the challenge
challenge.LastScoreTime = DateTimeOffset.UtcNow;
challenge.Score += model.PointsScored;

// save it up
Expand Down

0 comments on commit 734fa12

Please sign in to comment.