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); }
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); } }
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()); }
/// <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); } }
/// <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); }
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); } } } }
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); }