Skip to content

Commit fd25908

Browse files
committed
Merge chore/clean-network-robustness (QA combined)
2 parents 19b45ae + aa7909c commit fd25908

14 files changed

Lines changed: 143 additions & 56 deletions

File tree

Explorer/Assets/DCL/Infrastructure/ECS/Unity/StreamableLoading/NFTShapes/LoadNFTTypeSystem.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@ protected override async UniTask<StreamableLoadingResult<NftTypeResult>> FlowInt
3838
StreamableLoadingState state, IPartitionComponent partition, CancellationToken ct)
3939
{
4040
string imageUrl = await ImageUrlAsync(intention.CommonArguments, ct);
41-
string convertUrl = ktxEnabled ? string.Format(urlsSource.Url(DecentralandUrl.MediaConverter), Uri.EscapeDataString(imageUrl)) : imageUrl;
41+
42+
// Same guard as GetTextureWebRequest: non-publicly-routable URLs must not reach the hosted converter.
43+
string convertUrl = ktxEnabled && !DCL.WebRequests.WebRequestUtils.IsNonPubliclyRoutable(imageUrl)
44+
? string.Format(urlsSource.Url(DecentralandUrl.MediaConverter), Uri.EscapeDataString(imageUrl))
45+
: imageUrl;
4246
var contentInfo = await WebContentInfo.FetchAsync(convertUrl, ct);
4347

4448
if (!ktxEnabled && contentInfo is { Type: WebContentInfo.ContentType.Image, SizeInBytes: > MAX_PREVIEW_SIZE })

Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/LiveConnections/WebSocketArchipelagoLiveConnection.cs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ public class WebSocketArchipelagoLiveConnection : IArchipelagoLiveConnection
2020
{
2121
private const int BUFFER_SIZE = 1024 * 1024; //1MB
2222

23+
/// <summary>
24+
/// Budget for the graceful close handshake before hard-dropping the socket. The server ack
25+
/// is only worth waiting for briefly: the receive loop that would read it is already
26+
/// cancelled when a room stops, so an unbounded CloseAsync hung the teleport pipeline
27+
/// into its 10s timeout.
28+
/// </summary>
29+
private static readonly TimeSpan CLOSE_HANDSHAKE_BUDGET = TimeSpan.FromSeconds(1);
30+
2331
private readonly IMemoryPool memoryPool;
2432

2533
private Current? current;
@@ -48,8 +56,22 @@ public async UniTask<Result> DisconnectAsync(CancellationToken token)
4856
{
4957
try
5058
{
51-
TryUpdateWebSocket();
52-
await current!.Value.WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, token);
59+
if (current?.WebSocket.State is WebSocketState.Open or WebSocketState.Connecting)
60+
{
61+
using var closeCts = CancellationTokenSource.CreateLinkedTokenSource(token);
62+
closeCts.CancelAfter(CLOSE_HANDSHAKE_BUDGET);
63+
64+
try { await current!.Value.WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, closeCts.Token); }
65+
catch (OperationCanceledException) when (token.IsCancellationRequested) { throw; }
66+
catch (Exception)
67+
{
68+
// The handshake exceeded its budget or the socket errored out; hard-drop below.
69+
}
70+
}
71+
72+
// Install a fresh socket so a subsequent ConnectAsync can never race the old instance.
73+
current?.Dispose();
74+
current = Current.New();
5375
return Result.SuccessResult();
5476
}
5577
catch (Exception e) { return Result.ErrorResult($"Cannot disconnect: {e}"); }

Explorer/Assets/DCL/Multiplayer/Connections/Archipelago/Rooms/ArchipelagoIslandRoom.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,20 @@ ICurrentAdapterAddress currentAdapterAddress
5656
this.currentAdapterAddress = currentAdapterAddress;
5757
}
5858

