@@ -31,6 +31,7 @@ public sealed class PeerSimulation : IPeerSimulation
3131 private readonly ITimeProvider timeProvider ;
3232 private readonly ITransport transport ;
3333 private readonly ProfileBoard profileBoard ;
34+ private readonly EmoteBoard emoteBoard ;
3435 private readonly ILogger < PeerSimulation > logger ;
3536
3637 /// <summary>
@@ -69,6 +70,7 @@ public PeerSimulation(
6970 ITimeProvider timeProvider ,
7071 ITransport transport ,
7172 ProfileBoard profileBoard ,
73+ EmoteBoard emoteBoard ,
7274 ILogger < PeerSimulation > logger )
7375 {
7476 this . areaOfInterest = areaOfInterest ;
@@ -80,6 +82,7 @@ public PeerSimulation(
8082 this . timeProvider = timeProvider ;
8183 this . transport = transport ;
8284 this . profileBoard = profileBoard ;
85+ this . emoteBoard = emoteBoard ;
8386 this . logger = logger ;
8487
8588 BaseTickMs = simulationSteps [ 0 ] ;
@@ -116,6 +119,7 @@ public void SimulateTick(Dictionary<PeerIndex, PeerState> peers, uint tickCounte
116119 spatialGrid . Remove ( observerId ) ;
117120 identityBoard . Remove ( observerId ) ;
118121 profileBoard . Remove ( observerId ) ;
122+ emoteBoard . Remove ( observerId ) ;
119123 observerViews . Remove ( observerId ) ;
120124 peersToBeRemoved . Add ( observerId ) ;
121125 logger . LogInformation ( "Peer {Peer} removed after disconnected" , observerId ) ;
@@ -126,6 +130,9 @@ public void SimulateTick(Dictionary<PeerIndex, PeerState> peers, uint tickCounte
126130 if ( observerState . ConnectionState != PeerConnectionState . AUTHENTICATED )
127131 continue ;
128132
133+ // Completes the emote in case no observer is near this peer
134+ emoteBoard . TryComplete ( observerId , timeProvider . MonotonicTime ) ;
135+
129136 if ( ! snapshotBoard . TryRead ( observerId , out PeerSnapshot observerSnapshot ) )
130137 continue ;
131138
@@ -236,9 +243,12 @@ private void ProcessVisibleSubjects(
236243 else
237244 SendDelta ( view . LastSentSnapshot , ITransport . PacketMode . UNRELIABLE_SEQUENCED ) ;
238245
246+ // Only announce on delta because PlayerJoined is considered as an announcement
239247 TryAnnounceProfile ( ) ;
240248 }
241249
250+ SyncEmoteState ( ) ;
251+
242252 view . LastSentSeq = subjectSnapshot . Seq ;
243253 view . LastSentSnapshot = subjectSnapshot ;
244254 view . LastSeenTick = tickCounter ;
@@ -265,6 +275,46 @@ void TryAnnounceProfile()
265275 }
266276 }
267277
278+ void SyncEmoteState ( )
279+ {
280+ // If the emote completion has not been process by the peer's worker yet, try to complete it now
281+ emoteBoard . TryComplete ( entry . Subject , timeProvider . MonotonicTime ) ;
282+
283+ EmoteState ? emoteState = emoteBoard . Get ( entry . Subject ) ;
284+ string ? currentEmote = emoteState ? . EmoteId ;
285+
286+ if ( currentEmote == view . LastSentEmoteId )
287+ return ;
288+
289+ if ( currentEmote != null )
290+ {
291+ messagePipe . Send ( new OutgoingMessage ( observerId , new ServerMessage
292+ {
293+ EmoteStarted = new EmoteStarted
294+ {
295+ SubjectId = entry . Subject . Value ,
296+ ServerTick = emoteState ! . StartTick ,
297+ EmoteId = currentEmote ,
298+ PlayerState = CreatePlayerState ( subjectSnapshot ) ,
299+ } ,
300+ } , ITransport . PacketMode . RELIABLE ) ) ;
301+ }
302+ else if ( emoteState is { StopTick : not null , StopReason : not null } )
303+ {
304+ messagePipe . Send ( new OutgoingMessage ( observerId , new ServerMessage
305+ {
306+ EmoteStopped = new EmoteStopped
307+ {
308+ SubjectId = entry . Subject . Value ,
309+ ServerTick = emoteState . StopTick . Value ,
310+ Reason = emoteState . StopReason . Value ,
311+ } ,
312+ } , ITransport . PacketMode . RELIABLE ) ) ;
313+ }
314+
315+ view . LastSentEmoteId = currentEmote ;
316+ }
317+
268318 bool SendDelta ( PeerSnapshot baseline , ITransport . PacketMode packetMode )
269319 {
270320 if ( baseline . Seq == subjectSnapshot . Seq )
@@ -313,7 +363,16 @@ private void SweepStaleViews(PeerIndex observerId, Dictionary<PeerIndex, PeerToP
313363 }
314364 }
315365
316- private PlayerStateFull CreateFullState ( PeerIndex subjectId , PeerSnapshot snapshot )
366+ private PlayerStateFull CreateFullState ( PeerIndex subjectId , PeerSnapshot snapshot ) =>
367+ new ( )
368+ {
369+ SubjectId = subjectId . Value ,
370+ Sequence = snapshot . Seq ,
371+ ServerTick = snapshot . ServerTick ,
372+ State = CreatePlayerState ( snapshot ) ,
373+ } ;
374+
375+ private static PlayerState CreatePlayerState ( PeerSnapshot snapshot )
317376 {
318377 var state = new PlayerState
319378 {
@@ -333,12 +392,6 @@ private PlayerStateFull CreateFullState(PeerIndex subjectId, PeerSnapshot snapsh
333392 if ( snapshot . HeadPitch . HasValue )
334393 state . HeadPitch = snapshot . HeadPitch . Value ;
335394
336- return new PlayerStateFull
337- {
338- SubjectId = subjectId . Value ,
339- Sequence = snapshot . Seq ,
340- ServerTick = snapshot . ServerTick ,
341- State = state ,
342- } ;
395+ return state ;
343396 }
344397}
0 commit comments