Skip to content

Commit

Permalink
Improve synchronization across threads
Browse files Browse the repository at this point in the history
  • Loading branch information
aromaa committed Oct 12, 2024
1 parent 6c6d472 commit 1054499
Showing 1 changed file with 51 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ public abstract class ServerLoginPacketListenerImplMixin implements ServerLoginP
private static final int NEGOTIATION_HANDSHAKE = 2;

private static final int INTENT_SYNC_PLUGIN_DATA = 0;
private static final int INTENT_DONE = 1;

// Handshake state:
// 1. Sync registered plugin channels
Expand All @@ -121,9 +122,14 @@ public abstract class ServerLoginPacketListenerImplMixin implements ServerLoginP
private static final int HANDSHAKE_SYNC_CHANNEL_REGISTRATIONS = 2;
private static final int HANDSHAKE_CHANNEL_REGISTRATION = 3;
private static final int HANDSHAKE_SYNC_PLUGIN_DATA = 4;
private static final int HANDSHAKE_DONE = 5;

private volatile int impl$negotiationPhase = ServerLoginPacketListenerImplMixin.NEGOTIATION_NOT_STARTED;
private volatile int impl$negotiationState;
//Packed long. Avoids subtle threading related problems with
//two separate fields.
//Bits 63-32 are for the phase.
//Bits 31-0 are for the state.
private volatile long impl$negotiationValue = ServerLoginPacketListenerImplMixin.impl$packNegotiationValue(
ServerLoginPacketListenerImplMixin.NEGOTIATION_NOT_STARTED, 0);

@Override
public Connection bridge$getConnection() {
Expand Down Expand Up @@ -155,16 +161,12 @@ public abstract class ServerLoginPacketListenerImplMixin implements ServerLoginP
if (store.isEmpty()) {
this.impl$processVerification();
} else {
this.impl$changeNegotiationPhase(ServerLoginPacketListenerImplMixin.NEGOTIATION_INTENT, ServerLoginPacketListenerImplMixin.INTENT_SYNC_PLUGIN_DATA);
this.state = ServerLoginPacketListenerImpl.State.NEGOTIATING;
this.impl$negotiationPhase = ServerLoginPacketListenerImplMixin.NEGOTIATION_INTENT;
this.impl$negotiationState = ServerLoginPacketListenerImplMixin.INTENT_SYNC_PLUGIN_DATA;
}
}

private void impl$processVerification() {
this.impl$negotiationPhase = ServerLoginPacketListenerImplMixin.NEGOTIATION_HANDSHAKE;
this.impl$negotiationState = ServerLoginPacketListenerImplMixin.HANDSHAKE_NOT_STARTED;

((PlayerListBridge) this.server.getPlayerList()).bridge$canPlayerLogin(this.connection.getRemoteAddress(), this.authenticatedProfile)
.handle((componentOpt, throwable) -> {
if (throwable != null) {
Expand Down Expand Up @@ -214,6 +216,7 @@ public abstract class ServerLoginPacketListenerImplMixin implements ServerLoginP
return;
}

this.impl$changeNegotiationPhase(ServerLoginPacketListenerImplMixin.NEGOTIATION_HANDSHAKE, ServerLoginPacketListenerImplMixin.HANDSHAKE_NOT_STARTED);
this.state = ServerLoginPacketListenerImpl.State.NEGOTIATING;
}

Expand Down Expand Up @@ -298,53 +301,79 @@ public void handleKey(final ServerboundKeyPacket packet) {
@Inject(method = "tick", at = @At("HEAD"))
private void impl$onTick(final CallbackInfo ci) {
if (this.state == ServerLoginPacketListenerImpl.State.NEGOTIATING) {
if (this.impl$negotiationPhase == ServerLoginPacketListenerImplMixin.NEGOTIATION_INTENT) {
this.impl$handleIntentNegotiation();
} else if (this.impl$negotiationPhase == ServerLoginPacketListenerImplMixin.NEGOTIATION_HANDSHAKE) {
this.impl$handleHandshakeNegotiation();
final long value = this.impl$negotiationValue;
final int phase = ServerLoginPacketListenerImplMixin.impl$readNegotiationPhase(value);
final int state = ServerLoginPacketListenerImplMixin.impl$readNegotiationState(value);
if (phase == ServerLoginPacketListenerImplMixin.NEGOTIATION_INTENT) {
this.impl$handleIntentNegotiation(state);
} else if (phase == ServerLoginPacketListenerImplMixin.NEGOTIATION_HANDSHAKE) {
this.impl$handleHandshakeNegotiation(state);
}
}
}

private void impl$handleIntentNegotiation() {
if (this.impl$negotiationState == ServerLoginPacketListenerImplMixin.INTENT_SYNC_PLUGIN_DATA) {
private void impl$handleIntentNegotiation(final int state) {
if (state == ServerLoginPacketListenerImplMixin.INTENT_SYNC_PLUGIN_DATA) {
final ServerSideConnection connection = (ServerSideConnection) ((ConnectionBridge) this.connection).bridge$getEngineConnection();
final TransactionStore store = ConnectionUtil.getTransactionStore(connection);
if (store.isEmpty()) {
this.impl$changeNegotiationState(ServerLoginPacketListenerImplMixin.INTENT_DONE);
this.impl$processVerification();
}
}
}

private void impl$handleHandshakeNegotiation() {
private void impl$handleHandshakeNegotiation(final int state) {
final ServerSideConnection connection = (ServerSideConnection) ((ConnectionBridge) this.connection).bridge$getEngineConnection();
if (this.impl$negotiationState == ServerLoginPacketListenerImplMixin.HANDSHAKE_NOT_STARTED) {
this.impl$negotiationState = ServerLoginPacketListenerImplMixin.HANDSHAKE_CLIENT_TYPE;
if (state == ServerLoginPacketListenerImplMixin.HANDSHAKE_NOT_STARTED) {
this.impl$changeNegotiationState(ServerLoginPacketListenerImplMixin.HANDSHAKE_CLIENT_TYPE);

((SpongeChannelManager) Sponge.channelManager()).requestClientType(connection).thenAccept(result -> {
this.impl$negotiationState = ServerLoginPacketListenerImplMixin.HANDSHAKE_SYNC_CHANNEL_REGISTRATIONS;
this.impl$changeNegotiationState(ServerLoginPacketListenerImplMixin.HANDSHAKE_SYNC_CHANNEL_REGISTRATIONS);
});

} else if (this.impl$negotiationState == ServerLoginPacketListenerImplMixin.HANDSHAKE_SYNC_CHANNEL_REGISTRATIONS) {
this.impl$negotiationState = ServerLoginPacketListenerImplMixin.HANDSHAKE_CHANNEL_REGISTRATION;
} else if (state == ServerLoginPacketListenerImplMixin.HANDSHAKE_SYNC_CHANNEL_REGISTRATIONS) {
this.impl$changeNegotiationState(ServerLoginPacketListenerImplMixin.HANDSHAKE_CHANNEL_REGISTRATION);

((SpongeChannelManager) Sponge.channelManager()).sendLoginChannelRegistry(connection).thenAccept(result -> {
final Cause cause = Cause.of(EventContext.empty(), this);
final ServerSideConnectionEvent.Handshake event =
SpongeEventFactory.createServerSideConnectionEventHandshake(cause, connection, SpongeGameProfile.of(this.authenticatedProfile));
SpongeCommon.post(event);
this.impl$negotiationState = ServerLoginPacketListenerImplMixin.HANDSHAKE_SYNC_PLUGIN_DATA;
this.impl$changeNegotiationState(ServerLoginPacketListenerImplMixin.HANDSHAKE_SYNC_PLUGIN_DATA);
});
} else if (this.impl$negotiationState == ServerLoginPacketListenerImplMixin.HANDSHAKE_SYNC_PLUGIN_DATA) {
} else if (state == ServerLoginPacketListenerImplMixin.HANDSHAKE_SYNC_PLUGIN_DATA) {
final TransactionStore store = ConnectionUtil.getTransactionStore(connection);
if (store.isEmpty()) {
this.impl$changeNegotiationState(ServerLoginPacketListenerImplMixin.HANDSHAKE_DONE);
this.state = ServerLoginPacketListenerImpl.State.VERIFYING;
}
}
}

@Override
public boolean bridge$isIntentDone() {
return this.impl$negotiationState > ServerLoginPacketListenerImplMixin.NEGOTIATION_INTENT;
final int phase = ServerLoginPacketListenerImplMixin.impl$readNegotiationPhase(this.impl$negotiationValue);
return phase > ServerLoginPacketListenerImplMixin.NEGOTIATION_INTENT;
}

private void impl$changeNegotiationPhase(final int phase, final int state) {
this.impl$negotiationValue = ServerLoginPacketListenerImplMixin.impl$packNegotiationValue(phase, state);
}

private void impl$changeNegotiationState(final int state) {
this.impl$changeNegotiationPhase(ServerLoginPacketListenerImplMixin.impl$readNegotiationPhase(this.impl$negotiationValue), state);
}

private static long impl$packNegotiationValue(final int phase, final int state) {
return Integer.toUnsignedLong(phase) << 32 | Integer.toUnsignedLong(state);
}

private static int impl$readNegotiationPhase(final long value) {
return (int) (value >>> 32);
}

private static int impl$readNegotiationState(final long value) {
return (int) value;
}
}

0 comments on commit 1054499

Please sign in to comment.