public async Task <Challenge> Preview(NewChallenge model) { var entity = await Store.Load(model); if (entity is Data.Challenge) { return(Mapper.Map <Challenge>(entity)); } var spec = await Store.DbContext.ChallengeSpecs.FindAsync(model.SpecId); //check preview cache, else mojo var cachestate = _localcache.Get <string>(spec.ExternalId); if (cachestate == null) { var state = await Mojo.PreviewGamespaceAsync(spec.ExternalId); Transform(state); cachestate = JsonSerializer.Serialize(state); if (cachestate != null) { _localcache.Set(spec.ExternalId, cachestate, new TimeSpan(0, 60, 0)); } } var challenge = Mapper.Map <Data.Challenge>(spec); challenge.State = cachestate; return(Mapper.Map <Challenge>(challenge)); }
private async Task _validate(NewChallenge model) { if ((await PlayerExists(model.PlayerId)).Equals(false)) { throw new ResourceNotFound(); } if ((await SpecExists(model.SpecId)).Equals(false)) { throw new ResourceNotFound(); } var player = await _store.DbContext.Players.FindAsync(model.PlayerId); if (player.IsLive.Equals(false)) { throw new SessionNotActive(); } var spec = await _store.DbContext.ChallengeSpecs.FindAsync(model.SpecId); if (spec.GameId != player.GameId) { throw new ActionForbidden(); } // Note: not checking "already exists" since this is used idempotently await Task.CompletedTask; }
public async Task <Challenge> Create([FromBody] NewChallenge model) { AuthorizeAny( () => Actor.IsDirector, () => IsSelf(model.PlayerId).Result ); await Validate(model); if (Actor.IsTester.Equals(false)) { model.Variant = 0; } string graderUrl = string.Format( "{0}://{1}{2}", Request.Scheme, Request.Host, Url.Action("Grade") ); var result = await ChallengeService.GetOrAdd(model, Actor.Id, graderUrl); await Hub.Clients.Group(result.TeamId).ChallengeEvent( new HubEvent <Challenge>(result, EventAction.Updated) ); return(result); }
public async Task <Challenge> Preview([FromBody] NewChallenge model) { AuthorizeAny( () => IsSelf(model.PlayerId).Result ); await Validate(model); return(await ChallengeService.Preview(model)); }
public async Task <Challenge> Load(NewChallenge model) { var player = await DbContext.Players.FindAsync(model.PlayerId); return(await DbSet .Include(c => c.Player) .FirstOrDefaultAsync(c => c.SpecId == model.SpecId && ( c.PlayerId == model.PlayerId || c.TeamId == player.TeamId ) ) ); }
public async Task <Challenge> GetOrAdd(NewChallenge model, string actorId, string graderUrl) { var entity = await Store.Load(model); if (entity is not null) { return(Mapper.Map <Challenge>(entity)); } var player = await Store.DbContext.Players.FindAsync(model.PlayerId); var game = await Store.DbContext.Games .Include(g => g.Prerequisites) .Where(g => g.Id == player.GameId) .FirstOrDefaultAsync() ; if ((await Store.ChallengeGamespaceCount(player.TeamId)) >= game.GamespaceLimitPerSession) { throw new GamespaceLimitReached(); } if ((await IsUnlocked(player, game, model.SpecId)).Equals(false)) { throw new ChallengeLocked(); } var lockkey = $"{player.TeamId}{model.SpecId}"; var lockval = Guid.NewGuid(); var locked = _localcache.GetOrCreate(lockkey, entry => { entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(60); return(lockval); }); if (locked != lockval) { throw new ChallengeStartPending(); } var spec = await Store.DbContext.ChallengeSpecs.FindAsync(model.SpecId); string graderKey = Guid.NewGuid().ToString("n"); int playerCount = (game.AllowTeam) ? await Store.DbContext.Players.CountAsync( p => p.TeamId == player.TeamId ) : 1 ; entity = Mapper.Map <Data.Challenge>(model); Mapper.Map(spec, entity); entity.Player = player; entity.TeamId = player.TeamId; entity.GraderKey = graderKey.ToSha256(); try { var state = await Mojo.RegisterGamespaceAsync(new GamespaceRegistration { Players = new RegistrationPlayer[] { new RegistrationPlayer { SubjectId = player.TeamId, SubjectName = player.Name } }, ResourceId = entity.ExternalId, Variant = model.Variant, Points = spec.Points, MaxAttempts = game.MaxAttempts, StartGamespace = true, ExpirationTime = entity.Player.SessionEnd, GraderKey = graderKey, GraderUrl = graderUrl, PlayerCount = playerCount }); Transform(state); Mapper.Map(state, entity); entity.Events.Add(new Data.ChallengeEvent { Id = Guid.NewGuid().ToString("n"), UserId = actorId, TeamId = entity.TeamId, Timestamp = DateTimeOffset.UtcNow, Type = ChallengeEventType.Started }); await Store.Create(entity); await Store.UpdateEtd(entity.SpecId); } catch (Exception ex) { throw ex; } finally { _localcache.Remove(lockkey); } return(Mapper.Map <Challenge>(entity)); }