@@ -235,7 +235,7 @@ public class ServerJoinListener implements Listener {
235235 /**
236236 * A map for holding all currently connecting players.
237237 */
238- private final Map<PlayerCommonConnection , CompletableFuture<Boolean > > awaitingResponse = new HashMap <> ();
238+ private final Map<UUID , CompletableFuture<Boolean > > awaitingResponse = new ConcurrentHashMap <> ();
239239
240240 @EventHandler
241241 void onPlayerConfigure (AsyncPlayerConnectionConfigureEvent event ) {
@@ -248,46 +248,74 @@ public class ServerJoinListener implements Listener {
248248 return ;
249249 }
250250
251+ PlayerConfigurationConnection connection = event. getConnection();
252+ UUID uniqueId = connection. getProfile(). getId();
253+ if (uniqueId == null ) {
254+ return ;
255+ }
256+
251257 // Construct a new completable future without a task.
252258 CompletableFuture<Boolean > response = new CompletableFuture<> ();
259+ // Complete the future if nothing has been done after one minute.
260+ response. completeOnTimeout(false , 1 , TimeUnit . MINUTES );
253261
254262 // Put it into our map.
255- awaitingResponse. put(event . getConnection() , response);
263+ awaitingResponse. put(uniqueId , response);
256264
265+ Audience audience = connection. getAudience();
257266 // Show the connecting player the dialog.
258- event . getConnection() . getAudience() . showDialog(dialog);
267+ audience . showDialog(dialog);
259268
260269 // Wait until the future is complete. This step is necessary in order to keep the player in the configuration phase.
261270 if (! response. join()) {
271+ // We close the dialog manually because the client might not do it on its own.
272+ audience. closeDialog();
262273 // If the response is false, they declined. Therefore, we kick them from the server.
263- event . getConnection() . disconnect(Component . text(" You hate Paper-chan :(" , NamedTextColor . RED ));
274+ connection . disconnect(Component . text(" You hate Paper-chan :(" , NamedTextColor . RED ));
264275 }
265276
266277 // We clean the map to avoid unnecessary entry buildup.
267- awaitingResponse. remove(event . getConnection() );
278+ awaitingResponse. remove(uniqueId );
268279 }
269280
270281 /**
271282 * An event for handling dialog button click events.
272283 */
273284 @EventHandler
274285 void onHandleDialog (PlayerCustomClickEvent event ) {
275- Key key = event. getIdentifier();
286+ // Handle custom click only for configuration connection.
287+ if (! (event. getCommonConnection() instanceof PlayerConfigurationConnection configurationConnection)) {
288+ return ;
289+ }
276290
291+ UUID uniqueId = configurationConnection. getProfile(). getId();
292+ if (uniqueId == null ) {
293+ return ;
294+ }
295+
296+ Key key = event. getIdentifier();
277297 if (key. equals(Key . key(" papermc:paperchan/disagree" ))) {
278298 // If the identifier is the same as the disagree one, set the connection result to false.
279- setConnectionJoinResult(event . getCommonConnection() , false );
299+ setConnectionJoinResult(uniqueId , false );
280300 } else if (key. equals(Key . key(" papermc:paperchan/agree" ))) {
281301 // If it is the same as the agree one, set the result to true.
282- setConnectionJoinResult(event . getCommonConnection() , true );
302+ setConnectionJoinResult(uniqueId , true );
283303 }
284304 }
285305
306+ /**
307+ * An event handler for cleanup the map to avoid unnecessary entry buildup.
308+ */
309+ @EventHandler
310+ void onConnectionClose (PlayerConnectionCloseEvent event ) {
311+ awaitingResponse. remove(event. getPlayerUniqueId());
312+ }
313+
286314 /**
287315 * Simple utility method for setting a connection's dialog response result.
288316 */
289- private void setConnectionJoinResult (PlayerCommonConnection connection , boolean value ) {
290- CompletableFuture<Boolean > future = awaitingResponse. get(connection );
317+ private void setConnectionJoinResult (UUID uniqueId , boolean value ) {
318+ CompletableFuture<Boolean > future = awaitingResponse. get(uniqueId );
291319 if (future != null ) {
292320 future. complete(value);
293321 }
0 commit comments