Beispiel #1
0
        /// <summary>
        /// Invoked when the game has been started.
        /// </summary>
        private void HandleStartGame(MessageReader message)
        {
            OnTalkingEnd?.Invoke();

            _connection.SendReliableMessage(writer =>
            {
                writer.StartMessage((byte)MMTags.GameData);
                writer.Write(_lobbyCode);

                writer.StartMessage((byte)GameDataTags.Ready);
                writer.WritePacked(_clientId);
                writer.EndMessage();

                writer.EndMessage();
            });
        }
        /// <summary>
        /// Initializes this client by connecting to the specified host and attempting
        /// to join the specified lobby code. Will throw if connection fails, else will
        /// start servicing messages in the background. The caller is responsible for
        /// ensuring that the application stays running as long as the client is active.
        /// </summary>
        public async Task Connect(IPAddress address, string lobbyName, ushort port = MATCHMAKER_PORT)
        {
            _address   = address;
            _lobbyName = lobbyName;
            _lobbyCode = GameCode.GameNameToIntV2(lobbyName);

            var(connection, response) = await ConnectToMMAndSend(address, port, JoinGame);

            _port = (ushort)connection.EndPoint.Port;

            _connection = connection;
            _connection.DataReceived += OnMessageReceived;
            _connection.Disconnected += (sender, args) => { OnDisconnect?.Invoke(); };

            HandleJoinGameResult(response);

            if (!_hasPlayerData)
            {
                // If we don't have user data, send a SceneChange so that we receive a spawn.
                _connection.SendReliableMessage(writer =>
                {
                    writer.StartMessage((byte)MMTags.GameData);
                    writer.Write(_lobbyCode);
                    writer.StartMessage((byte)GameDataTags.SceneChange);
                    writer.WritePacked(_clientId); // note: must be _clientId since localplayer is not set yet
                    writer.Write("OnlineGame");
                    writer.EndMessage();
                    writer.EndMessage();
                });
            }
            else
            {
                // We have user data, invoke listeners.
                _hasReconnectedAfterPlayerData = true;

                OnConnect?.Invoke();
                OnPlayerDataUpdate?.Invoke(_playerData);
            }
        }
Beispiel #3
0
        /// <summary>
        /// Connect to the specified ip:port endpoint for the matchmaker, then send the specified
        /// message. Returns a task that resolves to a tuple of the established connection and the
        /// first message received from the matchmaker in response to the sent message (usually
        /// the join/host confirmal message). Will throw if the connection closes prematurely or
        /// otherwise errors. Otherwise, the task itself is responsible for disposing of the
        /// connection once the server disconnects.
        /// </summary>
        private static async Task <(UdpClientConnection, MessageReader)> ConnectToMMAndSend(IPAddress address,
                                                                                            ushort port, Action <MessageWriter> writeMessage)
        {
            var firstMessageTask = new TaskCompletionSource <MessageReader>();

            var connection = new UdpClientConnection(new IPEndPoint(address, port));

            connection.KeepAliveInterval    = 1000;
            connection.DisconnectTimeout    = 10000;
            connection.ResendPingMultiplier = 1.2f;

            // Set up an event handler to resolve the task on first non-reselect message received.
            Action <DataReceivedEventArgs> onDataReceived = null;

            onDataReceived = args =>
            {
                try
                {
                    var msg = args.Message.ReadMessage();
                    if (msg.Tag == (byte)MMTags.ReselectServer)
                    {
                        return;                                          // not interested
                    }
                    firstMessageTask.TrySetResult(msg);
                    connection.DataReceived -= onDataReceived;
                }
                finally
                {
                    args.Message.Recycle();
                }
            };
            connection.DataReceived += onDataReceived;

            // Set up an event handler to set an exception for the task on early disconnect.
            connection.Disconnected += (sender, args) =>
            {
                connection.Dispose();
                firstMessageTask.TrySetException(new AUException("Connection to matchmaker prematurely exited"));
            };

            // Connect to the endpoint.
            connection.ConnectAsync(HANDSHAKE);
            await connection.ConnectWaitLock.AsTask();

            // Send the contents.
            connection.SendReliableMessage(writeMessage);

            // Wait for the response to arrive.
            var response = await firstMessageTask.Task;

            // If this is not a redirect, return the result.
            if (response.Tag != (byte)MMTags.Redirect)
            {
                return(connection, response);
            }

            // This is a redirect, so do this again but with the new data.
            var newIp   = response.ReadUInt32();
            var newPort = response.ReadUInt16();

            // Reconnect to new host.
            return(await ConnectToMMAndSend(new IPAddress(newIp), newPort, writeMessage));
        }