public void OnGamePlayerPreJoinEvent(IGamePlayerJoiningEvent e)
        {
            var client = e.Player.Client;
            var host   = e.Game.Host;

            if (host != null)
            {
                var hostMods   = host.Client.GetReactor()?.Mods;
                var clientMods = client.GetReactor()?.Mods;

                var clientMissing = hostMods != null
                    ? hostMods.Where(mod => mod.Side == PluginSide.Both && (clientMods == null || !clientMods.Contains(mod))).ToArray()
                    : Array.Empty <Mod>();

                var hostMissing = clientMods != null
                    ? clientMods.Where(mod => mod.Side == PluginSide.Both && (hostMods == null || !hostMods.Contains(mod))).ToArray()
                    : Array.Empty <Mod>();

                if (clientMissing.Any() || hostMissing.Any())
                {
                    var message = new StringBuilder();

                    if (clientMissing.Any())
                    {
                        message.Append("You are missing: ");
                        message.AppendJoin(", ", clientMissing.Select(x => $"{x.Id} ({x.Version})"));
                        message.AppendLine();
                    }

                    if (hostMissing.Any())
                    {
                        message.Append("Host is missing: ");
                        message.AppendJoin(", ", hostMissing.Select(x => $"{x.Id} ({x.Version})"));
                        message.AppendLine();
                    }

                    e.JoinResult = GameJoinResult.CreateCustomError(message.ToString());
                }
            }
        }
Пример #2
0
        public async ValueTask <GameJoinResult> AddClientAsync(ClientBase client)
        {
            var hasLock = false;

            try
            {
                hasLock = await _clientAddLock.WaitAsync(TimeSpan.FromMinutes(1));

                if (hasLock)
                {
                    return(await AddClientSafeAsync(client));
                }
            }
            finally
            {
                if (hasLock)
                {
                    _clientAddLock.Release();
                }
            }

            return(GameJoinResult.FromError(GameJoinError.InvalidClient));
        }
