From 8cac8c9ec70c11272bfbcfc9c4675134b1c8e7ff Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 19 Jul 2026 23:13:23 +0100 Subject: [PATCH] Isolate realtime provider broadcasts from departed peers --- .../CultMeshQuicRealtimeTransport.cs | 22 +++++++++- src/GameCult.Mesh/docs/transport-planes.md | 4 ++ ...ultMeshNativeQuicRealtimeTransportTests.cs | 40 ++++++++++++++++++ unity/org.gamecult.cultlib/README.md | 4 +- .../Plugins/GameCult.Caching.MessagePack.dll | Bin 27136 -> 27136 bytes .../Plugins/GameCult.Caching.MessagePack.pdb | Bin 16744 -> 16744 bytes .../Runtime/Plugins/GameCult.Caching.dll | Bin 82944 -> 82944 bytes .../Runtime/Plugins/GameCult.Caching.pdb | Bin 29376 -> 29376 bytes .../Runtime/Plugins/GameCult.Logging.dll | Bin 6656 -> 6656 bytes .../Runtime/Plugins/GameCult.Logging.pdb | Bin 9536 -> 9536 bytes .../Plugins/GameCult.Mesh.Quic.Native.dll | Bin 15360 -> 15360 bytes .../Plugins/GameCult.Mesh.Quic.Native.pdb | Bin 12268 -> 12268 bytes .../Runtime/Plugins/GameCult.Mesh.dll | Bin 593920 -> 593920 bytes .../Runtime/Plugins/GameCult.Mesh.pdb | Bin 192780 -> 192780 bytes .../Runtime/Plugins/GameCult.Networking.dll | Bin 456192 -> 456192 bytes .../Runtime/Plugins/GameCult.Networking.pdb | Bin 229880 -> 229880 bytes unity/org.gamecult.cultlib/package.json | 2 +- 17 files changed, 68 insertions(+), 4 deletions(-) diff --git a/src/GameCult.Mesh.Quic/CultMeshQuicRealtimeTransport.cs b/src/GameCult.Mesh.Quic/CultMeshQuicRealtimeTransport.cs index b9c6f3c6..2bf691a6 100644 --- a/src/GameCult.Mesh.Quic/CultMeshQuicRealtimeTransport.cs +++ b/src/GameCult.Mesh.Quic/CultMeshQuicRealtimeTransport.cs @@ -217,10 +217,30 @@ public sealed class CultMeshQuicRealtimeServer : IAsyncDisposable CancellationToken cancellationToken = default) { if (frame == null) throw new ArgumentNullException(nameof(frame)); - var sends = _clients.Keys.Select(client => client.SendAsync(frame, cancellationToken)).ToArray(); + var sends = _clients.Keys.Select(client => SendToConnectedClientAsync(client, frame, cancellationToken)).ToArray(); if (sends.Length > 0) await Task.WhenAll(sends).ConfigureAwait(false); } + private async Task SendToConnectedClientAsync( + CultMeshQuicRealtimeTransport client, + CultMeshRealtimeFrame frame, + CancellationToken cancellationToken) + { + try + { + await client.SendAsync(frame, cancellationToken).ConfigureAwait(false); + } + catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) + { + throw; + } + catch + { + if (_clients.TryRemove(client, out _)) + client.Dispose(); + } + } + /// Receives the next client-originated state frame. public Task ReceiveAsync(CancellationToken cancellationToken = default) => _received.ReceiveAsync(cancellationToken); diff --git a/src/GameCult.Mesh/docs/transport-planes.md b/src/GameCult.Mesh/docs/transport-planes.md index 219df88a..2d529593 100644 --- a/src/GameCult.Mesh/docs/transport-planes.md +++ b/src/GameCult.Mesh/docs/transport-planes.md @@ -49,6 +49,10 @@ advertised mapping when it is actually reachable; otherwise it connects to the advertised QUIC route. Latest-only receivers must replace pending generations per body rather than queueing render debt. +Provider broadcast isolates physical peers. A client disconnect or send failure +removes that session without failing the publication tick for healthy clients; +only caller cancellation stops the broadcast operation. + **Derived state:** connector choice, physical endpoint, connection health, and transport diagnostics. None of these grant provider authority or become content identity. diff --git a/tests/GameCult.Mesh.Quic.Native.Tests/CultMeshNativeQuicRealtimeTransportTests.cs b/tests/GameCult.Mesh.Quic.Native.Tests/CultMeshNativeQuicRealtimeTransportTests.cs index f46f833e..b491d912 100644 --- a/tests/GameCult.Mesh.Quic.Native.Tests/CultMeshNativeQuicRealtimeTransportTests.cs +++ b/tests/GameCult.Mesh.Quic.Native.Tests/CultMeshNativeQuicRealtimeTransportTests.cs @@ -78,6 +78,46 @@ public sealed class CultMeshNativeQuicRealtimeTransportTests .WithMessage("*certificate*pin*"); } + [Test] + public async Task NativeClientDepartureCannotBreakProviderBroadcast() + { + if (!OperatingSystem.IsWindows() || !QuicListener.IsSupported) + Assert.Ignore("Native MsQuic integration requires Windows QUIC support."); + using var certificate = CreateCertificate(); + await using var server = await CultMeshQuicRealtimeServer.ListenAsync(new CultMeshQuicRealtimeServerOptions + { + ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0), + ServerCertificate = certificate + }); + var pin = Convert.ToHexString(SHA256.HashData(certificate.RawData)); + var endpoint = $"cultmesh-state+quic://127.0.0.1:{server.LocalEndPoint.Port}?cert-sha256={pin}"; + var connector = new CultMeshNativeQuicRealtimeTransportConnector(); + var client = await connector.ConnectAsync( + new CultMeshTransportCandidate(endpoint), + CultMeshEndpointId.Parse("aetheria.local")); + await WaitUntilAsync(() => server.ConnectionCount == 1); + await server.BroadcastAsync(Frame(sequence: 1)); + using var receiveTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)); + await client.ReceiveAsync(receiveTimeout.Token); + + client.Dispose(); + + Func broadcastAfterDeparture = () => server.BroadcastAsync(Frame(sequence: 2)); + await broadcastAfterDeparture.Should().NotThrowAsync( + "a short-lived realtime observer cannot terminate provider publication"); + } + + private static CultMeshRealtimeFrame Frame(long sequence) => new() + { + ChannelId = "aetheria.entities", + SchemaId = "gamecult.aetheria.entity_soa.v1", + BodyId = "aetheria.entities.current-zone", + ProducerEpoch = 17, + Sequence = sequence, + Delivery = CultMeshRealtimeDelivery.LatestOnly, + Payload = new byte[] { 1, 3, 3, 7 } + }; + private static async Task WaitUntilAsync(Func predicate) { using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)); diff --git a/unity/org.gamecult.cultlib/README.md b/unity/org.gamecult.cultlib/README.md index bd5bb268..136c38ab 100644 --- a/unity/org.gamecult.cultlib/README.md +++ b/unity/org.gamecult.cultlib/README.md @@ -9,10 +9,10 @@ runtime; the Microsoft license is included under `Third Party Notices`. Build it with `scripts/build-unity-package.ps1`. Do not edit generated plugin assemblies in the package output. -Install version `1.0.36` through Unity Package Manager with: +Install version `1.0.37` through Unity Package Manager with: ```text -https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.36 +https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.37 ``` CultMesh chooses mapped memory for reachable same-machine bodies and advertised diff --git a/unity/org.gamecult.cultlib/Runtime/Plugins/GameCult.Caching.MessagePack.dll b/unity/org.gamecult.cultlib/Runtime/Plugins/GameCult.Caching.MessagePack.dll index 9cfcb1e15ed7ae59c2e354d76e8d7cf4617aa3a1..9512f6b3d34f14c933b484d852486f91e99bca0c 100644 GIT binary patch delta 234 zcmZp;!q{+yaY6?R3#-QIg*`z6;!{oTzblgTSi@_(Q zX$&R|i44XJrVJ?z20$?j21}s05l}Pqa*K diff --git a/unity/org.gamecult.cultlib/Runtime/Plugins/GameCult.Caching.MessagePack.pdb b/unity/org.gamecult.cultlib/Runtime/Plugins/GameCult.Caching.MessagePack.pdb index b7893d1aea84bab759bc06137d7ff207bc5f87a1..d932026c34ea2a2ea59497915f0f7a6ae69556ff 100644 GIT binary patch delta 705 zcmV;y0zUodf&u7)0g!wYWqS&R!c~PqwZkj7-scBN=A1~;v5@o-f2@W|`7G(uE<8?1 zlq^mYfSorlP;mX%^S7W9K&}Gx8-dzW1pol8l-+9EKoEuR19^wRS1qk2%Z}YR35C=p zp-@9eQum_tVze4bLsq+HXVjoa>TC4L$||j~`vVSmF9g~>^PSzZ%cSY>pyT)`P2mDt z^}~uB1ym~D_LFhkf7dKvd_nMt0viG39U!RpHi^=Wd{LvTNI*SktSs3QG}wW`RtVS` z9|9SLoND~WJ^;D`9@$#R39J_dE;lr->B*Y~0&UEDOSnk?vFAfab9~eay9(|)QEJY2 zf0J7(Eg5C+&ZG1Ticpi=?(A9|M0xFxq%ddM=T**}% ztGwG{-I65~dJbsa1+Hq{g8cXnOHzatIqC1mc=xpEgY1ufPhtYE|JdObTN>slRKSu% zu%a6@50ImIh{=ljKv4Psi`MD_P5q3+MD~h;>^f%2D49%>(f*tduJoblvqk$q;H#|2 z#fB*nP5TDMqmdr%Giox>)*mmKwI~pw~eTY!I delta 705 zcmV;y0zUodf&u7)0g!wYhN=fwPemm_p*jhUGs@&XkgzAGv5@o-e?KV}4sQ|C?bvUK z9nxX!0K{JHSSw#+H{n$?c?hwnMSqX zRo-o}Zpjh~y#%!G0#~(eL4G{Jk`!S@PWt;X-aRk+Ap4^~keI;hKX!PK1LSBPVzQz>5R^W^qP4m}Q@`Lak-efIyN)NxC>f8F(f*u|uJoblvqk$q;G2_a zcA8DcvurXcf9K_RQcR|kY&JigQ8vZVY?{rB67%s3d~bp;TC;r>*Vs@DB8f?`tCFqE z_UGQSS~@s&b%3PfJ1bCe=LxU9u=>z_4tDSs2DR#1&eF7Et=l{?zDa){AdR0-ocD%( zSGeBpP_ch3@7}j;3-}UPY2gdH3RpGd6pTfYB-Z@|vw#y|11C2$GBh_cI5agiW;tdv zHDon6H8eOmGB{;4H(@Y1H#9k9W?^D8FtZ02r~(+&c97)&paB42vHp!G#eq+N>gq%) z`Tkdv=?)x|;~5c0@G=aw0Du4h%pKb2M7zX9m%;(Tj!4fEL1QbmIiX}26Go%7ZBcM7Xu$&=~pT=Mg T)M*CRWe8NCxINW@aW*pm;;%<} delta 243 zcmZqZU~TANozTH@rEJ2}#-6P`j5;C$2^;_Qsvm9iZ+P{}MeFCk@a-o=7?&%ko0}M! zm>XM|n3|?prWu>2n3|iKSXdfaq?(u~8d#W{Sf->UCK(%SpJvLqiJ3+9?XJVqeJmLR z1PZxdJ+hx4;5o_k2$zoBf!ov9STfejLq$&tK}A98c@^0dTqse-2E4FIwZ5LCn_5xRjdDs&|UPyvmd1zUm|J22P=0(M5c zC!>&2g%2zq(B-gZYat`B5)`=DuxUk)+8n^sM!c7V%k(dv4;{_%Q5)EmaNh-``eOGt zxue38Q5JU*p`S4Xsx#Zo{{$pgKWhuA8+6^3s^_d#dNQa74 zx5afwmN3x&5H+3WveGS*@895-6k$a!^zUQDJr>1>{i$D5FrJ&=94=a`3p5qYVIj`6 z-jh>k#CEm#reXsoR$hRI}- z4EN`G>k9F8f1f(qe-S<#<)iVmIK|0XJ{+CT#^clM3=QG97@yBZRJ@o@CugG>?yvj7 zOWvC8N4mk9VwOn^lU=z#*OJxJL6}wtNHk7bj*2@&+(=**-+lCU@Eit}Y7^#ZTC!Gc zmKdqiU#8RedB<5V*zbj#?Hv_2$nE{BmTeB7JS#0E4yS96WgVx^SPUVFeLnz`zcqs= zWHB-}W;9_lHDoX`I5{#hG-5SjV`X77W->TAF=b{qHa0UgF*#wgX*Q?=7~;pP*8qS3 Z07DR;&b5}zM7@f^^90y-;j<|@mpR>hFMR+2 delta 639 zcmV-_0)YL%by{Qh0uFc|1ll=s&f)AZX?k+db9|JhaDko5 zNkxu4DiyERm+>rXe-2=zAXs654FK{U5LCn_5xRjdYIGF^PyvmdC0l|9dob7q0(M5c zC!>&4jSnmy&=s&^Yau7F5)`=HuxU+?+8n^sMtm&^7wJDdA3B=jqb{(k;I0o!&H3(c za!aKpqb%+`Lcd@L)a16G{|!hkf7KS!wCMUvRnK{+^lZ?Of1>fIYzEK2?#<##uDaMI zZj0-dEMcIh5VgJMs@5%%AK&4I6ycs+Sig@E_gEAk_NRVB!FX=}aJcBKF3{98hlM!P zc~4HE727xB+nNoSScMg94UH6?MGrZeZ!!0v;weBW9+D4lm-~CUVoUer!>*UpWSERb z$#8$3cdiuQf2>nS`#-`Lr<3d~n~bK}cwElP(YP2-#@TdsHl=KW!|5cO6(#1QC+L3k zQgmkfk*=|!m}L^fWLNFawPdw)5T^SBBpN5JK*gOQZY8ja?>~7vcnO1AwGHz$tyt?W zPmDC_Z_{b~yytu^xZVrb+dC?5kej>L9oqsvdsbRV4nbEQt0qpJu^2)U`+fqGzcqs= zH#9OdH#0aiH8o~AW-~QpH8(XhI5{#nWi&ToFgQ0fIb>#GVlyzaX*Q?=7(=rSfdGI2 Z00>0|OEO-C_ diff --git a/unity/org.gamecult.cultlib/Runtime/Plugins/GameCult.Logging.dll b/unity/org.gamecult.cultlib/Runtime/Plugins/GameCult.Logging.dll index 0ea237f83abea18b4f415bac7e45ac9b406d7199..6dbc721fc91e21c144198a4f7464498230501cbd 100644 GIT binary patch delta 232 zcmZoLX)u}4!Sd+%>gyYOYc^E;zk8Uoe-&*POwG!3c=W8H^b$ zfV3$PrZHFou`!U90>tJ(Yyy{sjwVPA*2fVS6)dYV&u(pAR3a}^+VngFb zNs1P+7LCh?0s=znIrbz;J~gs|&^HAD0IihIY8x>S#_xf=!{Ad(lU=VJyKxcg{^#5a;~mHwsXLtArv)CRU1R$UOPFZZ{} zBbA1fu(Ovj^b3YSb#A(gKLe8MpLa`FH)y+S5zl!m^lE=plc4e_tVXZD4yM?WbsMvs z?Xe!o5C-}mP}6zVmGmIrU*MhuVNEt{`lFoPUOFF!qd!nko}1q)TyfnPg2uF3V{T&8 zwT_;$74ju2g3@_dwnpwaAFg)8hSuc6zFL-ylj$@W59fTerStX1S2X_uzR8MgGB3|? zIxEK6`C@-EIm>6L2q)#_e34Q4c0QlZvIP#;eekYmRlg0lSW~nix!XE4ob|+F=pdx^ z0UV99RG{GAm^Lo3INyEpW^@9hN~8%FX{uSOHcynR(_clW{ZZJw{M-I76#t0()%%uB z0iQh!EnPu39(7GtKp6}niFrQ&0Rw=gdm?0SV|9~Z2XiN6F)}u0G+{F}WH2!}IWjUd eVl`o7WnnUAGB`OgWo9=vHZwIbIbpN=2Spva1QdAy delta 592 zcmV-W0^LN^fv zD?nUZf+vFL>cpW-HPpOc&L~ZR&^HAD0Iif!Yuhjo$KMP64o99gw%TdqkfbexE~918 z!f3keVeH{7pXCEtGSYd)AE{qspKQq4Leg&FLi8r-?)N|bcT$?39CRHYr72uuEAB*- zC67YIhyF0Ghl+m#C|3|XVt{P`5a4o4lZrOl9Kh37yqAQ_^dB`J+M45|Hn7#O>Vi;xvA<33 zsWha7oxO;m-!TNLbJLx_3`nm2+$~+*pzW?iJm;;@n^AvFg363kTD{zO4}Zk|=R;<_;ejcK*U+{C79 z9X(|$AIOnr1ov(Xe(fkki?sS%&WwXgV zn@-C`IhlVJ)7dneFV5za&2T)QWs9Q3eDVt2FWwcc+HJ!P))Z|>ZZ{4MXFaeOItXcf z07v626)3njri}|M&UatE8NG*5CDMfRG}SCsnEEK$-BIXY{>|<$6#t03)yI}i z0pC0eEnPv^9(7GtKp6}niFv;P0Rw=gdm?0SV|9~Z2XiMkG%_?dGdMIgHD)k2UHNGx_rO+5)po7%VQA+=QsZ~N@w*= zVK8JcVlZP!V=!SzWH4qhWk_K#0E$^KSOUe3fTBr2u|$SshEyPF1XO1PmNNwM(-_Qw SI?cek41wwsH*d7?X9fTZ4oWTn delta 243 zcmZpuXsDRb!NT+VX~V{z1}%ZKL)Tj0=$vtwSZL%g{F-C#W*O~8EV||KeGDWwvlu*KWZ4`ab!hS% z!vKM3LGNwPQ?IzL*fr(;#hbhJCi@soRDcP-fC>UtJMF9AXKVb?zK}zE?X%5)jnY|t z%^6G>jDXmj!I;4UNSgv-8iOSe8v|J>Kx_`gCO|n$pgIeNRIu7a1_K})q}LLtCXFGH OAqlA7VDm-`e`WxbsZ4bM diff --git a/unity/org.gamecult.cultlib/Runtime/Plugins/GameCult.Mesh.Quic.Native.pdb b/unity/org.gamecult.cultlib/Runtime/Plugins/GameCult.Mesh.Quic.Native.pdb index 535ffbcf64bb680e6a75be91bc4219d06dda5f83..515a7472846761091c9193a8b7d59d362468995b 100644 GIT binary patch delta 857 zcmV-f1E&1!U+iCyd=wNu$Npf=3rDylNTk?I$JpN1U4XHWSqFdN`cag?=8P90KhZ-z z3N9S6s^oai4(h&`xt}?I9`9j+_<98Z0IgKpYTG~%eK+_Y20bODS@{yXaS{rtOG2S8 zZ7%LZ>BDF>k_J}0W_OgJPwH><%gQRPv1tn`cpn7Po;l~}%rI$sdeAc*q$ylsBkr^$ zOCE*tH{Hj09x8thpj}Szi~+U)$XY-U=ZuQbEqqy_Eh&IFkXTu;A*ithjZGk6tDX0x z1v0AeiJb?uIXts5kP#Rc1i09;NkuQ(9Kh37ypx2h^e>(d9nEmi1hysIw?U}B+<#Sf zR2b62&R$07R}6vb%(NGO0@U@dyQQl)XxnQM&sigMl+=GDs67hn>UR{h=gq#uqvRtBKM}0L=Y};b z8zU?V?_cd!$BOEB&g)Lx4j!S zv?d?+0~~*=k(y4`sQ+)iw}ta{*9bKKB0L%AjPvQ$1z92y-DA=z~X%S z=uPqpl1gL~F4DAQshUh_SEs+-r}6QDvre#E9=9I5i!830)&1LsO%7i?3oTtvHy+EH ztbjHcf{JxN0h0p?eJ5lwGB##3VKX&kFflkeGBPw`HDP0AVKQbiI5{z8W;ZrAGc_?e zVY8WAk0Du4hLlB?NwU*38y^6u}1lV@rM`3McLv?I)E=^@~Xf9-I zYybcOL7Cuy0DuVqi#z$dnIxi4s|66w5%kFCM`3McLv?I)E>2~1CUm delta 857 zcmV-f1E&1!U+iCyd=y&|OSb1_)J3Sel;6?WyDqa|TEelASqFbe_<98Z0IgKpYTG~%eGm8_20bODSvj$c?IaXZmxMxH z+Faa+(udJ%Bn_-~&F&~cpVZ&zmz9-UW78HK@IDBnJ#)^{nPJlO?4W0Gl%{Zvt+=y_ zEO`_v-u55kd8~gqfOZAJ3kKK#AnyP{oHHsyH}H9lwxR&yKw@RdhM>U?G`4|&t#;m% z7Raf_XLcUY7VyHxKu%y>5a4pdCN-_KIe@2)crOXp=|4OlI-0>z8`xIx&;_C9YWG#$ zQ)x&GJ9`zO-!KGfa?@S@4Ny0~@8+&qq3v!&Jm;;@angT~p!O)Nlaudzv)GcWHdZ;i z#dS}HFwkp=R-NanmMxN>KVU(Eup}G${%B{{m(GX%sozu3o>#jLkCRU*{7kS2Us~2M zZ;dP{)Zht?a1$GDEopHcO6TFpvUI%(CpCGnBtg&?=nL?zjTH%;4vyluBta!7d)vEZ zLrd~uH^6^$HB_UK8t(s_A8hG-(>DUm{|HadC)q_d8BMeCxSW-vaWS5Zv+3+&O4$U5 z(@8cfO3X*E(EaRP(dzBK-C;v9PZa}IFgq%)`TkdvtPLHLgbffF@G=aw0Du4h%pKb2 zM7zX9m%;(Tj}NmK7akkGD-{eywOM`3McLv?I)E>2~1CUBBw diff --git a/unity/org.gamecult.cultlib/Runtime/Plugins/GameCult.Mesh.dll b/unity/org.gamecult.cultlib/Runtime/Plugins/GameCult.Mesh.dll index 5024debc1ed53f81aafd4723dd45422148f745f8..3aac9d60873ef33141668aa7e8e747c48bc93999 100644 GIT binary patch delta 265 zcmZozpwh5FWkLsw6@ofpdaiPCVaUz{PR!oO+6(ky)BaqOoa;fuV(^k&#J~X<~9}qEVWWg{5I?nz@;o zv8kbD;`WG}96PjGQZ^m$n=Zo286dFlvSC7P@iN2kuT;4caJ}Qma_jW%ylkB8Y`!TBh73jwW(;WzCJc!T#tfzmDGUZcF$)GuptuoG pGzln{$dJsC3M7qy>Wsj0hCqHAgE>&A8CaJgP<`U|L>|uh%m9Z*Q;h%s delta 265 zcmZozpwh5FWkLtbZdReWjXkYBjIBLPtv$@GJuIy~tgSt4tv&2pdpNFe3Yd4e{PYZd z?=<1Ux}Lwyjr+G3aB&d})6d*PSViTa8 kB~YCOLn>HpB7*^t4bp1~RFlS#$dCk7Z?HX)hjTtN0Eeklx&QzG diff --git a/unity/org.gamecult.cultlib/Runtime/Plugins/GameCult.Mesh.pdb b/unity/org.gamecult.cultlib/Runtime/Plugins/GameCult.Mesh.pdb index feb471bbd8f5a723b1127a7e405f8cbaeeb0f8de..54429321866db39d6363ad77c23d5913a9221432 100644 GIT binary patch delta 807 zcmV+?1K9kG;tP!83y^#izSA*aePhu@vifYBN<~2wWU|MOfsnP30d`S;-3v^C!#u5H z8DX|nGZo<(l+Sk6IiAH@uz-Pj3* z)FmNMLrGHiPh`93%-` zV79Cct(aD0J0Vk#5ofO=nlTD(3Zp?4kT0-+7Q&(g2ILg zv{lY~QUV!O_)R+xXmfbh#z01391G}T$0ilMXln*fJMm5uu9H7_K6W(2LBraX@Yu3Y zeRcR7KTu&viFWoXKtCfh)tPA*zXRf%?b*`R8?^0>h}T&obQ0BnB&a-!R?+E~-YmAz zWfQ8L-Q#*7L&)?mM4Q&@vXVWLAKzg`g0Lpb-TNqKUk;t;{?zY?mDig?hbPgpF|s1A z7LaS+Zs*_$jp&-7Caxey!3>!?b=Gxbx{C%QOqLsl!g;vEnwE`;;A}Vu?Gg!Wy!%Q@ zI6CcZ^Hv*Lljp;KAk%mhPbTrG|8?(e;e6fsMDsVovoue~v*H3L(|nX(&d1}6Y>J9- zT#PU0DHX40v&l4_V}IQb-sO$jf4^-m5{H=k6Fq4$^#3&LBV-xcFZ94cXKx&9alZZN zP4o()N@T-}Bq_C2O%^LxC%+sg;qzW+onSXC?)C>)7%Qt7$G43(IehY3Xz6mg^;p(q z1(ZSNIMn?Fw;EId4u&UWF)}u0G+{F}WH2!}IWjUdVl`o7WnnUAGB`OgWo9=vHZwIb zIbpX?+5xo!7=y<3s{o(@0EF;*4-@+qPPZ1zrcv8z&zE7{0Ueh)-2o67(NN6e0Du4h z^jOY5#P-!gvO~Y8KB-Fh#+QUb z>XJ~Xp(LsMQ2H=hjifEBU9mH2&?n_L`ekJm*VwcLhk73b+C6j5*|WnW$@x*&aF8T$ zjjg!zk}P=?%HQ^zVLeuVnnAgo;0YPF0LVH(5a&!Bpj-H|LR%7pIFL|TXhTqA2MSvz z&{jF`NeN_B;ScRRpv~b)8v_}EaV(&VEt^#IqOBP`ZN+;@xK94!`Pk762Q6z`!b8VG z_0|4wd{2cTCED4m0R4*0RA;7J{0WF}HfKv$H)y*X5wEjW=p?FtNlspvXUK=pU7Bb@JPB5W|=1;tHlm(xmVsuAgZDm7s zY;-P6Z)ay|Zf7oJY-|7k0YkG5fdGI200>0|OsVdsw#iu=Xq$So3J(^9ge^e5OoVet-MD z$Sd15Rn%lYnB049N_sK+*`P&Il}L2;`?Rm;-g1fpr-I)hBKb{lvDK831SXScCuo delta 267 zcmZp8Bi-;udO`=wzq6K48hcuM7+ZUoT6>sVdsw#iu=Xq$uvk`nl_~GC$I>SU48)$R zY~8N0f^}t?uDOYkiMg?biK%ItWty>RimAD&iG`(+MXHH;qJf3EiDgP!Vv?}|1A_!3 zgYa}kE>_{~g7R#W53}^nY1=S;#Ur)=firpcT*BMBJa-?Ksmn9UtDMg9n5|U-DjHc0 z6$Pp{5DDjt-uJS!eeJo2OSi9m%ofe(YtCT8U5lF@oR{-L98Jm5qvuO)%XIOzH=hfsnP30lXf6p`b`tR3mjL zuL@}cCwOly&!B5|R^ayNx}$~0Z+Be1f#6I9006C&&uSYn5XSF;yu;vAOXJ*A8^1r2sHZU_YEU4X*xXUIzCEM zxWrc6P?IH(LdBat7}rC8#Q~Hn2-X;2BY?aE1aZzJQM!>YDzuschy#h4B^!boJ5bmL z0=CL|Pf8%C3Ln{dKwH3?je(rNxFEpgh9(s~d2;|y8}r^0F4KS1eCXC3AGLw4hGiFo z>WkfDa!;ipCG6})lzzbwsLo9{|67n;{kmJYxY$9K3TL0FLu{rf0qpDvvb`=j4cP@bDVDqOTi?nvga zB-MI4fr@oCY{-|+*Ju?%O(CF;Z7!V$jWvZdkbk7-m|wG@75T7ho+YQrc$}Q>&v|eA zx?&8?|A4QuA{$MA%QGBLiqq_THX5Df6I6twa&$h+sC+%0jwjg+_xJtiUD2xTSGmEO zV)K$alC|^S*y{ap4=jcb1zR1UuknKwD7f=?8y8rd?>>1mIDtVW(u8@MYL=?a6Xojk zcim~+K5*U}_T%7Ydo;ztal3rmvMJ!RXQ72F=-Q*M$qFb2gCQg_?&Apy5eAp*~9 zCuA`)HfA(oGc{x|F*rFgGBjc}VPj=sGG;P3IWc8sH#RmiH8DA1w?vHsssb2;#`UWJ zpaB4c@Olpu`xZ{O7R#nl+iB02yM+QBC_!#vYDl vOmAmrX>MmOWNd5z00H91tJeU4002V}pU$5l%x>O9SB8p7yTcrQZZd9pjhu$CfsnP30lXf6l5l2Qpxl7A zRirR0(4$7yv)8Kj{oG{COsOEar_^Sef#6I9006C&+iKfD5Qgsqd56JQEsZ57mhd5= zkh&xkl2GE*y(qmHtwz$))vnkX#psdx8hx^|N^9&Mzy|Nd!p{El{ln}saXdWgIz5SF zxWZQ4up~z6ReS8BY>;}1aZzpLAsGIDzqgrhyw|kg*F5=cA&6j z0&SJ^o|HgF6+Ua{0c{RzZ46`t#<74dHZ-Z|#alCY+L-s2a25Zf=3}?!^rU5NOIUU+ zRA25Mqem(XDbdbe2I&`MraCj-{BJ>Y{p)Vw>IQ9hE#h_73cVVC)Fh}pidKWyU-zci zLYHmGa(0XLNQRK<5Kz;3T~@LM`SBg@Nf1_Kxqlz!?8~L|yg&LqvGTh4qrydN^EFyUP!ltjq0NQ!P-0CSGx=va#Qa7ZT9M~n^YdsFjmObwf6fQn z*A-%D{s(+>K1nWrlF4|Qq-ilL#%Z2T(quZjm{Kyq(R7l`@&dE*0lpu-%UiYmDz{iu zXkPR{vUdKPw&nh~CoP7K1zR1Vui=B`D7f=?8^>Cl?>>1mIDgq%)`TkdzyM+QBC_!#vYDl vOmAmrX>MmOWNd5z00Bd@4S@iF000O@1x;HE?@g87nU~4!Kry$qkOD*T(^hLF diff --git a/unity/org.gamecult.cultlib/package.json b/unity/org.gamecult.cultlib/package.json index 743a4920..08930273 100644 --- a/unity/org.gamecult.cultlib/package.json +++ b/unity/org.gamecult.cultlib/package.json @@ -1,7 +1,7 @@ { "name": "org.gamecult.cultlib", "displayName": "CultLib", - "version": "1.0.36", + "version": "1.0.37", "unity": "2021.3", "description": "CultCache, CultNet, and CultMesh managed runtime assemblies for Unity.", "license": "MIT",