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>
|
<properties>
|
||||||
<adventure.version>4.8.0</adventure.version>
|
<adventure.version>4.8.0</adventure.version>
|
||||||
<fastutil.version>8.5.2</fastutil.version>
|
<fastutil.version>8.5.2</fastutil.version>
|
||||||
<jackson.version>2.10.2</jackson.version>
|
<jackson.version>2.12.4</jackson.version>
|
||||||
<netty.version>4.1.59.Final</netty.version>
|
<netty.version>4.1.66.Final</netty.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -167,7 +167,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.GeyserMC</groupId>
|
<groupId>com.github.GeyserMC</groupId>
|
||||||
<artifactId>PacketLib</artifactId>
|
<artifactId>PacketLib</artifactId>
|
||||||
<version>25eb4c4</version>
|
<version>86c9c38</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
|
|
|
@ -30,7 +30,9 @@ import com.nukkitx.protocol.bedrock.BedrockServerEventHandler;
|
||||||
import com.nukkitx.protocol.bedrock.BedrockServerSession;
|
import com.nukkitx.protocol.bedrock.BedrockServerSession;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.DefaultEventLoopGroup;
|
||||||
import io.netty.channel.socket.DatagramPacket;
|
import io.netty.channel.socket.DatagramPacket;
|
||||||
|
import io.netty.util.concurrent.DefaultThreadFactory;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.common.ping.GeyserPingInfo;
|
import org.geysermc.connector.common.ping.GeyserPingInfo;
|
||||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||||
|
@ -56,6 +58,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
|
||||||
private static final int MAGIC_RAKNET_LENGTH = 338;
|
private static final int MAGIC_RAKNET_LENGTH = 338;
|
||||||
|
|
||||||
private final GeyserConnector connector;
|
private final GeyserConnector connector;
|
||||||
|
private final DefaultEventLoopGroup eventLoopGroup = new DefaultEventLoopGroup(new DefaultThreadFactory("Geyser player thread"));
|
||||||
|
|
||||||
public ConnectorServerEventHandler(GeyserConnector connector) {
|
public ConnectorServerEventHandler(GeyserConnector connector) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
|
@ -162,7 +165,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
|
||||||
public void onSessionCreation(BedrockServerSession bedrockServerSession) {
|
public void onSessionCreation(BedrockServerSession bedrockServerSession) {
|
||||||
bedrockServerSession.setLogging(true);
|
bedrockServerSession.setLogging(true);
|
||||||
bedrockServerSession.setCompressionLevel(connector.getConfig().getBedrock().getCompressionLevel());
|
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.
|
// Set the packet codec to default just in case we need to send disconnect packets.
|
||||||
bedrockServerSession.setPacketCodec(BedrockProtocol.DEFAULT_BEDROCK_CODEC);
|
bedrockServerSession.setPacketCodec(BedrockProtocol.DEFAULT_BEDROCK_CODEC);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
return PacketTranslatorRegistry.BEDROCK_TRANSLATOR.translate(packet.getClass(), packet, session);
|
return PacketTranslatorRegistry.BEDROCK_TRANSLATOR.translate(packet.getClass(), packet, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean defaultHandler(BedrockPacket packet) {
|
||||||
|
return translateAndDefault(packet);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handle(LoginPacket loginPacket) {
|
public boolean handle(LoginPacket loginPacket) {
|
||||||
BedrockPacketCodec packetCodec = BedrockProtocol.getBedrockCodec(loginPacket.getProtocolVersion());
|
BedrockPacketCodec packetCodec = BedrockProtocol.getBedrockCodec(loginPacket.getProtocolVersion());
|
||||||
|
@ -156,7 +161,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handle(ModalFormResponsePacket packet) {
|
public boolean handle(ModalFormResponsePacket packet) {
|
||||||
session.getFormCache().handleResponse(packet);
|
session.getEventLoop().execute(() -> session.getFormCache().handleResponse(packet));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,11 +214,6 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
return translateAndDefault(packet);
|
return translateAndDefault(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean defaultHandler(BedrockPacket packet) {
|
|
||||||
return translateAndDefault(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handle(ResourcePackChunkRequestPacket packet) {
|
public boolean handle(ResourcePackChunkRequestPacket packet) {
|
||||||
ResourcePackChunkDataPacket data = new ResourcePackChunkDataPacket();
|
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.EntityData;
|
||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||||
import com.nukkitx.protocol.bedrock.packet.*;
|
import com.nukkitx.protocol.bedrock.packet.*;
|
||||||
|
import io.netty.channel.EventLoop;
|
||||||
import it.unimi.dsi.fastutil.ints.*;
|
import it.unimi.dsi.fastutil.ints.*;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
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.longs.Long2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||||
|
@ -102,7 +102,10 @@ import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
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;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -110,6 +113,10 @@ public class GeyserSession implements CommandSender {
|
||||||
|
|
||||||
private final GeyserConnector connector;
|
private final GeyserConnector connector;
|
||||||
private final UpstreamSession upstream;
|
private final UpstreamSession upstream;
|
||||||
|
/**
|
||||||
|
* The loop where all packets and ticking is processed to prevent concurrency issues.
|
||||||
|
*/
|
||||||
|
private final EventLoop eventLoop;
|
||||||
private TcpClientSession downstream;
|
private TcpClientSession downstream;
|
||||||
@Setter
|
@Setter
|
||||||
private AuthData authData;
|
private AuthData authData;
|
||||||
|
@ -158,11 +165,6 @@ public class GeyserSession implements CommandSender {
|
||||||
@Getter(AccessLevel.NONE)
|
@Getter(AccessLevel.NONE)
|
||||||
private final AtomicInteger itemNetId = new AtomicInteger(2);
|
private final AtomicInteger itemNetId = new AtomicInteger(2);
|
||||||
|
|
||||||
@Getter(AccessLevel.NONE)
|
|
||||||
private final Object inventoryLock = new Object();
|
|
||||||
@Getter(AccessLevel.NONE)
|
|
||||||
private CompletableFuture<Void> inventoryFuture;
|
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private ScheduledFuture<?> craftingGridFuture;
|
private ScheduledFuture<?> craftingGridFuture;
|
||||||
|
|
||||||
|
@ -183,8 +185,8 @@ public class GeyserSession implements CommandSender {
|
||||||
@Setter
|
@Setter
|
||||||
private ItemMappings itemMappings;
|
private ItemMappings itemMappings;
|
||||||
|
|
||||||
private final Map<Vector3i, SkullPlayerEntity> skullCache = new ConcurrentHashMap<>();
|
private final Map<Vector3i, SkullPlayerEntity> skullCache = new Object2ObjectOpenHashMap<>();
|
||||||
private final Long2ObjectMap<ClientboundMapItemDataPacket> storedMaps = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
|
private final Long2ObjectMap<ClientboundMapItemDataPacket> storedMaps = new Long2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the map between Java and Bedrock biome network IDs.
|
* Stores the map between Java and Bedrock biome network IDs.
|
||||||
|
@ -426,9 +428,10 @@ public class GeyserSession implements CommandSender {
|
||||||
|
|
||||||
private MinecraftProtocol protocol;
|
private MinecraftProtocol protocol;
|
||||||
|
|
||||||
public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) {
|
public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession, EventLoop eventLoop) {
|
||||||
this.connector = connector;
|
this.connector = connector;
|
||||||
this.upstream = new UpstreamSession(bedrockServerSession);
|
this.upstream = new UpstreamSession(bedrockServerSession);
|
||||||
|
this.eventLoop = eventLoop;
|
||||||
|
|
||||||
this.advancementsCache = new AdvancementsCache(this);
|
this.advancementsCache = new AdvancementsCache(this);
|
||||||
this.bookEditCache = new BookEditCache(this);
|
this.bookEditCache = new BookEditCache(this);
|
||||||
|
@ -447,7 +450,6 @@ public class GeyserSession implements CommandSender {
|
||||||
|
|
||||||
this.playerInventory = new PlayerInventory();
|
this.playerInventory = new PlayerInventory();
|
||||||
this.openInventory = null;
|
this.openInventory = null;
|
||||||
this.inventoryFuture = CompletableFuture.completedFuture(null);
|
|
||||||
this.craftingRecipes = new Int2ObjectOpenHashMap<>();
|
this.craftingRecipes = new Int2ObjectOpenHashMap<>();
|
||||||
this.unlockedRecipes = new ObjectOpenHashSet<>();
|
this.unlockedRecipes = new ObjectOpenHashSet<>();
|
||||||
this.lastRecipeNetId = new AtomicInteger(1);
|
this.lastRecipeNetId = new AtomicInteger(1);
|
||||||
|
@ -664,7 +666,7 @@ public class GeyserSession implements CommandSender {
|
||||||
boolean floodgate = this.remoteAuthType == AuthType.FLOODGATE;
|
boolean floodgate = this.remoteAuthType == AuthType.FLOODGATE;
|
||||||
|
|
||||||
// Start ticking
|
// 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);
|
downstream = new TcpClientSession(this.remoteAddress, this.remotePort, protocol);
|
||||||
disableSrvResolving();
|
disableSrvResolving();
|
||||||
|
@ -1095,39 +1097,6 @@ public class GeyserSession implements CommandSender {
|
||||||
upstream.sendPacket(startGamePacket);
|
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
|
* @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
|
* @param packet the java edition packet from MCProtocolLib
|
||||||
*/
|
*/
|
||||||
public void sendDownstreamPacket(Packet packet) {
|
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);
|
downstream.send(packet);
|
||||||
} else {
|
} else {
|
||||||
connector.getLogger().debug("Tried to send downstream packet " + packet.getClass().getSimpleName() + " before connected to the server");
|
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;
|
package org.geysermc.connector.network.session.cache;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.*;
|
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 it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.geysermc.connector.entity.Tickable;
|
import org.geysermc.connector.entity.Tickable;
|
||||||
|
@ -44,15 +46,15 @@ public class EntityCache {
|
||||||
private final GeyserSession session;
|
private final GeyserSession session;
|
||||||
|
|
||||||
@Getter
|
@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.
|
* A list of all entities that must be ticked.
|
||||||
*/
|
*/
|
||||||
private final List<Tickable> tickableEntities = Collections.synchronizedList(new ArrayList<>());
|
private final List<Tickable> tickableEntities = new ObjectArrayList<>();
|
||||||
private Long2LongMap entityIdTranslations = Long2LongMaps.synchronize(new Long2LongOpenHashMap());
|
private final Long2LongMap entityIdTranslations = new Long2LongOpenHashMap();
|
||||||
private Map<UUID, PlayerEntity> playerEntities = Collections.synchronizedMap(new HashMap<>());
|
private final Map<UUID, PlayerEntity> playerEntities = new Object2ObjectOpenHashMap<>();
|
||||||
private Map<UUID, BossBar> bossBars = Collections.synchronizedMap(new HashMap<>());
|
private final Map<UUID, BossBar> bossBars = new Object2ObjectOpenHashMap<>();
|
||||||
private final Long2LongMap cachedPlayerEntityLinks = Long2LongMaps.synchronize(new Long2LongOpenHashMap());
|
private final Long2LongMap cachedPlayerEntityLinks = new Long2LongOpenHashMap();
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final AtomicLong nextEntityId = new AtomicLong(2L);
|
private final AtomicLong nextEntityId = new AtomicLong(2L);
|
||||||
|
@ -156,13 +158,6 @@ public class EntityCache {
|
||||||
bossBars.values().forEach(BossBar::updateBossBar);
|
bossBars.values().forEach(BossBar::updateBossBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
entities = null;
|
|
||||||
entityIdTranslations = null;
|
|
||||||
playerEntities = null;
|
|
||||||
bossBars = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getCachedPlayerEntityLink(long playerId) {
|
public long getCachedPlayerEntityLink(long playerId) {
|
||||||
return cachedPlayerEntityLinks.remove(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.mc.protocol.packet.ingame.server.world.ServerUpdateLightPacket;
|
||||||
import com.github.steveice10.packetlib.packet.Packet;
|
import com.github.steveice10.packetlib.packet.Packet;
|
||||||
import com.nukkitx.protocol.bedrock.BedrockPacket;
|
import com.nukkitx.protocol.bedrock.BedrockPacket;
|
||||||
|
import io.netty.channel.EventLoop;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.common.PlatformType;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
@ -89,7 +90,12 @@ public class PacketTranslatorRegistry<T> {
|
||||||
try {
|
try {
|
||||||
PacketTranslator<P> translator = (PacketTranslator<P>) translators.get(clazz);
|
PacketTranslator<P> translator = (PacketTranslator<P>) translators.get(clazz);
|
||||||
if (translator != null) {
|
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;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if ((GeyserConnector.getInstance().getPlatformType() != PlatformType.STANDALONE || !(packet instanceof BedrockPacket)) && !IGNORED_PACKETS.contains(clazz)) {
|
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()) {
|
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.getConnector().getGeneralThreadPool().schedule(() ->
|
session.getEventLoop().schedule(() ->
|
||||||
session.sendDownstreamPacket(new ClientPlayerSwingArmPacket(Hand.MAIN_HAND)),
|
session.sendDownstreamPacket(new ClientPlayerSwingArmPacket(Hand.MAIN_HAND)),
|
||||||
25,
|
25,
|
||||||
TimeUnit.MILLISECONDS
|
TimeUnit.MILLISECONDS
|
||||||
|
|
|
@ -39,29 +39,27 @@ public class BedrockContainerCloseTranslator extends PacketTranslator<ContainerC
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ContainerClosePacket packet, GeyserSession session) {
|
public void translate(ContainerClosePacket packet, GeyserSession session) {
|
||||||
session.addInventoryTask(() -> {
|
byte windowId = packet.getId();
|
||||||
byte windowId = packet.getId();
|
|
||||||
|
|
||||||
//Client wants close confirmation
|
//Client wants close confirmation
|
||||||
session.sendUpstreamPacket(packet);
|
session.sendUpstreamPacket(packet);
|
||||||
session.setClosingInventory(false);
|
session.setClosingInventory(false);
|
||||||
|
|
||||||
if (windowId == -1 && session.getOpenInventory() instanceof MerchantContainer) {
|
if (windowId == -1 && session.getOpenInventory() instanceof MerchantContainer) {
|
||||||
// 1.16.200 - window ID is always -1 sent from Bedrock
|
// 1.16.200 - window ID is always -1 sent from Bedrock
|
||||||
windowId = (byte) session.getOpenInventory().getId();
|
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);
|
InventoryActionData containerAction = packet.getActions().get(1);
|
||||||
if (worldAction.getSource().getType() == InventorySource.Type.WORLD_INTERACTION
|
if (worldAction.getSource().getType() == InventorySource.Type.WORLD_INTERACTION
|
||||||
&& worldAction.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
|
&& worldAction.getSource().getFlag() == InventorySource.Flag.DROP_ITEM) {
|
||||||
session.addInventoryTask(() -> {
|
if (session.getPlayerInventory().getHeldItemSlot() != containerAction.getSlot() ||
|
||||||
if (session.getPlayerInventory().getHeldItemSlot() != containerAction.getSlot() ||
|
session.getPlayerInventory().getItemInHand().isEmpty()) {
|
||||||
session.getPlayerInventory().getItemInHand().isEmpty()) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
boolean dropAll = worldAction.getToItem().getCount() > 1;
|
boolean dropAll = worldAction.getToItem().getCount() > 1;
|
||||||
ClientPlayerActionPacket dropAllPacket = new ClientPlayerActionPacket(
|
ClientPlayerActionPacket dropAllPacket = new ClientPlayerActionPacket(
|
||||||
dropAll ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM,
|
dropAll ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM,
|
||||||
BlockUtils.POSITION_ZERO,
|
BlockUtils.POSITION_ZERO,
|
||||||
BlockFace.DOWN
|
BlockFace.DOWN
|
||||||
);
|
);
|
||||||
session.sendDownstreamPacket(dropAllPacket);
|
session.sendDownstreamPacket(dropAllPacket);
|
||||||
|
|
||||||
if (dropAll) {
|
if (dropAll) {
|
||||||
session.getPlayerInventory().setItemInHand(GeyserItemStack.EMPTY);
|
session.getPlayerInventory().setItemInHand(GeyserItemStack.EMPTY);
|
||||||
} else {
|
} else {
|
||||||
session.getPlayerInventory().getItemInHand().sub(1);
|
session.getPlayerInventory().getItemInHand().sub(1);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -222,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.getConnector().getGeneralThreadPool().schedule(() -> {
|
session.setBucketScheduledFuture(session.getEventLoop().schedule(() -> {
|
||||||
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));
|
||||||
|
|
|
@ -45,6 +45,6 @@ public class BedrockItemStackRequestTranslator extends PacketTranslator<ItemStac
|
||||||
return;
|
return;
|
||||||
|
|
||||||
InventoryTranslator translator = session.getInventoryTranslator();
|
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.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@Translator(packet = EntityEventPacket.class)
|
@Translator(packet = EntityEventPacket.class)
|
||||||
public class BedrockEntityEventTranslator extends PacketTranslator<EntityEventPacket> {
|
public class BedrockEntityEventTranslator extends PacketTranslator<EntityEventPacket> {
|
||||||
|
|
||||||
|
@ -48,12 +50,10 @@ public class BedrockEntityEventTranslator extends PacketTranslator<EntityEventPa
|
||||||
session.sendUpstreamPacket(packet);
|
session.sendUpstreamPacket(packet);
|
||||||
return;
|
return;
|
||||||
case COMPLETE_TRADE:
|
case COMPLETE_TRADE:
|
||||||
session.addInventoryTask(() -> {
|
ClientSelectTradePacket selectTradePacket = new ClientSelectTradePacket(packet.getData());
|
||||||
ClientSelectTradePacket selectTradePacket = new ClientSelectTradePacket(packet.getData());
|
session.sendDownstreamPacket(selectTradePacket);
|
||||||
session.sendDownstreamPacket(selectTradePacket);
|
|
||||||
});
|
|
||||||
|
|
||||||
session.addInventoryTask(() -> {
|
session.getEventLoop().schedule(() -> {
|
||||||
Entity villager = session.getPlayerEntity();
|
Entity villager = session.getPlayerEntity();
|
||||||
Inventory openInventory = session.getOpenInventory();
|
Inventory openInventory = session.getOpenInventory();
|
||||||
if (openInventory instanceof MerchantContainer) {
|
if (openInventory instanceof MerchantContainer) {
|
||||||
|
@ -66,7 +66,7 @@ public class BedrockEntityEventTranslator extends PacketTranslator<EntityEventPa
|
||||||
villager.updateBedrockMetadata(session);
|
villager.updateBedrockMetadata(session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100, TimeUnit.MILLISECONDS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
session.getConnector().getLogger().debug("Did not translate incoming EntityEventPacket: " + packet.toString());
|
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.setHealth(entity.getMaxHealth());
|
||||||
entity.getAttributes().put(GeyserAttributeType.HEALTH, entity.createHealthAttribute());
|
entity.getAttributes().put(GeyserAttributeType.HEALTH, entity.createHealthAttribute());
|
||||||
|
|
||||||
session.addInventoryTask(() -> {
|
session.setInventoryTranslator(InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR);
|
||||||
session.setInventoryTranslator(InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR);
|
session.setOpenInventory(null);
|
||||||
session.setOpenInventory(null);
|
session.setClosingInventory(false);
|
||||||
session.setClosingInventory(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
|
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
|
||||||
playerGameTypePacket.setGamemode(packet.getGamemode().ordinal());
|
playerGameTypePacket.setGamemode(packet.getGamemode().ordinal());
|
||||||
|
|
|
@ -36,14 +36,12 @@ public class JavaPlayerChangeHeldItemTranslator extends PacketTranslator<ServerP
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerPlayerChangeHeldItemPacket packet, GeyserSession session) {
|
public void translate(ServerPlayerChangeHeldItemPacket packet, GeyserSession session) {
|
||||||
session.addInventoryTask(() -> {
|
PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket();
|
||||||
PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket();
|
hotbarPacket.setContainerId(0);
|
||||||
hotbarPacket.setContainerId(0);
|
hotbarPacket.setSelectedHotbarSlot(packet.getSlot());
|
||||||
hotbarPacket.setSelectedHotbarSlot(packet.getSlot());
|
hotbarPacket.setSelectHotbarSlot(true);
|
||||||
hotbarPacket.setSelectHotbarSlot(true);
|
session.sendUpstreamPacket(hotbarPacket);
|
||||||
session.sendUpstreamPacket(hotbarPacket);
|
|
||||||
|
|
||||||
session.getPlayerInventory().setHeldItemSlot(packet.getSlot());
|
session.getPlayerInventory().setHeldItemSlot(packet.getSlot());
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,9 +36,8 @@ public class JavaCloseWindowTranslator extends PacketTranslator<ServerCloseWindo
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerCloseWindowPacket packet, GeyserSession session) {
|
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
|
||||||
// Sometimes the server can request a window close of ID 0... when the window isn't even open
|
// Don't confirm in this instance
|
||||||
// Don't confirm in this instance
|
InventoryUtils.closeInventory(session, packet.getWindowId(), (session.getOpenInventory() != null && session.getOpenInventory().getId() == packet.getWindowId()));
|
||||||
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.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
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.network.translators.inventory.InventoryTranslator;
|
||||||
import org.geysermc.connector.utils.InventoryUtils;
|
import org.geysermc.connector.utils.InventoryUtils;
|
||||||
import org.geysermc.connector.utils.LocaleUtils;
|
import org.geysermc.connector.utils.LocaleUtils;
|
||||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
|
||||||
|
|
||||||
@Translator(packet = ServerOpenWindowPacket.class)
|
@Translator(packet = ServerOpenWindowPacket.class)
|
||||||
public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowPacket> {
|
public class JavaOpenWindowTranslator extends PacketTranslator<ServerOpenWindowPacket> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerOpenWindowPacket packet, GeyserSession session) {
|
public void translate(ServerOpenWindowPacket packet, GeyserSession session) {
|
||||||
session.addInventoryTask(() -> {
|
if (packet.getWindowId() == 0) {
|
||||||
if (packet.getWindowId() == 0) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
InventoryTranslator newTranslator = InventoryTranslator.INVENTORY_TRANSLATORS.get(packet.getType());
|
InventoryTranslator newTranslator = InventoryTranslator.INVENTORY_TRANSLATORS.get(packet.getType());
|
||||||
Inventory openInventory = session.getOpenInventory();
|
Inventory openInventory = session.getOpenInventory();
|
||||||
//No translator exists for this window type. Close all windows and return.
|
//No translator exists for this window type. Close all windows and return.
|
||||||
if (newTranslator == null) {
|
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());
|
|
||||||
if (openInventory != null) {
|
if (openInventory != null) {
|
||||||
// If the window type is the same, don't close.
|
InventoryUtils.closeInventory(session, openInventory.getId(), true);
|
||||||
// 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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
ClientCloseWindowPacket closeWindowPacket = new ClientCloseWindowPacket(packet.getWindowId());
|
||||||
|
session.sendDownstreamPacket(closeWindowPacket);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
session.setInventoryTranslator(newTranslator);
|
String name = MessageTranslator.convertMessageLenient(packet.getName(), session.getLocale());
|
||||||
InventoryUtils.openInventory(session, newInventory);
|
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
|
@Override
|
||||||
public void translate(ServerSetSlotPacket packet, GeyserSession session) {
|
public void translate(ServerSetSlotPacket packet, GeyserSession session) {
|
||||||
session.addInventoryTask(() -> {
|
if (packet.getWindowId() == 255) { //cursor
|
||||||
if (packet.getWindowId() == 255) { //cursor
|
GeyserItemStack newItem = GeyserItemStack.from(packet.getItem());
|
||||||
GeyserItemStack newItem = GeyserItemStack.from(packet.getItem());
|
session.getPlayerInventory().setCursor(newItem, session);
|
||||||
session.getPlayerInventory().setCursor(newItem, session);
|
InventoryUtils.updateCursor(session);
|
||||||
InventoryUtils.updateCursor(session);
|
return;
|
||||||
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
|
GeyserItemStack newItem = GeyserItemStack.from(packet.getItem());
|
||||||
Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId());
|
if (packet.getWindowId() == 0 && !(translator instanceof PlayerInventoryTranslator)) {
|
||||||
if (inventory == null)
|
// In rare cases, the window ID can still be 0 but Java treats it as valid
|
||||||
return;
|
session.getPlayerInventory().setItem(packet.getSlot(), newItem, session);
|
||||||
|
InventoryTranslator.PLAYER_INVENTORY_TRANSLATOR.updateSlot(session, session.getPlayerInventory(), packet.getSlot());
|
||||||
inventory.setStateId(packet.getStateId());
|
} else {
|
||||||
|
inventory.setItem(packet.getSlot(), newItem, session);
|
||||||
InventoryTranslator translator = session.getInventoryTranslator();
|
translator.updateSlot(session, inventory, packet.getSlot());
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void updateCraftingGrid(GeyserSession session, ServerSetSlotPacket packet, Inventory inventory, InventoryTranslator translator) {
|
private static void updateCraftingGrid(GeyserSession session, ServerSetSlotPacket packet, Inventory inventory, InventoryTranslator translator) {
|
||||||
|
|
|
@ -39,25 +39,23 @@ public class JavaWindowItemsTranslator extends PacketTranslator<ServerWindowItem
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerWindowItemsPacket packet, GeyserSession session) {
|
public void translate(ServerWindowItemsPacket packet, GeyserSession session) {
|
||||||
session.addInventoryTask(() -> {
|
Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId());
|
||||||
Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId());
|
if (inventory == null)
|
||||||
if (inventory == null)
|
return;
|
||||||
return;
|
|
||||||
|
|
||||||
inventory.setStateId(packet.getStateId());
|
inventory.setStateId(packet.getStateId());
|
||||||
|
|
||||||
for (int i = 0; i < packet.getItems().length; i++) {
|
for (int i = 0; i < packet.getItems().length; i++) {
|
||||||
GeyserItemStack newItem = GeyserItemStack.from(packet.getItems()[i]);
|
GeyserItemStack newItem = GeyserItemStack.from(packet.getItems()[i]);
|
||||||
inventory.setItem(i, newItem, session);
|
inventory.setItem(i, newItem, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
InventoryTranslator translator = session.getInventoryTranslator();
|
InventoryTranslator translator = session.getInventoryTranslator();
|
||||||
if (translator != null) {
|
if (translator != null) {
|
||||||
translator.updateInventory(session, inventory);
|
translator.updateInventory(session, inventory);
|
||||||
}
|
}
|
||||||
|
|
||||||
session.getPlayerInventory().setCursor(GeyserItemStack.from(packet.getCarriedItem()), session);
|
session.getPlayerInventory().setCursor(GeyserItemStack.from(packet.getCarriedItem()), session);
|
||||||
InventoryUtils.updateCursor(session);
|
InventoryUtils.updateCursor(session);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,15 +38,13 @@ public class JavaWindowPropertyTranslator extends PacketTranslator<ServerWindowP
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(ServerWindowPropertyPacket packet, GeyserSession session) {
|
public void translate(ServerWindowPropertyPacket packet, GeyserSession session) {
|
||||||
session.addInventoryTask(() -> {
|
Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId());
|
||||||
Inventory inventory = InventoryUtils.getInventory(session, packet.getWindowId());
|
if (inventory == null)
|
||||||
if (inventory == null)
|
return;
|
||||||
return;
|
|
||||||
|
|
||||||
InventoryTranslator translator = session.getInventoryTranslator();
|
InventoryTranslator translator = session.getInventoryTranslator();
|
||||||
if (translator != null) {
|
if (translator != null) {
|
||||||
translator.updateProperty(session, inventory, packet.getRawProperty(), packet.getValue());
|
translator.updateProperty(session, inventory, packet.getRawProperty(), packet.getValue());
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.getConnector().getGeneralThreadPool().schedule(() -> {
|
SkullSkinManager.requestAndHandleSkin(player, session, (skin -> session.getEventLoop().schedule(() -> {
|
||||||
// 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);
|
||||||
|
|
|
@ -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.data.inventory.ItemData;
|
||||||
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
|
import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket;
|
||||||
import com.nukkitx.protocol.bedrock.packet.PlayerHotbarPacket;
|
import com.nukkitx.protocol.bedrock.packet.PlayerHotbarPacket;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
|
||||||
import org.geysermc.connector.common.ChatColor;
|
import org.geysermc.connector.common.ChatColor;
|
||||||
import org.geysermc.connector.inventory.Container;
|
import org.geysermc.connector.inventory.Container;
|
||||||
import org.geysermc.connector.inventory.GeyserItemStack;
|
import org.geysermc.connector.inventory.GeyserItemStack;
|
||||||
|
@ -80,17 +79,16 @@ 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()) {
|
||||||
GeyserConnector.getInstance().getGeneralThreadPool().schedule(() ->
|
session.getEventLoop().schedule(() -> {
|
||||||
session.addInventoryTask(() -> {
|
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);
|
translator.updateInventory(session, inventory);
|
||||||
translator.updateInventory(session, inventory);
|
} else if (openInv != null && openInv.isPending()) {
|
||||||
} else if (openInv != null && openInv.isPending()) {
|
// Presumably, this inventory is no longer relevant, and the client doesn't care about it
|
||||||
// Presumably, this inventory is no longer relevant, and the client doesn't care about it
|
displayInventory(session, openInv);
|
||||||
displayInventory(session, openInv);
|
}
|
||||||
}
|
}, 200, TimeUnit.MILLISECONDS);
|
||||||
}), 200, TimeUnit.MILLISECONDS);
|
|
||||||
} else {
|
} else {
|
||||||
translator.openInventory(session, inventory);
|
translator.openInventory(session, inventory);
|
||||||
translator.updateInventory(session, inventory);
|
translator.updateInventory(session, inventory);
|
||||||
|
|
Loading…
Reference in a new issue