mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Merge branch 'master' into tick-rate-handling
This commit is contained in:
commit
8d4f259ba6
19 changed files with 215 additions and 131 deletions
|
@ -30,7 +30,11 @@ import org.cloudburstmc.math.vector.Vector3f;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.ParticleType;
|
import org.cloudburstmc.protocol.bedrock.data.ParticleType;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
|
import org.cloudburstmc.protocol.bedrock.data.entity.EntityEventType;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.*;
|
import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.PlaySoundPacket;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.SpawnParticleEffectPacket;
|
||||||
import org.geysermc.geyser.entity.EntityDefinition;
|
import org.geysermc.geyser.entity.EntityDefinition;
|
||||||
import org.geysermc.geyser.entity.type.Tickable;
|
import org.geysermc.geyser.entity.type.Tickable;
|
||||||
import org.geysermc.geyser.entity.type.living.MobEntity;
|
import org.geysermc.geyser.entity.type.living.MobEntity;
|
||||||
|
@ -260,7 +264,7 @@ public class EnderDragonEntity extends MobEntity implements Tickable {
|
||||||
// so we need to manually spawn particles
|
// so we need to manually spawn particles
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
SpawnParticleEffectPacket spawnParticleEffectPacket = new SpawnParticleEffectPacket();
|
SpawnParticleEffectPacket spawnParticleEffectPacket = new SpawnParticleEffectPacket();
|
||||||
spawnParticleEffectPacket.setDimensionId(DimensionUtils.javaToBedrock(session.getDimension()));
|
spawnParticleEffectPacket.setDimensionId(DimensionUtils.javaToBedrock(session));
|
||||||
spawnParticleEffectPacket.setPosition(head.getPosition().add(random.nextGaussian() / 2f, random.nextGaussian() / 2f, random.nextGaussian() / 2f));
|
spawnParticleEffectPacket.setPosition(head.getPosition().add(random.nextGaussian() / 2f, random.nextGaussian() / 2f, random.nextGaussian() / 2f));
|
||||||
spawnParticleEffectPacket.setIdentifier("minecraft:dragon_breath_fire");
|
spawnParticleEffectPacket.setIdentifier("minecraft:dragon_breath_fire");
|
||||||
spawnParticleEffectPacket.setMolangVariablesJson(Optional.empty());
|
spawnParticleEffectPacket.setMolangVariablesJson(Optional.empty());
|
||||||
|
|
|
@ -28,6 +28,9 @@ package org.geysermc.geyser.item.type;
|
||||||
import org.geysermc.geyser.level.block.type.Block;
|
import org.geysermc.geyser.level.block.type.Block;
|
||||||
|
|
||||||
public class BlockItem extends Item {
|
public class BlockItem extends Item {
|
||||||
|
// If item is instanceof ItemNameBlockItem
|
||||||
|
private final boolean treatLikeBlock;
|
||||||
|
|
||||||
public BlockItem(Builder builder, Block block, Block... otherBlocks) {
|
public BlockItem(Builder builder, Block block, Block... otherBlocks) {
|
||||||
super(block.javaIdentifier().value(), builder);
|
super(block.javaIdentifier().value(), builder);
|
||||||
|
|
||||||
|
@ -36,6 +39,7 @@ public class BlockItem extends Item {
|
||||||
for (Block otherBlock : otherBlocks) {
|
for (Block otherBlock : otherBlocks) {
|
||||||
registerBlock(otherBlock, this);
|
registerBlock(otherBlock, this);
|
||||||
}
|
}
|
||||||
|
treatLikeBlock = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use this constructor if the item name is not the same as its primary block
|
// Use this constructor if the item name is not the same as its primary block
|
||||||
|
@ -46,5 +50,14 @@ public class BlockItem extends Item {
|
||||||
for (Block otherBlock : otherBlocks) {
|
for (Block otherBlock : otherBlocks) {
|
||||||
registerBlock(otherBlock, this);
|
registerBlock(otherBlock, this);
|
||||||
}
|
}
|
||||||
|
treatLikeBlock = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String translationKey() {
|
||||||
|
if (!treatLikeBlock) {
|
||||||
|
return super.translationKey();
|
||||||
|
}
|
||||||
|
return "block." + this.javaIdentifier.namespace() + "." + this.javaIdentifier.value();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.item.type;
|
package org.geysermc.geyser.item.type;
|
||||||
|
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
@ -59,7 +60,7 @@ import java.util.Map;
|
||||||
|
|
||||||
public class Item {
|
public class Item {
|
||||||
private static final Map<Block, Item> BLOCK_TO_ITEM = new HashMap<>();
|
private static final Map<Block, Item> BLOCK_TO_ITEM = new HashMap<>();
|
||||||
private final String javaIdentifier;
|
protected final Key javaIdentifier;
|
||||||
private int javaId = -1;
|
private int javaId = -1;
|
||||||
private final int stackSize;
|
private final int stackSize;
|
||||||
private final int attackDamage;
|
private final int attackDamage;
|
||||||
|
@ -68,7 +69,7 @@ public class Item {
|
||||||
private final boolean glint;
|
private final boolean glint;
|
||||||
|
|
||||||
public Item(String javaIdentifier, Builder builder) {
|
public Item(String javaIdentifier, Builder builder) {
|
||||||
this.javaIdentifier = MinecraftKey.key(javaIdentifier).asString().intern();
|
this.javaIdentifier = MinecraftKey.key(javaIdentifier);
|
||||||
this.stackSize = builder.stackSize;
|
this.stackSize = builder.stackSize;
|
||||||
this.maxDamage = builder.maxDamage;
|
this.maxDamage = builder.maxDamage;
|
||||||
this.attackDamage = builder.attackDamage;
|
this.attackDamage = builder.attackDamage;
|
||||||
|
@ -77,7 +78,7 @@ public class Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String javaIdentifier() {
|
public String javaIdentifier() {
|
||||||
return javaIdentifier;
|
return javaIdentifier.asString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int javaId() {
|
public int javaId() {
|
||||||
|
@ -108,6 +109,10 @@ public class Item {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String translationKey() {
|
||||||
|
return "item." + javaIdentifier.namespace() + "." + javaIdentifier.value();
|
||||||
|
}
|
||||||
|
|
||||||
/* Translation methods to Bedrock and back */
|
/* Translation methods to Bedrock and back */
|
||||||
|
|
||||||
public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
||||||
|
|
|
@ -25,15 +25,17 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.level;
|
package org.geysermc.geyser.level;
|
||||||
|
|
||||||
|
import net.kyori.adventure.key.Key;
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
|
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
|
||||||
|
import org.geysermc.geyser.util.DimensionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the information we store from the current Java dimension
|
* Represents the information we store from the current Java dimension
|
||||||
* @param piglinSafe Whether piglins and hoglins are safe from conversion in this dimension.
|
* @param piglinSafe Whether piglins and hoglins are safe from conversion in this dimension.
|
||||||
* This controls if they have the shaking effect applied in the dimension.
|
* This controls if they have the shaking effect applied in the dimension.
|
||||||
*/
|
*/
|
||||||
public record JavaDimension(int minY, int maxY, boolean piglinSafe, double worldCoordinateScale) {
|
public record JavaDimension(int minY, int maxY, boolean piglinSafe, double worldCoordinateScale, int bedrockId, boolean isNetherLike) {
|
||||||
|
|
||||||
public static JavaDimension read(RegistryEntryContext entry) {
|
public static JavaDimension read(RegistryEntryContext entry) {
|
||||||
NbtMap dimension = entry.data();
|
NbtMap dimension = entry.data();
|
||||||
|
@ -46,6 +48,22 @@ public record JavaDimension(int minY, int maxY, boolean piglinSafe, double world
|
||||||
// Load world coordinate scale for the world border
|
// Load world coordinate scale for the world border
|
||||||
double coordinateScale = dimension.getDouble("coordinate_scale");
|
double coordinateScale = dimension.getDouble("coordinate_scale");
|
||||||
|
|
||||||
return new JavaDimension(minY, maxY, piglinSafe, coordinateScale);
|
boolean isNetherLike;
|
||||||
|
// Cache the Bedrock version of this dimension, and base it off the ID - THE ID CAN CHANGE!!!
|
||||||
|
// https://github.com/GeyserMC/Geyser/issues/4837
|
||||||
|
int bedrockId;
|
||||||
|
Key id = entry.id();
|
||||||
|
if ("minecraft".equals(id.namespace())) {
|
||||||
|
String identifier = id.asString();
|
||||||
|
bedrockId = DimensionUtils.javaToBedrock(identifier);
|
||||||
|
isNetherLike = DimensionUtils.NETHER_IDENTIFIER.equals(identifier);
|
||||||
|
} else {
|
||||||
|
// Effects should give is a clue on how this (custom) dimension is supposed to look like
|
||||||
|
String effects = dimension.getString("effects");
|
||||||
|
bedrockId = DimensionUtils.javaToBedrock(effects);
|
||||||
|
isNetherLike = DimensionUtils.NETHER_IDENTIFIER.equals(effects);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JavaDimension(minY, maxY, piglinSafe, coordinateScale, bedrockId, isNetherLike);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -381,8 +381,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||||
* The dimension of the player.
|
* The dimension of the player.
|
||||||
* As all entities are in the same world, this can be safely applied to all other entities.
|
* As all entities are in the same world, this can be safely applied to all other entities.
|
||||||
*/
|
*/
|
||||||
@Setter
|
|
||||||
private int dimension = DimensionUtils.OVERWORLD;
|
|
||||||
@MonotonicNonNull
|
@MonotonicNonNull
|
||||||
@Setter
|
@Setter
|
||||||
private JavaDimension dimensionType = null;
|
private JavaDimension dimensionType = null;
|
||||||
|
|
|
@ -27,12 +27,14 @@ package org.geysermc.geyser.translator.protocol.bedrock;
|
||||||
|
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.ServerSettingsRequestPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.ServerSettingsRequestPacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.ServerSettingsResponsePacket;
|
import org.cloudburstmc.protocol.bedrock.packet.ServerSettingsResponsePacket;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.SetDifficultyPacket;
|
||||||
import org.geysermc.cumulus.form.CustomForm;
|
import org.geysermc.cumulus.form.CustomForm;
|
||||||
import org.geysermc.cumulus.form.impl.FormDefinitions;
|
import org.geysermc.cumulus.form.impl.FormDefinitions;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
import org.geysermc.geyser.translator.protocol.Translator;
|
import org.geysermc.geyser.translator.protocol.Translator;
|
||||||
import org.geysermc.geyser.util.SettingsUtils;
|
import org.geysermc.geyser.util.SettingsUtils;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.data.game.setting.Difficulty;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@ -47,6 +49,14 @@ public class BedrockServerSettingsRequestTranslator extends PacketTranslator<Ser
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Peaceful difficulty allows always eating food - hence, we just do not send it to Bedrock.
|
||||||
|
// However, in order for server settings to show it properly, let's revert while we are in the menu!
|
||||||
|
if (session.getWorldCache().getDifficulty() == Difficulty.PEACEFUL) {
|
||||||
|
SetDifficultyPacket setDifficultyPacket = new SetDifficultyPacket();
|
||||||
|
setDifficultyPacket.setDifficulty(Difficulty.PEACEFUL.ordinal());
|
||||||
|
session.sendUpstreamPacket(setDifficultyPacket);
|
||||||
|
}
|
||||||
|
|
||||||
CustomForm form = SettingsUtils.buildForm(session);
|
CustomForm form = SettingsUtils.buildForm(session);
|
||||||
int formId = session.getFormCache().addForm(form);
|
int formId = session.getFormCache().addForm(form);
|
||||||
|
|
||||||
|
|
|
@ -25,21 +25,29 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.translator.protocol.java;
|
package org.geysermc.geyser.translator.protocol.java;
|
||||||
|
|
||||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundChangeDifficultyPacket;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.SetDifficultyPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.SetDifficultyPacket;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
import org.geysermc.geyser.translator.protocol.Translator;
|
import org.geysermc.geyser.translator.protocol.Translator;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.data.game.setting.Difficulty;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundChangeDifficultyPacket;
|
||||||
|
|
||||||
@Translator(packet = ClientboundChangeDifficultyPacket.class)
|
@Translator(packet = ClientboundChangeDifficultyPacket.class)
|
||||||
public class JavaChangeDifficultyTranslator extends PacketTranslator<ClientboundChangeDifficultyPacket> {
|
public class JavaChangeDifficultyTranslator extends PacketTranslator<ClientboundChangeDifficultyPacket> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(GeyserSession session, ClientboundChangeDifficultyPacket packet) {
|
public void translate(GeyserSession session, ClientboundChangeDifficultyPacket packet) {
|
||||||
|
Difficulty difficulty = packet.getDifficulty();
|
||||||
|
session.getWorldCache().setDifficulty(difficulty);
|
||||||
|
|
||||||
|
// Peaceful difficulty allows always eating food - hence, we just do not send it to Bedrock.
|
||||||
|
if (difficulty == Difficulty.PEACEFUL) {
|
||||||
|
difficulty = Difficulty.EASY;
|
||||||
|
}
|
||||||
|
|
||||||
SetDifficultyPacket setDifficultyPacket = new SetDifficultyPacket();
|
SetDifficultyPacket setDifficultyPacket = new SetDifficultyPacket();
|
||||||
setDifficultyPacket.setDifficulty(packet.getDifficulty().ordinal());
|
setDifficultyPacket.setDifficulty(difficulty.ordinal());
|
||||||
session.sendUpstreamPacket(setDifficultyPacket);
|
session.sendUpstreamPacket(setDifficultyPacket);
|
||||||
|
|
||||||
session.getWorldCache().setDifficulty(packet.getDifficulty());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.translator.protocol.java;
|
||||||
|
|
||||||
import net.kyori.adventure.key.Key;
|
import net.kyori.adventure.key.Key;
|
||||||
import org.geysermc.erosion.Constants;
|
import org.geysermc.erosion.Constants;
|
||||||
|
import org.geysermc.geyser.level.JavaDimension;
|
||||||
import org.geysermc.geyser.util.MinecraftKey;
|
import org.geysermc.geyser.util.MinecraftKey;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerSpawnInfo;
|
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerSpawnInfo;
|
||||||
import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket;
|
import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket;
|
||||||
|
@ -65,12 +66,15 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerSpawnInfo spawnInfo = packet.getCommonPlayerSpawnInfo();
|
PlayerSpawnInfo spawnInfo = packet.getCommonPlayerSpawnInfo();
|
||||||
|
JavaDimension newDimension = session.getRegistryCache().dimensions().byId(spawnInfo.getDimension());
|
||||||
|
boolean forceDimSwitch = false;
|
||||||
|
|
||||||
// If the player is already initialized and a join game packet is sent, they
|
// If the player is already initialized and a join game packet is sent, they
|
||||||
// are swapping servers
|
// are swapping servers
|
||||||
if (session.isSpawned()) {
|
if (session.isSpawned()) {
|
||||||
int fakeDim = DimensionUtils.getTemporaryDimension(session.getDimension(), spawnInfo.getDimension());
|
int fakeDim = DimensionUtils.getTemporaryDimension(session.getDimensionType().bedrockId(), newDimension.bedrockId());
|
||||||
DimensionUtils.switchDimension(session, fakeDim);
|
DimensionUtils.fastSwitchDimension(session, fakeDim);
|
||||||
|
forceDimSwitch = true;
|
||||||
|
|
||||||
session.getWorldCache().removeScoreboard();
|
session.getWorldCache().removeScoreboard();
|
||||||
|
|
||||||
|
@ -84,13 +88,12 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
|
||||||
session.setWorldName(spawnInfo.getWorldName());
|
session.setWorldName(spawnInfo.getWorldName());
|
||||||
session.setLevels(Arrays.stream(packet.getWorldNames()).map(Key::asString).toArray(String[]::new));
|
session.setLevels(Arrays.stream(packet.getWorldNames()).map(Key::asString).toArray(String[]::new));
|
||||||
session.setGameMode(spawnInfo.getGameMode());
|
session.setGameMode(spawnInfo.getGameMode());
|
||||||
int newDimension = spawnInfo.getDimension();
|
|
||||||
|
|
||||||
boolean needsSpawnPacket = !session.isSentSpawnPacket();
|
boolean needsSpawnPacket = !session.isSentSpawnPacket();
|
||||||
if (needsSpawnPacket) {
|
if (needsSpawnPacket) {
|
||||||
// The player has yet to spawn so let's do that using some of the information in this Java packet
|
// The player has yet to spawn so let's do that using some of the information in this Java packet
|
||||||
session.setDimension(newDimension);
|
session.setDimensionType(newDimension);
|
||||||
DimensionUtils.setBedrockDimension(session, newDimension);
|
DimensionUtils.setBedrockDimension(session, newDimension.bedrockId());
|
||||||
session.connect();
|
session.connect();
|
||||||
|
|
||||||
// It is now safe to send these packets
|
// It is now safe to send these packets
|
||||||
|
@ -124,9 +127,9 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
|
||||||
}
|
}
|
||||||
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(register, Constants.PLUGIN_MESSAGE.getBytes(StandardCharsets.UTF_8)));
|
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(register, Constants.PLUGIN_MESSAGE.getBytes(StandardCharsets.UTF_8)));
|
||||||
|
|
||||||
if (newDimension != session.getDimension()) {
|
if (newDimension != session.getDimensionType() || forceDimSwitch) {
|
||||||
DimensionUtils.switchDimension(session, newDimension);
|
DimensionUtils.switchDimension(session, newDimension);
|
||||||
} else if (DimensionUtils.isCustomBedrockNetherId() && newDimension == DimensionUtils.NETHER) {
|
} else if (DimensionUtils.isCustomBedrockNetherId() && newDimension.isNetherLike()) {
|
||||||
// If the player is spawning into the "fake" nether, send them some fog
|
// If the player is spawning into the "fake" nether, send them some fog
|
||||||
session.camera().sendFog(DimensionUtils.BEDROCK_FOG_HELL);
|
session.camera().sendFog(DimensionUtils.BEDROCK_FOG_HELL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.SetPlayerGameTypePacket;
|
import org.cloudburstmc.protocol.bedrock.packet.SetPlayerGameTypePacket;
|
||||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||||
|
import org.geysermc.geyser.level.JavaDimension;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
|
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
|
||||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
|
@ -92,12 +93,12 @@ public class JavaRespawnTranslator extends PacketTranslator<ClientboundRespawnPa
|
||||||
session.setThunder(false);
|
session.setThunder(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
int newDimension = spawnInfo.getDimension();
|
JavaDimension newDimension = session.getRegistryCache().dimensions().byId(spawnInfo.getDimension());
|
||||||
if (session.getDimension() != newDimension || !spawnInfo.getWorldName().equals(session.getWorldName())) {
|
if (session.getDimensionType() != newDimension || !spawnInfo.getWorldName().equals(session.getWorldName())) {
|
||||||
// Switching to a new world (based off the world name change or new dimension); send a fake dimension change
|
// Switching to a new world (based off the world name change or new dimension); send a fake dimension change
|
||||||
if (DimensionUtils.javaToBedrock(session.getDimension()) == DimensionUtils.javaToBedrock(newDimension)) {
|
if (session.getDimensionType().bedrockId() == newDimension.bedrockId()) {
|
||||||
int fakeDim = DimensionUtils.getTemporaryDimension(session.getDimension(), newDimension);
|
int fakeDim = DimensionUtils.getTemporaryDimension(session.getDimensionType().bedrockId(), newDimension.bedrockId());
|
||||||
DimensionUtils.switchDimension(session, fakeDim);
|
DimensionUtils.fastSwitchDimension(session, fakeDim);
|
||||||
}
|
}
|
||||||
session.setWorldName(spawnInfo.getWorldName());
|
session.setWorldName(spawnInfo.getWorldName());
|
||||||
DimensionUtils.switchDimension(session, newDimension);
|
DimensionUtils.switchDimension(session, newDimension);
|
||||||
|
|
|
@ -253,7 +253,8 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
|
||||||
// We can get the correct order for button pressing
|
// We can get the correct order for button pressing
|
||||||
data.getValue().sort(Comparator.comparing((stoneCuttingRecipeData ->
|
data.getValue().sort(Comparator.comparing((stoneCuttingRecipeData ->
|
||||||
Registries.JAVA_ITEMS.get().get(stoneCuttingRecipeData.getResult().getId())
|
Registries.JAVA_ITEMS.get().get(stoneCuttingRecipeData.getResult().getId())
|
||||||
.javaIdentifier())));
|
// See RecipeManager#getRecipesFor as of 1.21
|
||||||
|
.translationKey())));
|
||||||
|
|
||||||
// Now that it's sorted, let's translate these recipes
|
// Now that it's sorted, let's translate these recipes
|
||||||
int buttonId = 0;
|
int buttonId = 0;
|
||||||
|
|
|
@ -92,7 +92,7 @@ public class JavaAnimateTranslator extends PacketTranslator<ClientboundAnimatePa
|
||||||
// Spawn custom particle
|
// Spawn custom particle
|
||||||
SpawnParticleEffectPacket stringPacket = new SpawnParticleEffectPacket();
|
SpawnParticleEffectPacket stringPacket = new SpawnParticleEffectPacket();
|
||||||
stringPacket.setIdentifier("geyseropt:enchanted_hit_multiple");
|
stringPacket.setIdentifier("geyseropt:enchanted_hit_multiple");
|
||||||
stringPacket.setDimensionId(DimensionUtils.javaToBedrock(session.getDimension()));
|
stringPacket.setDimensionId(DimensionUtils.javaToBedrock(session));
|
||||||
stringPacket.setPosition(Vector3f.ZERO);
|
stringPacket.setPosition(Vector3f.ZERO);
|
||||||
stringPacket.setUniqueEntityId(entity.getGeyserId());
|
stringPacket.setUniqueEntityId(entity.getGeyserId());
|
||||||
stringPacket.setMolangVariablesJson(Optional.empty());
|
stringPacket.setMolangVariablesJson(Optional.empty());
|
||||||
|
|
|
@ -28,7 +28,6 @@ package org.geysermc.geyser.translator.protocol.java.level;
|
||||||
import org.cloudburstmc.math.vector.Vector3f;
|
import org.cloudburstmc.math.vector.Vector3f;
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
|
|
||||||
import org.cloudburstmc.protocol.bedrock.data.ParticleType;
|
import org.cloudburstmc.protocol.bedrock.data.ParticleType;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.LevelEventGenericPacket;
|
import org.cloudburstmc.protocol.bedrock.packet.LevelEventGenericPacket;
|
||||||
|
@ -481,7 +480,7 @@ public class JavaLevelEventTranslator extends PacketTranslator<ClientboundLevelE
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void spawnOminousTrialSpawnerParticles(GeyserSession session, Vector3f pos) {
|
private static void spawnOminousTrialSpawnerParticles(GeyserSession session, Vector3f pos) {
|
||||||
int dimensionId = DimensionUtils.javaToBedrock(session.getDimension());
|
int dimensionId = DimensionUtils.javaToBedrock(session);
|
||||||
SpawnParticleEffectPacket stringPacket = new SpawnParticleEffectPacket();
|
SpawnParticleEffectPacket stringPacket = new SpawnParticleEffectPacket();
|
||||||
stringPacket.setIdentifier("minecraft:trial_spawner_detection_ominous");
|
stringPacket.setIdentifier("minecraft:trial_spawner_detection_ominous");
|
||||||
stringPacket.setDimensionId(dimensionId);
|
stringPacket.setDimensionId(dimensionId);
|
||||||
|
|
|
@ -191,7 +191,7 @@ public class JavaLevelParticlesTranslator extends PacketTranslator<ClientboundLe
|
||||||
return packet;
|
return packet;
|
||||||
};
|
};
|
||||||
} else if (particleMapping.identifier() != null) {
|
} else if (particleMapping.identifier() != null) {
|
||||||
int dimensionId = DimensionUtils.javaToBedrock(session.getDimension());
|
int dimensionId = DimensionUtils.javaToBedrock(session);
|
||||||
return (position) -> {
|
return (position) -> {
|
||||||
SpawnParticleEffectPacket stringPacket = new SpawnParticleEffectPacket();
|
SpawnParticleEffectPacket stringPacket = new SpawnParticleEffectPacket();
|
||||||
stringPacket.setIdentifier(particleMapping.identifier());
|
stringPacket.setIdentifier(particleMapping.identifier());
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class JavaMapItemDataTranslator extends PacketTranslator<ClientboundMapIt
|
||||||
org.cloudburstmc.protocol.bedrock.packet.ClientboundMapItemDataPacket mapItemDataPacket = new org.cloudburstmc.protocol.bedrock.packet.ClientboundMapItemDataPacket();
|
org.cloudburstmc.protocol.bedrock.packet.ClientboundMapItemDataPacket mapItemDataPacket = new org.cloudburstmc.protocol.bedrock.packet.ClientboundMapItemDataPacket();
|
||||||
|
|
||||||
mapItemDataPacket.setUniqueMapId(packet.getMapId());
|
mapItemDataPacket.setUniqueMapId(packet.getMapId());
|
||||||
mapItemDataPacket.setDimensionId(DimensionUtils.javaToBedrock(session.getDimension()));
|
mapItemDataPacket.setDimensionId(DimensionUtils.javaToBedrock(session));
|
||||||
mapItemDataPacket.setLocked(packet.isLocked());
|
mapItemDataPacket.setLocked(packet.isLocked());
|
||||||
mapItemDataPacket.setOrigin(Vector3i.ZERO); // Required since 1.19.20
|
mapItemDataPacket.setOrigin(Vector3i.ZERO); // Required since 1.19.20
|
||||||
mapItemDataPacket.setScale(packet.getScale());
|
mapItemDataPacket.setScale(packet.getScale());
|
||||||
|
|
|
@ -39,7 +39,7 @@ public class JavaSetDefaultSpawnPositionTranslator extends PacketTranslator<Clie
|
||||||
public void translate(GeyserSession session, ClientboundSetDefaultSpawnPositionPacket packet) {
|
public void translate(GeyserSession session, ClientboundSetDefaultSpawnPositionPacket packet) {
|
||||||
SetSpawnPositionPacket spawnPositionPacket = new SetSpawnPositionPacket();
|
SetSpawnPositionPacket spawnPositionPacket = new SetSpawnPositionPacket();
|
||||||
spawnPositionPacket.setBlockPosition(packet.getPosition());
|
spawnPositionPacket.setBlockPosition(packet.getPosition());
|
||||||
spawnPositionPacket.setDimensionId(DimensionUtils.javaToBedrock(session.getDimension()));
|
spawnPositionPacket.setDimensionId(DimensionUtils.javaToBedrock(session));
|
||||||
spawnPositionPacket.setSpawnType(SetSpawnPositionPacket.Type.WORLD_SPAWN);
|
spawnPositionPacket.setSpawnType(SetSpawnPositionPacket.Type.WORLD_SPAWN);
|
||||||
session.sendUpstreamPacket(spawnPositionPacket);
|
session.sendUpstreamPacket(spawnPositionPacket);
|
||||||
}
|
}
|
||||||
|
|
|
@ -203,8 +203,7 @@ public class ChunkUtils {
|
||||||
* This must be done after the player has switched dimensions so we know what their dimension is
|
* This must be done after the player has switched dimensions so we know what their dimension is
|
||||||
*/
|
*/
|
||||||
public static void loadDimension(GeyserSession session) {
|
public static void loadDimension(GeyserSession session) {
|
||||||
JavaDimension dimension = session.getRegistryCache().dimensions().byId(session.getDimension());
|
JavaDimension dimension = session.getDimensionType();
|
||||||
session.setDimensionType(dimension);
|
|
||||||
int minY = dimension.minY();
|
int minY = dimension.minY();
|
||||||
int maxY = dimension.maxY();
|
int maxY = dimension.maxY();
|
||||||
|
|
||||||
|
@ -223,7 +222,7 @@ public class ChunkUtils {
|
||||||
session.getGeyser().getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.network.translator.chunk.out_of_bounds",
|
session.getGeyser().getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.network.translator.chunk.out_of_bounds",
|
||||||
String.valueOf(bedrockDimension.minY()),
|
String.valueOf(bedrockDimension.minY()),
|
||||||
String.valueOf(bedrockDimension.height()),
|
String.valueOf(bedrockDimension.height()),
|
||||||
session.getDimension()));
|
session.getRegistryCache().dimensions().byValue(session.getDimensionType())));
|
||||||
}
|
}
|
||||||
|
|
||||||
session.getChunkCache().setMinY(minY);
|
session.getChunkCache().setMinY(minY);
|
||||||
|
|
|
@ -29,9 +29,15 @@ import org.cloudburstmc.math.vector.Vector3f;
|
||||||
import org.cloudburstmc.math.vector.Vector3i;
|
import org.cloudburstmc.math.vector.Vector3i;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
|
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.PlayerActionType;
|
import org.cloudburstmc.protocol.bedrock.data.PlayerActionType;
|
||||||
import org.cloudburstmc.protocol.bedrock.packet.*;
|
import org.cloudburstmc.protocol.bedrock.packet.ChangeDimensionPacket;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.ChunkRadiusUpdatedPacket;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.MobEffectPacket;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.PlayerActionPacket;
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.StopSoundPacket;
|
||||||
import org.geysermc.geyser.entity.type.Entity;
|
import org.geysermc.geyser.entity.type.Entity;
|
||||||
import org.geysermc.geyser.level.BedrockDimension;
|
import org.geysermc.geyser.level.BedrockDimension;
|
||||||
|
import org.geysermc.geyser.level.JavaDimension;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect;
|
import org.geysermc.mcprotocollib.protocol.data.game.entity.Effect;
|
||||||
|
|
||||||
|
@ -44,22 +50,18 @@ public class DimensionUtils {
|
||||||
|
|
||||||
public static final String BEDROCK_FOG_HELL = "minecraft:fog_hell";
|
public static final String BEDROCK_FOG_HELL = "minecraft:fog_hell";
|
||||||
|
|
||||||
/**
|
public static final String NETHER_IDENTIFIER = "minecraft:the_nether";
|
||||||
* String reference to vanilla Java overworld dimension identifier
|
|
||||||
*/
|
|
||||||
public static final int OVERWORLD = 0;
|
|
||||||
/**
|
|
||||||
* String reference to vanilla Java nether dimension identifier
|
|
||||||
*/
|
|
||||||
public static final int NETHER = 3;
|
|
||||||
/**
|
|
||||||
* String reference to vanilla Java end dimension identifier
|
|
||||||
*/
|
|
||||||
public static final int THE_END = 2;
|
|
||||||
|
|
||||||
public static void switchDimension(GeyserSession session, int javaDimension) {
|
private static final int BEDROCK_OVERWORLD_ID = 0;
|
||||||
int bedrockDimension = javaToBedrock(javaDimension); // new bedrock dimension
|
private static final int BEDROCK_DEFAULT_NETHER_ID = 1;
|
||||||
int previousDimension = session.getDimension(); // previous java dimension
|
private static final int BEDROCK_END_ID = 2;
|
||||||
|
|
||||||
|
public static void switchDimension(GeyserSession session, JavaDimension javaDimension) {
|
||||||
|
switchDimension(session, javaDimension, javaDimension.bedrockId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void switchDimension(GeyserSession session, JavaDimension javaDimension, int bedrockDimension) {
|
||||||
|
JavaDimension previousDimension = session.getDimensionType(); // previous java dimension
|
||||||
|
|
||||||
Entity player = session.getPlayerEntity();
|
Entity player = session.getPlayerEntity();
|
||||||
|
|
||||||
|
@ -70,35 +72,9 @@ public class DimensionUtils {
|
||||||
session.getPistonCache().clear();
|
session.getPistonCache().clear();
|
||||||
session.getSkullCache().clear();
|
session.getSkullCache().clear();
|
||||||
|
|
||||||
if (session.getServerRenderDistance() > 32 && !session.isEmulatePost1_13Logic()) {
|
changeDimension(session, bedrockDimension);
|
||||||
// The server-sided view distance wasn't a thing until Minecraft Java 1.14
|
|
||||||
// So ViaVersion compensates by sending a "view distance" of 64
|
|
||||||
// That's fine, except when the actual view distance sent from the server is five chunks
|
|
||||||
// The client locks up when switching dimensions, expecting more chunks than it's getting
|
|
||||||
// To solve this, we cap at 32 unless we know that the render distance actually exceeds 32
|
|
||||||
// Also, as of 1.19: PS4 crashes with a ChunkRadiusUpdatedPacket too large
|
|
||||||
session.getGeyser().getLogger().debug("Applying dimension switching workaround for Bedrock render distance of "
|
|
||||||
+ session.getServerRenderDistance());
|
|
||||||
ChunkRadiusUpdatedPacket chunkRadiusUpdatedPacket = new ChunkRadiusUpdatedPacket();
|
|
||||||
chunkRadiusUpdatedPacket.setRadius(32);
|
|
||||||
session.sendUpstreamPacket(chunkRadiusUpdatedPacket);
|
|
||||||
// Will be re-adjusted on spawn
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3f pos = Vector3f.from(0, Short.MAX_VALUE, 0);
|
session.setDimensionType(javaDimension);
|
||||||
|
|
||||||
ChangeDimensionPacket changeDimensionPacket = new ChangeDimensionPacket();
|
|
||||||
changeDimensionPacket.setDimension(bedrockDimension);
|
|
||||||
changeDimensionPacket.setRespawn(true);
|
|
||||||
changeDimensionPacket.setPosition(pos);
|
|
||||||
session.sendUpstreamPacket(changeDimensionPacket);
|
|
||||||
|
|
||||||
session.setDimension(javaDimension);
|
|
||||||
setBedrockDimension(session, javaDimension);
|
|
||||||
|
|
||||||
player.setPosition(pos);
|
|
||||||
session.setSpawned(false);
|
|
||||||
session.setLastChunkPosition(null);
|
|
||||||
|
|
||||||
Set<Effect> entityEffects = session.getEffectCache().getEntityEffects();
|
Set<Effect> entityEffects = session.getEffectCache().getEntityEffects();
|
||||||
for (Effect effect : entityEffects) {
|
for (Effect effect : entityEffects) {
|
||||||
|
@ -125,6 +101,60 @@ public class DimensionUtils {
|
||||||
session.sendUpstreamPacket(stopThunderPacket);
|
session.sendUpstreamPacket(stopThunderPacket);
|
||||||
session.setThunder(false);
|
session.setThunder(false);
|
||||||
|
|
||||||
|
finalizeDimensionSwitch(session, player);
|
||||||
|
|
||||||
|
// If the bedrock nether height workaround is enabled, meaning the client is told it's in the end dimension,
|
||||||
|
// we check if the player is entering the nether and apply the nether fog to fake the fact that the client
|
||||||
|
// thinks they are in the end dimension.
|
||||||
|
if (isCustomBedrockNetherId()) {
|
||||||
|
if (javaDimension.isNetherLike()) {
|
||||||
|
session.camera().sendFog(BEDROCK_FOG_HELL);
|
||||||
|
} else if (previousDimension.isNetherLike()) {
|
||||||
|
session.camera().removeFog(BEDROCK_FOG_HELL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch dimensions without clearing internal logic.
|
||||||
|
*/
|
||||||
|
public static void fastSwitchDimension(GeyserSession session, int bedrockDimension) {
|
||||||
|
changeDimension(session, bedrockDimension);
|
||||||
|
finalizeDimensionSwitch(session, session.getPlayerEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void changeDimension(GeyserSession session, int bedrockDimension) {
|
||||||
|
if (session.getServerRenderDistance() > 32 && !session.isEmulatePost1_13Logic()) {
|
||||||
|
// The server-sided view distance wasn't a thing until Minecraft Java 1.14
|
||||||
|
// So ViaVersion compensates by sending a "view distance" of 64
|
||||||
|
// That's fine, except when the actual view distance sent from the server is five chunks
|
||||||
|
// The client locks up when switching dimensions, expecting more chunks than it's getting
|
||||||
|
// To solve this, we cap at 32 unless we know that the render distance actually exceeds 32
|
||||||
|
// Also, as of 1.19: PS4 crashes with a ChunkRadiusUpdatedPacket too large
|
||||||
|
session.getGeyser().getLogger().debug("Applying dimension switching workaround for Bedrock render distance of "
|
||||||
|
+ session.getServerRenderDistance());
|
||||||
|
ChunkRadiusUpdatedPacket chunkRadiusUpdatedPacket = new ChunkRadiusUpdatedPacket();
|
||||||
|
chunkRadiusUpdatedPacket.setRadius(32);
|
||||||
|
session.sendUpstreamPacket(chunkRadiusUpdatedPacket);
|
||||||
|
// Will be re-adjusted on spawn
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f pos = Vector3f.from(0, Short.MAX_VALUE, 0);
|
||||||
|
|
||||||
|
ChangeDimensionPacket changeDimensionPacket = new ChangeDimensionPacket();
|
||||||
|
changeDimensionPacket.setDimension(bedrockDimension);
|
||||||
|
changeDimensionPacket.setRespawn(true);
|
||||||
|
changeDimensionPacket.setPosition(pos);
|
||||||
|
session.sendUpstreamPacket(changeDimensionPacket);
|
||||||
|
|
||||||
|
setBedrockDimension(session, bedrockDimension);
|
||||||
|
|
||||||
|
session.getPlayerEntity().setPosition(pos);
|
||||||
|
session.setSpawned(false);
|
||||||
|
session.setLastChunkPosition(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void finalizeDimensionSwitch(GeyserSession session, Entity player) {
|
||||||
//let java server handle portal travel sound
|
//let java server handle portal travel sound
|
||||||
StopSoundPacket stopSoundPacket = new StopSoundPacket();
|
StopSoundPacket stopSoundPacket = new StopSoundPacket();
|
||||||
stopSoundPacket.setStoppingAllSound(true);
|
stopSoundPacket.setStoppingAllSound(true);
|
||||||
|
@ -145,23 +175,12 @@ public class DimensionUtils {
|
||||||
// TODO - fix this hack of a fix by sending the final dimension switching logic after sections have been sent.
|
// TODO - fix this hack of a fix by sending the final dimension switching logic after sections have been sent.
|
||||||
// The client wants sections sent to it before it can successfully respawn.
|
// The client wants sections sent to it before it can successfully respawn.
|
||||||
ChunkUtils.sendEmptyChunks(session, player.getPosition().toInt(), 3, true);
|
ChunkUtils.sendEmptyChunks(session, player.getPosition().toInt(), 3, true);
|
||||||
|
|
||||||
// If the bedrock nether height workaround is enabled, meaning the client is told it's in the end dimension,
|
|
||||||
// we check if the player is entering the nether and apply the nether fog to fake the fact that the client
|
|
||||||
// thinks they are in the end dimension.
|
|
||||||
if (isCustomBedrockNetherId()) {
|
|
||||||
if (NETHER == javaDimension) {
|
|
||||||
session.camera().sendFog(BEDROCK_FOG_HELL);
|
|
||||||
} else if (NETHER == previousDimension) {
|
|
||||||
session.camera().removeFog(BEDROCK_FOG_HELL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setBedrockDimension(GeyserSession session, int javaDimension) {
|
public static void setBedrockDimension(GeyserSession session, int bedrockDimension) {
|
||||||
session.getChunkCache().setBedrockDimension(switch (javaDimension) {
|
session.getChunkCache().setBedrockDimension(switch (bedrockDimension) {
|
||||||
case DimensionUtils.THE_END -> BedrockDimension.THE_END;
|
case BEDROCK_END_ID -> BedrockDimension.THE_END;
|
||||||
case DimensionUtils.NETHER -> DimensionUtils.isCustomBedrockNetherId() ? BedrockDimension.THE_END : BedrockDimension.THE_NETHER;
|
case BEDROCK_DEFAULT_NETHER_ID -> BedrockDimension.THE_NETHER; // JavaDimension *should* be set to BEDROCK_END_ID if the Nether workaround is enabled.
|
||||||
default -> BedrockDimension.OVERWORLD;
|
default -> BedrockDimension.OVERWORLD;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -170,26 +189,12 @@ public class DimensionUtils {
|
||||||
if (dimension == BedrockDimension.THE_NETHER) {
|
if (dimension == BedrockDimension.THE_NETHER) {
|
||||||
return BEDROCK_NETHER_ID;
|
return BEDROCK_NETHER_ID;
|
||||||
} else if (dimension == BedrockDimension.THE_END) {
|
} else if (dimension == BedrockDimension.THE_END) {
|
||||||
return 2;
|
return BEDROCK_END_ID;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return BEDROCK_OVERWORLD_ID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Map the Java edition dimension IDs to Bedrock edition
|
|
||||||
*
|
|
||||||
* @param javaDimension Dimension ID to convert
|
|
||||||
* @return Converted Bedrock edition dimension ID
|
|
||||||
*/
|
|
||||||
public static int javaToBedrock(int javaDimension) {
|
|
||||||
return switch (javaDimension) {
|
|
||||||
case NETHER -> BEDROCK_NETHER_ID;
|
|
||||||
case THE_END -> 2;
|
|
||||||
default -> 0;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map the Java edition dimension IDs to Bedrock edition
|
* Map the Java edition dimension IDs to Bedrock edition
|
||||||
*
|
*
|
||||||
|
@ -198,12 +203,23 @@ public class DimensionUtils {
|
||||||
*/
|
*/
|
||||||
public static int javaToBedrock(String javaDimension) {
|
public static int javaToBedrock(String javaDimension) {
|
||||||
return switch (javaDimension) {
|
return switch (javaDimension) {
|
||||||
case "minecraft:the_nether" -> BEDROCK_NETHER_ID;
|
case NETHER_IDENTIFIER -> BEDROCK_NETHER_ID;
|
||||||
case "minecraft:the_end" -> 2;
|
case "minecraft:the_end" -> 2;
|
||||||
default -> 0;
|
default -> 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the Bedrock dimension ID, with a safety check if a packet is created before the player is logged/spawned in.
|
||||||
|
*/
|
||||||
|
public static int javaToBedrock(GeyserSession session) {
|
||||||
|
JavaDimension dimension = session.getDimensionType();
|
||||||
|
if (dimension == null) {
|
||||||
|
return BEDROCK_OVERWORLD_ID;
|
||||||
|
}
|
||||||
|
return dimension.bedrockId();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Nether dimension in Bedrock does not permit building above Y128 - the Bedrock above the dimension.
|
* The Nether dimension in Bedrock does not permit building above Y128 - the Bedrock above the dimension.
|
||||||
* This workaround sets the Nether as the End dimension to ignore this limit.
|
* This workaround sets the Nether as the End dimension to ignore this limit.
|
||||||
|
@ -212,28 +228,28 @@ public class DimensionUtils {
|
||||||
*/
|
*/
|
||||||
public static void changeBedrockNetherId(boolean isAboveNetherBedrockBuilding) {
|
public static void changeBedrockNetherId(boolean isAboveNetherBedrockBuilding) {
|
||||||
// Change dimension ID to the End to allow for building above Bedrock
|
// Change dimension ID to the End to allow for building above Bedrock
|
||||||
BEDROCK_NETHER_ID = isAboveNetherBedrockBuilding ? 2 : 1;
|
BEDROCK_NETHER_ID = isAboveNetherBedrockBuilding ? BEDROCK_END_ID : BEDROCK_DEFAULT_NETHER_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the fake, temporary dimension we send clients to so we aren't switching to the same dimension without an additional
|
* Gets the fake, temporary dimension we send clients to so we aren't switching to the same dimension without an additional
|
||||||
* dimension switch.
|
* dimension switch.
|
||||||
*
|
*
|
||||||
* @param currentDimension the current dimension of the player
|
* @param currentBedrockDimension the current dimension of the player
|
||||||
* @param newDimension the new dimension that the player will be transferred to
|
* @param newBedrockDimension the new dimension that the player will be transferred to
|
||||||
* @return the fake dimension to transfer to
|
* @return the Bedrock fake dimension to transfer to
|
||||||
*/
|
*/
|
||||||
public static int getTemporaryDimension(int currentDimension, int newDimension) {
|
public static int getTemporaryDimension(int currentBedrockDimension, int newBedrockDimension) {
|
||||||
if (isCustomBedrockNetherId()) {
|
if (isCustomBedrockNetherId()) {
|
||||||
// Prevents rare instances of Bedrock locking up
|
// Prevents rare instances of Bedrock locking up
|
||||||
return javaToBedrock(newDimension) == 2 ? OVERWORLD : NETHER;
|
return newBedrockDimension == BEDROCK_END_ID ? BEDROCK_OVERWORLD_ID : BEDROCK_END_ID;
|
||||||
}
|
}
|
||||||
// Check current Bedrock dimension and not just the Java dimension.
|
// Check current Bedrock dimension and not just the Java dimension.
|
||||||
// Fixes rare instances like https://github.com/GeyserMC/Geyser/issues/3161
|
// Fixes rare instances like https://github.com/GeyserMC/Geyser/issues/3161
|
||||||
return javaToBedrock(currentDimension) == 0 ? NETHER : OVERWORLD;
|
return currentBedrockDimension == BEDROCK_OVERWORLD_ID ? BEDROCK_DEFAULT_NETHER_ID : BEDROCK_OVERWORLD_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isCustomBedrockNetherId() {
|
public static boolean isCustomBedrockNetherId() {
|
||||||
return BEDROCK_NETHER_ID == 2;
|
return BEDROCK_NETHER_ID == BEDROCK_END_ID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
package org.geysermc.geyser.util;
|
package org.geysermc.geyser.util;
|
||||||
|
|
||||||
|
import org.cloudburstmc.protocol.bedrock.packet.SetDifficultyPacket;
|
||||||
import org.geysermc.cumulus.component.DropdownComponent;
|
import org.geysermc.cumulus.component.DropdownComponent;
|
||||||
import org.geysermc.cumulus.form.CustomForm;
|
import org.geysermc.cumulus.form.CustomForm;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
@ -33,6 +34,7 @@ import org.geysermc.geyser.level.WorldManager;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
import org.geysermc.geyser.text.MinecraftLocale;
|
import org.geysermc.geyser.text.MinecraftLocale;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.data.game.setting.Difficulty;
|
||||||
|
|
||||||
public class SettingsUtils {
|
public class SettingsUtils {
|
||||||
/**
|
/**
|
||||||
|
@ -96,6 +98,7 @@ public class SettingsUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.validResultHandler((response) -> {
|
builder.validResultHandler((response) -> {
|
||||||
|
applyDifficultyFix(session);
|
||||||
if (showClientSettings) {
|
if (showClientSettings) {
|
||||||
// Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config.
|
// Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config.
|
||||||
if (showCoordinates) {
|
if (showCoordinates) {
|
||||||
|
@ -134,9 +137,21 @@ public class SettingsUtils {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.closedOrInvalidResultHandler($ -> applyDifficultyFix(session));
|
||||||
|
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void applyDifficultyFix(GeyserSession session) {
|
||||||
|
// Peaceful difficulty allows always eating food - hence, we just do not send it to Bedrock.
|
||||||
|
// Since we sent the real difficulty before opening the server settings form, let's restore it to our workaround here
|
||||||
|
if (session.getWorldCache().getDifficulty() == Difficulty.PEACEFUL) {
|
||||||
|
SetDifficultyPacket setDifficultyPacket = new SetDifficultyPacket();
|
||||||
|
setDifficultyPacket.setDifficulty(Difficulty.EASY.ordinal());
|
||||||
|
session.sendUpstreamPacket(setDifficultyPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static String translateEntry(String key, String locale) {
|
private static String translateEntry(String key, String locale) {
|
||||||
if (key.startsWith("%")) {
|
if (key.startsWith("%")) {
|
||||||
// Bedrock will translate
|
// Bedrock will translate
|
||||||
|
|
|
@ -107,7 +107,7 @@ public class StatisticsUtils {
|
||||||
|
|
||||||
for (Object2IntMap.Entry<Statistic> entry : session.getStatistics().object2IntEntrySet()) {
|
for (Object2IntMap.Entry<Statistic> entry : session.getStatistics().object2IntEntrySet()) {
|
||||||
if (entry.getKey() instanceof BreakItemStatistic statistic) {
|
if (entry.getKey() instanceof BreakItemStatistic statistic) {
|
||||||
String item = itemRegistry.get(statistic.getId()).javaIdentifier();
|
Item item = itemRegistry.get(statistic.getId());
|
||||||
content.add(getItemTranslateKey(item, language) + ": " + entry.getIntValue());
|
content.add(getItemTranslateKey(item, language) + ": " + entry.getIntValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ public class StatisticsUtils {
|
||||||
|
|
||||||
for (Object2IntMap.Entry<Statistic> entry : session.getStatistics().object2IntEntrySet()) {
|
for (Object2IntMap.Entry<Statistic> entry : session.getStatistics().object2IntEntrySet()) {
|
||||||
if (entry.getKey() instanceof CraftItemStatistic statistic) {
|
if (entry.getKey() instanceof CraftItemStatistic statistic) {
|
||||||
String item = itemRegistry.get(statistic.getId()).javaIdentifier();
|
Item item = itemRegistry.get(statistic.getId());
|
||||||
content.add(getItemTranslateKey(item, language) + ": " + entry.getIntValue());
|
content.add(getItemTranslateKey(item, language) + ": " + entry.getIntValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,7 @@ public class StatisticsUtils {
|
||||||
|
|
||||||
for (Object2IntMap.Entry<Statistic> entry : session.getStatistics().object2IntEntrySet()) {
|
for (Object2IntMap.Entry<Statistic> entry : session.getStatistics().object2IntEntrySet()) {
|
||||||
if (entry.getKey() instanceof UseItemStatistic statistic) {
|
if (entry.getKey() instanceof UseItemStatistic statistic) {
|
||||||
String item = itemRegistry.get(statistic.getId()).javaIdentifier();
|
Item item = itemRegistry.get(statistic.getId());
|
||||||
content.add(getItemTranslateKey(item, language) + ": " + entry.getIntValue());
|
content.add(getItemTranslateKey(item, language) + ": " + entry.getIntValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ public class StatisticsUtils {
|
||||||
|
|
||||||
for (Object2IntMap.Entry<Statistic> entry : session.getStatistics().object2IntEntrySet()) {
|
for (Object2IntMap.Entry<Statistic> entry : session.getStatistics().object2IntEntrySet()) {
|
||||||
if (entry.getKey() instanceof PickupItemStatistic statistic) {
|
if (entry.getKey() instanceof PickupItemStatistic statistic) {
|
||||||
String item = itemRegistry.get(statistic.getId()).javaIdentifier();
|
Item item = itemRegistry.get(statistic.getId());
|
||||||
content.add(getItemTranslateKey(item, language) + ": " + entry.getIntValue());
|
content.add(getItemTranslateKey(item, language) + ": " + entry.getIntValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ public class StatisticsUtils {
|
||||||
|
|
||||||
for (Object2IntMap.Entry<Statistic> entry : session.getStatistics().object2IntEntrySet()) {
|
for (Object2IntMap.Entry<Statistic> entry : session.getStatistics().object2IntEntrySet()) {
|
||||||
if (entry.getKey() instanceof DropItemStatistic statistic) {
|
if (entry.getKey() instanceof DropItemStatistic statistic) {
|
||||||
String item = itemRegistry.get(statistic.getId()).javaIdentifier();
|
Item item = itemRegistry.get(statistic.getId());
|
||||||
content.add(getItemTranslateKey(item, language) + ": " + entry.getIntValue());
|
content.add(getItemTranslateKey(item, language) + ": " + entry.getIntValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,14 +208,8 @@ public class StatisticsUtils {
|
||||||
* @param language the language to search in
|
* @param language the language to search in
|
||||||
* @return the full name of the item
|
* @return the full name of the item
|
||||||
*/
|
*/
|
||||||
private static String getItemTranslateKey(String item, String language) {
|
private static String getItemTranslateKey(Item item, String language) {
|
||||||
item = item.replace("minecraft:", "item.minecraft.");
|
return MinecraftLocale.getLocaleString(item.translationKey(), language);
|
||||||
String translatedItem = MinecraftLocale.getLocaleString(item, language);
|
|
||||||
if (translatedItem.equals(item)) {
|
|
||||||
// Didn't translate; must be a block
|
|
||||||
translatedItem = MinecraftLocale.getLocaleString(item.replace("item.", "block."), language);
|
|
||||||
}
|
|
||||||
return translatedItem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String translate(String keys, String locale) {
|
private static String translate(String keys, String locale) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue