@@ -266,6 +266,7 @@ export default class CssQuakeMultiplayerRoom implements Party.Server {
266266 this . clearConnectionRejects ( sender ) ;
267267 if ( ! this . roomKey ) this . roomKey = roomKey ;
268268 if ( validation . envelope . type === "client.hello" ) {
269+ this . seedPendingHelloAuthority ( sender , validation . envelope , authority . state , receivedAt ) ;
269270 const trustedDefinitionsReady = this . ensureTrustedGameplayDefinitions ( validation . envelope , sender , roomKey ) ;
270271 if ( isPromiseLike ( trustedDefinitionsReady ) ) {
271272 return trustedDefinitionsReady . then ( ( ok ) => {
@@ -458,8 +459,9 @@ export default class CssQuakeMultiplayerRoom implements Party.Server {
458459 lastAcceptedInputSequence : nextPlayer . lastInputSequence ,
459460 } ) ) ;
460461 }
462+ const latestAuthority = this . latestConnectionAuthority ( sender , message . payload . clientId , authority ) ;
461463 const state = {
462- authority,
464+ authority : latestAuthority ,
463465 clientId : message . payload . clientId ,
464466 displayName : message . payload . displayName ,
465467 lastSeenAt : receivedAt ,
@@ -498,8 +500,9 @@ export default class CssQuakeMultiplayerRoom implements Party.Server {
498500 authority : QuakeMultiplayerClientAuthorityState ,
499501 receivedAt : number ,
500502 ) : void {
503+ const latestAuthority = this . latestConnectionAuthority ( sender , message . payload . clientId , authority ) ;
501504 const state = {
502- authority,
505+ authority : latestAuthority ,
503506 clientId : message . payload . clientId ,
504507 displayName : message . payload . displayName ,
505508 lastSeenAt : receivedAt ,
@@ -1997,6 +2000,29 @@ export default class CssQuakeMultiplayerRoom implements Party.Server {
19972000 return this . connectionPlayers . get ( connection . id ) ?? ( connection . state as CssQuakeConnectionState | null ) ;
19982001 }
19992002
2003+ private seedPendingHelloAuthority (
2004+ connection : Party . Connection ,
2005+ message : Extract < QuakeMultiplayerClientEnvelope , { type : "client.hello" } > ,
2006+ authority : QuakeMultiplayerClientAuthorityState ,
2007+ lastSeenAt : number ,
2008+ ) : void {
2009+ const state = this . connectionState ( connection ) ;
2010+ if ( state ) {
2011+ this . updateConnectionAuthority ( connection , authority , lastSeenAt ) ;
2012+ return ;
2013+ }
2014+ const next = {
2015+ authority,
2016+ clientId : message . payload . clientId ,
2017+ displayName : message . payload . displayName ,
2018+ lastSeenAt,
2019+ presenceStatus : "active" as const ,
2020+ role : "player" as const ,
2021+ } ;
2022+ this . connectionPlayers . set ( connection . id , next ) ;
2023+ connection . setState ( next ) ;
2024+ }
2025+
20002026 private updateConnectionAuthority (
20012027 connection : Party . Connection ,
20022028 authority : QuakeMultiplayerClientAuthorityState ,
@@ -2009,6 +2035,21 @@ export default class CssQuakeMultiplayerRoom implements Party.Server {
20092035 connection . setState ( next ) ;
20102036 }
20112037
2038+ private latestConnectionAuthority (
2039+ connection : Party . Connection ,
2040+ clientId : string ,
2041+ fallback : QuakeMultiplayerClientAuthorityState ,
2042+ ) : QuakeMultiplayerClientAuthorityState {
2043+ const current = this . connectionState ( connection ) ?. authority ;
2044+ if (
2045+ current ?. clientId === clientId &&
2046+ ( current . lastEnvelopeSequence ?? - 1 ) >= ( fallback . lastEnvelopeSequence ?? - 1 )
2047+ ) {
2048+ return current ;
2049+ }
2050+ return fallback ;
2051+ }
2052+
20122053 private handleClientPong (
20132054 message : Extract < QuakeMultiplayerClientEnvelope , { type : "client.pong" } > ,
20142055 sender : Party . Connection ,
0 commit comments