private void UpdateReceivers()
    {
        var confirmPacketInfos = new List <ConfirmPacketInfo>();
        var senderPacketInfos  = new Dictionary <int, SenderPacketInfo>();

        try
        {
            SenderPacketClassifier.Classify(udpSocket, receivers.Values, confirmPacketInfos, senderPacketInfos);
        }
        catch (UdpSocketException e)
        {
            TextToaster.Toast($"Error from SenderPacketClassifier.Classify(): {e.Message}");
        }

        foreach (var confirmPacketInfo in confirmPacketInfos)
        {
            int receiverId = confirmPacketInfo.ConfirmPacket.receiverId;
            print($"confirmPacketInfo - 1: receiver: {receiverId}");
            // Ignore if there is already a receiver with the receiver ID.
            if (receivers.ContainsKey(receiverId))
            {
                continue;
            }

            print($"confirmPacketInfo - 2: receiver: {receiverId}, sender: {confirmPacketInfo.ConfirmPacket.senderId}");
            // Receiver and KinectOrigin gets created together.
            // When destroying any of them, the other of the pair should also be destroyed.
            var receiver = new Receiver(receiverId, confirmPacketInfo.ConfirmPacket.senderId, confirmPacketInfo.SenderEndPoint);
            receivers.Add(confirmPacketInfo.ConfirmPacket.receiverId, receiver);
            var kinectOrigin = sharedSpaceScene.AddKinectNode(receiverId);

            // Apply transformation of kinectSenderElement if there is a corresponding one.
            if (controllerScene != null)
            {
                var controllerNode = controllerScene.FindNode(receiver.SenderEndPoint);
                if (controllerNode != null)
                {
                    kinectOrigin.transform.localPosition = controllerNode.position;
                    kinectOrigin.transform.localRotation = controllerNode.rotation;
                }
            }
        }

        // Send heartbeat packets to senders.
        foreach (var receiver in receivers.Values)
        {
            receiver.SendHeartBeat(udpSocket);
        }

        // Using a copy of remoteSenders through ToList() as this allows removal of elements from remoteSenders.
        foreach (var senderPacketInfoPair in senderPacketInfos)
        {
            // The keys of senderPacketInfos are sender IDs, not receiver IDs like other collections.
            int senderId = senderPacketInfoPair.Key;
            // Since senderPacketInfos were built based on receivers, there should be a corresponding receiver.
            var receiver   = receivers.Values.First(x => x.SenderId == senderId);
            var kinectNode = sharedSpaceScene.GetKinectNode(receiver.ReceiverId);
            receiver.ReceivePackets(udpSocket, senderPacketInfoPair.Value, kinectNode.KinectRenderer.Material, kinectNode.Speaker.RingBuffer);

            // Remove receivers that did not receive any packet for too long.
            if (receiver.IsTimedOut())
            {
                TextToaster.Toast($"Receiver {receiver.ReceiverId} timed out.");
                receivers.Remove(receiver.ReceiverId);
                sharedSpaceScene.RemoveKinectNode(receiver.ReceiverId);
                continue;
            }

            if (kinectNode.KinectRenderer.State == PrepareState.Unprepared)
            {
                if (receiver.VideoMessages.Count > 0)
                {
                    foreach (var videoMessage in receiver.VideoMessages.Values)
                    {
                        CoroutineRunner.RunWithTotalTimeOut(kinectNode.KinectRenderer.Prepare(videoMessage));
                        break;
                    }
                }
            }
            else if (kinectNode.KinectRenderer.State == PrepareState.Preparing)
            {
                kinectNode.SetProgressText(receiver.SenderEndPoint, kinectNode.kinectRenderer.Progress);
                kinectNode.ProgressTextVisibility = true;
            }
            else if (kinectNode.KinectRenderer.State == PrepareState.Prepared)
            {
                kinectNode.ProgressTextVisibility = false;
            }

            var floorVideoMessage = receiver.VideoMessages.Values.FirstOrDefault(x => x.floor != null);
            if (floorVideoMessage != null)
            {
                kinectNode.UpdateFrame(floorVideoMessage.floor);
            }

            receiver.UpdateFrame(udpSocket);
        }
    }