private static void HandleUpdate(Session session, UpdateType updateType, byte[] payload, UpdateParameters parameters)
        {
            ClientUpdatePacket updatePacket = PacketManager.CreateUpdatePacket(updateType);

            if (updatePacket == null)
            {
                return;
            }

            using (var stream = new MemoryStream(payload))
                using (var reader = new BinaryReader(stream))
                    updatePacket.ReadUpdate(reader);

            PacketManager.InvokeUpdatePacketHandler(session, updateType, updatePacket, parameters);
        }
Exemple #2
0
        private void OnReceiveData(List <Packet.Packet> packets)
        {
            // We received packets from the server, which means the server is still alive
            OnHeartBeat?.Invoke();

            foreach (var packet in packets)
            {
                // Create a ClientUpdatePacket from the raw packet instance,
                // and read the values into it
                var clientUpdatePacket = new ClientUpdatePacket(packet);
                clientUpdatePacket.ReadPacket();

                UpdateManager.OnReceivePacket(clientUpdatePacket);

                _packetManager.HandleClientPacket(clientUpdatePacket);
            }
        }
Exemple #3
0
        public void SendPlayerUpdate(ushort id, ClientUpdatePacket packet)
        {
            ushort ackSequenceNumber;

            Queue <ushort> ackQueue;

            if (!_toAckSequenceNumbers.ContainsKey(id))
            {
                ackQueue = new Queue <ushort>();
                _toAckSequenceNumbers.Add(id, ackQueue);
            }
            else
            {
                ackQueue = _toAckSequenceNumbers[id];
            }

            if (ackQueue.Count == 0)
            {
                // The queue is somehow empty, this shouldn't happen,
                // but we can still send the update packet
                Logger.Warn(this, "No more client packets to acknowledge, our queue is empty!");

                ackSequenceNumber = 0;
            }
            else
            {
                // Retrieve a sequence number that we need to acknowledge and
                // add it to the packet
                ackSequenceNumber = ackQueue.Dequeue();
            }

            packet.SequenceNumber = ackSequenceNumber;

            // Create the packet and send it
            SendUdp(id, packet.CreatePacket());
        }
Exemple #4
0
        /// <summary>
        /// Callback method for when the net client receives data.
        /// </summary>
        /// <param name="packets">A list of raw received packets.</param>
        private void OnReceiveData(List <Packet.Packet> packets)
        {
            foreach (var packet in packets)
            {
                // Create a ClientUpdatePacket from the raw packet instance,
                // and read the values into it
                var clientUpdatePacket = new ClientUpdatePacket(packet);
                if (!clientUpdatePacket.ReadPacket())
                {
                    // If ReadPacket returns false, we received a malformed packet, which we simply ignore for now
                    continue;
                }

                UpdateManager.OnReceivePacket <ClientUpdatePacket, ClientPacketId>(clientUpdatePacket);

                // If we are not yet connected we check whether this packet contains a login response,
                // so we can finish connecting
                if (!IsConnected)
                {
                    if (clientUpdatePacket.GetPacketData().TryGetValue(
                            ClientPacketId.LoginResponse,
                            out var packetData)
                        )
                    {
                        var loginResponse = (LoginResponse)packetData;

                        switch (loginResponse.LoginResponseStatus)
                        {
                        case LoginResponseStatus.Success:
                            OnConnect(loginResponse);
                            break;

                        case LoginResponseStatus.NotWhiteListed:
                            OnConnectFailed(new ConnectFailedResult {
                                Type = ConnectFailedResult.FailType.NotWhiteListed
                            });
                            return;

                        case LoginResponseStatus.Banned:
                            OnConnectFailed(new ConnectFailedResult {
                                Type = ConnectFailedResult.FailType.Banned
                            });
                            return;

                        case LoginResponseStatus.InvalidAddons:
                            OnConnectFailed(new ConnectFailedResult {
                                Type      = ConnectFailedResult.FailType.InvalidAddons,
                                AddonData = loginResponse.AddonData
                            });
                            return;

                        case LoginResponseStatus.InvalidUsername:
                            OnConnectFailed(new ConnectFailedResult {
                                Type = ConnectFailedResult.FailType.InvalidUsername
                            });
                            return;

                        default:
                            OnConnectFailed(new ConnectFailedResult {
                                Type = ConnectFailedResult.FailType.Unknown
                            });
                            return;
                        }

                        break;
                    }
                }

                _packetManager.HandleClientPacket(clientUpdatePacket);
            }
        }
