mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Handle chunks on the player thread
This commit is contained in:
parent
59e6fc0285
commit
393c2b0f91
28 changed files with 219 additions and 239 deletions
|
@ -36,6 +36,7 @@ import com.nukkitx.protocol.bedrock.BedrockServer;
|
|||
import io.netty.channel.epoll.Epoll;
|
||||
import io.netty.channel.kqueue.KQueue;
|
||||
import io.netty.util.NettyRuntime;
|
||||
import io.netty.util.concurrent.DefaultThreadFactory;
|
||||
import io.netty.util.internal.SystemPropertyUtil;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
@ -118,7 +119,7 @@ public class GeyserConnector {
|
|||
|
||||
private volatile boolean shuttingDown = false;
|
||||
|
||||
private final ScheduledExecutorService generalThreadPool;
|
||||
private final ScheduledExecutorService scheduledThread;
|
||||
|
||||
private final BedrockServer bedrockServer;
|
||||
private final PlatformType platformType;
|
||||
|
@ -144,7 +145,7 @@ public class GeyserConnector {
|
|||
logger.info("");
|
||||
logger.info("******************************************");
|
||||
|
||||
this.generalThreadPool = Executors.newScheduledThreadPool(config.getGeneralThreadPool());
|
||||
this.scheduledThread = Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory("Geyser Scheduled Thread"));
|
||||
|
||||
logger.setDebug(config.isDebugMode());
|
||||
|
||||
|
@ -404,7 +405,7 @@ public class GeyserConnector {
|
|||
bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown.kick.done"));
|
||||
}
|
||||
|
||||
generalThreadPool.shutdown();
|
||||
scheduledThread.shutdown();
|
||||
bedrockServer.close();
|
||||
if (timeSyncer != null) {
|
||||
timeSyncer.shutdown();
|
||||
|
|
|
@ -97,7 +97,7 @@ public final class LocalSession extends TcpSession {
|
|||
exceptionCaught(null, future.cause());
|
||||
}
|
||||
});
|
||||
} catch(Throwable t) {
|
||||
} catch (Throwable t) {
|
||||
exceptionCaught(null, t);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,8 +68,6 @@ public interface GeyserConfiguration {
|
|||
|
||||
boolean isDebugMode();
|
||||
|
||||
int getGeneralThreadPool();
|
||||
|
||||
boolean isAllowThirdPartyCapes();
|
||||
|
||||
boolean isAllowThirdPartyEars();
|
||||
|
|
|
@ -96,9 +96,6 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||
@JsonProperty("debug-mode")
|
||||
private boolean debugMode = false;
|
||||
|
||||
@JsonProperty("general-thread-pool")
|
||||
private int generalThreadPool = 32;
|
||||
|
||||
@JsonProperty("allow-third-party-capes")
|
||||
private boolean allowThirdPartyCapes = true;
|
||||
|
||||
|
|
|
@ -133,9 +133,7 @@ public class BoatEntity extends Entity {
|
|||
// Get the entity by the first stored passenger and convey motion in this manner
|
||||
Entity entity = session.getEntityCache().getEntityByJavaId(this.passengers.iterator().nextLong());
|
||||
if (entity != null) {
|
||||
session.getConnector().getGeneralThreadPool().execute(() ->
|
||||
updateLeftPaddle(session, entity)
|
||||
);
|
||||
updateLeftPaddle(session, entity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -150,9 +148,7 @@ public class BoatEntity extends Entity {
|
|||
if (!this.passengers.isEmpty()) {
|
||||
Entity entity = session.getEntityCache().getEntityByJavaId(this.passengers.iterator().nextLong());
|
||||
if (entity != null) {
|
||||
session.getConnector().getGeneralThreadPool().execute(() ->
|
||||
updateRightPaddle(session, entity)
|
||||
);
|
||||
updateRightPaddle(session, entity);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -180,7 +176,7 @@ public class BoatEntity extends Entity {
|
|||
paddleTimeLeft += ROWING_SPEED;
|
||||
sendAnimationPacket(session, rower, AnimatePacket.Action.ROW_LEFT, paddleTimeLeft);
|
||||
|
||||
session.getConnector().getGeneralThreadPool().schedule(() ->
|
||||
session.scheduleInEventLoop(() ->
|
||||
updateLeftPaddle(session, rower),
|
||||
100,
|
||||
TimeUnit.MILLISECONDS
|
||||
|
@ -193,7 +189,7 @@ public class BoatEntity extends Entity {
|
|||
paddleTimeRight += ROWING_SPEED;
|
||||
sendAnimationPacket(session, rower, AnimatePacket.Action.ROW_RIGHT, paddleTimeRight);
|
||||
|
||||
session.getConnector().getGeneralThreadPool().schedule(() ->
|
||||
session.scheduleInEventLoop(() ->
|
||||
updateRightPaddle(session, rower),
|
||||
100,
|
||||
TimeUnit.MILLISECONDS
|
||||
|
|
|
@ -42,8 +42,6 @@ import org.geysermc.connector.network.session.GeyserSession;
|
|||
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||
import org.geysermc.connector.registry.type.ItemMapping;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Item frames are an entity in Java but a block entity in Bedrock.
|
||||
*/
|
||||
|
@ -99,11 +97,8 @@ public class ItemFrameEntity extends Entity {
|
|||
|
||||
session.getItemFrameCache().put(bedrockPosition, this);
|
||||
|
||||
// Delay is required, or else loading in frames on chunk load is sketchy at best
|
||||
session.getConnector().getGeneralThreadPool().schedule(() -> {
|
||||
updateBlock(session);
|
||||
session.getConnector().getLogger().debug("Spawned item frame at location " + bedrockPosition + " with java id " + entityId);
|
||||
}, 500, TimeUnit.MILLISECONDS);
|
||||
updateBlock(session);
|
||||
session.getConnector().getLogger().debug("Spawned item frame at location " + bedrockPosition + " with java id " + entityId);
|
||||
valid = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,13 +29,11 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadat
|
|||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class TNTEntity extends Entity {
|
||||
public class TNTEntity extends Entity implements Tickable {
|
||||
|
||||
private int currentTick;
|
||||
|
||||
|
@ -49,16 +47,26 @@ public class TNTEntity extends Entity {
|
|||
currentTick = (int) entityMetadata.getValue();
|
||||
metadata.getFlags().setFlag(EntityFlag.IGNITED, true);
|
||||
metadata.put(EntityData.FUSE_LENGTH, currentTick);
|
||||
ScheduledFuture<?> future = session.getConnector().getGeneralThreadPool().scheduleAtFixedRate(() -> {
|
||||
if (currentTick % 5 == 0) {
|
||||
metadata.put(EntityData.FUSE_LENGTH, currentTick);
|
||||
}
|
||||
currentTick--;
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}, 50, 50, TimeUnit.MILLISECONDS); // 5 ticks
|
||||
session.getConnector().getGeneralThreadPool().schedule(() -> future.cancel(true), (int) entityMetadata.getValue() / 20, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(GeyserSession session) {
|
||||
if (currentTick == 0) {
|
||||
// No need to update the fuse when there is none
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentTick % 5 == 0) {
|
||||
metadata.put(EntityData.FUSE_LENGTH, currentTick);
|
||||
|
||||
SetEntityDataPacket packet = new SetEntityDataPacket();
|
||||
packet.setRuntimeEntityId(geyserId);
|
||||
packet.getMetadata().put(EntityData.FUSE_LENGTH, currentTick);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
currentTick--;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,7 +129,7 @@ public class PlayerEntity extends LivingEntity {
|
|||
if (session.getEntityCache().getPlayerEntity(uuid) == null)
|
||||
return;
|
||||
|
||||
if (session.getUpstream().isInitialized() && session.getEntityCache().getEntityByGeyserId(geyserId) == null) {
|
||||
if (session.getEntityCache().getEntityByGeyserId(geyserId) == null) {
|
||||
session.getEntityCache().spawnEntity(this);
|
||||
} else {
|
||||
spawnEntity(session);
|
||||
|
@ -288,7 +288,7 @@ public class PlayerEntity extends LivingEntity {
|
|||
linkPacket.setEntityLink(new EntityLinkData(geyserId, parrot.getGeyserId(), type, false, false));
|
||||
// Delay, or else spawned-in players won't get the link
|
||||
// TODO: Find a better solution. This problem also exists with item frames
|
||||
session.getConnector().getGeneralThreadPool().schedule(() -> session.sendUpstreamPacket(linkPacket), 500, TimeUnit.MILLISECONDS);
|
||||
session.scheduleInEventLoop(() -> session.sendUpstreamPacket(linkPacket), 500, TimeUnit.MILLISECONDS);
|
||||
if (isLeft) {
|
||||
leftParrot = parrot;
|
||||
} else {
|
||||
|
|
|
@ -114,7 +114,7 @@ public class Metrics {
|
|||
* Starts the Scheduler which submits our data every 30 minutes.
|
||||
*/
|
||||
private void startSubmitting() {
|
||||
connector.getGeneralThreadPool().scheduleAtFixedRate(this::submitData, 1, 30, TimeUnit.MINUTES);
|
||||
connector.getScheduledThread().scheduleAtFixedRate(this::submitData, 1, 30, TimeUnit.MINUTES);
|
||||
// Submit the data every 30 minutes, first time after 1 minutes to give other plugins enough time to start
|
||||
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
|
||||
// WARNING: Just don't do it!
|
||||
|
|
|
@ -104,6 +104,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
}
|
||||
resourcePacksInfo.setForcedToAccept(GeyserConnector.getInstance().getConfig().isForceResourcePacks());
|
||||
session.sendUpstreamPacket(resourcePacksInfo);
|
||||
|
||||
LanguageUtils.loadGeyserLocale(session.getLocale());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -111,7 +113,12 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
public boolean handle(ResourcePackClientResponsePacket packet) {
|
||||
switch (packet.getStatus()) {
|
||||
case COMPLETED:
|
||||
session.connect();
|
||||
if (connector.getConfig().getRemote().getAuthType() != AuthType.ONLINE) {
|
||||
session.authenticate(session.getAuthData().getName());
|
||||
} else if (!couldLoginUserByName(session.getAuthData().getName())) {
|
||||
// We must spawn the white world
|
||||
session.connect();
|
||||
}
|
||||
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.connect", session.getAuthData().getName()));
|
||||
break;
|
||||
|
||||
|
@ -182,9 +189,6 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.auth.stored_credentials", session.getAuthData().getName()));
|
||||
session.setMicrosoftAccount(info.isMicrosoftAccount());
|
||||
session.authenticate(info.getEmail(), info.getPassword());
|
||||
|
||||
// TODO send a message to bedrock user telling them they are connected (if nothing like a motd
|
||||
// somes from the Java server w/in a few seconds)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -192,20 +196,6 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(SetLocalPlayerAsInitializedPacket packet) {
|
||||
LanguageUtils.loadGeyserLocale(session.getLocale());
|
||||
|
||||
if (!session.isLoggedIn() && !session.isLoggingIn() && session.getRemoteAuthType() == AuthType.ONLINE) {
|
||||
// TODO it is safer to key authentication on something that won't change (UUID, not username)
|
||||
if (!couldLoginUserByName(session.getAuthData().getName())) {
|
||||
LoginEncryptionUtils.buildAndShowLoginWindow(session);
|
||||
}
|
||||
// else we were able to log the user in
|
||||
}
|
||||
return translateAndDefault(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(MovePlayerPacket packet) {
|
||||
if (session.isLoggingIn()) {
|
||||
|
|
|
@ -229,6 +229,8 @@ public class GeyserSession implements CommandSender {
|
|||
private Vector2i lastChunkPosition = null;
|
||||
private int renderDistance;
|
||||
|
||||
private boolean sentSpawnPacket;
|
||||
|
||||
private boolean loggedIn;
|
||||
private boolean loggingIn;
|
||||
|
||||
|
@ -501,6 +503,10 @@ public class GeyserSession implements CommandSender {
|
|||
disconnect(disconnectReason.name());
|
||||
connector.getSessionManager().removeSession(this);
|
||||
});
|
||||
|
||||
this.remoteAddress = connector.getConfig().getRemote().getAddress();
|
||||
this.remotePort = connector.getConfig().getRemote().getPort();
|
||||
this.remoteAuthType = connector.getConfig().getRemote().getAuthType();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -508,9 +514,7 @@ public class GeyserSession implements CommandSender {
|
|||
*/
|
||||
public void connect() {
|
||||
startGame();
|
||||
this.remoteAddress = connector.getConfig().getRemote().getAddress();
|
||||
this.remotePort = connector.getConfig().getRemote().getPort();
|
||||
this.remoteAuthType = connector.getConfig().getRemote().getAuthType();
|
||||
sentSpawnPacket = true;
|
||||
|
||||
// Set the hardcoded shield ID to the ID we just defined in StartGamePacket
|
||||
upstream.getSession().getHardcodedBlockingId().set(this.itemMappings.getStoredItems().shield().getBedrockId());
|
||||
|
@ -685,27 +689,36 @@ public class GeyserSession implements CommandSender {
|
|||
if (loggedIn || closed) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
msaAuthenticationService.login();
|
||||
GameProfile profile = msaAuthenticationService.getSelectedProfile();
|
||||
if (profile == null) {
|
||||
// Java account is offline
|
||||
disconnect(LanguageUtils.getPlayerLocaleString("geyser.network.remote.invalid_account", clientData.getLanguageCode()));
|
||||
CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
msaAuthenticationService.login();
|
||||
GameProfile profile = msaAuthenticationService.getSelectedProfile();
|
||||
if (profile == null) {
|
||||
// Java account is offline
|
||||
disconnect(LanguageUtils.getPlayerLocaleString("geyser.network.remote.invalid_account", clientData.getLanguageCode()));
|
||||
return null;
|
||||
}
|
||||
|
||||
return new MinecraftProtocol(profile, msaAuthenticationService.getAccessToken());
|
||||
} catch (RequestException e) {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}).whenComplete((response, ex) -> {
|
||||
if (ex != null) {
|
||||
if (!(ex instanceof CompletionException completionException) || !(completionException.getCause() instanceof AuthPendingException)) {
|
||||
connector.getLogger().error("Failed to log in with Microsoft code!", ex);
|
||||
disconnect(ex.toString());
|
||||
} else {
|
||||
// Wait one second before trying again
|
||||
connector.getScheduledThread().schedule(() -> attemptCodeAuthentication(msaAuthenticationService), 1, TimeUnit.SECONDS);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
protocol = new MinecraftProtocol(profile, msaAuthenticationService.getAccessToken());
|
||||
|
||||
connectDownstream();
|
||||
} catch (RequestException e) {
|
||||
if (!(e instanceof AuthPendingException)) {
|
||||
connector.getLogger().error("Failed to log in with Microsoft code!", e);
|
||||
disconnect(e.toString());
|
||||
} else {
|
||||
// Wait one second before trying again
|
||||
connector.getGeneralThreadPool().schedule(() -> attemptCodeAuthentication(msaAuthenticationService), 1, TimeUnit.SECONDS);
|
||||
if (!closed) {
|
||||
this.protocol = response;
|
||||
connectDownstream();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -725,6 +738,7 @@ public class GeyserSession implements CommandSender {
|
|||
downstream = new TcpClientSession(this.remoteAddress, this.remotePort, this.protocol);
|
||||
disableSrvResolving();
|
||||
}
|
||||
|
||||
if (connector.getConfig().getRemote().isUseProxyProtocol()) {
|
||||
downstream.setFlag(BuiltinFlags.ENABLE_CLIENT_PROXY_PROTOCOL, true);
|
||||
downstream.setFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS, upstream.getAddress());
|
||||
|
@ -1133,7 +1147,11 @@ public class GeyserSession implements CommandSender {
|
|||
StartGamePacket startGamePacket = new StartGamePacket();
|
||||
startGamePacket.setUniqueEntityId(playerEntity.getGeyserId());
|
||||
startGamePacket.setRuntimeEntityId(playerEntity.getGeyserId());
|
||||
startGamePacket.setPlayerGameType(GameType.SURVIVAL);
|
||||
startGamePacket.setPlayerGameType(switch (gameMode) {
|
||||
case CREATIVE -> GameType.CREATIVE;
|
||||
case ADVENTURE -> GameType.ADVENTURE;
|
||||
default -> GameType.SURVIVAL;
|
||||
});
|
||||
startGamePacket.setPlayerPosition(Vector3f.from(0, 69, 0));
|
||||
startGamePacket.setRotation(Vector2f.from(1, 1));
|
||||
|
||||
|
|
|
@ -65,8 +65,7 @@ public class FormCache {
|
|||
NetworkStackLatencyPacket latencyPacket = new NetworkStackLatencyPacket();
|
||||
latencyPacket.setFromServer(true);
|
||||
latencyPacket.setTimestamp(-System.currentTimeMillis());
|
||||
session.getConnector().getGeneralThreadPool().schedule(
|
||||
() -> session.sendUpstreamPacket(latencyPacket),
|
||||
session.scheduleInEventLoop(() -> session.sendUpstreamPacket(latencyPacket),
|
||||
500, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ package org.geysermc.connector.network.translators.bedrock;
|
|||
|
||||
import com.nukkitx.protocol.bedrock.packet.ClientboundMapItemDataPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.MapInfoRequestPacket;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
@ -44,7 +43,7 @@ public class BedrockMapInfoRequestTranslator extends PacketTranslator<MapInfoReq
|
|||
ClientboundMapItemDataPacket mapPacket = session.getStoredMaps().remove(mapId);
|
||||
if (mapPacket != null) {
|
||||
// Delay the packet 100ms to prevent the client from ignoring the packet
|
||||
GeyserConnector.getInstance().getGeneralThreadPool().schedule(() -> session.sendUpstreamPacket(mapPacket),
|
||||
session.scheduleInEventLoop(() -> session.sendUpstreamPacket(mapPacket),
|
||||
100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
|
|||
// Activate shield since we are already sneaking
|
||||
// (No need to send a release item packet - Java doesn't do this when swapping items)
|
||||
// Required to do it a tick later or else it doesn't register
|
||||
session.getConnector().getGeneralThreadPool().schedule(() -> session.sendDownstreamPacket(new ServerboundUseItemPacket(Hand.MAIN_HAND)),
|
||||
session.scheduleInEventLoop(() -> session.sendDownstreamPacket(new ServerboundUseItemPacket(Hand.MAIN_HAND)),
|
||||
50, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
|
|
|
@ -76,8 +76,7 @@ public class BedrockNetworkStackLatencyTranslator extends PacketTranslator<Netwo
|
|||
attributesPacket.setAttributes(Collections.singletonList(GeyserAttributeType.EXPERIENCE_LEVEL.getAttribute(0)));
|
||||
}
|
||||
|
||||
session.getConnector().getGeneralThreadPool().schedule(
|
||||
() -> session.sendUpstreamPacket(attributesPacket),
|
||||
session.scheduleInEventLoop(() -> session.sendUpstreamPacket(attributesPacket),
|
||||
500, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ public class BedrockServerSettingsRequestTranslator extends PacketTranslator<Ser
|
|||
int windowId = session.getFormCache().addForm(window);
|
||||
|
||||
// Fixes https://bugs.mojang.com/browse/MCPE-94012 because of the delay
|
||||
session.getConnector().getGeneralThreadPool().schedule(() -> {
|
||||
session.scheduleInEventLoop(() -> {
|
||||
ServerSettingsResponsePacket serverSettingsResponsePacket = new ServerSettingsResponsePacket();
|
||||
serverSettingsResponsePacket.setFormData(window.getJsonData());
|
||||
serverSettingsResponsePacket.setFormId(windowId);
|
||||
|
|
|
@ -25,14 +25,12 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.bedrock;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetLocalPlayerAsInitializedPacket;
|
||||
import org.geysermc.connector.entity.player.PlayerEntity;
|
||||
import org.geysermc.connector.common.AuthType;
|
||||
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.skin.SkinManager;
|
||||
import org.geysermc.connector.skin.SkullSkinManager;
|
||||
import org.geysermc.connector.utils.LoginEncryptionUtils;
|
||||
|
||||
@Translator(packet = SetLocalPlayerAsInitializedPacket.class)
|
||||
public class BedrockSetLocalPlayerAsInitializedTranslator extends PacketTranslator<SetLocalPlayerAsInitializedPacket> {
|
||||
|
@ -41,23 +39,12 @@ public class BedrockSetLocalPlayerAsInitializedTranslator extends PacketTranslat
|
|||
if (session.getPlayerEntity().getGeyserId() == packet.getRuntimeEntityId()) {
|
||||
if (!session.getUpstream().isInitialized()) {
|
||||
session.getUpstream().setInitialized(true);
|
||||
session.login();
|
||||
|
||||
for (PlayerEntity entity : session.getEntityCache().getEntitiesByType(PlayerEntity.class)) {
|
||||
if (!entity.isValid()) {
|
||||
SkinManager.requestAndHandleSkinAndCape(entity, session, null);
|
||||
entity.sendPlayer(session);
|
||||
if (session.getRemoteAuthType() == AuthType.ONLINE) {
|
||||
if (!session.isLoggedIn()) {
|
||||
LoginEncryptionUtils.buildAndShowLoginWindow(session);
|
||||
}
|
||||
}
|
||||
|
||||
// Send Skulls
|
||||
for (PlayerEntity entity : session.getSkullCache().values()) {
|
||||
entity.spawnEntity(session);
|
||||
|
||||
SkullSkinManager.requestAndHandleSkin(entity, session, (skin) -> {
|
||||
entity.getMetadata().getFlags().setFlag(EntityFlag.INVISIBLE, false);
|
||||
entity.updateBedrockMetadata(session);
|
||||
});
|
||||
// else we were able to log the user in
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,19 +70,25 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
|
|||
BiomeTranslator.loadServerBiomes(session, packet.getDimensionCodec());
|
||||
session.getTagCache().clear();
|
||||
|
||||
session.setGameMode(packet.getGameMode());
|
||||
|
||||
boolean needsSpawnPacket = !session.isSentSpawnPacket();
|
||||
if (needsSpawnPacket) {
|
||||
// The player has yet to spawn so let's do that using some of the information in this Java packet
|
||||
session.setDimension(newDimension);
|
||||
session.connect();
|
||||
}
|
||||
|
||||
AdventureSettingsPacket bedrockPacket = new AdventureSettingsPacket();
|
||||
bedrockPacket.setUniqueEntityId(session.getPlayerEntity().getGeyserId());
|
||||
bedrockPacket.setPlayerPermission(PlayerPermission.MEMBER);
|
||||
session.sendUpstreamPacket(bedrockPacket);
|
||||
|
||||
PlayStatusPacket playStatus = new PlayStatusPacket();
|
||||
playStatus.setStatus(PlayStatusPacket.Status.LOGIN_SUCCESS);
|
||||
// session.sendPacket(playStatus);
|
||||
|
||||
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
|
||||
playerGameTypePacket.setGamemode(packet.getGameMode().ordinal());
|
||||
session.sendUpstreamPacket(playerGameTypePacket);
|
||||
session.setGameMode(packet.getGameMode());
|
||||
if (!needsSpawnPacket) {
|
||||
SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket();
|
||||
playerGameTypePacket.setGamemode(packet.getGameMode().ordinal());
|
||||
session.sendUpstreamPacket(playerGameTypePacket);
|
||||
}
|
||||
|
||||
SetEntityDataPacket entityDataPacket = new SetEntityDataPacket();
|
||||
entityDataPacket.setRuntimeEntityId(entity.getGeyserId());
|
||||
|
|
|
@ -109,7 +109,7 @@ public class JavaPlayerInfoTranslator extends PacketTranslator<ClientboundPlayer
|
|||
}
|
||||
}
|
||||
|
||||
if (!translate.getEntries().isEmpty() && (packet.getAction() == PlayerListEntryAction.REMOVE_PLAYER || session.getUpstream().isInitialized())) {
|
||||
if (!translate.getEntries().isEmpty()) {
|
||||
session.sendUpstreamPacket(translate);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,6 +43,8 @@ import org.geysermc.connector.network.translators.world.BiomeTranslator;
|
|||
import org.geysermc.connector.network.translators.world.chunk.ChunkSection;
|
||||
import org.geysermc.connector.utils.ChunkUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.geysermc.connector.utils.ChunkUtils.MINIMUM_ACCEPTED_HEIGHT;
|
||||
import static org.geysermc.connector.utils.ChunkUtils.MINIMUM_ACCEPTED_HEIGHT_OVERWORLD;
|
||||
|
||||
|
@ -61,85 +63,79 @@ public class JavaLevelChunkTranslator extends PacketTranslator<ClientboundLevelC
|
|||
// Ensure that, if the player is using lower world heights, the position is not offset
|
||||
int yOffset = session.getChunkCache().getChunkMinY();
|
||||
|
||||
GeyserConnector.getInstance().getGeneralThreadPool().execute(() -> {
|
||||
try {
|
||||
if (session.isClosed()) {
|
||||
return;
|
||||
}
|
||||
ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(session, column, yOffset);
|
||||
ChunkSection[] sections = chunkData.sections();
|
||||
ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(session, column, yOffset);
|
||||
ChunkSection[] sections = chunkData.sections();
|
||||
|
||||
// Find highest section
|
||||
int sectionCount = sections.length - 1;
|
||||
while (sectionCount >= 0 && sections[sectionCount] == null) {
|
||||
sectionCount--;
|
||||
}
|
||||
sectionCount++;
|
||||
// Find highest section
|
||||
int sectionCount = sections.length - 1;
|
||||
while (sectionCount >= 0 && sections[sectionCount] == null) {
|
||||
sectionCount--;
|
||||
}
|
||||
sectionCount++;
|
||||
|
||||
// Estimate chunk size
|
||||
int size = 0;
|
||||
for (int i = 0; i < sectionCount; i++) {
|
||||
ChunkSection section = sections[i];
|
||||
size += (section != null ? section : session.getBlockMappings().getEmptyChunkSection()).estimateNetworkSize();
|
||||
}
|
||||
size += ChunkUtils.EMPTY_CHUNK_DATA.length; // Consists only of biome data
|
||||
size += 1; // Border blocks
|
||||
size += 1; // Extra data length (always 0)
|
||||
size += chunkData.blockEntities().length * 64; // Conservative estimate of 64 bytes per tile entity
|
||||
// Estimate chunk size
|
||||
int size = 0;
|
||||
for (int i = 0; i < sectionCount; i++) {
|
||||
ChunkSection section = sections[i];
|
||||
size += (section != null ? section : session.getBlockMappings().getEmptyChunkSection()).estimateNetworkSize();
|
||||
}
|
||||
size += ChunkUtils.EMPTY_CHUNK_DATA.length; // Consists only of biome data
|
||||
size += 1; // Border blocks
|
||||
size += 1; // Extra data length (always 0)
|
||||
size += chunkData.blockEntities().length * 64; // Conservative estimate of 64 bytes per tile entity
|
||||
|
||||
// Allocate output buffer
|
||||
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(size);
|
||||
byte[] payload;
|
||||
try {
|
||||
for (int i = 0; i < sectionCount; i++) {
|
||||
ChunkSection section = sections[i];
|
||||
(section != null ? section : session.getBlockMappings().getEmptyChunkSection()).writeToNetwork(byteBuf);
|
||||
}
|
||||
|
||||
// At this point we're dealing with Bedrock chunk sections
|
||||
boolean overworld = session.getChunkCache().isExtendedHeight();
|
||||
int dimensionOffset = (overworld ? MINIMUM_ACCEPTED_HEIGHT_OVERWORLD : MINIMUM_ACCEPTED_HEIGHT) >> 4;
|
||||
for (int i = 0; i < sectionCount; i++) {
|
||||
int biomeYOffset = dimensionOffset + i;
|
||||
if (biomeYOffset < yOffset) {
|
||||
// Ignore this biome section since it goes below the height of the Java world
|
||||
byteBuf.writeBytes(ChunkUtils.EMPTY_BIOME_DATA);
|
||||
continue;
|
||||
}
|
||||
BiomeTranslator.toNewBedrockBiome(session, column.getBiomeData(), i + (dimensionOffset - yOffset)).writeToNetwork(byteBuf);
|
||||
}
|
||||
|
||||
// As of 1.17.10, Bedrock hardcodes to always read 32 biome sections
|
||||
int remainingEmptyBiomes = 32 - sectionCount;
|
||||
for (int i = 0; i < remainingEmptyBiomes; i++) {
|
||||
byteBuf.writeBytes(ChunkUtils.EMPTY_BIOME_DATA);
|
||||
}
|
||||
|
||||
byteBuf.writeByte(0); // Border blocks - Edu edition only
|
||||
VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now
|
||||
|
||||
// Encode tile entities into buffer
|
||||
NBTOutputStream nbtStream = NbtUtils.createNetworkWriter(new ByteBufOutputStream(byteBuf));
|
||||
for (NbtMap blockEntity : chunkData.blockEntities()) {
|
||||
nbtStream.writeTag(blockEntity);
|
||||
}
|
||||
|
||||
// Copy data into byte[], because the protocol lib really likes things that are s l o w
|
||||
byteBuf.readBytes(payload = new byte[byteBuf.readableBytes()]);
|
||||
} finally {
|
||||
byteBuf.release(); // Release buffer to allow buffer pooling to be useful
|
||||
}
|
||||
|
||||
LevelChunkPacket levelChunkPacket = new LevelChunkPacket();
|
||||
levelChunkPacket.setSubChunksLength(sectionCount);
|
||||
levelChunkPacket.setCachingEnabled(false);
|
||||
levelChunkPacket.setChunkX(column.getX());
|
||||
levelChunkPacket.setChunkZ(column.getZ());
|
||||
levelChunkPacket.setData(payload);
|
||||
session.sendUpstreamPacket(levelChunkPacket);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
// Allocate output buffer
|
||||
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(size);
|
||||
byte[] payload;
|
||||
try {
|
||||
for (int i = 0; i < sectionCount; i++) {
|
||||
ChunkSection section = sections[i];
|
||||
(section != null ? section : session.getBlockMappings().getEmptyChunkSection()).writeToNetwork(byteBuf);
|
||||
}
|
||||
});
|
||||
|
||||
// At this point we're dealing with Bedrock chunk sections
|
||||
boolean overworld = session.getChunkCache().isExtendedHeight();
|
||||
int dimensionOffset = (overworld ? MINIMUM_ACCEPTED_HEIGHT_OVERWORLD : MINIMUM_ACCEPTED_HEIGHT) >> 4;
|
||||
for (int i = 0; i < sectionCount; i++) {
|
||||
int biomeYOffset = dimensionOffset + i;
|
||||
if (biomeYOffset < yOffset) {
|
||||
// Ignore this biome section since it goes below the height of the Java world
|
||||
byteBuf.writeBytes(ChunkUtils.EMPTY_BIOME_DATA);
|
||||
continue;
|
||||
}
|
||||
BiomeTranslator.toNewBedrockBiome(session, column.getBiomeData(), i + (dimensionOffset - yOffset)).writeToNetwork(byteBuf);
|
||||
}
|
||||
|
||||
// As of 1.17.10, Bedrock hardcodes to always read 32 biome sections
|
||||
int remainingEmptyBiomes = 32 - sectionCount;
|
||||
for (int i = 0; i < remainingEmptyBiomes; i++) {
|
||||
byteBuf.writeBytes(ChunkUtils.EMPTY_BIOME_DATA);
|
||||
}
|
||||
|
||||
byteBuf.writeByte(0); // Border blocks - Edu edition only
|
||||
VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now
|
||||
|
||||
// Encode tile entities into buffer
|
||||
NBTOutputStream nbtStream = NbtUtils.createNetworkWriter(new ByteBufOutputStream(byteBuf));
|
||||
for (NbtMap blockEntity : chunkData.blockEntities()) {
|
||||
nbtStream.writeTag(blockEntity);
|
||||
}
|
||||
|
||||
// Copy data into byte[], because the protocol lib really likes things that are s l o w
|
||||
byteBuf.readBytes(payload = new byte[byteBuf.readableBytes()]);
|
||||
} catch (IOException e) {
|
||||
session.getConnector().getLogger().error("IO error while encoding chunk", e);
|
||||
return;
|
||||
} finally {
|
||||
byteBuf.release(); // Release buffer to allow buffer pooling to be useful
|
||||
}
|
||||
|
||||
LevelChunkPacket levelChunkPacket = new LevelChunkPacket();
|
||||
levelChunkPacket.setSubChunksLength(sectionCount);
|
||||
levelChunkPacket.setCachingEnabled(false);
|
||||
levelChunkPacket.setChunkX(column.getX());
|
||||
levelChunkPacket.setChunkZ(column.getZ());
|
||||
levelChunkPacket.setData(payload);
|
||||
session.sendUpstreamPacket(levelChunkPacket);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,15 +141,12 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
|
|||
// Cache entity
|
||||
session.getSkullCache().put(blockPosition, player);
|
||||
|
||||
// Only send to session if we are initialized, otherwise it will happen then.
|
||||
if (session.getUpstream().isInitialized()) {
|
||||
player.spawnEntity(session);
|
||||
player.spawnEntity(session);
|
||||
|
||||
SkullSkinManager.requestAndHandleSkin(player, session, (skin -> session.scheduleInEventLoop(() -> {
|
||||
// Delay to minimize split-second "player" pop-in
|
||||
player.getMetadata().getFlags().setFlag(EntityFlag.INVISIBLE, false);
|
||||
player.updateBedrockMetadata(session);
|
||||
}, 250, TimeUnit.MILLISECONDS)));
|
||||
}
|
||||
SkullSkinManager.requestAndHandleSkin(player, session, (skin -> session.scheduleInEventLoop(() -> {
|
||||
// Delay to minimize split-second "player" pop-in
|
||||
player.getMetadata().getFlags().setFlag(EntityFlag.INVISIBLE, false);
|
||||
player.updateBedrockMetadata(session);
|
||||
}, 250, TimeUnit.MILLISECONDS)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn
|
|||
// Ensure delay is not zero
|
||||
int interval = (connector.getConfig().getPingPassthroughInterval() == 0) ? 1 : connector.getConfig().getPingPassthroughInterval();
|
||||
connector.getLogger().debug("Scheduling ping passthrough at an interval of " + interval + " second(s).");
|
||||
connector.getGeneralThreadPool().scheduleAtFixedRate(pingPassthrough, 1, interval, TimeUnit.SECONDS);
|
||||
connector.getScheduledThread().scheduleAtFixedRate(pingPassthrough, 1, interval, TimeUnit.SECONDS);
|
||||
return pingPassthrough;
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -212,14 +212,14 @@ public final class FloodgateSkinUploader {
|
|||
|
||||
private void reconnectLater(GeyserConnector connector) {
|
||||
// we ca only reconnect when the thread pool is open
|
||||
if (connector.getGeneralThreadPool().isShutdown() || closed) {
|
||||
if (connector.getScheduledThread().isShutdown() || closed) {
|
||||
logger.info("The skin uploader has been closed");
|
||||
return;
|
||||
}
|
||||
|
||||
long additionalTime = ThreadLocalRandom.current().nextInt(7);
|
||||
// we don't have to check the result. onClose will handle that for us
|
||||
connector.getGeneralThreadPool()
|
||||
connector.getScheduledThread()
|
||||
.schedule(client::reconnect, 8 + additionalTime, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
|
|
@ -167,31 +167,29 @@ public class SkinManager {
|
|||
}
|
||||
}
|
||||
|
||||
if (session.getUpstream().isInitialized()) {
|
||||
PlayerListPacket.Entry updatedEntry = buildEntryManually(
|
||||
session,
|
||||
entity.getUuid(),
|
||||
entity.getUsername(),
|
||||
entity.getGeyserId(),
|
||||
skin.getTextureUrl(),
|
||||
skin.getSkinData(),
|
||||
cape.getCapeId(),
|
||||
cape.getCapeData(),
|
||||
geometry
|
||||
);
|
||||
PlayerListPacket.Entry updatedEntry = buildEntryManually(
|
||||
session,
|
||||
entity.getUuid(),
|
||||
entity.getUsername(),
|
||||
entity.getGeyserId(),
|
||||
skin.getTextureUrl(),
|
||||
skin.getSkinData(),
|
||||
cape.getCapeId(),
|
||||
cape.getCapeData(),
|
||||
geometry
|
||||
);
|
||||
|
||||
|
||||
PlayerListPacket playerAddPacket = new PlayerListPacket();
|
||||
playerAddPacket.setAction(PlayerListPacket.Action.ADD);
|
||||
playerAddPacket.getEntries().add(updatedEntry);
|
||||
session.sendUpstreamPacket(playerAddPacket);
|
||||
PlayerListPacket playerAddPacket = new PlayerListPacket();
|
||||
playerAddPacket.setAction(PlayerListPacket.Action.ADD);
|
||||
playerAddPacket.getEntries().add(updatedEntry);
|
||||
session.sendUpstreamPacket(playerAddPacket);
|
||||
|
||||
if (!entity.isPlayerList()) {
|
||||
PlayerListPacket playerRemovePacket = new PlayerListPacket();
|
||||
playerRemovePacket.setAction(PlayerListPacket.Action.REMOVE);
|
||||
playerRemovePacket.getEntries().add(updatedEntry);
|
||||
session.sendUpstreamPacket(playerRemovePacket);
|
||||
}
|
||||
if (!entity.isPlayerList()) {
|
||||
PlayerListPacket playerRemovePacket = new PlayerListPacket();
|
||||
playerRemovePacket.setAction(PlayerListPacket.Action.REMOVE);
|
||||
playerRemovePacket.getEntries().add(updatedEntry);
|
||||
session.sendUpstreamPacket(playerRemovePacket);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.skin.fail", entity.getUuid()), e);
|
||||
|
|
|
@ -101,7 +101,7 @@ public class SkinProvider {
|
|||
|
||||
// Schedule Daily Image Expiry if we are caching them
|
||||
if (GeyserConnector.getInstance().getConfig().getCacheImages() > 0) {
|
||||
GeyserConnector.getInstance().getGeneralThreadPool().scheduleAtFixedRate(() -> {
|
||||
GeyserConnector.getInstance().getScheduledThread().scheduleAtFixedRate(() -> {
|
||||
File cacheFolder = GeyserConnector.getInstance().getBootstrap().getConfigFolder().resolve("cache").resolve("images").toFile();
|
||||
if (!cacheFolder.exists()) {
|
||||
return;
|
||||
|
|
|
@ -55,15 +55,13 @@ public class SkullSkinManager extends SkinManager {
|
|||
SkinProvider.requestSkin(entity.getUuid(), data.skinUrl(), true)
|
||||
.whenCompleteAsync((skin, throwable) -> {
|
||||
try {
|
||||
if (session.getUpstream().isInitialized()) {
|
||||
PlayerSkinPacket packet = new PlayerSkinPacket();
|
||||
packet.setUuid(entity.getUuid());
|
||||
packet.setOldSkinName("");
|
||||
packet.setNewSkinName(skin.getTextureUrl());
|
||||
packet.setSkin(buildSkullEntryManually(skin.getTextureUrl(), skin.getSkinData()));
|
||||
packet.setTrustedSkin(true);
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
PlayerSkinPacket packet = new PlayerSkinPacket();
|
||||
packet.setUuid(entity.getUuid());
|
||||
packet.setOldSkinName("");
|
||||
packet.setNewSkinName(skin.getTextureUrl());
|
||||
packet.setSkin(buildSkullEntryManually(skin.getTextureUrl(), skin.getSkinData()));
|
||||
packet.setTrustedSkin(true);
|
||||
session.sendUpstreamPacket(packet);
|
||||
} catch (Exception e) {
|
||||
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.skin.fail", entity.getUuid()), e);
|
||||
}
|
||||
|
|
|
@ -92,7 +92,8 @@ public class CooldownUtils {
|
|||
titlePacket.setPlatformOnlineId("");
|
||||
session.sendUpstreamPacket(titlePacket);
|
||||
if (hasCooldown(session)) {
|
||||
session.getConnector().getGeneralThreadPool().schedule(() -> computeCooldown(session, sessionPreference, lastHitTime), 50, TimeUnit.MILLISECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50
|
||||
session.scheduleInEventLoop(() ->
|
||||
computeCooldown(session, sessionPreference, lastHitTime), 50, TimeUnit.MILLISECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50
|
||||
} else {
|
||||
SetTitlePacket removeTitlePacket = new SetTitlePacket();
|
||||
if (sessionPreference == CooldownType.ACTIONBAR) {
|
||||
|
|
|
@ -110,9 +110,6 @@ max-players: 100
|
|||
# If debug messages should be sent through console
|
||||
debug-mode: false
|
||||
|
||||
# Thread pool size
|
||||
general-thread-pool: 32
|
||||
|
||||
# Allow third party capes to be visible. Currently allowing:
|
||||
# OptiFine capes, LabyMod capes, 5Zig capes and MinecraftCapes
|
||||
allow-third-party-capes: true
|
||||
|
|
Loading…
Reference in a new issue