Cancel Erosion futures when disconnecting/switching servers (#5026)

This commit is contained in:
AJ Ferguson 2024-09-07 22:29:27 -04:00 committed by GitHub
parent 34f5d71e58
commit e194880f7e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 119 additions and 11 deletions

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2024 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.erosion;
import java.io.Serial;
import java.util.concurrent.CancellationException;
public class ErosionCancellationException extends CancellationException {
@Serial
private static final long serialVersionUID = 1L;
}

View file

@ -42,7 +42,6 @@ public final class GeyserboundHandshakePacketHandler extends AbstractGeyserbound
public void handleHandshake(GeyserboundHandshakePacket packet) {
boolean useTcp = packet.getTransportType().getSocketAddress() == null;
GeyserboundPacketHandlerImpl handler = new GeyserboundPacketHandlerImpl(session, useTcp ? new GeyserErosionPacketSender(session) : new NettyPacketSender<>());
session.setErosionHandler(handler);
if (!useTcp) {
if (session.getGeyser().getErosionUnixListener() == null) {
session.disconnect("Erosion configurations using Unix socket handling are not supported on this hardware!");
@ -52,6 +51,7 @@ public final class GeyserboundHandshakePacketHandler extends AbstractGeyserbound
} else {
handler.onConnect();
}
session.setErosionHandler(handler);
session.ensureInEventLoop(() -> session.getChunkCache().clear());
}

View file

@ -171,10 +171,10 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke
@Override
public void handleHandshake(GeyserboundHandshakePacket packet) {
this.close();
var handler = new GeyserboundHandshakePacketHandler(this.session);
session.setErosionHandler(handler);
handler.handleHandshake(packet);
this.close();
}
@Override
@ -198,6 +198,17 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke
public void close() {
this.packetSender.close();
if (pendingLookup != null) {
pendingLookup.completeExceptionally(new ErosionCancellationException());
}
if (pendingBatchLookup != null) {
pendingBatchLookup.completeExceptionally(new ErosionCancellationException());
}
if (pickBlockLookup != null) {
pickBlockLookup.completeExceptionally(new ErosionCancellationException());
}
asyncPendingLookups.forEach(($, future) -> future.completeExceptionally(new ErosionCancellationException()));
}
public int getNextTransactionId() {

View file

@ -35,6 +35,7 @@ import org.geysermc.erosion.packet.backendbound.BackendboundBatchBlockRequestPac
import org.geysermc.erosion.packet.backendbound.BackendboundBlockRequestPacket;
import org.geysermc.erosion.packet.backendbound.BackendboundPickBlockPacket;
import org.geysermc.erosion.util.BlockPositionIterator;
import org.geysermc.geyser.erosion.ErosionCancellationException;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
@ -49,6 +50,8 @@ public class GeyserWorldManager extends WorldManager {
var erosionHandler = session.getErosionHandler().getAsActive();
if (erosionHandler == null) {
return session.getChunkCache().getBlockAt(x, y, z);
} else if (session.isClosed()) {
throw new ErosionCancellationException();
}
CompletableFuture<Integer> future = new CompletableFuture<>(); // Boxes
erosionHandler.setPendingLookup(future);
@ -61,6 +64,8 @@ public class GeyserWorldManager extends WorldManager {
var erosionHandler = session.getErosionHandler().getAsActive();
if (erosionHandler == null) {
return super.getBlockAtAsync(session, x, y, z);
} else if (session.isClosed()) {
return CompletableFuture.failedFuture(new ErosionCancellationException());
}
CompletableFuture<Integer> future = new CompletableFuture<>(); // Boxes
int transactionId = erosionHandler.getNextTransactionId();
@ -74,6 +79,8 @@ public class GeyserWorldManager extends WorldManager {
var erosionHandler = session.getErosionHandler().getAsActive();
if (erosionHandler == null) {
return super.getBlocksAt(session, iter);
} else if (session.isClosed()) {
throw new ErosionCancellationException();
}
CompletableFuture<int[]> future = new CompletableFuture<>();
erosionHandler.setPendingBatchLookup(future);
@ -124,6 +131,8 @@ public class GeyserWorldManager extends WorldManager {
var erosionHandler = session.getErosionHandler().getAsActive();
if (erosionHandler == null) {
return super.getPickItemComponents(session, x, y, z, addNbtData);
} else if (session.isClosed()) {
return CompletableFuture.failedFuture(new ErosionCancellationException());
}
CompletableFuture<Int2ObjectMap<byte[]>> future = new CompletableFuture<>();
erosionHandler.setPickBlockLookup(future);

View file

@ -31,6 +31,7 @@ import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.Clien
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundLightUpdatePacket;
import io.netty.channel.EventLoop;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.erosion.ErosionCancellationException;
import org.geysermc.geyser.registry.loader.RegistryLoaders;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.GeyserLocale;
@ -87,6 +88,8 @@ public class PacketTranslatorRegistry<T> extends AbstractMappedRegistry<Class<?
try {
translator.translate(session, packet);
} catch (ErosionCancellationException ex) {
GeyserImpl.getInstance().getLogger().debug("Caught ErosionCancellationException");
} catch (Throwable ex) {
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.network.translator.packet.failed", packet.getClass().getSimpleName()), ex);
ex.printStackTrace();

View file

@ -130,6 +130,7 @@ import org.geysermc.geyser.entity.type.Tickable;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.entity.vehicle.ClientVehicle;
import org.geysermc.geyser.erosion.AbstractGeyserboundPacketHandler;
import org.geysermc.geyser.erosion.ErosionCancellationException;
import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler;
import org.geysermc.geyser.impl.camera.CameraDefinitions;
import org.geysermc.geyser.impl.camera.GeyserCameraData;
@ -258,7 +259,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@NonNull
@Setter
private AbstractGeyserboundPacketHandler erosionHandler;
private volatile AbstractGeyserboundPacketHandler erosionHandler;
@Accessors(fluent = true)
@Setter
@ -1190,9 +1191,9 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
tickThread.cancel(false);
}
erosionHandler.close();
// Mark session as closed before cancelling erosion futures
closed = true;
erosionHandler.close();
}
/**
@ -1213,6 +1214,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
eventLoop.execute(() -> {
try {
runnable.run();
} catch (ErosionCancellationException e) {
geyser.getLogger().debug("Caught ErosionCancellationException");
} catch (Throwable e) {
geyser.getLogger().error("Error thrown in " + this.bedrockUsername() + "'s event loop!", e);
}
@ -1230,6 +1233,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
if (!closed) {
runnable.run();
}
} catch (ErosionCancellationException e) {
geyser.getLogger().debug("Caught ErosionCancellationException");
} catch (Throwable e) {
geyser.getLogger().error("Error thrown in " + this.bedrockUsername() + "'s event loop!", e);
}

View file

@ -33,7 +33,6 @@ import org.geysermc.erosion.Constants;
import org.geysermc.floodgate.pluginmessage.PluginMessageChannels;
import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler;
import org.geysermc.geyser.level.JavaDimension;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
@ -57,11 +56,6 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
SessionPlayerEntity entity = session.getPlayerEntity();
entity.setEntityId(packet.getEntityId());
if (session.getErosionHandler().isActive()) {
session.getErosionHandler().close();
session.setErosionHandler(new GeyserboundHandshakePacketHandler(session));
}
PlayerSpawnInfo spawnInfo = packet.getCommonPlayerSpawnInfo();
JavaDimension newDimension = session.getRegistryCache().dimensions().byId(spawnInfo.getDimension());

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2024 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.translator.protocol.java;
import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundStartConfigurationPacket;
@Translator(packet = ClientboundStartConfigurationPacket.class)
public class JavaStartConfigurationTranslator extends PacketTranslator<ClientboundStartConfigurationPacket> {
@Override
public void translate(GeyserSession session, ClientboundStartConfigurationPacket packet) {
var erosionHandler = session.getErosionHandler();
if (erosionHandler.isActive()) {
// Set new handler before closing
session.setErosionHandler(new GeyserboundHandshakePacketHandler(session));
erosionHandler.close();
}
}
@Override
public boolean shouldExecuteInEventLoop() {
// Execute outside of event loop to cancel any pending erosion futures
return false;
}
}