private async void receiveMessage(ushort port)
        {
            using (var udpClient = new UdpClient(port))
            {
                while (true)
                {
                    var receivedResult = await udpClient.ReceiveAsync();

                    ServerUpdatePacket packet = new ServerUpdatePacket(this);
                    packet.Handle(receivedResult.Buffer);

                    //Console.WriteLine(BitConverter.ToString(receivedResult.Buffer));
                }
            }
        }
Пример #2
0
 /// <summary>
 /// Broadcast <see cref="ServerUpdatePacket"/>, the way the packet will be broadcast will dictated by the supplied <see cref="UpdateParameters"/>.
 /// </summary>
 public static void BroadcastUpdate(Session session, ServerUpdatePacket packet, UpdateParameters parameters)
 {
     if (!string.IsNullOrEmpty(parameters.Fellowship))
     {
         BroadcastUpdate(session, packet, parameters.Fellowship);
     }
     else if (parameters.Sequence != 0u)
     {
         BroadcastUpdate(session, packet, parameters.Sequence);
     }
     else
     {
         BroadcastUpdate(session, packet);
     }
 }
Пример #3
0
        public UdpUpdateManager(UdpNetClient udpNetClient)
        {
            _udpNetClient = udpNetClient;

            _sentQueue      = new ConcurrentDictionary <ushort, Stopwatch>();
            _sequenceNumber = 0;

            // TODO: is this a good initial value?
            _averageRtt = 0f;
            _currentSwitchTimeThreshold = 10000;

            _currentUpdatePacket = new ServerUpdatePacket();

            _sendStopwatch              = new Stopwatch();
            _belowThresholdStopwatch    = new Stopwatch();
            _currentCongestionStopwatch = new Stopwatch();
        }
Пример #4
0
        /// <summary>
        /// Handle a list of packets from a registered client.
        /// </summary>
        /// <param name="client">The registered client.</param>
        /// <param name="packets">The list of packets to handle.</param>
        private void HandlePacketsRegisteredClient(NetServerClient client, List <Packet.Packet> packets)
        {
            var id = client.Id;

            foreach (var packet in packets)
            {
                // Create a server update packet from the raw packet instance
                var serverUpdatePacket = new ServerUpdatePacket(packet);
                if (!serverUpdatePacket.ReadPacket())
                {
                    // If ReadPacket returns false, we received a malformed packet, which we simply ignore for now
                    continue;
                }

                client.UpdateManager.OnReceivePacket <ServerUpdatePacket, ServerPacketId>(serverUpdatePacket);

                // Let the packet manager handle the received data
                _packetManager.HandleServerPacket(id, serverUpdatePacket);
            }
        }
Пример #5
0
        /**
         * Callback for when UDP traffic is received
         */
        private void OnUdpReceive(IAsyncResult result)
        {
            // Initialize default IPEndPoint for reference in data receive method
            var endPoint = new IPEndPoint(IPAddress.Any, 0);

            byte[] receivedData = { };
            try {
                receivedData = _udpClient.EndReceive(result, ref endPoint);
            } catch (Exception e) {
                Logger.Get().Warn(this, $"UDP Receive exception: {e.Message}");
            }

            // Immediately start receiving data again
            _udpClient.BeginReceive(OnUdpReceive, null);

            // Figure out which client ID this data is from
            ushort id      = 0;
            var    idFound = false;

            foreach (var client in _clients.GetCopy().Values)
            {
                if (client.HasAddress(endPoint))
                {
                    id      = client.GetId();
                    idFound = true;
                    break;
                }
            }

            if (!idFound)
            {
                Logger.Get().Warn(this,
                                  $"Received UDP data from {endPoint.Address}, but there was no matching known client");

                return;
            }

            List <Packet> packets;

            // Lock the leftover data array for synchronous data handling
            // This makes sure that from another asynchronous receive callback we don't
            // read/write to it in different places
            lock (_lock) {
                packets = PacketManager.HandleReceivedData(receivedData, ref _leftoverData);
            }

            // We received packets from this client, which means they are still alive
            OnHeartBeat?.Invoke(id);

            foreach (var packet in packets)
            {
                // Create a server update packet from the raw packet instance
                var serverUpdatePacket = new ServerUpdatePacket(packet);
                serverUpdatePacket.ReadPacket();

                _clients[id].UpdateManager.OnReceivePacket(serverUpdatePacket);

                // Let the packet manager handle the received data
                _packetManager.HandleServerPacket(id, serverUpdatePacket);
            }
        }
