private async Task SubmitTransaction(TransactionCommand cmd) { using (await _transactionLock.LockAsync()) { _logger.Log(LogLevel.Debug, "gameSession", "Transaction submitted.", new { map = PlayerMap, players = _players.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Id), Cmd = cmd }); var responses = await Task.WhenAll(PlayerMap.Select(async p => { IScenePeerClient peer = null; _players.TryGetValue(p.Value, out peer); TransactionResponse response = null; if (peer != null) { try { var r = await peer.RpcTask <TransactionCommand, TransactionResponse>(VALIDATE_TRANSACTION_RPC, cmd); r.Success = true; response = r; } catch (Exception)//Failed to execute transaction update on client { response = new TransactionResponse { Success = false }; } } return(Tuple.Create(p.Key, response)); })); var receivedResponses = responses.Where(t => t.Item2 != null); var isValid = true; if (receivedResponses.Any()) { isValid = receivedResponses.All(t => t.Item2.Success) && receivedResponses.Select(t => t.Item2).Distinct().Count() == 1; } if (!isValid) { await EnsureTransactionFailed($"game states hash comparaison failed: [{string.Join(", ", responses.Select(t => $"'{t.Item1}' => {t.Item2}"))}]"); } var hash = responses.FirstOrDefault(r => r.Item2 != null)?.Item2.Hash; _commands.Add(new TransactionLogItem { Command = cmd, ResultHash = hash ?? 0, HashAvailable = hash.HasValue }); } }
/// <summary> /// Adds a command to the game state /// </summary> /// <param name="userId">User that issued the command</param> /// <param name="playerId">Player that issued the command. (Different from userId to allow IA players or several players on a single client)</param> /// <param name="command">The command name</param> /// <param name="args"></param> /// <param name="updatedGameHash"></param> /// <returns></returns> public Task SubmitTransaction(string userId, string playerId, string command, JObject args) { if (userId != null) { if (playerId == null) { throw new ClientException("Missing playerId"); } string u; if (!PlayerMap.TryGetValue(playerId, out u)) { throw new InvalidOperationException("The user is not in the PlayerMap"); } if (u != userId) { throw new InvalidOperationException("The user cannot handler the specified playerId"); } if (!_players.ContainsKey(userId)) { throw new InvalidOperationException("The user is not connected"); } } if (command == null) { throw new ClientException("Missing command"); } var cmd = new TransactionCommand { Cmd = command, Arguments = args.ToString(), CreatedOn = DateTime.UtcNow, IssuerPlayerId = playerId, IssuerUserId = userId, FinalStepId = CurrrentStepId++ }; return(SubmitTransaction(cmd)); }