Exemple #5
0
        /// <summary>
        ///
        /// </summary>
        public static void InvokeUpdatePacketHandler(NetworkSession session, UpdateType updateType, ClientUpdatePacket packet, UpdateParameters parameters)
        {
            if (!updatePacketHandlers.TryGetValue(updateType, out UpdatePacketHandler handler))
            {
                log.Warn($"Received unhandled update type 0x{updateType:X}!");
                return;
            }

            handler.Invoke(session, packet, parameters);
        }
Exemple #6
0
        private void OnUpdatePacket(ClientUpdatePacket packet)
        {
            // We received an update from the server, so we can reset the heart beat stopwatch
            _heartBeatReceiveStopwatch.Reset();
            // Only start the stopwatch again if we are actually connected
            if (_netClient.IsConnected)
            {
                _heartBeatReceiveStopwatch.Start();
            }

            if (packet.UpdateTypes.Contains(UpdateType.PlayerUpdate))
            {
                // Update the values of the player objects in the packet
                foreach (var playerUpdate in packet.PlayerUpdates)
                {
                    if (playerUpdate.UpdateTypes.Contains(PlayerUpdateType.Position))
                    {
                        _playerManager.UpdatePosition(playerUpdate.Id, playerUpdate.Position);
                    }

                    if (playerUpdate.UpdateTypes.Contains(PlayerUpdateType.Scale))
                    {
                        _playerManager.UpdateScale(playerUpdate.Id, playerUpdate.Scale);
                    }

                    if (playerUpdate.UpdateTypes.Contains(PlayerUpdateType.MapPosition))
                    {
                        _mapManager.OnPlayerMapUpdate(playerUpdate.Id, playerUpdate.MapPosition);
                    }

                    if (playerUpdate.UpdateTypes.Contains(PlayerUpdateType.Animation))
                    {
                        foreach (var animationInfo in playerUpdate.AnimationInfos)
                        {
                            _animationManager.OnPlayerAnimationUpdate(
                                playerUpdate.Id,
                                animationInfo.ClipId,
                                animationInfo.Frame,
                                animationInfo.EffectInfo
                                );
                        }
                    }
                }
            }

            // We only propagate entity updates to the entity manager if we have determined the scene host
            if (_sceneHostDetermined && packet.UpdateTypes.Contains(UpdateType.EntityUpdate))
            {
                foreach (var entityUpdate in packet.EntityUpdates)
                {
                    if (entityUpdate.UpdateTypes.Contains(EntityUpdateType.Position))
                    {
                        _entityManager.UpdateEntityPosition(entityUpdate.EntityType, entityUpdate.Id,
                                                            entityUpdate.Position);
                    }

                    // First update the variables, afterwards the state
                    if (entityUpdate.UpdateTypes.Contains(EntityUpdateType.Variables))
                    {
                        _entityManager.UpdateEntityVariables(entityUpdate.EntityType, entityUpdate.Id, entityUpdate.FsmVariables);
                    }

                    if (entityUpdate.UpdateTypes.Contains(EntityUpdateType.State))
                    {
                        _entityManager.UpdateEntityState(entityUpdate.EntityType, entityUpdate.Id, entityUpdate.StateIndex);
                    }
                }
            }
        }
Exemple #7
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);
        }