59+
public override async UniTask StopAsync()
60+
{
61+
await base.StopAsync();
62+
63+
// Close the sign-flow ws on stop. Otherwise the next StartAsync reuses the
64+
// still-open socket (EnsureConnectionAsync sees IsConnected and skips the
65+
// handshake), the server keeps the old peer+island, never re-sends
66+
// IslandChanged — and the restarted room waits for a connection string that
67+
// never arrives (observed as 3x10s RestartRoom timeouts on every
68+
// world->genesis realm change).
69+
try { await signFlow.DisconnectAsync(CancellationToken.None); }
70+
catch (Exception e) { ReportHub.LogWarning(ReportCategory.COMMS_SCENE_HANDLER, $"ArchipelagoIslandRoom stop: disconnect failed: {e.Message}"); }
71+
}
72+
5973
protected override async UniTask PrewarmAsync(CancellationToken token)
6074
{
6175
// Reset the per-session state: the room instance is reused across StopAsync/StartAsync (teleport, logout)

Explorer/Assets/DCL/Multiplayer/Connections/GateKeeper/Rooms/IGateKeeperSceneRoom.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ public interface IGateKeeperSceneRoom : IActivatableConnectiveRoom, ISceneRoomSt
3535

3636
class Fake : Null, IGateKeeperSceneRoom
3737
{
38-
public event Action? CurrentSceneRoomConnected;
39-
public event Action? CurrentSceneRoomDisconnected;
40-
public event Action? CurrentSceneRoomForbiddenAccess;
38+
public event Action? CurrentSceneRoomConnected { add { } remove { } }
39+
public event Action? CurrentSceneRoomDisconnected { add { } remove { } }
40+
public event Action? CurrentSceneRoomForbiddenAccess { add { } remove { } }
4141
public MetaData? ConnectedScene { get; } = new MetaData("Fake", Vector2Int.zero, new MetaData.Input());
4242

4343
public bool Activated => true;

Explorer/Assets/DCL/Multiplayer/Connections/Rooms/NullRoom.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,21 @@ public class NullRoom : IRoom
3434
public IAudioStreams AudioStreams => NullAudioStreams.INSTANCE;
3535
public ILocalTracks LocalTracks => NullLocalTracks.INSTANCE;
3636

37-
public event LocalPublishDelegate? LocalTrackPublished;
38-
public event LocalPublishDelegate? LocalTrackUnpublished;
39-
public event PublishDelegate? TrackPublished;
40-
public event PublishDelegate? TrackUnpublished;
41-
public event SubscribeDelegate? TrackSubscribed;
42-
public event SubscribeDelegate? TrackUnsubscribed;
43-
public event MuteDelegate? TrackMuted;
44-
public event MuteDelegate? TrackUnmuted;
37+
public event LocalPublishDelegate? LocalTrackPublished { add { } remove { } }
38+
public event LocalPublishDelegate? LocalTrackUnpublished { add { } remove { } }
39+
public event PublishDelegate? TrackPublished { add { } remove { } }
40+
public event PublishDelegate? TrackUnpublished { add { } remove { } }
41+
public event SubscribeDelegate? TrackSubscribed { add { } remove { } }
42+
public event SubscribeDelegate? TrackUnsubscribed { add { } remove { } }
43+
public event MuteDelegate? TrackMuted { add { } remove { } }
44+
public event MuteDelegate? TrackUnmuted { add { } remove { } }
4545
#endif
4646

47-
public event ConnectionQualityChangeDelegate? ConnectionQualityChanged;
48-
public event ConnectionStateChangeDelegate? ConnectionStateChanged;
49-
public event ConnectionDelegate? ConnectionUpdated;
50-
public event Room.MetaDelegate? RoomMetadataChanged;
51-
public event Room.SidDelegate? RoomSidChanged;
47+
public event ConnectionQualityChangeDelegate? ConnectionQualityChanged { add { } remove { } }
48+
public event ConnectionStateChangeDelegate? ConnectionStateChanged { add { } remove { } }
49+
public event ConnectionDelegate? ConnectionUpdated { add { } remove { } }
50+
public event Room.MetaDelegate? RoomMetadataChanged { add { } remove { } }
51+
public event Room.SidDelegate? RoomSidChanged { add { } remove { } }
5252

5353
public void UpdateLocalMetadata(string metadata)
5454
{

Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Nulls/NullActiveSpeakers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public class NullActiveSpeakers : IActiveSpeakers
1111

1212
public int Count => 0;
1313

14-
public event Action? Updated;
14+
public event Action? Updated { add { } remove { } }
1515

1616
public IEnumerator<string> GetEnumerator()
1717
{

Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Nulls/NullDataPipe.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class NullDataPipe : IDataPipe
1010
{
1111
public static readonly NullDataPipe INSTANCE = new ();
1212

13-
public event ReceivedDataDelegate? DataReceived;
13+
public event ReceivedDataDelegate? DataReceived { add { } remove { } }
1414

1515
public void PublishData(Span<byte> data, string topic, IReadOnlyCollection<string> destinationSids, LKDataPacketKind kind = LKDataPacketKind.KindLossy)
1616
{

Explorer/Assets/DCL/Multiplayer/Connections/Rooms/Nulls/NullParticipantsHub.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class NullParticipantsHub : IParticipantsHub
1212
public static readonly LKParticipant NULL_PARTICIPANT = new ();
1313
public static readonly WeakReference<LKParticipant> WEAK_NULL_PARTICIPANT = new (NULL_PARTICIPANT);
1414

15-
public event ParticipantDelegate? UpdatesFromParticipant;
15+
public event ParticipantDelegate? UpdatesFromParticipant { add { } remove { } }
1616

1717
public LKParticipant LocalParticipant() =>
1818
NULL_PARTICIPANT;

Explorer/Assets/DCL/Multiplayer/Connections/Systems/RoomIndicator.prefab

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ GameObject:
99
serializedVersion: 6
1010
m_Component:
1111
- component: {fileID: 6615945845340605545}
12-
- component: {fileID: 6145739770501069584}
1312
m_Layer: 5
1413
m_Name: RoomIndicator
1514
m_TagString: Untagged
@@ -39,33 +38,6 @@ RectTransform:
3938
m_AnchoredPosition: {x: 0, y: 0}
4039
m_SizeDelta: {x: 0.45, y: 0.3}
4140
m_Pivot: {x: 0.5, y: 0.5}
42-
--- !u!114 &6145739770501069584
43-
MonoBehaviour:
44-
m_ObjectHideFlags: 0
45-
m_CorrespondingSourceObject: {fileID: 0}
46-
m_PrefabInstance: {fileID: 0}
47-
m_PrefabAsset: {fileID: 0}
48-
m_GameObject: {fileID: 2074114889938352466}
49-
m_Enabled: 1
50-
m_EditorHideFlags: 0
51-
m_Script: {fileID: 11500000, guid: d7d34f10598c4f638b94710f3b0ff918, type: 3}
52-
m_Name:
53-
m_EditorClassIdentifier:
54-
colors:
55-
- source: 0
56-
indicator: N/A
57-
color: {r: 0.41509432, g: 0.41509432, b: 0.41509432, a: 1}
58-
- source: 1
59-
indicator: G
60-
color: {r: 0.07143695, g: 0.26501098, b: 0.5283019, a: 1}
61-
- source: 2
62-
indicator: I
63-
color: {r: 0.07058824, g: 0.5294118, b: 0.22292237, a: 1}
64-
- source: 3
65-
indicator: I+G
66-
color: {r: 0.55345905, g: 0.06787699, b: 0.48883247, a: 1}
67-
background: {fileID: 2968721729092005038}
68-
text: {fileID: 1326886004246566054}
6941
--- !u!1 &2433736487283086053
7042
GameObject:
7143
m_ObjectHideFlags: 0
@@ -123,6 +95,8 @@ MeshRenderer:
12395
m_RayTracingAccelStructBuildFlagsOverride: 0
12496
m_RayTracingAccelStructBuildFlags: 1
12597
m_SmallMeshCulling: 1
98+
m_ForceMeshLod: -1
99+
m_MeshLodSelectionBias: 0
126100
m_RenderingLayerMask: 1
127101
m_RendererPriority: 0
128102
m_Materials:
@@ -144,9 +118,11 @@ MeshRenderer:
144118
m_AutoUVMaxDistance: 0.5
145119
m_AutoUVMaxAngle: 89
146120
m_LightmapParameters: {fileID: 0}
121+
m_GlobalIlluminationMeshLod: 0
147122
m_SortingLayerID: 0
148123
m_SortingLayer: 0
149124
m_SortingOrder: 10
125+
m_MaskInteraction: 0
150126
m_AdditionalVertexStreams: {fileID: 0}
151127
--- !u!114 &1326886004246566054
152128
MonoBehaviour:
@@ -206,6 +182,7 @@ MonoBehaviour:
206182
m_VerticalAlignment: 512
207183
m_textAlignment: 65535
208184
m_characterSpacing: 0
185+
m_characterHorizontalScale: 1
209186
m_wordSpacing: 0
210187
m_lineSpacing: 0
211188
m_lineSpacingMax: 0
@@ -280,6 +257,7 @@ RectTransform:
280257
m_Pivot: {x: 0.5, y: 0.5}
281258
--- !u!212 &544308281080233652
282259
SpriteRenderer:
260+
serializedVersion: 2
283261
m_ObjectHideFlags: 0
284262
m_CorrespondingSourceObject: {fileID: 0}
285263
m_PrefabInstance: {fileID: 0}
@@ -298,6 +276,8 @@ SpriteRenderer:
298276
m_RayTracingAccelStructBuildFlagsOverride: 0
299277
m_RayTracingAccelStructBuildFlags: 1
300278
m_SmallMeshCulling: 1
279+
m_ForceMeshLod: -1
280+
m_MeshLodSelectionBias: 0
301281
m_RenderingLayerMask: 1
302282
m_RendererPriority: 0
303283
m_Materials:
@@ -319,9 +299,11 @@ SpriteRenderer:
319299
m_AutoUVMaxDistance: 0.5
320300
m_AutoUVMaxAngle: 89
321301
m_LightmapParameters: {fileID: 0}
302+
m_GlobalIlluminationMeshLod: 0
322303
m_SortingLayerID: 0
323304
m_SortingLayer: 0
324305
m_SortingOrder: 1
306+
m_MaskInteraction: 0
325307
m_Sprite: {fileID: 21300000, guid: 37894a231569347e8b89612f29611b85, type: 3}
326308
m_Color: {r: 0.13836467, g: 0.13836467, b: 0.13836467, a: 1}
327309
m_FlipX: 0
@@ -331,7 +313,6 @@ SpriteRenderer:
331313
m_AdaptiveModeThreshold: 0.5
332314
m_SpriteTileMode: 0
333315
m_WasSpriteAssigned: 1
334-
m_MaskInteraction: 0
335316
m_SpriteSortPoint: 0
336317
--- !u!1 &3907013848479446928
337318
GameObject:
@@ -371,6 +352,7 @@ RectTransform:
371352
m_Pivot: {x: 0.5, y: 0.5}
372353
--- !u!212 &2968721729092005038
373354
SpriteRenderer:
355+
serializedVersion: 2
374356
m_ObjectHideFlags: 0
375357
m_CorrespondingSourceObject: {fileID: 0}
376358
m_PrefabInstance: {fileID: 0}
@@ -389,6 +371,8 @@ SpriteRenderer:
389371
m_RayTracingAccelStructBuildFlagsOverride: 0
390372
m_RayTracingAccelStructBuildFlags: 1
391373
m_SmallMeshCulling: 1
374+
m_ForceMeshLod: -1
375+
m_MeshLodSelectionBias: 0
392376
m_RenderingLayerMask: 1
393377
m_RendererPriority: 0
394378
m_Materials:
@@ -410,9 +394,11 @@ SpriteRenderer:
410394
m_AutoUVMaxDistance: 0.5
411395
m_AutoUVMaxAngle: 89
412396
m_LightmapParameters: {fileID: 0}
397+
m_GlobalIlluminationMeshLod: 0
413398
m_SortingLayerID: 0
414399
m_SortingLayer: 0
415400
m_SortingOrder: 0
401+
m_MaskInteraction: 0
416402
m_Sprite: {fileID: 21300000, guid: 12dd1efc4e826764f9b02be515a9a033, type: 3}
417403
m_Color: {r: 0.5529412, g: 0.06666667, b: 0.49019608, a: 1}
418404
m_FlipX: 0
@@ -422,5 +408,4 @@ SpriteRenderer:
422408
m_AdaptiveModeThreshold: 0.5
423409
m_SpriteTileMode: 0
424410
m_WasSpriteAssigned: 1
425-
m_MaskInteraction: 0
426411
m_SpriteSortPoint: 0

Explorer/Assets/DCL/Social/WebSocketRpcTransport.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ async UniTaskVoid ListenAndProcessIncomingDataAsync(CancellationToken ct)
9494
}
9595
}
9696
catch (OperationCanceledException) { break; }
97+
catch (ObjectDisposedException) { break; }
9798
catch (WebSocketException e)
9899
{
99100
OnErrorEvent?.Invoke(e);
@@ -119,6 +120,12 @@ public virtual async UniTask SendMessageAsync(byte[] data, CancellationToken ct)
119120
{
120121
OnErrorEvent?.Invoke(e);
121122
}
123+
// The socket can be disposed mid-send during teardown/reconnect (isDisposed was false at
124+
// entry); surface it like a send failure so callers don't believe the message was delivered.
125+
catch (ObjectDisposedException e)
126+
{
127+
OnErrorEvent?.Invoke(e);
128+
}
122129
}
123130

124131
public virtual async UniTask SendMessageAsync(string data, CancellationToken ct)
@@ -130,6 +137,10 @@ public virtual async UniTask SendMessageAsync(string data, CancellationToken ct)
130137
{
131138
OnErrorEvent?.Invoke(e);
132139
}
140+
catch (ObjectDisposedException e)
141+
{
142+
OnErrorEvent?.Invoke(e);
143+
}
133144
}
134145

135146
public void Close() =>
@@ -141,7 +152,8 @@ public async UniTask CloseAsync(CancellationToken ct)
141152

142153
if (State is WebSocketState.Open or WebSocketState.CloseReceived)
143154
{
144-
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", ct);
155+
try { await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", ct); }
156+
catch (ObjectDisposedException) { return; }
145157
OnCloseEvent?.Invoke();
146158
}
147159
}

0 commit comments

Comments
 (0)