Пример #3
0
        public override async ValueTask HandleMessageAsync(IMessageReader reader, MessageType messageType)
        {
            byte flag = reader.Tag;

            _logger.LogTrace("[{0}] Server got {1}.", Id, flag);

            switch (flag)
            {
            case MessageFlags.HostGame:
            {
                // Read game settings.
                GameOptionsData gameInfo = Message00HostGameC2S.Deserialize(reader, out _);

                // Create game.
                IGame?game = await _gameManager.CreateAsync(this, gameInfo);

                if (game == null)
                {
                    await DisconnectAsync(DisconnectReason.GameMissing);

                    return;
                }

                // Code in the packet below will be used in JoinGame.
                using (var writer = MessageWriter.Get(MessageType.Reliable))
                {
                    Message00HostGameS2C.Serialize(writer, game.Code);
                    await Connection.SendAsync(writer);
                }

                break;
            }

            case MessageFlags.JoinGame:
            {
                Message01JoinGameC2S.Deserialize(reader, out GameCode gameCode);

                Game?game = _gameManager.Find(gameCode);
                if (game == null)
                {
                    await DisconnectAsync(DisconnectReason.GameMissing);

                    return;
                }

                GameJoinResult result = await game.AddClientAsync(this);

                switch (result.Error)
                {
                case GameJoinError.None:
                    break;

                case GameJoinError.InvalidClient:
                    await DisconnectAsync(DisconnectReason.Custom, "Client is in an invalid state.");

                    break;

                case GameJoinError.Banned:
                    await DisconnectAsync(DisconnectReason.Banned);

                    break;

                case GameJoinError.GameFull:
                    await DisconnectAsync(DisconnectReason.GameFull);

                    break;

                case GameJoinError.InvalidLimbo:
                    await DisconnectAsync(DisconnectReason.Custom, "Invalid limbo state while joining.");

                    break;

                case GameJoinError.GameStarted:
                    await DisconnectAsync(DisconnectReason.GameStarted);

                    break;

                case GameJoinError.GameDestroyed:
                    await DisconnectAsync(DisconnectReason.Custom, DisconnectMessages.Destroyed);

                    break;

                case GameJoinError.Custom:
                    await DisconnectAsync(DisconnectReason.Custom, result.Message);

                    break;

                default:
                    await DisconnectAsync(DisconnectReason.Custom, "Unknown error.");

                    break;
                }

                break;
            }

            case MessageFlags.StartGame:
            {
                if (!IsPacketAllowed(reader, true))
                {
                    return;
                }

                await Player !.Game.HandleStartGame(reader);
                break;
            }

            // No idea how this flag is triggered.
            case MessageFlags.RemoveGame:
                break;

            case MessageFlags.RemovePlayer:
            {
                if (!IsPacketAllowed(reader, true))
                {
                    return;
                }

                Message04RemovePlayerC2S.Deserialize(
                    reader,
                    out int playerId,
                    out byte reason);

                await Player !.Game.HandleRemovePlayer(playerId, (DisconnectReason)reason);
                break;
            }

            case MessageFlags.GameData:
            case MessageFlags.GameDataTo:
            {
                if (!IsPacketAllowed(reader, false))
                {
                    return;
                }

                bool toPlayer = flag == MessageFlags.GameDataTo;

                int  position = reader.Position;
                bool verified = await Player !.Game.HandleGameDataAsync(reader, Player, toPlayer);
                reader.Seek(position);

                if (verified && Player != null)
                {
                    // Broadcast packet to all other players.
                    using (var writer = MessageWriter.Get(messageType))
                    {
                        if (toPlayer)
                        {
                            int target = reader.ReadPackedInt32();
                            reader.CopyTo(writer);
                            await Player.Game.SendToAsync(writer, target);
                        }
                        else
                        {
                            reader.CopyTo(writer);
                            await Player.Game.SendToAllExceptAsync(writer, Id);
                        }
                    }
                }

                break;
            }

            case MessageFlags.EndGame:
            {
                if (!IsPacketAllowed(reader, true))
                {
                    return;
                }

                Message08EndGameC2S.Deserialize(
                    reader,
                    out GameOverReason gameOverReason);

                await Player !.Game.HandleEndGame(reader, gameOverReason);
                break;
            }

            case MessageFlags.AlterGame:
            {
                if (!IsPacketAllowed(reader, true))
                {
                    return;
                }

                Message10AlterGameC2S.Deserialize(
                    reader,
                    out AlterGameTags gameTag,
                    out bool value);

                if (gameTag != AlterGameTags.ChangePrivacy)
                {
                    return;
                }

                await Player !.Game.HandleAlterGame(reader, Player, value);
                break;
            }

            case MessageFlags.KickPlayer:
            {
                if (!IsPacketAllowed(reader, true))
                {
                    return;
                }

                Message11KickPlayerC2S.Deserialize(
                    reader,
                    out int playerId,
                    out bool isBan);

                await Player !.Game.HandleKickPlayer(playerId, isBan);
                break;
            }

            case MessageFlags.GetGameListV2:
            {
                Message16GetGameListC2S.Deserialize(reader, out var options, out _);
                await OnRequestGameListAsync(options);

                break;
            }

            default:
                if (_customMessageManager.TryGet(flag, out var customRootMessage))
                {
                    await customRootMessage.HandleMessageAsync(this, reader, messageType);

                    break;
                }

                _logger.LogWarning("Server received unknown flag {0}.", flag);
                break;
            }

#if DEBUG
            if (flag != MessageFlags.GameData &&
                flag != MessageFlags.GameDataTo &&
                flag != MessageFlags.EndGame &&
                reader.Position < reader.Length)
            {
                _logger.LogWarning(
                    "Server did not consume all bytes from {0} ({1} < {2}).",
                    flag,
                    reader.Position,
                    reader.Length);
            }
#endif
        }
