mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Ensure every packet is ran on the same thread per player (#2473)
This removes a lot of concurrency checking that needs to be done, because there should be no way two packets can be handled at the same time.
This commit is contained in:
parent
eca0691db0
commit
7ae91a40ec
20 changed files with 198 additions and 233 deletions
|
@ -13,8 +13,8 @@
|
|||
<properties>
|
||||
<adventure.version>4.8.0</adventure.version>
|
||||
<fastutil.version>8.5.2</fastutil.version>
|
||||
<jackson.version>2.10.2</jackson.version>
|
||||
<netty.version>4.1.59.Final</netty.version>
|
||||
<jackson.version>2.12.4</jackson.version>
|
||||
<netty.version>4.1.66.Final</netty.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
@ -167,7 +167,7 @@
|
|||
<dependency>
|
||||
<groupId>com.github.GeyserMC</groupId>
|
||||
<artifactId>PacketLib</artifactId>
|
||||
<version>25eb4c4</version>
|
||||
<version>86c9c38</version>
|
||||
<scope>compile</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
|
|
|
@ -30,7 +30,9 @@ import com.nukkitx.protocol.bedrock.BedrockServerEventHandler;
|
|||
import com.nukkitx.protocol.bedrock.BedrockServerSession;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.DefaultEventLoopGroup;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
import io.netty.util.concurrent.DefaultThreadFactory;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.common.ping.GeyserPingInfo;
|
||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||
|
@ -56,6 +58,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
|
|||
private static final int MAGIC_RAKNET_LENGTH = 338;
|
||||
|
||||
private final GeyserConnector connector;
|
||||
private final DefaultEventLoopGroup eventLoopGroup = new DefaultEventLoopGroup(new DefaultThreadFactory("Geyser player thread"));
|
||||
|
||||
public ConnectorServerEventHandler(GeyserConnector connector) {
|
||||
this.connector = connector;
|
||||
|
@ -162,7 +165,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
|
|||
public void onSessionCreation(BedrockServerSession bedrockServerSession) {
|
||||
bedrockServerSession.setLogging(true);
|
||||
bedrockServerSession.setCompressionLevel(connector.getConfig().getBedrock().getCompressionLevel());
|
||||
bedrockServerSession.setPacketHandler(new UpstreamPacketHandler(connector, new GeyserSession(connector, bedrockServerSession)));
|
||||
bedrockServerSession.setPacketHandler(new UpstreamPacketHandler(connector, new GeyserSession(connector, bedrockServerSession, eventLoopGroup.next())));
|
||||
// Set the packet codec to default just in case we need to send disconnect packets.
|
||||
bedrockServerSession.setPacketCodec(BedrockProtocol.DEFAULT_BEDROCK_CODEC);
|
||||
}
|
||||
|
|
|
@ -52,6 +52,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
return PacketTranslatorRegistry.BEDROCK_TRANSLATOR.translate(packet.getClass(), packet, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean defaultHandler(BedrockPacket packet) {
|
||||
return translateAndDefault(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(LoginPacket loginPacket) {
|
||||
BedrockPacketCodec packetCodec = BedrockProtocol.getBedrockCodec(loginPacket.getProtocolVersion());
|
||||
|
@ -156,7 +161,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
|
||||
@Override
|
||||
public boolean handle(ModalFormResponsePacket packet) {
|
||||
session.getFormCache().handleResponse(packet);
|
||||
session.getEventLoop().execute(() -> session.getFormCache().handleResponse(packet));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -209,11 +214,6 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
return translateAndDefault(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean defaultHandler(BedrockPacket packet) {
|
||||
return translateAndDefault(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(ResourcePackChunkRequestPacket packet) {
|
||||
ResourcePackChunkDataPacket data = new ResourcePackChunkDataPacket();
|
||||
|
|
|
@ -57,9 +57,9 @@ import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
|
|||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||
import com.nukkitx.protocol.bedrock.packet.*;
|
||||
import io.netty.channel.EventLoop;
|
||||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||
|
@ -102,7 +102,10 @@ import java.net.InetAddress;
|
|||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Getter
|
||||
|
@ -110,6 +113,10 @@ public class GeyserSession implements CommandSender {
|
|||
|
||||
private final GeyserConnector connector;
|
||||
private final UpstreamSession upstream;
|
||||
/**
|
||||
* The loop where all packets and ticking is processed to prevent concurrency issues.
|
||||
*/
|
||||
private final EventLoop eventLoop;
|
||||
private TcpClientSession downstream;
|
||||
@Setter
|
||||
private AuthData authData;
|
||||
|
@ -158,11 +165,6 @@ public class GeyserSession implements CommandSender {
|
|||
@Getter(AccessLevel.NONE)
|
||||
private final AtomicInteger itemNetId = new AtomicInteger(2);
|
||||
|
||||
@Getter(AccessLevel.NONE)
|
||||
private final Object inventoryLock = new Object();
|
||||
@Getter(AccessLevel.NONE)
|
||||
private CompletableFuture<Void> inventoryFuture;
|
||||
|
||||
@Setter
|
||||
private ScheduledFuture<?> craftingGridFuture;
|
||||
|
||||
|
@ -183,8 +185,8 @@ public class GeyserSession implements CommandSender {
|
|||
@Setter
|
||||
private ItemMappings itemMappings;
|
||||
|
||||
private final Map<Vector3i, SkullPlayerEntity> skullCache = new ConcurrentHashMap<>();
|
||||
private final Long2ObjectMap<ClientboundMapItemDataPacket> storedMaps = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
|
||||
private final Map<Vector3i, SkullPlayerEntity> skullCache = new Object2ObjectOpenHashMap<>();
|
||||
private final Long2ObjectMap<ClientboundMapItemDataPacket> storedMaps = new Long2ObjectOpenHashMap<>();
|
||||
|
||||
/**
|
||||
* Stores the map between Java and Bedrock biome network IDs.
|
||||
|
@ -426,9 +428,10 @@ public class GeyserSession implements CommandSender {
|
|||
|
||||
private MinecraftProtocol protocol;
|
||||
|
||||
public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) {
|
||||
public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession, EventLoop eventLoop) {
|
||||
this.connector = connector;
|
||||
this.upstream = new UpstreamSession(bedrockServerSession);
|
||||
this.eventLoop = eventLoop;
|
||||
|
||||
this.advancementsCache = new AdvancementsCache(this);
|
||||
this.bookEditCache = new BookEditCache(this);
|
||||
|
@ -447,7 +450,6 @@ public class GeyserSession implements CommandSender {
|
|||
|
||||
this.playerInventory = new PlayerInventory();
|
||||
this.openInventory = null;
|
||||
this.inventoryFuture = CompletableFuture.completedFuture(null);
|
||||
this.craftingRecipes = new Int2ObjectOpenHashMap<>();
|
||||
this.unlockedRecipes = new ObjectOpenHashSet<>();
|
||||
this.lastRecipeNetId = new AtomicInteger(1);
|
||||
|
@ -664,7 +666,7 @@ public class GeyserSession implements CommandSender {
|
|||
boolean floodgate = this.remoteAuthType == AuthType.FLOODGATE;
|
||||
|
||||
// Start ticking
|
||||
tickThread = connector.getGeneralThreadPool().scheduleAtFixedRate(this::tick, 50, 50, TimeUnit.MILLISECONDS);
|
||||
tickThread = eventLoop.scheduleAtFixedRate(this::tick, 50, 50, TimeUnit.MILLISECONDS);
|
||||
|
||||
downstream = new TcpClientSession(this.remoteAddress, this.remotePort, protocol);
|
||||
disableSrvResolving();
|
||||
|
@ -1095,39 +1097,6 @@ public class GeyserSession implements CommandSender {
|
|||
upstream.sendPacket(startGamePacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new inventory task.
|
||||
* Inventory tasks are executed one at a time, in order.
|
||||
*
|
||||
* @param task the task to run
|
||||
*/
|
||||
public void addInventoryTask(Runnable task) {
|
||||
synchronized (inventoryLock) {
|
||||
inventoryFuture = inventoryFuture.thenRun(task).exceptionally(throwable -> {
|
||||
GeyserConnector.getInstance().getLogger().error("Error processing inventory task", throwable.getCause());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new inventory task with a delay.
|
||||
* The delay is achieved by scheduling with the Geyser general thread pool.
|
||||
* Inventory tasks are executed one at a time, in order.
|
||||
*
|
||||
* @param task the delayed task to run
|
||||
* @param delayMillis delay in milliseconds
|
||||
*/
|
||||
public void addInventoryTask(Runnable task, long delayMillis) {
|
||||
synchronized (inventoryLock) {
|
||||
Executor delayedExecutor = command -> GeyserConnector.getInstance().getGeneralThreadPool().schedule(command, delayMillis, TimeUnit.MILLISECONDS);
|
||||
inventoryFuture = inventoryFuture.thenRunAsync(task, delayedExecutor).exceptionally(throwable -> {
|
||||
GeyserConnector.getInstance().getLogger().error("Error processing inventory task", throwable.getCause());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the next Bedrock item network ID to use for a new item
|
||||
*/
|
||||
|
@ -1229,7 +1198,18 @@ public class GeyserSession implements CommandSender {
|
|||
* @param packet the java edition packet from MCProtocolLib
|
||||
*/
|
||||
public void sendDownstreamPacket(Packet packet) {
|
||||
if (downstream != null && (protocol.getSubProtocol().equals(SubProtocol.GAME) || packet.getClass() == LoginPluginResponsePacket.class)) {
|
||||
if (!closed && this.downstream != null) {
|
||||
EventLoop eventLoop = this.downstream.getChannel().eventLoop();
|
||||
if (eventLoop.inEventLoop()) {
|
||||
sendDownstreamPacket0(packet);
|
||||
} else {
|
||||
eventLoop.execute(() -> sendDownstreamPacket0(packet));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendDownstreamPacket0(Packet packet) {
|
||||
if (protocol.getSubProtocol().equals(SubProtocol.GAME) || packet.getClass() == LoginPluginResponsePacket.class) {
|
||||
downstream.send(packet);
|
||||
} else {
|
||||
connector.getLogger().debug("Tried to send downstream packet " + packet.getClass().getSimpleName() + " before connected to the server");
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
package org.geysermc.connector.network.session.cache;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.*;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.connector.entity.Tickable;
|
||||
|
@ -44,15 +46,15 @@ public class EntityCache {
|
|||
private final GeyserSession session;
|
||||
|
||||
@Getter
|
||||
private Long2ObjectMap<Entity> entities = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
|
||||
private final Long2ObjectMap<Entity> entities = new Long2ObjectOpenHashMap<>();
|
||||
/**
|
||||
* A list of all entities that must be ticked.
|
||||
*/
|
||||
private final List<Tickable> tickableEntities = Collections.synchronizedList(new ArrayList<>());
|
||||
private Long2LongMap entityIdTranslations = Long2LongMaps.synchronize(new Long2LongOpenHashMap());
|
||||
private Map<UUID, PlayerEntity> playerEntities = Collections.synchronizedMap(new HashMap<>());
|
||||
private Map<UUID, BossBar> bossBars = Collections.synchronizedMap(new HashMap<>());
|
||||
private final Long2LongMap cachedPlayerEntityLinks = Long2LongMaps.synchronize(new Long2LongOpenHashMap());
|
||||
private final List<Tickable> tickableEntities = new ObjectArrayList<>();
|
||||
private final Long2LongMap entityIdTranslations = new Long2LongOpenHashMap();
|
||||
private final Map<UUID, PlayerEntity> playerEntities = new Object2ObjectOpenHashMap<>();
|
||||
private final Map<UUID, BossBar> bossBars = new Object2ObjectOpenHashMap<>();
|
||||
private final Long2LongMap cachedPlayerEntityLinks = new Long2LongOpenHashMap();
|
||||
|
||||
@Getter
|
||||
private final AtomicLong nextEntityId = new AtomicLong(2L);
|
||||
|
@ -156,13 +158,6 @@ public class EntityCache {
|
|||
bossBars.values().forEach(BossBar::updateBossBar);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
entities = null;
|
||||
entityIdTranslations = null;
|
||||
playerEntities = null;
|
||||
bossBars = null;
|
||||
}
|
||||
|
||||
public long getCachedPlayerEntityLink(long playerId) {
|
||||
return cachedPlayerEntityLinks.remove(playerId);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.server.ServerPlayerListDa
|
|||
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdateLightPacket;
|
||||
import com.github.steveice10.packetlib.packet.Packet;
|
||||
import com.nukkitx.protocol.bedrock.BedrockPacket;
|
||||
import io.netty.channel.EventLoop;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import org.geysermc.common.PlatformType;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
|
@ -89,7 +90,12 @@ public class PacketTranslatorRegistry<T> {
|
|||
try {
|
||||
PacketTranslator<P> translator = (PacketTranslator<P>) translators.get(clazz);
|
||||
if (translator != null) {
|
||||
translator.translate(packet, session);
|
||||
EventLoop eventLoop = session.getEventLoop();
|
||||
if (eventLoop.inEventLoop()) {
|
||||
translator.translate(packet, session);
|
||||
} else {
|
||||
eventLoop.execute(() -> translator.translate(packet, session));
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if ((GeyserConnector.getInstance().getPlatformType() != PlatformType.STANDALONE || !(packet instanceof BedrockPacket)) && !IGNORED_PACKETS.contains(clazz)) {
|
||||
|
|
|
@ -48,7 +48,7 @@ public class BedrockAnimateTranslator extends PacketTranslator<AnimatePacket> {
|
|||
switch (packet.getAction()) {
|
||||
case SWING_ARM:
|
||||
// Delay so entity damage can be processed first
|
||||
session.getConnector().getGeneralThreadPool().schedule(() ->
|
||||
session.getEventLoop().schedule(() ->
|
||||
session.sendDownstreamPacket(new ClientPlayerSwingArmPacket(Hand.MAIN_HAND)),
|
||||
25,
|
||||
TimeUnit.MILLISECONDS
|
||||
|
|
|
@ -39,29 +39,27 @@ public class BedrockContainerCloseTranslator extends PacketTranslator<ContainerC
|
|||
|
||||
@Override
|
||||
public void translate(ContainerClosePacket packet, GeyserSession session) {
|
||||
session.addInventoryTask(() -> {
|
||||
byte windowId = packet.getId();
|
||||
byte windowId = packet.getId();
|
||||
|
||||
//Client wants close confirmation
|
||||
session.sendUpstreamPacket(packet);
|
||||
session.setClosingInventory(false);
|
||||
//Client wants close confirmation
|
||||
session.sendUpstreamPacket(packet);
|
||||
session.setClosingInventory(false);
|
||||
|
||||
if (windowId == -1 && session.getOpenInventory() instanceof MerchantContainer) {
|
||||
// 1.16.200 - window ID is always -1 sent from Bedrock
|
||||
windowId = (byte) session.getOpenInventory().getId();
|
||||
if (windowId == -1 && session.getOpenInventory() instanceof MerchantContainer) {
|
||||
// 1.16.200 - window ID is always -1 sent from Bedrock
|
||||
windowId = (byte) session.getOpenInventory().getId();
|
||||
}
|
||||
|
||||
Inventory openInventory = session.getOpenInventory();
|
||||
if (openInventory != null) {
|
||||
if (windowId == openInventory.getId()) {
|
||||
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId);
|
||||
session.sendDownstreamPacket(closeWindowPacket);
|
||||
InventoryUtils.closeInventory(session, windowId, false);
|
||||
} else if (openInventory.isPending()) {
|
||||
InventoryUtils.displayInventory(session, openInventory);
|
||||
openInventory.setPending(false);
|
||||
}
|
||||
|
||||
Inventory openInventory = session.getOpenInventory();
|
||||
if (openInventory != null) {
|
||||
if (windowId == openInventory.getId()) {
|
||||
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(windowId);
|
||||
session.sendDownstreamPacket(closeWindowPacket);
|
||||
InventoryUtils.closeInventory(session, windowId, false);
|
||||
} else if (openInventory.isPending()) {
|
||||
InventoryUtils.displayInventory(session, openInventory);
|
||||
openInventory.setPending(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,26 +83,24 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
InventoryActionData containerAction = packet.getActions().get(1);
|
||||
if (worldAction.getSource().getType() == InventorySource.Type.WORLD_INTERACTION
|
||||
&& worldAction.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
|
||||
session.addInventoryTask(() -> {
|
||||
if (session.getPlayerInventory().getHeldItemSlot() != containerAction.getSlot() ||
|
||||
session.getPlayerInventory().getItemInHand().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (session.getPlayerInventory().getHeldItemSlot() != containerAction.getSlot() ||
|
||||
session.getPlayerInventory().getItemInHand().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean dropAll = worldAction.getToItem().getCount() > 1;
|
||||
ClientPlayerActionPacket dropAllPacket = new ClientPlayerActionPacket(
|
||||
dropAll ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM,
|
||||
BlockUtils.POSITION_ZERO,
|
||||
BlockFace.DOWN
|
||||
);
|
||||
session.sendDownstreamPacket(dropAllPacket);
|
||||
boolean dropAll = worldAction.getToItem().getCount() > 1;
|
||||
ClientPlayerActionPacket dropAllPacket = new ClientPlayerActionPacket(
|
||||
dropAll ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM,
|
||||
BlockUtils.POSITION_ZERO,
|
||||
BlockFace.DOWN
|
||||
);
|
||||
session.sendDownstreamPacket(dropAllPacket);
|
||||
|
||||
if (dropAll) {
|
||||
session.getPlayerInventory().setItemInHand(GeyserItemStack.EMPTY);
|
||||
} else {
|
||||
session.getPlayerInventory().getItemInHand().sub(1);
|
||||
}
|
||||
});
|
||||
if (dropAll) {
|
||||
session.getPlayerInventory().setItemInHand(GeyserItemStack.EMPTY);
|
||||
} else {
|
||||
session.getPlayerInventory().getItemInHand().sub(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -222,7 +220,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
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
|
||||
// See BedrockActionTranslator.java
|
||||
session.setBucketScheduledFuture(session.getConnector().getGeneralThreadPool().schedule(() -> {
|
||||
session.setBucketScheduledFuture(session.getEventLoop().schedule(() -> {
|
||||
ClientPlayerUseItemPacket itemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
|
||||
session.sendDownstreamPacket(itemPacket);
|
||||
}, 5, TimeUnit.MILLISECONDS));
|
||||
|
|
|
@ -45,6 +45,6 @@ public class BedrockItemStackRequestTranslator extends PacketTranslator<ItemStac
|
|||
return;
|
||||
|
||||
InventoryTranslator translator = session.getInventoryTranslator();
|
||||
session.addInventoryTask(() -> translator.translateRequests(session, inventory, packet.getRequests()));
|
||||
translator.translateRequests(session, inventory, packet.getRequests());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,8 @@ import org.geysermc.connector.network.session.GeyserSession;
|
|||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Translator(packet = EntityEventPacket.class)
|
||||
public class BedrockEntityEventTranslator extends PacketTranslator<EntityEventPacket> {
|
||||
|
||||
|
@ -48,12 +50,10 @@ public class BedrockEntityEventTranslator extends PacketTranslator<EntityEventPa
|
|||
session.sendUpstreamPacket(packet);
|
||||
return;
|
||||
case COMPLETE_TRADE:
|
||||
session.addInventoryTask(() -> {
|
||||
ClientSelectTradePacket selectTradePacket = new ClientSelectTradePacket(packet.getData());
|
||||
session.sendDownstreamPacket(selectTradePacket);
|
||||
});
|
||||
ClientSelectTradePacket selectTradePacket = new ClientSelectTradePacket(packet.getData());
|
||||
session.sendDownstreamPacket(selectTradePacket);
|
||||
|
||||
session.addInventoryTask(() -> {
|
||||
session.getEventLoop().schedule(() -> {
|
||||
Entity villager = session.getPlayerEntity();
|
||||
Inventory openInventory = session.getOpenInventory();
|
||||
if (openInventory instanceof MerchantContainer) {
|
||||
|
@ -66,7 +66,7 @@ public class BedrockEntityEventTranslator extends PacketTranslator<EntityEventPa
|
|||
villager.updateBedrockMetadata(session);
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
}, 100, TimeUnit.MILLISECONDS);
|
||||
return;
|
||||
}
|
||||
session.getConnector().getLogger().debug("Did not translate incoming EntityEventPacket: " + packet.toString());
|
||||
|
|
|
@ -49,11 +49,9 @@ public class JavaRespawnTranslator extends PacketTranslator<ServerRespawnPacket>
|
|||
entity.setHealth(entity.getMaxHealth());
|
||||
entity.getAttributes().put(GeyserAttributeType.HEALTH, entity.createHealthAttribute());
|
||||
|
||||
session.addInventoryTask(() -> {
|
||||
session.setInventoryTranslator(InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR);
|
||||
session.setOpenInventory(null);
|
||||
session.setClosingInventory(false);
|
||||
});
|
||||
session.setInventoryTranslator(InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR);
|
||||
session.setOpenInventory(null);
|
||||
session.setClosingInventory(false);
|
||||
|
||||
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
|
||||
playerGameTypePacket.setGamemode(packet.getGamemode().ordinal());
|
||||
|
|
|
@ -36,14 +36,12 @@ public class JavaPlayerChangeHeldItemTranslator extends PacketTranslator<ServerP
|
|||
|
||||
@Override
|
||||
public void translate(ServerPlayerChangeHeldItemPacket packet, GeyserSession session) {
|
||||
session.addInventoryTask(() -> {
|
||||
PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket();
|
||||
hotbarPacket.setContainerId(0);
|
||||
hotbarPacket.setSelectedHotbarSlot(packet.getSlot());
|
||||
hotbarPacket.setSelectHotbarSlot(true);
|
||||
session.sendUpstreamPacket(hotbarPacket);
|
||||
PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket();
|
||||
hotbarPacket.setContainerId(0);
|
||||
hotbarPacket.setSelectedHotbarSlot(packet.getSlot());
|
||||
hotbarPacket.setSelectHotbarSlot(true);
|
||||
session.sendUpstreamPacket(hotbarPacket);
|
||||
|
||||
session.getPlayerInventory().setHeldItemSlot(packet.getSlot());
|
||||
});
|
||||
session.getPlayerInventory().setHeldItemSlot(packet.getSlot());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,9 +36,8 @@ public class JavaCloseWindowTranslator extends PacketTranslator<ServerCloseWindo
|
|||
|
||||
@Override
|
||||
public void translate(ServerCloseWindowPacket packet, GeyserSession session) {
|
||||
session.addInventoryTask(() ->
|
||||
// Sometimes the server can request a window close of ID 0... when the window isn't even open
|
||||
// Don't confirm in this instance
|
||||
InventoryUtils.closeInventory(session, packet.getWindowId(), (session.getOpenInventory() != null && session.getOpenInventory().getId() == packet.getWindowId())));
|
||||
// Sometimes the server can request a window close of ID 0... when the window isn't even open
|
||||
// Don't confirm in this instance
|
||||
InventoryUtils.closeInventory(session, packet.getWindowId(), (session.getOpenInventory() != null && session.getOpenInventory().getId() == packet.getWindowId()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,48 +31,46 @@ import org.geysermc.connector.inventory.Inventory;
|
|||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
import org.geysermc.connector.network.translators.inventory.InventoryTranslator;
|
||||
import org.geysermc.connector.utils.InventoryUtils;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
@Translator(packet = ServerOpenWindowPacket.class)
|
||||
public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(ServerOpenWindowPacket packet, GeyserSession session) {
|
||||
session.addInventoryTask(() -> {
|
||||
if (packet.getWindowId() == 0) {
|
||||
return;
|
||||
}
|
||||
if (packet.getWindowId() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
InventoryTranslator newTranslator = InventoryTranslator.INVENTORY_TRANSLATORS.get(packet.getType());
|
||||
Inventory openInventory = session.getOpenInventory();
|
||||
//No translator exists for this window type. Close all windows and return.
|
||||
if (newTranslator == null) {
|
||||
if (openInventory != null) {
|
||||
InventoryUtils.closeInventory(session, openInventory.getId(), true);
|
||||
}
|
||||
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(packet.getWindowId());
|
||||
session.sendDownstreamPacket(closeWindowPacket);
|
||||
return;
|
||||
}
|
||||
|
||||
String name = MessageTranslator.convertMessageLenient(packet.getName(), session.getLocale());
|
||||
name = LocaleUtils.getLocaleString(name, session.getLocale());
|
||||
|
||||
Inventory newInventory = newTranslator.createInventory(name, packet.getWindowId(), packet.getType(), session.getPlayerInventory());
|
||||
InventoryTranslator newTranslator = InventoryTranslator.INVENTORY_TRANSLATORS.get(packet.getType());
|
||||
Inventory openInventory = session.getOpenInventory();
|
||||
//No translator exists for this window type. Close all windows and return.
|
||||
if (newTranslator == null) {
|
||||
if (openInventory != null) {
|
||||
// If the window type is the same, don't close.
|
||||
// In rare cases, inventories can do funny things where it keeps the same window type up but change the contents.
|
||||
if (openInventory.getWindowType() != packet.getType()) {
|
||||
// Sometimes the server can double-open an inventory with the same ID - don't confirm in that instance.
|
||||
InventoryUtils.closeInventory(session, openInventory.getId(), openInventory.getId() != packet.getWindowId());
|
||||
}
|
||||
InventoryUtils.closeInventory(session, openInventory.getId(), true);
|
||||
}
|
||||
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(packet.getWindowId());
|
||||
session.sendDownstreamPacket(closeWindowPacket);
|
||||
return;
|
||||
}
|
||||
|
||||
session.setInventoryTranslator(newTranslator);
|
||||
InventoryUtils.openInventory(session, newInventory);
|
||||
});
|
||||
String name = MessageTranslator.convertMessageLenient(packet.getName(), session.getLocale());
|
||||
name = LocaleUtils.getLocaleString(name, session.getLocale());
|
||||
|
||||
Inventory newInventory = newTranslator.createInventory(name, packet.getWindowId(), packet.getType(), session.getPlayerInventory());
|
||||
if (openInventory != null) {
|
||||
// If the window type is the same, don't close.
|
||||
// In rare cases, inventories can do funny things where it keeps the same window type up but change the contents.
|
||||
if (openInventory.getWindowType() != packet.getType()) {
|
||||
// Sometimes the server can double-open an inventory with the same ID - don't confirm in that instance.
|
||||
InventoryUtils.closeInventory(session, openInventory.getId(), openInventory.getId() != packet.getWindowId());
|
||||
}
|
||||
}
|
||||
|
||||
session.setInventoryTranslator(newTranslator);
|
||||
InventoryUtils.openInventory(session, newInventory);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,39 +59,37 @@ public class JavaSetSlotTranslator extends PacketTranslator<ServerSetSlotPacket>
|
|||
|
||||
@Override
|
||||
public void translate(ServerSetSlotPacket packet, GeyserSession session) {
|
||||
session.addInventoryTask(() -> {
|
||||
if (packet.getWindowId() == 255) { //cursor
|
||||
GeyserItemStack newItem = GeyserItemStack.from(packet.getItem());
|
||||
session.getPlayerInventory().setCursor(newItem, session);
|
||||
InventoryUtils.updateCursor(session);
|
||||
return;
|
||||
if (packet.getWindowId() == 255) { //cursor
|
||||
GeyserItemStack newItem = GeyserItemStack.from(packet.getItem());
|
||||
session.getPlayerInventory().setCursor(newItem, session);
|
||||
InventoryUtils.updateCursor(session);
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: support window id -2, should update player inventory
|
||||
Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId());
|
||||
if (inventory == null)
|
||||
return;
|
||||
|
||||
inventory.setStateId(packet.getStateId());
|
||||
|
||||
InventoryTranslator translator = session.getInventoryTranslator();
|
||||
if (translator != null) {
|
||||
if (session.getCraftingGridFuture() != null) {
|
||||
session.getCraftingGridFuture().cancel(false);
|
||||
}
|
||||
session.setCraftingGridFuture(session.getEventLoop().schedule(() -> updateCraftingGrid(session, packet, inventory, translator), 150, TimeUnit.MILLISECONDS));
|
||||
|
||||
//TODO: support window id -2, should update player inventory
|
||||
Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId());
|
||||
if (inventory == null)
|
||||
return;
|
||||
|
||||
inventory.setStateId(packet.getStateId());
|
||||
|
||||
InventoryTranslator translator = session.getInventoryTranslator();
|
||||
if (translator != null) {
|
||||
if (session.getCraftingGridFuture() != null) {
|
||||
session.getCraftingGridFuture().cancel(false);
|
||||
}
|
||||
session.setCraftingGridFuture(session.getConnector().getGeneralThreadPool().schedule(() -> session.addInventoryTask(() -> updateCraftingGrid(session, packet, inventory, translator)), 150, TimeUnit.MILLISECONDS));
|
||||
|
||||
GeyserItemStack newItem = GeyserItemStack.from(packet.getItem());
|
||||
if (packet.getWindowId() == 0 && !(translator instanceof PlayerInventoryTranslator)) {
|
||||
// In rare cases, the window ID can still be 0 but Java treats it as valid
|
||||
session.getPlayerInventory().setItem(packet.getSlot(), newItem, session);
|
||||
InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateSlot(session, session.getPlayerInventory(), packet.getSlot());
|
||||
} else {
|
||||
inventory.setItem(packet.getSlot(), newItem, session);
|
||||
translator.updateSlot(session, inventory, packet.getSlot());
|
||||
}
|
||||
GeyserItemStack newItem = GeyserItemStack.from(packet.getItem());
|
||||
if (packet.getWindowId() == 0 && !(translator instanceof PlayerInventoryTranslator)) {
|
||||
// In rare cases, the window ID can still be 0 but Java treats it as valid
|
||||
session.getPlayerInventory().setItem(packet.getSlot(), newItem, session);
|
||||
InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateSlot(session, session.getPlayerInventory(), packet.getSlot());
|
||||
} else {
|
||||
inventory.setItem(packet.getSlot(), newItem, session);
|
||||
translator.updateSlot(session, inventory, packet.getSlot());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateCraftingGrid(GeyserSession session, ServerSetSlotPacket packet, Inventory inventory, InventoryTranslator translator) {
|
||||
|
|
|
@ -39,25 +39,23 @@ public class JavaWindowItemsTranslator extends PacketTranslator<ServerWindowItem
|
|||
|
||||
@Override
|
||||
public void translate(ServerWindowItemsPacket packet, GeyserSession session) {
|
||||
session.addInventoryTask(() -> {
|
||||
Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId());
|
||||
if (inventory == null)
|
||||
return;
|
||||
Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId());
|
||||
if (inventory == null)
|
||||
return;
|
||||
|
||||
inventory.setStateId(packet.getStateId());
|
||||
inventory.setStateId(packet.getStateId());
|
||||
|
||||
for (int i = 0; i < packet.getItems().length; i++) {
|
||||
GeyserItemStack newItem = GeyserItemStack.from(packet.getItems()[i]);
|
||||
inventory.setItem(i, newItem, session);
|
||||
}
|
||||
for (int i = 0; i < packet.getItems().length; i++) {
|
||||
GeyserItemStack newItem = GeyserItemStack.from(packet.getItems()[i]);
|
||||
inventory.setItem(i, newItem, session);
|
||||
}
|
||||
|
||||
InventoryTranslator translator = session.getInventoryTranslator();
|
||||
if (translator != null) {
|
||||
translator.updateInventory(session, inventory);
|
||||
}
|
||||
InventoryTranslator translator = session.getInventoryTranslator();
|
||||
if (translator != null) {
|
||||
translator.updateInventory(session, inventory);
|
||||
}
|
||||
|
||||
session.getPlayerInventory().setCursor(GeyserItemStack.from(packet.getCarriedItem()), session);
|
||||
InventoryUtils.updateCursor(session);
|
||||
});
|
||||
session.getPlayerInventory().setCursor(GeyserItemStack.from(packet.getCarriedItem()), session);
|
||||
InventoryUtils.updateCursor(session);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,15 +38,13 @@ public class JavaWindowPropertyTranslator extends PacketTranslator<ServerWindowP
|
|||
|
||||
@Override
|
||||
public void translate(ServerWindowPropertyPacket packet, GeyserSession session) {
|
||||
session.addInventoryTask(() -> {
|
||||
Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId());
|
||||
if (inventory == null)
|
||||
return;
|
||||
Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId());
|
||||
if (inventory == null)
|
||||
return;
|
||||
|
||||
InventoryTranslator translator = session.getInventoryTranslator();
|
||||
if (translator != null) {
|
||||
translator.updateProperty(session, inventory, packet.getRawProperty(), packet.getValue());
|
||||
}
|
||||
});
|
||||
InventoryTranslator translator = session.getInventoryTranslator();
|
||||
if (translator != null) {
|
||||
translator.updateProperty(session, inventory, packet.getRawProperty(), packet.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -147,7 +147,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
|
|||
if (session.getUpstream().isInitialized()) {
|
||||
player.spawnEntity(session);
|
||||
|
||||
SkullSkinManager.requestAndHandleSkin(player, session, (skin -> session.getConnector().getGeneralThreadPool().schedule(() -> {
|
||||
SkullSkinManager.requestAndHandleSkin(player, session, (skin -> session.getEventLoop().schedule(() -> {
|
||||
// Delay to minimize split-second "player" pop-in
|
||||
player.getMetadata().getFlags().setFlag(EntityFlag.INVISIBLE, false);
|
||||
player.updateBedrockMetadata(session);
|
||||
|
|
|
@ -37,7 +37,6 @@ import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
|
|||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.PlayerHotbarPacket;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.common.ChatColor;
|
||||
import org.geysermc.connector.inventory.Container;
|
||||
import org.geysermc.connector.inventory.GeyserItemStack;
|
||||
|
@ -80,17 +79,16 @@ public class InventoryUtils {
|
|||
if (translator != null) {
|
||||
translator.prepareInventory(session, inventory);
|
||||
if (translator instanceof DoubleChestInventoryTranslator && !((Container) inventory).isUsingRealBlock()) {
|
||||
GeyserConnector.getInstance().getGeneralThreadPool().schedule(() ->
|
||||
session.addInventoryTask(() -> {
|
||||
Inventory openInv = session.getOpenInventory();
|
||||
if (openInv != null && openInv.getId() == inventory.getId()) {
|
||||
translator.openInventory(session, inventory);
|
||||
translator.updateInventory(session, inventory);
|
||||
} else if (openInv != null && openInv.isPending()) {
|
||||
// Presumably, this inventory is no longer relevant, and the client doesn't care about it
|
||||
displayInventory(session, openInv);
|
||||
}
|
||||
}), 200, TimeUnit.MILLISECONDS);
|
||||
session.getEventLoop().schedule(() -> {
|
||||
Inventory openInv = session.getOpenInventory();
|
||||
if (openInv != null && openInv.getId() == inventory.getId()) {
|
||||
translator.openInventory(session, inventory);
|
||||
translator.updateInventory(session, inventory);
|
||||
} else if (openInv != null && openInv.isPending()) {
|
||||
// Presumably, this inventory is no longer relevant, and the client doesn't care about it
|
||||
displayInventory(session, openInv);
|
||||
}
|
||||
}, 200, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
translator.openInventory(session, inventory);
|
||||
translator.updateInventory(session, inventory);
|
||||
|
|
Loading…
Reference in a new issue