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