Beispiel #1
0
        public override void OnOperateRequest(byte[] bytes, ClientPeer peer, SendParameters sendParameters)
        {
            ReadyData readyData = PackageHelper.Desirialize <ReadyData>(bytes);

            GameApplication application = GameApplication.Instance as GameApplication;

            if (application == null)
            {
                return;
            }

            Room       room       = application.GetRoom(readyData.roomID);
            ClientInfo clientInfo = application.GetClientInfo(peer as GameClientPeer);

            if (room != null && clientInfo != null)
            {
                room.Ready(clientInfo);
            }
        }
Beispiel #2
0
        /// <summary>
        /// Constructs a lobby
        /// </summary>
        /// <param name="clientWebSocket">Client web socket</param>
        /// <param name="isConnectionSecure">Is connection secure</param>
        /// <param name="lobbyID">Lobby ID</param>
        /// <param name="minimalDrawingTime">Minimal drawing time in seconds</param>
        /// <param name="maximalDrawingTime">Maximal drawing time in seconds</param>
        /// <param name="minimalRoundCount">Minimal round count</param>
        /// <param name="maximalRoundCount">Maximal round count</param>
        /// <param name="minimalMaximalPlayerCount">Minimal of maximal player count</param>
        /// <param name="maximalMaximalPlayerCount">Maximal of maximal player count</param>
        /// <param name="minimalClientsPerIPLimit">Minimal clients per IP limit</param>
        /// <param name="maximalClientsPerIPLimit">Maximal clients per IP limit</param>
        /// <param name="maximalPlayerCount">Maximal player count</param>
        /// <param name="currentMaximalRoundCount">Current maximal round count</param>
        /// <param name="isLobbyPublic">Is lobby public</param>
        /// <param name="isVotekickingEnabled">Is votekicking enabled</param>
        /// <param name="customWordsChance">Custom words chance</param>
        /// <param name="allowedClientsPerIPCount">Allowed clients per IP count</param>
        /// <param name="drawingBoardBaseWidth">Drawing board base width</param>
        /// <param name="drawingBoardBaseHeight">Drawing board base height</param>
        /// <param name="minimalBrushSize">Minimal brush size</param>
        /// <param name="maximalBrushSize">Maximal brush size</param>
        /// <param name="suggestedBrushSizes">Suggested brush sizes</param>
        /// <param name="canvasColor">Canvas color</param>
        public Lobby
        (
            ClientWebSocket clientWebSocket,
            bool isConnectionSecure,
            string lobbyID,
            uint minimalDrawingTime,
            uint maximalDrawingTime,
            uint minimalRoundCount,
            uint maximalRoundCount,
            uint minimalMaximalPlayerCount,
            uint maximalMaximalPlayerCount,
            uint minimalClientsPerIPLimit,
            uint maximalClientsPerIPLimit,
            uint maximalPlayerCount,
            uint currentMaximalRoundCount,
            bool isLobbyPublic,
            bool isVotekickingEnabled,
            uint customWordsChance,
            uint allowedClientsPerIPCount,
            uint drawingBoardBaseWidth,
            uint drawingBoardBaseHeight,
            uint minimalBrushSize,
            uint maximalBrushSize,
            IEnumerable<uint> suggestedBrushSizes,
            IColor canvasColor
        )
        {
            Limits = new LobbyLimits
            (
                minimalDrawingTime,
                maximalDrawingTime,
                minimalRoundCount,
                maximalRoundCount,
                minimalMaximalPlayerCount,
                maximalMaximalPlayerCount,
                minimalClientsPerIPLimit,
                maximalClientsPerIPLimit,
                minimalBrushSize,
                maximalBrushSize
            );
            if (maximalPlayerCount < minimalMaximalPlayerCount)
            {
                throw new ArgumentException("Maximal player count can't be smaller than minimal of maximal player count.", nameof(maximalPlayerCount));
            }
            if (maximalPlayerCount > maximalMaximalPlayerCount)
            {
                throw new ArgumentException("Maximal player count can't be bigger than maximal of maximal player count.", nameof(maximalPlayerCount));
            }
            if (currentMaximalRoundCount < minimalRoundCount)
            {
                throw new ArgumentException("Current maximal round count can't be smaller than minimal round count.", nameof(currentMaximalRoundCount));
            }
            if (currentMaximalRoundCount > maximalRoundCount)
            {
                throw new ArgumentException("Current maximal round count can't be bigger than maximal round count.", nameof(currentMaximalRoundCount));
            }
            if (customWordsChance > 100U)
            {
                throw new ArgumentException("Custom words chance can't be bigger than 100.", nameof(customWordsChance));
            }
            if (allowedClientsPerIPCount < minimalClientsPerIPLimit)
            {
                throw new ArgumentException("Clients per IP limit can't be smaller than minimal clients per IP limit.", nameof(allowedClientsPerIPCount));
            }
            if (allowedClientsPerIPCount > maximalClientsPerIPLimit)
            {
                throw new ArgumentException("Clients per IP limit can't be bigger than maximal clients per IP limit.", nameof(allowedClientsPerIPCount));
            }
            if (drawingBoardBaseWidth < 1U)
            {
                throw new ArgumentException("Drawing board base width can't be smaller than one.", nameof(drawingBoardBaseWidth));
            }
            if (drawingBoardBaseHeight < 1U)
            {
                throw new ArgumentException("Drawing board base height can't be smaller than one.", nameof(drawingBoardBaseHeight));
            }
            this.clientWebSocket = clientWebSocket ?? throw new ArgumentNullException(nameof(clientWebSocket));
            IsConnectionSecure = isConnectionSecure;
            LobbyID = lobbyID ?? throw new ArgumentNullException(nameof(lobbyID));
            MaximalPlayerCount = maximalPlayerCount;
            CurrentMaximalRoundCount = currentMaximalRoundCount;
            IsLobbyPublic = isLobbyPublic;
            IsVotekickingEnabled = isVotekickingEnabled;
            CustomWordsChance = customWordsChance;
            AllowedClientsPerIPCount = allowedClientsPerIPCount;
            DrawingBoardBaseWidth = drawingBoardBaseWidth;
            DrawingBoardBaseHeight = drawingBoardBaseHeight;
            SuggestedBrushSizes = suggestedBrushSizes ?? throw new ArgumentNullException(nameof(suggestedBrushSizes));
            CanvasColor = canvasColor;
            AddMessageParser<ReadyReceiveGameMessageData>
            (
                (gameMessage, json) =>
                {
                    ReadyData ready = gameMessage.Data;
                    uint current_round = ready.CurrentRound;
                    uint current_maximal_round_count = ready.CurrentMaximalRoundCount;
                    long current_drawing_time = ready.CurrentDrawingTime;
                    if (current_maximal_round_count > Limits.MaximalRoundCount)
                    {
                        throw new InvalidDataException($"Current maximal round count can't be bigger than maximal round count, which is { Limits.MaximalRoundCount }.");
                    }
                    if (current_round > current_maximal_round_count)
                    {
                        throw new InvalidDataException($"Current round can't be bigger than current maximal round count, which is { current_maximal_round_count }.");
                    }
                    if (current_drawing_time > (Limits.MaximalDrawingTime * 1000L))
                    {
                        throw new InvalidDataException($"Current drawing time can't be bigger than the specified maximal maximal player count, which is { Limits.MaximalMaximalPlayerCount }.");
                    }
                    IsPlayerAllowedToDraw = ready.IsPlayerAllowedToDraw;
                    IsVotekickingEnabled = ready.IsVotekickingEnabled;
                    GameState = ready.GameState;
                    CurrentRound = current_round;
                    CurrentMaximalRoundCount = current_maximal_round_count;
                    CurrentDrawingTime = ready.CurrentDrawingTime;
                    if (ready.WordHints == null)
                    {
                        wordHints = Array.Empty<IWordHint>();
                    }
                    else
                    {
                        if (wordHints.Length != ready.WordHints.Count)
                        {
                            wordHints = new IWordHint[ready.WordHints.Count];
                        }
#if SCRIBBLERS_SHARP_NO_PARALLEL_LOOPS
                        for (int index = 0; index < wordHints.Length; index++)
#else
                        Parallel.For(0, wordHints.Length, (index) =>
#endif
                        {
                            WordHintData word_hint_data = ready.WordHints[index];
                            wordHints[index] = new WordHint(word_hint_data.Character, word_hint_data.Underline);
                        }
#if !SCRIBBLERS_SHARP_NO_PARALLEL_LOOPS
                        );
#endif
                    }
                    UpdateAllPlayers(ready.Players);
                    MyPlayer = players.ContainsKey(ready.PlayerID) ? players[ready.PlayerID] : null;
                    Owner = players.ContainsKey(ready.OwnerID) ? players[ready.OwnerID] : null;
                    currentDrawing.Clear();
                    JObject json_object = JObject.Parse(json);
                    if (json_object.ContainsKey("data"))
                    {
                        if (json_object["data"] is JObject json_data_object)
                        {
                            if (json_data_object.ContainsKey("currentDrawing"))
                            {
                                if (json_data_object["currentDrawing"] is JArray json_draw_commands)
                                {
                                    ParseCurrentDrawingFromJSON(json_draw_commands);
                                }
                            }
                        }
                    }
                    OnReadyGameMessageReceived?.Invoke();
                },
                MessageParseFailedEvent
            );
            AddMessageParser<NextTurnReceiveGameMessageData>
            (
                (gameMessage, json) =>
                {
                    NextTurnData next_turn = gameMessage.Data;
                    IsPlayerAllowedToDraw = false;
                    GameState = EGameState.Ongoing;
                    CurrentRound = next_turn.Round;
                    CurrentDrawingTime = next_turn.RoundEndTime;
                    UpdateAllPlayers(next_turn.Players);
                    PreviousWord = next_turn.PreviousWord ?? PreviousWord;
                    currentDrawing.Clear();
                    OnNextTurnGameMessageReceived?.Invoke();
                }, MessageParseFailedEvent
            );
            AddMessageParser<GameOverReceiveGameMessage>
            (
                (gameMessage, json) =>
                {
                    GameOverData game_over = gameMessage.Data;
                    IsPlayerAllowedToDraw = false;
                    GameState = EGameState.Ongoing;
                    CurrentRound = game_over.CurrentRound;
                    CurrentDrawingTime = game_over.CurrentDrawingTime;
                    UpdateAllPlayers(game_over.Players);
                    PreviousWord = game_over.PreviousWord ?? PreviousWord;
                    currentDrawing.Clear();
                    OnGameOverMessageReceived?.Invoke();
                }, MessageParseFailedEvent
            );
            AddMessageParser<NameChangeReceiveGameMessageData>
            (
                (gameMessage, json) =>
                {
                    NameChangeData name_change = gameMessage.Data;
                    if (players.ContainsKey(name_change.PlayerID) && players[name_change.PlayerID] is IInternalPlayer internal_player)
                    {
                        internal_player.UpdateNameInternally(name_change.PlayerName);
                        OnNameChangeGameMessageReceived?.Invoke(internal_player);
                    }
                },
                MessageParseFailedEvent
            );
            AddMessageParser<UpdatePlayersReceiveGameMessageData>
            (
                (gameMessage, json) =>
                {
                    UpdateAllPlayers(gameMessage.Data);
                    OnUpdatePlayersGameMessageReceived?.Invoke(players);
                },
                MessageParseFailedEvent
            );
            AddMessageParser<UpdateWordhintReceiveGameMessageData>
            (
                (gameMessage, json) =>
                {
                    GameState = EGameState.Ongoing;
                    WordHintData[] word_hints = gameMessage.Data;
                    if (wordHints.Length != word_hints.Length)
                    {
                        wordHints = new IWordHint[word_hints.Length];
                    }
#if SCRIBBLERS_SHARP_NO_PARALLEL_LOOPS
                    for (int index = 0; index < wordHints.Length; index++)
#else
                    Parallel.For(0, wordHints.Length, (index) =>
#endif
                    {
                        WordHintData word_hint_data = word_hints[index];
                        wordHints[index] = new WordHint(word_hint_data.Character, word_hint_data.Underline);
                    }
#if !SCRIBBLERS_SHARP_NO_PARALLEL_LOOPS
                    );
#endif
                    OnUpdateWordhintGameMessageReceived?.Invoke(wordHints);
                },
                MessageParseFailedEvent
            );
            AddMessageParser<MessageReceiveGameMessageData>((gameMessage, json) => OnMessageGameMessageReceived?.Invoke(players.ContainsKey(gameMessage.Data.AuthorID) ? players[gameMessage.Data.AuthorID] : null, gameMessage.Data.Content), MessageParseFailedEvent);
            AddMessageParser<NonGuessingPlayerMessageReceiveGameMessageData>((gameMessage, json) => OnNonGuessingPlayerMessageGameMessageReceived?.Invoke(players.ContainsKey(gameMessage.Data.AuthorID) ? players[gameMessage.Data.AuthorID] : null, gameMessage.Data.Content), MessageParseFailedEvent);
            AddMessageParser<SystemMessageReceiveGameMessageData>((gameMessage, json) => OnSystemMessageGameMessageReceived?.Invoke(gameMessage.Data), MessageParseFailedEvent);
            AddMessageParser<LineReceiveGameMessageData>
            (
                (gameMessage, json) =>
                {
                    LineData line = gameMessage.Data;
                    IColor color = (Color)line.Color;
                    GameState = EGameState.Ongoing;
                    currentDrawing.Add(new DrawCommand(EDrawCommandType.Line, line.FromX, line.FromY, line.ToX, line.ToY, color, line.LineWidth));
                    OnLineGameMessageReceived?.Invoke(line.FromX, line.FromY, line.ToX, line.ToY, color, line.LineWidth);
                },
                MessageParseFailedEvent
            );
            AddMessageParser<FillReceiveGameMessageData>
            (
                (gameMessage, json) =>
                {
                    FillData fill = gameMessage.Data;
                    IColor color = (Color)fill.Color;
                    currentDrawing.Add(new DrawCommand(EDrawCommandType.Fill, fill.X, fill.Y, fill.X, fill.Y, color, 0.0f));
                    OnFillGameMessageReceived(fill.X, fill.Y, color);
                },
                MessageParseFailedEvent
            );
            AddMessageParser<ClearDrawingBoardReceiveGameMessageData>
            (
                (gameMessage, json) =>
                {
                    currentDrawing.Clear();
                    OnClearDrawingBoardGameMessageReceived?.Invoke();
                },
                MessageParseFailedEvent
            );
            AddMessageParser<YourTurnReceiveGameMessageData>
            (
                (gameMessage, json) =>
                {
                    IsPlayerAllowedToDraw = true;
                    currentDrawing.Clear();
                    OnYourTurnGameMessageReceived?.Invoke((string[])gameMessage.Data.Clone());
                },
                MessageParseFailedEvent
            );
            AddMessageParser<CloseGuessReceiveGameMessageData>((gameMessage, json) => OnCloseGuessGameMessageReceived?.Invoke(gameMessage.Data), MessageParseFailedEvent);
            AddMessageParser<CorrectGuessReceiveGameMessageData>((gameMessage, json) => OnCorrectGuessGameMessageReceived?.Invoke(players.ContainsKey(gameMessage.Data) ? players[gameMessage.Data] : null), MessageParseFailedEvent);
            AddMessageParser<KickVoteReceiveGameMessageData>
            (
                (gameMessage, json) =>
                {
                    KickVoteData kick_vote = gameMessage.Data;
                    if (players.ContainsKey(kick_vote.PlayerID) && players[kick_vote.PlayerID] is IInternalPlayer internal_player)
                    {
                        internal_player.UpdateNameInternally(kick_vote.PlayerName);
                        OnKickVoteGameMessageReceived?.Invoke(internal_player, kick_vote.VoteCount, kick_vote.RequiredVoteCount);
                    }
                },
                MessageParseFailedEvent
            );
            AddMessageParser<DrawerKickedReceiveGameMessageData>((gameMessage, json) => OnDrawerKickedGameMessageReceived?.Invoke(), MessageParseFailedEvent);
            AddMessageParser<OwnerChangeReceiveGameMessageData>
            (
                (gameMessage, json) =>
                {
                    OwnerChangeData owner_change = gameMessage.Data;
                    if (players.ContainsKey(owner_change.PlayerID) && players[owner_change.PlayerID] is IInternalPlayer internal_player)
                    {
                        internal_player.UpdateNameInternally(owner_change.PlayerName);
                        OnOwnerChangeGameMessageReceived?.Invoke(internal_player);
                    }
                },
                MessageParseFailedEvent
            );
            AddMessageParser<DrawingReceiveGameMessageData>
            (
                (gameMessage, json) =>
                {
                    currentDrawing.Clear();
                    JObject json_object = JObject.Parse(json);
                    if (json_object.ContainsKey("data"))
                    {
                        if (json_object["data"] is JArray json_draw_commands)
                        {
                            ParseCurrentDrawingFromJSON(json_draw_commands);
                        }
                    }
                    OnDrawingGameMessageReceived?.Invoke(currentDrawing);
                },
                MessageParseFailedEvent
            );
            AddMessageParser<LobbySettingsChangedReceiveGameMessageData>
            (
                (gameMessage, json) =>
                {
                    LobbySettingsChangeData lobby_settings_change = gameMessage.Data;
                    uint maximal_player_count = lobby_settings_change.MaximalPlayerCount;
                    uint custom_words_chance = lobby_settings_change.CustomWordsChance;
                    if (maximal_player_count < Limits.MinimalMaximalPlayerCount)
                    {
                        throw new InvalidDataException($"Maximal player count can't be smaller than the specified minimal maximal player count, which is { Limits.MinimalMaximalPlayerCount }.");
                    }
                    if (maximal_player_count > Limits.MaximalMaximalPlayerCount)
                    {
                        throw new InvalidDataException($"Maximal player count can't be bigger than the specified maximal maximal player count, which is { Limits.MaximalMaximalPlayerCount }.");
                    }
                    if (custom_words_chance > 100U)
                    {
                        throw new InvalidDataException("Custom words chance can't be bigger than one hundred.");
                    }
                    MaximalPlayerCount = maximal_player_count;
                    IsLobbyPublic = lobby_settings_change.IsLobbyPublic;
                    IsVotekickingEnabled = lobby_settings_change.IsVotekickingEnabled;
                    CustomWordsChance = custom_words_chance;
                },
                MessageParseFailedEvent
            );
            webSocketSendThread = new Thread(async () =>
            {
                while ((this.clientWebSocket != null) && (this.clientWebSocket.State == WebSocketState.Open))
                {
                    while (sendGameMessageQueue.TryDequeue(out string send_game_message))
                    {
                        await clientWebSocket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(send_game_message)), WebSocketMessageType.Text, true, default);
                    }
                }
            });
            webSocketReceiveThread = new Thread(async () =>
            {
                using (MemoryStream memory_stream = new MemoryStream())
                {
                    using (StreamReader reader = new StreamReader(memory_stream))
                    {
                        while ((this.clientWebSocket != null) && (this.clientWebSocket.State == WebSocketState.Open))
                        {
                            try
                            {
                                WebSocketReceiveResult result = await this.clientWebSocket.ReceiveAsync(receiveBuffer, default);
                                if (result != null)
                                {
                                    memory_stream.Write(receiveBuffer.Array, 0, result.Count);
                                    if (result.EndOfMessage)
                                    {
                                        memory_stream.Seek(0L, SeekOrigin.Begin);
                                        receivedGameMessages.Enqueue(reader.ReadToEnd());
                                        memory_stream.Seek(0L, SeekOrigin.Begin);
                                        memory_stream.SetLength(0L);
                                    }
                                }
                            }
                            catch (Exception e)
                            {
                                Console.Error.WriteLine(e);
                                this.clientWebSocket.Dispose();
                                this.clientWebSocket = null;
                            }
                        }
                    }
                }
            });
            webSocketSendThread.Start();
            webSocketReceiveThread.Start();
        }