Пример #6
0
        /// <summary>
        /// Handle a list of packets from an unregistered client.
        /// </summary>
        /// <param name="client">The unregistered client.</param>
        /// <param name="packets">The list of packets to handle.</param>
        private void HandlePacketsUnregisteredClient(NetServerClient client, List <Packet.Packet> packets)
        {
            for (var i = 0; i < packets.Count; i++)
            {
                var packet = packets[i];

                // Create a server update packet from the raw packet instance
                var serverUpdatePacket = new ServerUpdatePacket(packet);
                if (!serverUpdatePacket.ReadPacket())
                {
                    // If ReadPacket returns false, we received a malformed packet, which we simply ignore for now
                    // Logger.Get().Warn(this, "Received malformed packet, ignoring");
                    continue;
                }

                client.UpdateManager.OnReceivePacket <ServerUpdatePacket, ServerPacketId>(serverUpdatePacket);

                if (!serverUpdatePacket.GetPacketData().TryGetValue(
                        ServerPacketId.LoginRequest,
                        out var packetData
                        ))
                {
                    continue;
                }

                var loginRequest = (LoginRequest)packetData;

                Logger.Get().Info(this, $"Received login request from '{loginRequest.Username}'");

                // Invoke the handler of the login request and decide what to do with the client based on the result
                var allowClient = LoginRequestEvent?.Invoke(
                    client.Id,
                    client.EndPoint,
                    loginRequest,
                    client.UpdateManager
                    );
                if (!allowClient.HasValue)
                {
                    Logger.Get().Error(this, "Login request has no handler");
                    return;
                }

                if (allowClient.Value)
                {
                    // Logger.Get().Info(this, $"Login request from '{loginRequest.Username}' approved");
                    // client.UpdateManager.SetLoginResponseData(LoginResponseStatus.Success);

                    // Register the client and add them to the dictionary
                    client.IsRegistered           = true;
                    _registeredClients[client.Id] = client;

                    // Now that the client is registered, we forward the rest of the packets to the other handler
                    var leftoverPackets = packets.GetRange(
                        i + 1,
                        packets.Count - i - 1
                        );

                    HandlePacketsRegisteredClient(client, leftoverPackets);
                }
                else
                {
                    client.Disconnect();
                    _clients.Remove(client);
                }

                break;
            }
        }
Пример #7
0
 /// <summary>
 /// Broadcast <see cref="ServerUpdatePacket"/> to character.
 /// </summary>
 public static void BroadcastUpdate(Session session, ServerUpdatePacket packet, uint sequence)
 {
     session.Fellowships.ForEach(f => f.BroadcastMessage(packet, sequence));
 }
Пример #8
0
 /// <summary>
 /// Broadcast <see cref="ServerUpdatePacket"/> to session fellowship.
 /// </summary>
 public static void BroadcastUpdate(Session session, ServerUpdatePacket packet, string fellowship, bool excludeSelf = false)
 {
     session.Fellowships.SingleOrDefault(f => f.Info.Name == fellowship)?.BroadcastMessage(packet, excludeSelf ? session.Character : null);
 }
