mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-01-06 10:44:29 +01:00
Ensure that exceptions in player event loop are handled
Any stray exception means that the entire event loop comes crashing down.
This commit is contained in:
parent
76399881a3
commit
3d04a957d0
9 changed files with 57 additions and 27 deletions
|
@ -161,7 +161,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handle(ModalFormResponsePacket packet) {
|
public boolean handle(ModalFormResponsePacket packet) {
|
||||||
session.getEventLoop().execute(() -> session.getFormCache().handleResponse(packet));
|
session.executeInEventLoop(() -> session.getFormCache().handleResponse(packet));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -115,6 +115,7 @@ public class GeyserSession implements CommandSender {
|
||||||
private final UpstreamSession upstream;
|
private final UpstreamSession upstream;
|
||||||
/**
|
/**
|
||||||
* The loop where all packets and ticking is processed to prevent concurrency issues.
|
* The loop where all packets and ticking is processed to prevent concurrency issues.
|
||||||
|
* If this is manually called, ensure that any exceptions are properly handled.
|
||||||
*/
|
*/
|
||||||
private final EventLoop eventLoop;
|
private final EventLoop eventLoop;
|
||||||
private TcpClientSession downstream;
|
private TcpClientSession downstream;
|
||||||
|
@ -804,9 +805,8 @@ public class GeyserSession implements CommandSender {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void packetReceived(PacketReceivedEvent event) {
|
public void packetReceived(PacketReceivedEvent event) {
|
||||||
if (!closed) {
|
Packet packet = event.getPacket();
|
||||||
PacketTranslatorRegistry.JAVA_TRANSLATOR.translate(event.getPacket().getClass(), event.getPacket(), GeyserSession.this);
|
PacketTranslatorRegistry.JAVA_TRANSLATOR.translate(packet.getClass(), packet, GeyserSession.this);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -874,6 +874,32 @@ public class GeyserSession implements CommandSender {
|
||||||
disconnect(LanguageUtils.getPlayerLocaleString("geyser.network.close", getClientData().getLanguageCode()));
|
disconnect(LanguageUtils.getPlayerLocaleString("geyser.network.close", getClientData().getLanguageCode()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a task and prints a stack trace if an error occurs.
|
||||||
|
*/
|
||||||
|
public void executeInEventLoop(Runnable runnable) {
|
||||||
|
eventLoop.execute(() -> {
|
||||||
|
try {
|
||||||
|
runnable.run();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedules a task and prints a stack trace if an error occurs.
|
||||||
|
*/
|
||||||
|
public ScheduledFuture<?> scheduleInEventLoop(Runnable runnable, long duration, TimeUnit timeUnit) {
|
||||||
|
return eventLoop.schedule(() -> {
|
||||||
|
try {
|
||||||
|
runnable.run();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}, duration, timeUnit);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called every 50 milliseconds - one Minecraft tick.
|
* Called every 50 milliseconds - one Minecraft tick.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -87,27 +87,31 @@ public class PacketTranslatorRegistry<T> {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <P extends T> boolean translate(Class<? extends P> clazz, P packet, GeyserSession session) {
|
public <P extends T> boolean translate(Class<? extends P> clazz, P packet, GeyserSession session) {
|
||||||
if (!session.getUpstream().isClosed() && !session.isClosed()) {
|
if (!session.getUpstream().isClosed() && !session.isClosed()) {
|
||||||
try {
|
PacketTranslator<P> translator = (PacketTranslator<P>) translators.get(clazz);
|
||||||
PacketTranslator<P> translator = (PacketTranslator<P>) translators.get(clazz);
|
if (translator != null) {
|
||||||
if (translator != null) {
|
EventLoop eventLoop = session.getEventLoop();
|
||||||
EventLoop eventLoop = session.getEventLoop();
|
if (eventLoop.inEventLoop()) {
|
||||||
if (eventLoop.inEventLoop()) {
|
translate0(session, translator, packet);
|
||||||
translator.translate(packet, session);
|
|
||||||
} else {
|
|
||||||
eventLoop.execute(() -> translator.translate(packet, session));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else {
|
} else {
|
||||||
if ((GeyserConnector.getInstance().getPlatformType() != PlatformType.STANDALONE || !(packet instanceof BedrockPacket)) && !IGNORED_PACKETS.contains(clazz)) {
|
eventLoop.execute(() -> translate0(session, translator, packet));
|
||||||
// Other debug logs already take care of Bedrock packets for us if on standalone
|
}
|
||||||
GeyserConnector.getInstance().getLogger().debug("Could not find packet for " + (packet.toString().length() > 25 ? packet.getClass().getSimpleName() : packet));
|
return true;
|
||||||
}
|
} else {
|
||||||
|
if ((GeyserConnector.getInstance().getPlatformType() != PlatformType.STANDALONE || !(packet instanceof BedrockPacket)) && !IGNORED_PACKETS.contains(clazz)) {
|
||||||
|
// Other debug logs already take care of Bedrock packets for us if on standalone
|
||||||
|
GeyserConnector.getInstance().getLogger().debug("Could not find packet for " + (packet.toString().length() > 25 ? packet.getClass().getSimpleName() : packet));
|
||||||
}
|
}
|
||||||
} catch (Throwable ex) {
|
|
||||||
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.packet.failed", packet.getClass().getSimpleName()), ex);
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private <P extends T> void translate0(GeyserSession session, PacketTranslator<P> translator, P packet) {
|
||||||
|
try {
|
||||||
|
translator.translate(packet, session);
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.packet.failed", packet.getClass().getSimpleName()), ex);
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class BedrockAnimateTranslator extends PacketTranslator<AnimatePacket> {
|
||||||
switch (packet.getAction()) {
|
switch (packet.getAction()) {
|
||||||
case SWING_ARM:
|
case SWING_ARM:
|
||||||
// Delay so entity damage can be processed first
|
// Delay so entity damage can be processed first
|
||||||
session.getEventLoop().schedule(() ->
|
session.scheduleInEventLoop(() ->
|
||||||
session.sendDownstreamPacket(new ClientPlayerSwingArmPacket(Hand.MAIN_HAND)),
|
session.sendDownstreamPacket(new ClientPlayerSwingArmPacket(Hand.MAIN_HAND)),
|
||||||
25,
|
25,
|
||||||
TimeUnit.MILLISECONDS
|
TimeUnit.MILLISECONDS
|
||||||
|
|
|
@ -220,7 +220,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
||||||
if (session.isSneaking() || blockState != BlockRegistries.JAVA_IDENTIFIERS.get("minecraft:crafting_table")) {
|
if (session.isSneaking() || blockState != BlockRegistries.JAVA_IDENTIFIERS.get("minecraft:crafting_table")) {
|
||||||
// Delay the interaction in case the client doesn't intend to actually use the bucket
|
// Delay the interaction in case the client doesn't intend to actually use the bucket
|
||||||
// See BedrockActionTranslator.java
|
// See BedrockActionTranslator.java
|
||||||
session.setBucketScheduledFuture(session.getEventLoop().schedule(() -> {
|
session.setBucketScheduledFuture(session.scheduleInEventLoop(() -> {
|
||||||
ClientPlayerUseItemPacket itemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
|
ClientPlayerUseItemPacket itemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
|
||||||
session.sendDownstreamPacket(itemPacket);
|
session.sendDownstreamPacket(itemPacket);
|
||||||
}, 5, TimeUnit.MILLISECONDS));
|
}, 5, TimeUnit.MILLISECONDS));
|
||||||
|
|
|
@ -53,7 +53,7 @@ public class BedrockEntityEventTranslator extends PacketTranslator<EntityEventPa
|
||||||
ClientSelectTradePacket selectTradePacket = new ClientSelectTradePacket(packet.getData());
|
ClientSelectTradePacket selectTradePacket = new ClientSelectTradePacket(packet.getData());
|
||||||
session.sendDownstreamPacket(selectTradePacket);
|
session.sendDownstreamPacket(selectTradePacket);
|
||||||
|
|
||||||
session.getEventLoop().schedule(() -> {
|
session.scheduleInEventLoop(() -> {
|
||||||
Entity villager = session.getPlayerEntity();
|
Entity villager = session.getPlayerEntity();
|
||||||
Inventory openInventory = session.getOpenInventory();
|
Inventory openInventory = session.getOpenInventory();
|
||||||
if (openInventory instanceof MerchantContainer) {
|
if (openInventory instanceof MerchantContainer) {
|
||||||
|
|
|
@ -78,7 +78,7 @@ public class JavaSetSlotTranslator extends PacketTranslator<ServerSetSlotPacket>
|
||||||
if (session.getCraftingGridFuture() != null) {
|
if (session.getCraftingGridFuture() != null) {
|
||||||
session.getCraftingGridFuture().cancel(false);
|
session.getCraftingGridFuture().cancel(false);
|
||||||
}
|
}
|
||||||
session.setCraftingGridFuture(session.getEventLoop().schedule(() -> updateCraftingGrid(session, packet, inventory, translator), 150, TimeUnit.MILLISECONDS));
|
session.setCraftingGridFuture(session.scheduleInEventLoop(() -> updateCraftingGrid(session, packet, inventory, translator), 150, TimeUnit.MILLISECONDS));
|
||||||
|
|
||||||
GeyserItemStack newItem = GeyserItemStack.from(packet.getItem());
|
GeyserItemStack newItem = GeyserItemStack.from(packet.getItem());
|
||||||
if (packet.getWindowId() == 0 && !(translator instanceof PlayerInventoryTranslator)) {
|
if (packet.getWindowId() == 0 && !(translator instanceof PlayerInventoryTranslator)) {
|
||||||
|
|
|
@ -147,7 +147,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
|
||||||
if (session.getUpstream().isInitialized()) {
|
if (session.getUpstream().isInitialized()) {
|
||||||
player.spawnEntity(session);
|
player.spawnEntity(session);
|
||||||
|
|
||||||
SkullSkinManager.requestAndHandleSkin(player, session, (skin -> session.getEventLoop().schedule(() -> {
|
SkullSkinManager.requestAndHandleSkin(player, session, (skin -> session.scheduleInEventLoop(() -> {
|
||||||
// Delay to minimize split-second "player" pop-in
|
// Delay to minimize split-second "player" pop-in
|
||||||
player.getMetadata().getFlags().setFlag(EntityFlag.INVISIBLE, false);
|
player.getMetadata().getFlags().setFlag(EntityFlag.INVISIBLE, false);
|
||||||
player.updateBedrockMetadata(session);
|
player.updateBedrockMetadata(session);
|
||||||
|
|
|
@ -79,7 +79,7 @@ public class InventoryUtils {
|
||||||
if (translator != null) {
|
if (translator != null) {
|
||||||
translator.prepareInventory(session, inventory);
|
translator.prepareInventory(session, inventory);
|
||||||
if (translator instanceof DoubleChestInventoryTranslator && !((Container) inventory).isUsingRealBlock()) {
|
if (translator instanceof DoubleChestInventoryTranslator && !((Container) inventory).isUsingRealBlock()) {
|
||||||
session.getEventLoop().schedule(() -> {
|
session.scheduleInEventLoop(() -> {
|
||||||
Inventory openInv = session.getOpenInventory();
|
Inventory openInv = session.getOpenInventory();
|
||||||
if (openInv != null && openInv.getId() == inventory.getId()) {
|
if (openInv != null && openInv.getId() == inventory.getId()) {
|
||||||
translator.openInventory(session, inventory);
|
translator.openInventory(session, inventory);
|
||||||
|
|
Loading…
Reference in a new issue