Пример #4
0
        private async ValueTask <GameJoinResult> AddClientSafeAsync(ClientBase client)
        {
            // Check if the IP of the player is banned.
            if (client.Connection != null && _bannedIps.Contains(client.Connection.EndPoint.Address))
            {
                return(GameJoinResult.FromError(GameJoinError.Banned));
            }

            var player = client.Player;

            // Check if;
            // - The player is already in this game.
            // - The game is full.
            if (player?.Game != this && _players.Count >= Options.MaxPlayers)
            {
                return(GameJoinResult.FromError(GameJoinError.GameFull));
            }

            if (GameState == GameStates.Starting || GameState == GameStates.Started)
            {
                return(GameJoinResult.FromError(GameJoinError.GameStarted));
            }

            if (GameState == GameStates.Destroyed)
            {
                return(GameJoinResult.FromError(GameJoinError.GameDestroyed));
            }

            if (Host != null)
            {
                foreach (var hostMod in Host.Client.Mods)
                {
                    if (hostMod.Side == PluginSide.Both && client.Mods.All(clientMod => hostMod.Id != clientMod.Id))
                    {
                        return(GameJoinResult.CreateCustomError($"You are missing {hostMod.Id} - {hostMod.Version}"));
                    }
                }

                foreach (var clientMod in client.Mods)
                {
                    if (clientMod.Side == PluginSide.Both && Host.Client.Mods.All(hostMod => clientMod.Id != hostMod.Id))
                    {
                        return(GameJoinResult.CreateCustomError($"Host of this game is missing {clientMod.Id} - {clientMod.Version}"));
                    }
                }
            }

            var isNew = false;

            if (player == null || player.Game != this)
            {
                var clientPlayer = new ClientPlayer(_serviceProvider.GetRequiredService <ILogger <ClientPlayer> >(), client, this);

                if (!_clientManager.Validate(client))
                {
                    return(GameJoinResult.FromError(GameJoinError.InvalidClient));
                }

                isNew         = true;
                player        = clientPlayer;
                client.Player = clientPlayer;
            }

            // Check current player state.
            if (player.Limbo == LimboStates.NotLimbo)
            {
                return(GameJoinResult.FromError(GameJoinError.InvalidLimbo));
            }

            if (GameState == GameStates.Ended)
            {
                await HandleJoinGameNext(player, isNew);

                return(GameJoinResult.CreateSuccess(player));
            }

            await HandleJoinGameNew(player, isNew);

            return(GameJoinResult.CreateSuccess(player));
        }
Пример #5
0
        private async ValueTask <GameJoinResult> AddClientSafeAsync(ClientBase client)
        {
            // Check if the IP of the player is banned.
            if (_bannedIps.Contains(client.Connection.EndPoint.Address))
            {
                return(GameJoinResult.FromError(GameJoinError.Banned));
            }

            ClientPlayer?player = client.Player;

            // Check if;
            // - The player is already in this game.
            // - The game is full.
            if (player?.Game != this && _players.Count >= Options.MaxPlayers)
            {
                return(GameJoinResult.FromError(GameJoinError.GameFull));
            }

            if (GameState == GameStates.Starting || GameState == GameStates.Started)
            {
                return(GameJoinResult.FromError(GameJoinError.GameStarted));
            }

            if (GameState == GameStates.Destroyed)
            {
                return(GameJoinResult.FromError(GameJoinError.GameDestroyed));
            }

            bool isNew = false;

            if (player == null || player.Game != this)
            {
                var clientPlayer = new ClientPlayer(_serviceProvider.GetRequiredService <ILogger <ClientPlayer> >(), client, this);

                if (!_clientManager.Validate(client))
                {
                    return(GameJoinResult.FromError(GameJoinError.InvalidClient));
                }

                isNew         = true;
                player        = clientPlayer;
                client.Player = clientPlayer;
            }

            // Check current player state.
            if (player.Limbo == LimboStates.NotLimbo)
            {
                return(GameJoinResult.FromError(GameJoinError.InvalidLimbo));
            }

            if (GameState == GameStates.Ended)
            {
                await HandleJoinGameNext(player, isNew);

                return(GameJoinResult.CreateSuccess(player));
            }

            var @event = new GamePlayerJoiningEvent(this, player);
            await _eventManager.CallAsync(@event);

            if (@event.JoinResult != null && [email protected])
            {
                return(@event.JoinResult.Value);
            }

            await HandleJoinGameNew(player, isNew);

            return(GameJoinResult.CreateSuccess(player));
        }