Пример #9
0
        private void OnPlayerUpdate(ushort id, ServerUpdatePacket packet)
        {
            if (!_playerData.TryGetValue(id, out var playerData))
            {
                Logger.Warn(this, $"Received PlayerUpdate packet, but player with ID {id} is not in mapping");
                return;
            }

            // Since we received an update from the player, we can reset their heart beat stopwatch
            playerData.HeartBeatStopwatch.Reset();
            playerData.HeartBeatStopwatch.Start();

            if (packet.UpdateTypes.Contains(UpdateType.PlayerUpdate))
            {
                var playerUpdate = packet.PlayerUpdate;

                if (playerUpdate.UpdateTypes.Contains(PlayerUpdateType.Position))
                {
                    playerData.LastPosition = playerUpdate.Position;
                }

                if (playerUpdate.UpdateTypes.Contains(PlayerUpdateType.Scale))
                {
                    playerData.LastScale = playerUpdate.Scale;
                }

                if (playerUpdate.UpdateTypes.Contains(PlayerUpdateType.MapPosition))
                {
                    playerData.LastMapPosition = playerUpdate.MapPosition;
                }

                if (playerUpdate.UpdateTypes.Contains(PlayerUpdateType.Animation))
                {
                    var animationInfos = playerUpdate.AnimationInfos;

                    // Check whether there is any animation info to be stored
                    if (animationInfos.Count != 0)
                    {
                        // Set the last animation clip to be the last clip in the animation info list
                        // Since that is the last clip that the player updated
                        playerData.LastAnimationClip = animationInfos[animationInfos.Count - 1].ClipId;

                        // Now we need to update each playerData instance to include all animation info instances,
                        // that way when we send them an update packet (as response), we can include that animation info
                        // of this player
                        foreach (var idPlayerDataPair in _playerData.GetCopy())
                        {
                            // Skip over the player that we received from
                            if (idPlayerDataPair.Key == id)
                            {
                                continue;
                            }

                            var otherPd = idPlayerDataPair.Value;

                            // We only queue the animation info if the players are on the same scene,
                            // otherwise the animations get spammed once the players enter the same scene
                            if (!otherPd.CurrentScene.Equals(playerData.CurrentScene))
                            {
                                continue;
                            }

                            // If the queue did not exist yet, we create it and add it
                            if (!otherPd.AnimationInfoToSend.TryGetValue(id, out var animationInfoQueue))
                            {
                                animationInfoQueue = new ConcurrentQueue <AnimationInfo>();

                                otherPd.AnimationInfoToSend[id] = animationInfoQueue;
                            }
                            else
                            {
                                animationInfoQueue = otherPd.AnimationInfoToSend[id];
                            }

                            // For each of the animationInfo that the player sent, add them to this other player data instance
                            foreach (var animationInfo in animationInfos)
                            {
                                animationInfoQueue.Enqueue(animationInfo);
                            }
                        }
                    }
                }
            }

            if (packet.UpdateTypes.Contains(UpdateType.EntityUpdate))
            {
                var packetEntityUpdates = packet.EntityUpdates;

                foreach (var idPlayerDataPair in _playerData.GetCopy())
                {
                    // Skip over the player that we received from
                    if (idPlayerDataPair.Key == id)
                    {
                        continue;
                    }

                    var otherPd = idPlayerDataPair.Value;

                    // We only queue entity updates for players in the same scene
                    if (!otherPd.CurrentScene.Equals(playerData.CurrentScene))
                    {
                        continue;
                    }

                    // For each entity update, we enqueue it
                    foreach (var entityUpdate in packetEntityUpdates)
                    {
                        otherPd.EntityUpdates.Enqueue(entityUpdate);
                    }
                }
            }

            // Now we need to update the player from which we received an update of all current (and relevant)
            // information of the other players
            var clientUpdatePacket = new ClientUpdatePacket();

            foreach (var idPlayerDataPair in _playerData.GetCopy())
            {
                if (idPlayerDataPair.Key == id)
                {
                    continue;
                }

                var otherPd = idPlayerDataPair.Value;

                // Keep track of whether we actually update any value of the player
                // we are looping over, otherwise, we don't have to add the PlayerUpdate instance
                var wasUpdated = false;

                // Create a new PlayerUpdate instance
                var playerUpdate = new PlayerUpdate {
                    Id = idPlayerDataPair.Key
                };

                // If the players are on the same scene, we need to update
                // position, scale and all unsent animations
                if (playerData.CurrentScene.Equals(otherPd.CurrentScene))
                {
                    wasUpdated = true;

                    playerUpdate.UpdateTypes.Add(PlayerUpdateType.Position);
                    playerUpdate.Position = otherPd.LastPosition;

                    playerUpdate.UpdateTypes.Add(PlayerUpdateType.Scale);
                    playerUpdate.Scale = otherPd.LastScale;

                    // Get the queue of animation info corresponding to the player that we are
                    // currently looping over, which is meant for the player we need to update
                    // If the queue exists and is non-empty, we add the info
                    if (playerData.AnimationInfoToSend.TryGetValue(idPlayerDataPair.Key, out var animationInfoQueue))
                    {
                        var infoQueueCopy = animationInfoQueue.GetCopy();

                        if (infoQueueCopy.Count > 0)
                        {
                            playerUpdate.UpdateTypes.Add(PlayerUpdateType.Animation);
                            playerUpdate.AnimationInfos.AddRange(infoQueueCopy);

                            animationInfoQueue.Clear();
                        }
                    }
                }

                // If the map icons need to be broadcast, we add those to the player update
                if (_gameSettings.AlwaysShowMapIcons || _gameSettings.OnlyBroadcastMapIconWithWaywardCompass)
                {
                    wasUpdated = true;

                    playerUpdate.UpdateTypes.Add(PlayerUpdateType.MapPosition);
                    playerUpdate.MapPosition = otherPd.LastMapPosition;
                }

                // Finally, add the finalized playerUpdate instance to the packet
                // However, we only do this if any values were updated
                if (wasUpdated)
                {
                    clientUpdatePacket.UpdateTypes.Add(UpdateType.PlayerUpdate);
                    clientUpdatePacket.PlayerUpdates.Add(playerUpdate);
                }
            }

            // Get a copy of the queue and check whether it is non-empty
            var entityUpdates = playerData.EntityUpdates.GetCopy();

            if (entityUpdates.Count > 0)
            {
                // Add the entity updates to the packet and signal that we are
                // sending entity updates in this packet
                clientUpdatePacket.UpdateTypes.Add(UpdateType.EntityUpdate);

                clientUpdatePacket.EntityUpdates.AddRange(entityUpdates);

                // Clear the original queue
                playerData.EntityUpdates.Clear();
            }

            // Once this is done for each player that needs updates,
            // and all entity updates that were stored we can send the packet
            _netServer.SendPlayerUpdate(id, clientUpdatePacket);
        }