| 697 | } |
| 698 | |
| 699 | void SendReplication(ScriptingObject* obj, NetworkClientsMask targetClients) |
| 700 | { |
| 701 | auto it = Objects.Find(obj->GetID()); |
| 702 | if (it.IsEnd()) |
| 703 | return; |
| 704 | auto& item = it->Item; |
| 705 | const bool isClient = NetworkManager::IsClient(); |
| 706 | const NetworkClientsMask fullTargetClients = targetClients; |
| 707 | |
| 708 | // If server has no recipients, skip early. |
| 709 | if (!isClient && !targetClients) |
| 710 | return; |
| 711 | |
| 712 | if (item.AsNetworkObject) |
| 713 | item.AsNetworkObject->OnNetworkSerialize(); |
| 714 | |
| 715 | // Serialize object |
| 716 | NetworkStream* stream = CachedWriteStream; |
| 717 | stream->Initialize(); |
| 718 | const bool failed = NetworkReplicator::InvokeSerializer(obj->GetTypeHandle(), obj, stream, true); |
| 719 | if (failed) |
| 720 | { |
| 721 | //NETWORK_REPLICATOR_LOG(Error, "[NetworkReplicator] Cannot serialize object {} of type {} (missing serialization logic)", item.ToString(), obj->GetType().ToString()); |
| 722 | return; |
| 723 | } |
| 724 | const uint32 size = stream->GetPosition(); |
| 725 | if (size > MAX_uint16) |
| 726 | { |
| 727 | LOG(Error, "Too much data for object {} replication ({} bytes provided while limit is {}).", item.ToString(), size, MAX_uint16); |
| 728 | return; |
| 729 | } |
| 730 | |
| 731 | #if USE_NETWORK_REPLICATOR_CACHE |
| 732 | // Process replication cache to skip sending object data if it didn't change |
| 733 | if (item.RepCache.Data.Length() == size && Platform::MemoryCompare(item.RepCache.Data.Get(), stream->GetBuffer(), size) == 0) |
| 734 | { |
| 735 | // Check if only newly joined clients are missing this data to avoid resending it to everyone |
| 736 | NetworkClientsMask missingClients = targetClients & ~item.RepCache.Mask; |
| 737 | |
| 738 | // If data is the same and only the client set changed, replicate to missing clients only |
| 739 | if (!missingClients) |
| 740 | return; |
| 741 | targetClients = missingClients; |
| 742 | } |
| 743 | item.RepCache.Mask = fullTargetClients; |
| 744 | item.RepCache.Data.Copy(stream->GetBuffer(), size); |
| 745 | #endif |
| 746 | // TODO: use Unreliable for dynamic objects that are replicated every frame? (eg. player state) |
| 747 | constexpr NetworkChannelType repChannel = NetworkChannelType::Reliable; |
| 748 | |
| 749 | // Skip serialization of objects that none will receive |
| 750 | if (!isClient) |
| 751 | { |
| 752 | BuildCachedTargets(item, targetClients); |
| 753 | if (CachedTargets.Count() == 0) |
| 754 | return; |
| 755 | } |
| 756 |
no test coverage detected