3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-10-03 08:21:06 +02:00

Merge branch 'master' of https://github.com/GeyserMC/Geyser into feature/extensions

Dieser Commit ist enthalten in:
Camotoy 2022-04-07 19:22:46 -04:00
Commit 1ebc3fd8f6
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 7EEFB66FE798081F
44 geänderte Dateien mit 611 neuen und 432 gelöschten Zeilen

Datei anzeigen

@ -1,20 +1,25 @@
val paperVersion = "1.17.1-R0.1-SNAPSHOT" // Needed because we do not support Java 17 yet val paperVersion = "1.17.1-R0.1-SNAPSHOT" // Needed because we do not support Java 17 yet
val viaVersion = "4.0.0" val viaVersion = "4.0.0"
val adaptersVersion = "1.4-SNAPSHOT" val adaptersVersion = "1.4-SNAPSHOT"
val commodoreVersion = "1.13"
dependencies { dependencies {
api(projects.core) api(projects.core)
implementation("org.geysermc.geyser.adapters", "spigot-all", adaptersVersion) implementation("org.geysermc.geyser.adapters", "spigot-all", adaptersVersion)
implementation("me.lucko", "commodore", commodoreVersion)
} }
platformRelocate("it.unimi.dsi.fastutil") platformRelocate("it.unimi.dsi.fastutil")
platformRelocate("com.fasterxml.jackson") platformRelocate("com.fasterxml.jackson")
platformRelocate("net.kyori") platformRelocate("net.kyori")
platformRelocate("org.objectweb.asm") platformRelocate("org.objectweb.asm")
platformRelocate("me.lucko.commodore")
// These dependencies are already present on the platform // These dependencies are already present on the platform
provided("io.papermc.paper", "paper-api", paperVersion) provided("io.papermc.paper", "paper-api", paperVersion)
provided("io.papermc.paper", "paper-mojangapi", paperVersion)
provided("com.viaversion", "viaversion", viaVersion) provided("com.viaversion", "viaversion", viaVersion)
application { application {
@ -42,5 +47,8 @@ tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar> {
exclude(dependency("io.netty:netty-codec-dns:.*")) exclude(dependency("io.netty:netty-codec-dns:.*"))
exclude(dependency("io.netty:netty-resolver-dns:.*")) exclude(dependency("io.netty:netty-resolver-dns:.*"))
exclude(dependency("io.netty:netty-resolver-dns-native-macos:.*")) exclude(dependency("io.netty:netty-resolver-dns-native-macos:.*"))
// Commodore includes Brigadier
exclude(dependency("com.mojang:.*"))
} }
} }

Datei anzeigen

@ -29,7 +29,9 @@ import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.data.MappingData; import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry; import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import me.lucko.commodore.CommodoreProvider;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.geysermc.common.PlatformType; import org.geysermc.common.PlatformType;
import org.geysermc.geyser.Constants; import org.geysermc.geyser.Constants;
@ -43,6 +45,7 @@ import org.geysermc.geyser.level.WorldManager;
import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.platform.spigot.command.GeyserBrigadierSupport;
import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandExecutor; import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandExecutor;
import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandManager; import org.geysermc.geyser.platform.spigot.command.GeyserSpigotCommandManager;
import org.geysermc.geyser.platform.spigot.command.SpigotCommandSource; import org.geysermc.geyser.platform.spigot.command.SpigotCommandSource;
@ -235,7 +238,14 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
Bukkit.getServer().getPluginManager().registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this); Bukkit.getServer().getPluginManager().registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this);
this.getCommand("geyser").setExecutor(new GeyserSpigotCommandExecutor(geyser)); PluginCommand pluginCommand = this.getCommand("geyser");
pluginCommand.setExecutor(new GeyserSpigotCommandExecutor(geyser));
boolean brigadierSupported = CommodoreProvider.isSupported();
geyserLogger.debug("Brigadier supported? " + brigadierSupported);
if (brigadierSupported) {
GeyserBrigadierSupport.loadBrigadier(this, pluginCommand);
}
// Check to ensure the current setup can support the protocol version Geyser uses // Check to ensure the current setup can support the protocol version Geyser uses
GeyserSpigotVersionChecker.checkForSupportedProtocol(geyserLogger, isViaVersion); GeyserSpigotVersionChecker.checkForSupportedProtocol(geyserLogger, isViaVersion);

Datei anzeigen

@ -0,0 +1,61 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.spigot.command;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.lucko.commodore.Commodore;
import me.lucko.commodore.CommodoreProvider;
import org.bukkit.Bukkit;
import org.bukkit.command.PluginCommand;
import org.geysermc.geyser.platform.spigot.GeyserSpigotPlugin;
/**
* Needs to be a separate class so pre-1.13 loads correctly.
*/
public final class GeyserBrigadierSupport {
public static void loadBrigadier(GeyserSpigotPlugin plugin, PluginCommand pluginCommand) {
// Enable command completions if supported
// This is beneficial because this is sent over the network and Bedrock can see it
Commodore commodore = CommodoreProvider.getCommodore(plugin);
LiteralArgumentBuilder<?> builder = LiteralArgumentBuilder.literal("geyser");
for (String command : plugin.getGeyserCommandManager().getCommands().keySet()) {
builder.then(LiteralArgumentBuilder.literal(command));
}
commodore.register(pluginCommand, builder);
try {
Class.forName("com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent");
Bukkit.getServer().getPluginManager().registerEvents(new GeyserPaperCommandListener(), plugin);
plugin.getGeyserLogger().debug("Successfully registered AsyncPlayerSendCommandsEvent listener.");
} catch (ClassNotFoundException e) {
plugin.getGeyserLogger().debug("Not registering AsyncPlayerSendCommandsEvent listener.");
}
}
private GeyserBrigadierSupport() {
}
}

Datei anzeigen

@ -0,0 +1,87 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.spigot.command;
import com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent;
import com.mojang.brigadier.tree.CommandNode;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.command.Command;
import java.net.InetSocketAddress;
import java.util.Iterator;
import java.util.Map;
public final class GeyserPaperCommandListener implements Listener {
@EventHandler
@SuppressWarnings("deprecation") // Used to indicate an unstable event
public void onCommandSend(AsyncPlayerSendCommandsEvent<?> event) {
// Documentation says to check (event.isAsynchronous() || !event.hasFiredAsync()), but as of Paper 1.18.2
// event.hasFiredAsync is never true
if (event.isAsynchronous()) {
CommandNode<?> geyserBrigadier = event.getCommandNode().getChild("geyser");
if (geyserBrigadier != null) {
Player player = event.getPlayer();
boolean isJavaPlayer = isProbablyJavaPlayer(player);
Map<String, Command> commands = GeyserImpl.getInstance().commandManager().getCommands();
Iterator<? extends CommandNode<?>> it = geyserBrigadier.getChildren().iterator();
while (it.hasNext()) {
CommandNode<?> subnode = it.next();
Command command = commands.get(subnode.getName());
if (command != null) {
if ((command.isBedrockOnly() && isJavaPlayer) || !player.hasPermission(command.permission())) {
// Remove this from the node as we don't have permission to use it
it.remove();
}
}
}
}
}
}
/**
* This early on, there is a rare chance that Geyser has yet to process the connection. We'll try to minimize that
* chance, though.
*/
private boolean isProbablyJavaPlayer(Player player) {
if (GeyserImpl.getInstance().connectionByUuid(player.getUniqueId()) != null) {
// For sure this is a Bedrock player
return false;
}
if (GeyserImpl.getInstance().getConfig().isUseDirectConnection()) {
InetSocketAddress address = player.getAddress();
if (address != null) {
return address.getPort() != 0;
}
}
return true;
}
}

Datei anzeigen

@ -26,6 +26,7 @@
package org.geysermc.geyser.platform.spigot.command; package org.geysermc.geyser.platform.spigot.command;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandMap; import org.bukkit.command.CommandMap;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
@ -35,16 +36,24 @@ import java.lang.reflect.Field;
public class GeyserSpigotCommandManager extends GeyserCommandManager { public class GeyserSpigotCommandManager extends GeyserCommandManager {
private static CommandMap COMMAND_MAP; private static final CommandMap COMMAND_MAP;
static { static {
CommandMap commandMap = null;
try { try {
Field cmdMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap"); // Paper-only
cmdMapField.setAccessible(true); Server.class.getMethod("getCommandMap");
COMMAND_MAP = (CommandMap) cmdMapField.get(Bukkit.getServer()); commandMap = Bukkit.getServer().getCommandMap();
} catch (NoSuchFieldException | IllegalAccessException ex) { } catch (NoSuchMethodException e) {
ex.printStackTrace(); try {
Field cmdMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
cmdMapField.setAccessible(true);
commandMap = (CommandMap) cmdMapField.get(Bukkit.getServer());
} catch (NoSuchFieldException | IllegalAccessException ex) {
ex.printStackTrace();
}
} }
COMMAND_MAP = commandMap;
} }
public GeyserSpigotCommandManager(GeyserImpl geyser) { public GeyserSpigotCommandManager(GeyserImpl geyser) {

Datei anzeigen

@ -24,7 +24,7 @@
*/ */
object Versions { object Versions {
const val jacksonVersion = "2.12.4" const val jacksonVersion = "2.13.2"
const val fastutilVersion = "8.5.2" const val fastutilVersion = "8.5.2"
const val nettyVersion = "4.1.66.Final" const val nettyVersion = "4.1.66.Final"
const val guavaVersion = "29.0-jre" const val guavaVersion = "29.0-jre"

Datei anzeigen

@ -12,7 +12,7 @@ dependencies {
// Jackson JSON and YAML serialization // Jackson JSON and YAML serialization
api("com.fasterxml.jackson.core", "jackson-annotations", Versions.jacksonVersion) api("com.fasterxml.jackson.core", "jackson-annotations", Versions.jacksonVersion)
api("com.fasterxml.jackson.core", "jackson-databind", Versions.jacksonVersion) api("com.fasterxml.jackson.core", "jackson-databind", Versions.jacksonVersion + ".1") // Extra .1 as databind is a slightly different version
api("com.fasterxml.jackson.dataformat", "jackson-dataformat-yaml", Versions.jacksonVersion) api("com.fasterxml.jackson.dataformat", "jackson-dataformat-yaml", Versions.jacksonVersion)
api("com.google.guava", "guava", Versions.guavaVersion) api("com.google.guava", "guava", Versions.guavaVersion)

Datei anzeigen

@ -25,7 +25,6 @@
package org.geysermc.geyser.entity.type.player; package org.geysermc.geyser.entity.type.player;
import com.github.steveice10.mc.auth.data.GameProfile;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
@ -61,15 +60,21 @@ import org.geysermc.geyser.translator.text.MessageTranslator;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Collections; import java.util.Collections;
import java.util.Optional; import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@Getter @Setter @Getter @Setter
public class PlayerEntity extends LivingEntity { public class PlayerEntity extends LivingEntity {
public static final float SNEAKING_POSE_HEIGHT = 1.5f; public static final float SNEAKING_POSE_HEIGHT = 1.5f;
private GameProfile profile;
private String username; private String username;
private boolean playerList = true; // Player is in the player list private boolean playerList = true; // Player is in the player list
/**
* The textures property from the GameProfile.
*/
@Nullable
private String texturesProperty;
private Vector3i bedPosition; private Vector3i bedPosition;
@ -82,11 +87,12 @@ public class PlayerEntity extends LivingEntity {
*/ */
private ParrotEntity rightParrot; private ParrotEntity rightParrot;
public PlayerEntity(GeyserSession session, int entityId, long geyserId, GameProfile gameProfile, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) { public PlayerEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, Vector3f position,
super(session, entityId, geyserId, gameProfile.getId(), EntityDefinitions.PLAYER, position, motion, yaw, pitch, headYaw); Vector3f motion, float yaw, float pitch, float headYaw, String username, @Nullable String texturesProperty) {
super(session, entityId, geyserId, uuid, EntityDefinitions.PLAYER, position, motion, yaw, pitch, headYaw);
profile = gameProfile; this.username = username;
username = gameProfile.getName(); this.texturesProperty = texturesProperty;
} }
@Override @Override

Datei anzeigen

@ -25,7 +25,6 @@
package org.geysermc.geyser.entity.type.player; package org.geysermc.geyser.entity.type.player;
import com.github.steveice10.mc.auth.data.GameProfile;
import com.github.steveice10.mc.protocol.data.game.entity.attribute.Attribute; import com.github.steveice10.mc.protocol.data.game.entity.attribute.Attribute;
import com.github.steveice10.mc.protocol.data.game.entity.attribute.AttributeType; import com.github.steveice10.mc.protocol.data.game.entity.attribute.AttributeType;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
@ -71,7 +70,7 @@ public class SessionPlayerEntity extends PlayerEntity {
private int fakeTradeXp; private int fakeTradeXp;
public SessionPlayerEntity(GeyserSession session) { public SessionPlayerEntity(GeyserSession session) {
super(session, -1, 1, new GameProfile(UUID.randomUUID(), "unknown"), Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0); super(session, -1, 1, UUID.randomUUID(), Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, "unknown", null);
valid = true; valid = true;
} }

Datei anzeigen

@ -25,7 +25,6 @@
package org.geysermc.geyser.entity.type.player; package org.geysermc.geyser.entity.type.player;
import com.github.steveice10.mc.auth.data.GameProfile;
import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i; import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.PlayerPermission; import com.nukkitx.protocol.bedrock.data.PlayerPermission;
@ -36,6 +35,8 @@ import com.nukkitx.protocol.bedrock.packet.AddPlayerPacket;
import lombok.Getter; import lombok.Getter;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import java.util.UUID;
/** /**
* A wrapper to handle skulls more effectively - skulls have to be treated as entities since there are no * A wrapper to handle skulls more effectively - skulls have to be treated as entities since there are no
* custom player skulls in Bedrock. * custom player skulls in Bedrock.
@ -48,8 +49,8 @@ public class SkullPlayerEntity extends PlayerEntity {
@Getter @Getter
private final int blockState; private final int blockState;
public SkullPlayerEntity(GeyserSession session, long geyserId, GameProfile gameProfile, Vector3f position, float rotation, int blockState) { public SkullPlayerEntity(GeyserSession session, long geyserId, Vector3f position, float rotation, int blockState, String texturesProperty) {
super(session, 0, geyserId, gameProfile, position, Vector3f.ZERO, rotation, 0, rotation); super(session, 0, geyserId, UUID.randomUUID(), position, Vector3f.ZERO, rotation, 0, rotation, "", texturesProperty);
this.blockState = blockState; this.blockState = blockState;
setPlayerList(false); setPlayerList(false);
} }

Datei anzeigen

@ -391,7 +391,7 @@ public final class ClickPlan {
public IntSet getAffectedSlots() { public IntSet getAffectedSlots() {
IntSet affectedSlots = new IntOpenHashSet(); IntSet affectedSlots = new IntOpenHashSet();
for (ClickAction action : plan) { for (ClickAction action : plan) {
if (translator.getSlotType(action.slot) == SlotType.NORMAL && action.slot != Click.OUTSIDE_SLOT) { if (translator.getSlotType(action.slot) != SlotType.OUTPUT && action.slot != Click.OUTSIDE_SLOT) {
affectedSlots.add(action.slot); affectedSlots.add(action.slot);
if (action.click.actionType == ContainerActionType.MOVE_TO_HOTBAR_SLOT) { if (action.click.actionType == ContainerActionType.MOVE_TO_HOTBAR_SLOT) {
//TODO won't work if offhand is added //TODO won't work if offhand is added

Datei anzeigen

@ -52,7 +52,6 @@ public class StoredItemMappings {
private final int goldIngot; private final int goldIngot;
private final int ironIngot; private final int ironIngot;
private final int lead; private final int lead;
private final ItemMapping lodestoneCompass;
private final ItemMapping milkBucket; private final ItemMapping milkBucket;
private final int nameTag; private final int nameTag;
private final ItemMapping powderSnowBucket; private final ItemMapping powderSnowBucket;
@ -80,7 +79,6 @@ public class StoredItemMappings {
this.goldIngot = load(itemMappings, "gold_ingot").getJavaId(); this.goldIngot = load(itemMappings, "gold_ingot").getJavaId();
this.ironIngot = load(itemMappings, "iron_ingot").getJavaId(); this.ironIngot = load(itemMappings, "iron_ingot").getJavaId();
this.lead = load(itemMappings, "lead").getJavaId(); this.lead = load(itemMappings, "lead").getJavaId();
this.lodestoneCompass = load(itemMappings, "lodestone_compass");
this.milkBucket = load(itemMappings, "milk_bucket"); this.milkBucket = load(itemMappings, "milk_bucket");
this.nameTag = load(itemMappings, "name_tag").getJavaId(); this.nameTag = load(itemMappings, "name_tag").getJavaId();
this.powderSnowBucket = load(itemMappings, "powder_snow_bucket"); this.powderSnowBucket = load(itemMappings, "powder_snow_bucket");

Datei anzeigen

@ -1,92 +0,0 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.level.chunk;
import com.nukkitx.network.util.Preconditions;
public class NibbleArray implements Cloneable {
private final byte[] data;
public NibbleArray(int length) {
data = new byte[length / 2];
}
public NibbleArray(byte[] array) {
data = array;
}
public byte get(int index) {
Preconditions.checkElementIndex(index, data.length * 2);
byte val = data[index / 2];
if ((index & 1) == 0) {
return (byte) (val & 0x0f);
} else {
return (byte) ((val & 0xf0) >>> 4);
}
}
public void set(int index, byte value) {
Preconditions.checkArgument(value >= 0 && value < 16, "Nibbles must have a value between 0 and 15.");
Preconditions.checkElementIndex(index, data.length * 2);
value &= 0xf;
int half = index / 2;
byte previous = data[half];
if ((index & 1) == 0) {
data[half] = (byte) (previous & 0xf0 | value);
} else {
data[half] = (byte) (previous & 0x0f | value << 4);
}
}
public void fill(byte value) {
Preconditions.checkArgument(value >= 0 && value < 16, "Nibbles must have a value between 0 and 15.");
value &= 0xf;
for (int i = 0; i < data.length; i++) {
data[i] = (byte) ((value << 4) | value);
}
}
public void copyFrom(byte[] bytes) {
Preconditions.checkNotNull(bytes, "bytes");
Preconditions.checkArgument(bytes.length == data.length, "length of provided byte array is %s but expected %s", bytes.length,
data.length);
System.arraycopy(bytes, 0, data, 0, data.length);
}
public void copyFrom(NibbleArray array) {
Preconditions.checkNotNull(array, "array");
copyFrom(array.data);
}
public byte[] getData() {
return data;
}
public NibbleArray copy() {
return new NibbleArray(getData().clone());
}
}

Datei anzeigen

@ -128,7 +128,7 @@ public class ItemRegistryPopulator {
IntList spawnEggs = new IntArrayList(); IntList spawnEggs = new IntArrayList();
List<ItemData> carpets = new ObjectArrayList<>(); List<ItemData> carpets = new ObjectArrayList<>();
Int2ObjectMap<ItemMapping> mappings = new Int2ObjectOpenHashMap<>(); List<ItemMapping> mappings = new ObjectArrayList<>();
// Temporary mapping to create stored items // Temporary mapping to create stored items
Map<String, ItemMapping> identifierToMapping = new Object2ObjectOpenHashMap<>(); Map<String, ItemMapping> identifierToMapping = new Object2ObjectOpenHashMap<>();
@ -166,6 +166,9 @@ public class ItemRegistryPopulator {
if (identifier.equals("minecraft:debug_stick")) { if (identifier.equals("minecraft:debug_stick")) {
// Just shows an empty texture; either way it doesn't exist in the creative menu on Java // Just shows an empty texture; either way it doesn't exist in the creative menu on Java
continue; continue;
} else if (identifier.equals("minecraft:empty_map") && damage == 2) {
// Bedrock-only as its own item
continue;
} }
StartGamePacket.ItemEntry entry = entries.get(identifier); StartGamePacket.ItemEntry entry = entries.get(identifier);
int id = -1; int id = -1;
@ -240,6 +243,8 @@ public class ItemRegistryPopulator {
if (usingFurnaceMinecart && javaIdentifier.equals("minecraft:furnace_minecart")) { if (usingFurnaceMinecart && javaIdentifier.equals("minecraft:furnace_minecart")) {
javaFurnaceMinecartId = itemIndex; javaFurnaceMinecartId = itemIndex;
itemIndex++; itemIndex++;
// Will be added later
mappings.add(null);
continue; continue;
} }
@ -416,7 +421,7 @@ public class ItemRegistryPopulator {
spawnEggs.add(mapping.getBedrockId()); spawnEggs.add(mapping.getBedrockId());
} }
mappings.put(itemIndex, mapping); mappings.add(mapping);
identifierToMapping.put(javaIdentifier, mapping); identifierToMapping.put(javaIdentifier, mapping);
itemNames.add(javaIdentifier); itemNames.add(javaIdentifier);
@ -437,16 +442,14 @@ public class ItemRegistryPopulator {
// Add the lodestone compass since it doesn't exist on java but we need it for item conversion // Add the lodestone compass since it doesn't exist on java but we need it for item conversion
ItemMapping lodestoneEntry = ItemMapping.builder() ItemMapping lodestoneEntry = ItemMapping.builder()
.javaIdentifier("minecraft:lodestone_compass") .javaIdentifier("")
.bedrockIdentifier("minecraft:lodestone_compass") .bedrockIdentifier("minecraft:lodestone_compass")
.javaId(itemIndex) .javaId(-1)
.bedrockId(lodestoneCompassId) .bedrockId(lodestoneCompassId)
.bedrockData(0) .bedrockData(0)
.bedrockBlockId(-1) .bedrockBlockId(-1)
.stackSize(1) .stackSize(1)
.build(); .build();
mappings.put(itemIndex, lodestoneEntry);
identifierToMapping.put(lodestoneEntry.getJavaIdentifier(), lodestoneEntry);
ComponentItemData furnaceMinecartData = null; ComponentItemData furnaceMinecartData = null;
if (usingFurnaceMinecart) { if (usingFurnaceMinecart) {
@ -455,7 +458,7 @@ public class ItemRegistryPopulator {
entries.put("geysermc:furnace_minecart", new StartGamePacket.ItemEntry("geysermc:furnace_minecart", (short) furnaceMinecartId, true)); entries.put("geysermc:furnace_minecart", new StartGamePacket.ItemEntry("geysermc:furnace_minecart", (short) furnaceMinecartId, true));
mappings.put(javaFurnaceMinecartId, ItemMapping.builder() mappings.set(javaFurnaceMinecartId, ItemMapping.builder()
.javaIdentifier("minecraft:furnace_minecart") .javaIdentifier("minecraft:furnace_minecart")
.bedrockIdentifier("geysermc:furnace_minecart") .bedrockIdentifier("geysermc:furnace_minecart")
.javaId(javaFurnaceMinecartId) .javaId(javaFurnaceMinecartId)
@ -506,9 +509,9 @@ public class ItemRegistryPopulator {
} }
ItemMappings itemMappings = ItemMappings.builder() ItemMappings itemMappings = ItemMappings.builder()
.items(mappings) .items(mappings.toArray(new ItemMapping[0]))
.creativeItems(creativeItems.toArray(new ItemData[0])) .creativeItems(creativeItems.toArray(new ItemData[0]))
.itemEntries(new ArrayList<>(entries.values())) .itemEntries(List.copyOf(entries.values()))
.itemNames(itemNames.toArray(new String[0])) .itemNames(itemNames.toArray(new String[0]))
.storedItems(new StoredItemMappings(identifierToMapping)) .storedItems(new StoredItemMappings(identifierToMapping))
.javaOnlyItems(javaOnlyItems) .javaOnlyItems(javaOnlyItems)
@ -517,6 +520,7 @@ public class ItemRegistryPopulator {
.spawnEggIds(spawnEggs) .spawnEggIds(spawnEggs)
.carpets(carpets) .carpets(carpets)
.furnaceMinecartData(furnaceMinecartData) .furnaceMinecartData(furnaceMinecartData)
.lodestoneCompass(lodestoneEntry)
.build(); .build();
Registries.ITEMS.register(palette.getValue().protocolVersion(), itemMappings); Registries.ITEMS.register(palette.getValue().protocolVersion(), itemMappings);

Datei anzeigen

@ -29,13 +29,13 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData; import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import com.nukkitx.protocol.bedrock.packet.StartGamePacket; import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.ints.IntList;
import lombok.Builder; import lombok.Builder;
import lombok.Value; import lombok.Value;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.item.StoredItemMappings; import org.geysermc.geyser.inventory.item.StoredItemMappings;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -48,7 +48,12 @@ public class ItemMappings {
Map<String, ItemMapping> cachedJavaMappings = new WeakHashMap<>(); Map<String, ItemMapping> cachedJavaMappings = new WeakHashMap<>();
Int2ObjectMap<ItemMapping> items; ItemMapping[] items;
/**
* A unique exception as this is an item in Bedrock, but not in Java.
*/
ItemMapping lodestoneCompass;
ItemData[] creativeItems; ItemData[] creativeItems;
List<StartGamePacket.ItemEntry> itemEntries; List<StartGamePacket.ItemEntry> itemEntries;
@ -70,6 +75,7 @@ public class ItemMappings {
* @param itemStack the itemstack * @param itemStack the itemstack
* @return an item entry from the given java edition identifier * @return an item entry from the given java edition identifier
*/ */
@Nonnull
public ItemMapping getMapping(ItemStack itemStack) { public ItemMapping getMapping(ItemStack itemStack) {
return this.getMapping(itemStack.getId()); return this.getMapping(itemStack.getId());
} }
@ -81,8 +87,9 @@ public class ItemMappings {
* @param javaId the id * @param javaId the id
* @return an item entry from the given java edition identifier * @return an item entry from the given java edition identifier
*/ */
@Nonnull
public ItemMapping getMapping(int javaId) { public ItemMapping getMapping(int javaId) {
return this.items.get(javaId); return javaId >= 0 && javaId < this.items.length ? this.items[javaId] : ItemMapping.AIR;
} }
/** /**
@ -94,7 +101,7 @@ public class ItemMappings {
*/ */
public ItemMapping getMapping(String javaIdentifier) { public ItemMapping getMapping(String javaIdentifier) {
return this.cachedJavaMappings.computeIfAbsent(javaIdentifier, key -> { return this.cachedJavaMappings.computeIfAbsent(javaIdentifier, key -> {
for (ItemMapping mapping : this.items.values()) { for (ItemMapping mapping : this.items) {
if (mapping.getJavaIdentifier().equals(key)) { if (mapping.getJavaIdentifier().equals(key)) {
return mapping; return mapping;
} }
@ -110,11 +117,18 @@ public class ItemMappings {
* @return an item entry from the given item data * @return an item entry from the given item data
*/ */
public ItemMapping getMapping(ItemData data) { public ItemMapping getMapping(ItemData data) {
int id = data.getId();
if (id == 0) {
return ItemMapping.AIR;
} else if (id == lodestoneCompass.getBedrockId()) {
return lodestoneCompass;
}
boolean isBlock = data.getBlockRuntimeId() != 0; boolean isBlock = data.getBlockRuntimeId() != 0;
boolean hasDamage = data.getDamage() != 0; boolean hasDamage = data.getDamage() != 0;
for (ItemMapping mapping : this.items.values()) { for (ItemMapping mapping : this.items) {
if (mapping.getBedrockId() == data.getId()) { if (mapping.getBedrockId() == id) {
if (isBlock && !hasDamage) { // Pre-1.16.220 will not use block runtime IDs at all, so we shouldn't check either if (isBlock && !hasDamage) { // Pre-1.16.220 will not use block runtime IDs at all, so we shouldn't check either
if (data.getBlockRuntimeId() != mapping.getBedrockBlockId()) { if (data.getBlockRuntimeId() != mapping.getBedrockBlockId()) {
continue; continue;
@ -135,7 +149,7 @@ public class ItemMappings {
} }
// This will hide the message when the player clicks with an empty hand // This will hide the message when the player clicks with an empty hand
if (data.getId() != 0 && data.getDamage() != 0) { if (id != 0 && data.getDamage() != 0) {
GeyserImpl.getInstance().getLogger().debug("Missing mapping for bedrock item " + data.getId() + ":" + data.getDamage()); GeyserImpl.getInstance().getLogger().debug("Missing mapping for bedrock item " + data.getId() + ":" + data.getDamage());
} }
return ItemMapping.AIR; return ItemMapping.AIR;

Datei anzeigen

@ -26,24 +26,36 @@
package org.geysermc.geyser.session.cache; package org.geysermc.geyser.session.cache;
import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
import com.nukkitx.protocol.bedrock.packet.SetTitlePacket;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.scoreboard.Scoreboard; import org.geysermc.geyser.scoreboard.Scoreboard;
import org.geysermc.geyser.scoreboard.ScoreboardUpdater.ScoreboardSession; import org.geysermc.geyser.scoreboard.ScoreboardUpdater.ScoreboardSession;
import org.geysermc.geyser.session.GeyserSession;
@Getter public final class WorldCache {
public class WorldCache {
private final GeyserSession session; private final GeyserSession session;
@Getter
private final ScoreboardSession scoreboardSession; private final ScoreboardSession scoreboardSession;
@Getter
private Scoreboard scoreboard; private Scoreboard scoreboard;
@Getter
@Setter @Setter
private Difficulty difficulty = Difficulty.EASY; private Difficulty difficulty = Difficulty.EASY;
/**
* Whether our cooldown changed the title time, and the true title times need to be re-sent.
*/
private boolean titleTimesNeedReset = false;
private int trueTitleFadeInTime;
private int trueTitleStayTime;
private int trueTitleFadeOutTime;
public WorldCache(GeyserSession session) { public WorldCache(GeyserSession session) {
this.session = session; this.session = session;
this.scoreboard = new Scoreboard(session); this.scoreboard = new Scoreboard(session);
scoreboardSession = new ScoreboardSession(session); scoreboardSession = new ScoreboardSession(session);
resetTitleTimes(false);
} }
public void removeScoreboard() { public void removeScoreboard() {
@ -58,4 +70,55 @@ public class WorldCache {
int pps = scoreboardSession.getPacketsPerSecond(); int pps = scoreboardSession.getPacketsPerSecond();
return Math.max(pps, pendingPps); return Math.max(pps, pendingPps);
} }
public void markTitleTimesAsIncorrect() {
titleTimesNeedReset = true;
}
/**
* Store the true active title times.
*/
public void setTitleTimes(int fadeInTime, int stayTime, int fadeOutTime) {
trueTitleFadeInTime = fadeInTime;
trueTitleStayTime = stayTime;
trueTitleFadeOutTime = fadeOutTime;
// The translator will sync this for us
titleTimesNeedReset = false;
}
/**
* If needed, ensure that the Bedrock client will use the correct timings for titles.
*/
public void synchronizeCorrectTitleTimes() {
if (titleTimesNeedReset) {
forceSyncCorrectTitleTimes();
}
}
private void forceSyncCorrectTitleTimes() {
SetTitlePacket titlePacket = new SetTitlePacket();
titlePacket.setType(SetTitlePacket.Type.TIMES);
titlePacket.setText("");
titlePacket.setFadeInTime(trueTitleFadeInTime);
titlePacket.setStayTime(trueTitleStayTime);
titlePacket.setFadeOutTime(trueTitleFadeOutTime);
titlePacket.setPlatformOnlineId("");
titlePacket.setXuid("");
session.sendUpstreamPacket(titlePacket);
titleTimesNeedReset = false;
}
/**
* Reset the true active title times to the (Java Edition 1.18.2) defaults.
*/
public void resetTitleTimes(boolean clientSync) {
trueTitleFadeInTime = 10;
trueTitleStayTime = 70;
trueTitleFadeOutTime = 20;
if (clientSync) {
forceSyncCorrectTitleTimes();
}
}
} }

Datei anzeigen

@ -25,7 +25,6 @@
package org.geysermc.geyser.skin; package org.geysermc.geyser.skin;
import com.github.steveice10.mc.auth.data.GameProfile;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader; import com.google.common.cache.CacheLoader;
@ -106,7 +105,7 @@ public class FakeHeadProvider {
session.getPlayerWithCustomHeads().add(entity.getUuid()); session.getPlayerWithCustomHeads().add(entity.getUuid());
GameProfile.Property texturesProperty = entity.getProfile().getProperty("textures"); String texturesProperty = entity.getTexturesProperty();
SkinProvider.EXECUTOR_SERVICE.execute(() -> { SkinProvider.EXECUTOR_SERVICE.execute(() -> {
try { try {
@ -182,7 +181,7 @@ public class FakeHeadProvider {
@Getter @Getter
@Setter @Setter
private static class FakeHeadEntry { private static class FakeHeadEntry {
private final GameProfile.Property texturesProperty; private final String texturesProperty;
private final String fakeHeadSkinUrl; private final String fakeHeadSkinUrl;
private PlayerEntity entity; private PlayerEntity entity;
@ -192,18 +191,7 @@ public class FakeHeadProvider {
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
FakeHeadEntry that = (FakeHeadEntry) o; FakeHeadEntry that = (FakeHeadEntry) o;
return equals(texturesProperty, that.texturesProperty) && Objects.equals(fakeHeadSkinUrl, that.fakeHeadSkinUrl); return Objects.equals(texturesProperty, that.texturesProperty) && Objects.equals(fakeHeadSkinUrl, that.fakeHeadSkinUrl);
}
private boolean equals(GameProfile.Property a, GameProfile.Property b) {
//TODO actually fix this in MCAuthLib
if (a == b) {
return true;
}
if (a == null || b == null) {
return false;
}
return Objects.equals(a.getName(), b.getName()) && Objects.equals(a.getValue(), b.getValue()) && Objects.equals(a.getSignature(), b.getSignature());
} }
@Override @Override

Datei anzeigen

@ -26,7 +26,6 @@
package org.geysermc.geyser.skin; package org.geysermc.geyser.skin;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.github.steveice10.mc.auth.data.GameProfile;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.StringTag;
@ -54,7 +53,7 @@ public class SkinManager {
* Builds a Bedrock player list entry from our existing, cached Bedrock skin information * Builds a Bedrock player list entry from our existing, cached Bedrock skin information
*/ */
public static PlayerListPacket.Entry buildCachedEntry(GeyserSession session, PlayerEntity playerEntity) { public static PlayerListPacket.Entry buildCachedEntry(GeyserSession session, PlayerEntity playerEntity) {
GameProfileData data = GameProfileData.from(playerEntity.getProfile()); GameProfileData data = GameProfileData.from(playerEntity);
SkinProvider.Cape cape = SkinProvider.getCachedCape(data.capeUrl()); SkinProvider.Cape cape = SkinProvider.getCachedCape(data.capeUrl());
SkinProvider.SkinGeometry geometry = SkinProvider.SkinGeometry.getLegacy(data.isAlex()); SkinProvider.SkinGeometry geometry = SkinProvider.SkinGeometry.getLegacy(data.isAlex());
@ -65,8 +64,8 @@ public class SkinManager {
return buildEntryManually( return buildEntryManually(
session, session,
playerEntity.getProfile().getId(), playerEntity.getUuid(),
playerEntity.getProfile().getName(), playerEntity.getUsername(),
playerEntity.getGeyserId(), playerEntity.getGeyserId(),
skin.getTextureUrl(), skin.getTextureUrl(),
skin.getSkinData(), skin.getSkinData(),
@ -227,31 +226,31 @@ public class SkinManager {
} }
/** /**
* Generate the GameProfileData from the given GameProfile * Generate the GameProfileData from the given player entity
* *
* @param profile GameProfile to build the GameProfileData from * @param entity entity to build the GameProfileData from
* @return The built GameProfileData * @return The built GameProfileData
*/ */
public static GameProfileData from(GameProfile profile) { public static GameProfileData from(PlayerEntity entity) {
try { try {
GameProfile.Property skinProperty = profile.getProperty("textures"); String texturesProperty = entity.getTexturesProperty();
if (skinProperty == null) { if (texturesProperty == null) {
// Likely offline mode // Likely offline mode
return loadBedrockOrOfflineSkin(profile); return loadBedrockOrOfflineSkin(entity);
} }
GameProfileData data = loadFromJson(skinProperty.getValue()); GameProfileData data = loadFromJson(texturesProperty);
if (data != null) { if (data != null) {
return data; return data;
} else { } else {
return loadBedrockOrOfflineSkin(profile); return loadBedrockOrOfflineSkin(entity);
} }
} catch (IOException exception) { } catch (IOException exception) {
GeyserImpl.getInstance().getLogger().debug("Something went wrong while processing skin for " + profile.getName()); GeyserImpl.getInstance().getLogger().debug("Something went wrong while processing skin for " + entity.getUsername());
if (GeyserImpl.getInstance().getConfig().isDebugMode()) { if (GeyserImpl.getInstance().getConfig().isDebugMode()) {
exception.printStackTrace(); exception.printStackTrace();
} }
return loadBedrockOrOfflineSkin(profile); return loadBedrockOrOfflineSkin(entity);
} }
} }
@ -280,14 +279,15 @@ public class SkinManager {
* @return default skin with default cape when texture data is invalid, or the Bedrock player's skin if this * @return default skin with default cape when texture data is invalid, or the Bedrock player's skin if this
* is a Bedrock player. * is a Bedrock player.
*/ */
private static GameProfileData loadBedrockOrOfflineSkin(GameProfile profile) { private static GameProfileData loadBedrockOrOfflineSkin(PlayerEntity entity) {
// Fallback to the offline mode of working it out // Fallback to the offline mode of working it out
boolean isAlex = (Math.abs(profile.getId().hashCode() % 2) == 1); UUID uuid = entity.getUuid();
boolean isAlex = (Math.abs(uuid.hashCode() % 2) == 1);
String skinUrl = isAlex ? SkinProvider.EMPTY_SKIN_ALEX.getTextureUrl() : SkinProvider.EMPTY_SKIN.getTextureUrl(); String skinUrl = isAlex ? SkinProvider.EMPTY_SKIN_ALEX.getTextureUrl() : SkinProvider.EMPTY_SKIN.getTextureUrl();
String capeUrl = SkinProvider.EMPTY_CAPE.getTextureUrl(); String capeUrl = SkinProvider.EMPTY_CAPE.getTextureUrl();
if (("steve".equals(skinUrl) || "alex".equals(skinUrl)) && GeyserImpl.getInstance().getConfig().getRemote().getAuthType() != AuthType.ONLINE) { if (("steve".equals(skinUrl) || "alex".equals(skinUrl)) && GeyserImpl.getInstance().getConfig().getRemote().getAuthType() != AuthType.ONLINE) {
GeyserSession session = GeyserImpl.getInstance().connectionByUuid(profile.getId()); GeyserSession session = GeyserImpl.getInstance().connectionByUuid(uuid);
if (session != null) { if (session != null) {
skinUrl = session.getClientData().getSkinId(); skinUrl = session.getClientData().getSkinId();

Datei anzeigen

@ -27,7 +27,6 @@ package org.geysermc.geyser.skin;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.steveice10.mc.auth.data.GameProfile;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntArrayTag; import com.github.steveice10.opennbt.tag.builtin.IntArrayTag;
import com.github.steveice10.opennbt.tag.builtin.Tag; import com.github.steveice10.opennbt.tag.builtin.Tag;
@ -53,7 +52,6 @@ import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -157,7 +155,7 @@ public class SkinProvider {
} }
public static CompletableFuture<SkinProvider.SkinData> requestSkinData(PlayerEntity entity) { public static CompletableFuture<SkinProvider.SkinData> requestSkinData(PlayerEntity entity) {
SkinManager.GameProfileData data = SkinManager.GameProfileData.from(entity.getProfile()); SkinManager.GameProfileData data = SkinManager.GameProfileData.from(entity);
return requestSkinAndCape(entity.getUuid(), data.skinUrl(), data.capeUrl()) return requestSkinAndCape(entity.getUuid(), data.skinUrl(), data.capeUrl())
.thenApplyAsync(skinAndCape -> { .thenApplyAsync(skinAndCape -> {
@ -546,12 +544,11 @@ public class SkinProvider {
* @param skullOwner the CompoundTag of the skull with no textures * @param skullOwner the CompoundTag of the skull with no textures
* @return a completable GameProfile with textures included * @return a completable GameProfile with textures included
*/ */
public static CompletableFuture<GameProfile> requestTexturesFromUsername(CompoundTag skullOwner) { public static CompletableFuture<String> requestTexturesFromUsername(CompoundTag skullOwner) {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
Tag uuidTag = skullOwner.get("Id"); Tag uuidTag = skullOwner.get("Id");
String uuidToString = ""; String uuidToString = "";
JsonNode node; JsonNode node;
GameProfile gameProfile = new GameProfile(UUID.randomUUID(), "");
boolean retrieveUuidFromInternet = !(uuidTag instanceof IntArrayTag); // also covers null check boolean retrieveUuidFromInternet = !(uuidTag instanceof IntArrayTag); // also covers null check
if (!retrieveUuidFromInternet) { if (!retrieveUuidFromInternet) {
@ -577,15 +574,12 @@ public class SkinProvider {
// Get textures from UUID // Get textures from UUID
node = WebUtils.getJson("https://sessionserver.mojang.com/session/minecraft/profile/" + uuidToString); node = WebUtils.getJson("https://sessionserver.mojang.com/session/minecraft/profile/" + uuidToString);
List<GameProfile.Property> profileProperties = new ArrayList<>();
JsonNode properties = node.get("properties"); JsonNode properties = node.get("properties");
if (properties == null) { if (properties == null) {
GeyserImpl.getInstance().getLogger().debug("No properties found in Mojang response for " + uuidToString); GeyserImpl.getInstance().getLogger().debug("No properties found in Mojang response for " + uuidToString);
return null; return null;
} }
profileProperties.add(new GameProfile.Property("textures", node.get("properties").get(0).get("value").asText())); return node.get("properties").get(0).get("value").asText();
gameProfile.setProperties(profileProperties);
return gameProfile;
} catch (Exception e) { } catch (Exception e) {
if (GeyserImpl.getInstance().getConfig().isDebugMode()) { if (GeyserImpl.getInstance().getConfig().isDebugMode()) {
e.printStackTrace(); e.printStackTrace();

Datei anzeigen

@ -50,7 +50,7 @@ public class SkullSkinManager extends SkinManager {
public static void requestAndHandleSkin(PlayerEntity entity, GeyserSession session, public static void requestAndHandleSkin(PlayerEntity entity, GeyserSession session,
Consumer<SkinProvider.Skin> skinConsumer) { Consumer<SkinProvider.Skin> skinConsumer) {
GameProfileData data = GameProfileData.from(entity.getProfile()); GameProfileData data = GameProfileData.from(entity);
SkinProvider.requestSkin(entity.getUuid(), data.skinUrl(), true) SkinProvider.requestSkin(entity.getUuid(), data.skinUrl(), true)
.whenCompleteAsync((skin, throwable) -> { .whenCompleteAsync((skin, throwable) -> {

Datei anzeigen

@ -41,13 +41,13 @@ import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequ
import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket; import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket;
import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import org.geysermc.geyser.inventory.BedrockContainerSlot;
import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.Inventory;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.inventory.BedrockContainerSlot;
import org.geysermc.geyser.inventory.SlotType; import org.geysermc.geyser.inventory.SlotType;
import org.geysermc.geyser.inventory.updater.UIInventoryUpdater; import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
import org.geysermc.geyser.translator.inventory.item.BannerTranslator; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.item.nbt.BannerTranslator;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;

Datei anzeigen

@ -35,28 +35,18 @@ import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.registry.type.ItemMappings;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ItemRemapper @ItemRemapper
public class CompassTranslator extends ItemTranslator { public class CompassTranslator extends ItemTranslator {
private final List<ItemMapping> appliedItems;
public CompassTranslator() {
appliedItems = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion())
.getItems()
.values()
.stream()
.filter(entry -> entry.getJavaIdentifier().endsWith("compass"))
.collect(Collectors.toList());
}
@Override @Override
protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) {
if (isLodestoneCompass(itemStack.getNbt())) { if (isLodestoneCompass(itemStack.getNbt())) {
// NBT will be translated in nbt/LodestoneCompassTranslator if applicable // NBT will be translated in nbt/LodestoneCompassTranslator if applicable
return super.translateToBedrock(itemStack, mappings.getStoredItems().lodestoneCompass(), mappings); return super.translateToBedrock(itemStack, mappings.getLodestoneCompass(), mappings);
} }
return super.translateToBedrock(itemStack, mapping, mappings); return super.translateToBedrock(itemStack, mapping, mappings);
} }
@ -64,7 +54,7 @@ public class CompassTranslator extends ItemTranslator {
@Override @Override
protected ItemMapping getItemMapping(int javaId, CompoundTag nbt, ItemMappings mappings) { protected ItemMapping getItemMapping(int javaId, CompoundTag nbt, ItemMappings mappings) {
if (isLodestoneCompass(nbt)) { if (isLodestoneCompass(nbt)) {
return mappings.getStoredItems().lodestoneCompass(); return mappings.getLodestoneCompass();
} }
return super.getItemMapping(javaId, nbt, mappings); return super.getItemMapping(javaId, nbt, mappings);
} }
@ -89,6 +79,9 @@ public class CompassTranslator extends ItemTranslator {
@Override @Override
public List<ItemMapping> getAppliedItems() { public List<ItemMapping> getAppliedItems() {
return appliedItems; return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion())
.getItems())
.filter(entry -> entry.getJavaIdentifier().endsWith("compass"))
.collect(Collectors.toList());
} }
} }

Datei anzeigen

@ -0,0 +1,68 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.translator.inventory.item;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings;
import java.util.Collections;
import java.util.List;
@ItemRemapper
public class FilledMapTranslator extends ItemTranslator {
@Override
protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) {
ItemData.Builder builder = super.translateToBedrock(itemStack, mapping, mappings);
CompoundTag nbt = itemStack.getNbt();
if (nbt != null && nbt.get("display") instanceof CompoundTag display) {
// Note: damage 5 treasure map, 6 ???
Tag mapColor = display.get("MapColor");
if (mapColor != null && mapColor.getValue() instanceof Number color) {
// Java Edition allows any color; Bedrock only allows some. So let's take what colors we can get
switch (color.intValue()) {
case 3830373 -> builder.damage(3); // Ocean Monument
case 5393476 -> builder.damage(4); // Woodland explorer
}
}
}
return builder;
}
@Override
public List<ItemMapping> getAppliedItems() {
return Collections.singletonList(
Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion())
.getMapping("minecraft:filled_map")
);
}
}

Datei anzeigen

@ -47,6 +47,7 @@ import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.FileUtils; import org.geysermc.geyser.util.FileUtils;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.lang.reflect.InvocationTargetException;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -71,11 +72,11 @@ public abstract class ItemTranslator {
try { try {
if (NbtItemStackTranslator.class.isAssignableFrom(clazz)) { if (NbtItemStackTranslator.class.isAssignableFrom(clazz)) {
NbtItemStackTranslator nbtItemTranslator = (NbtItemStackTranslator) clazz.newInstance(); NbtItemStackTranslator nbtItemTranslator = (NbtItemStackTranslator) clazz.getDeclaredConstructor().newInstance();
loadedNbtItemTranslators.put(nbtItemTranslator, priority); loadedNbtItemTranslators.put(nbtItemTranslator, priority);
continue; continue;
} }
ItemTranslator itemStackTranslator = (ItemTranslator) clazz.newInstance(); ItemTranslator itemStackTranslator = (ItemTranslator) clazz.getDeclaredConstructor().newInstance();
List<ItemMapping> appliedItems = itemStackTranslator.getAppliedItems(); List<ItemMapping> appliedItems = itemStackTranslator.getAppliedItems();
for (ItemMapping item : appliedItems) { for (ItemMapping item : appliedItems) {
ItemTranslator registered = ITEM_STACK_TRANSLATORS.get(item.getJavaId()); ItemTranslator registered = ITEM_STACK_TRANSLATORS.get(item.getJavaId());
@ -87,7 +88,7 @@ public abstract class ItemTranslator {
} }
ITEM_STACK_TRANSLATORS.put(item.getJavaId(), itemStackTranslator); ITEM_STACK_TRANSLATORS.put(item.getJavaId(), itemStackTranslator);
} }
} catch (InstantiationException | IllegalAccessException e) { } catch (InstantiationException | InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
GeyserImpl.getInstance().getLogger().error("Could not instantiate annotated item translator " + clazz.getCanonicalName()); GeyserImpl.getInstance().getLogger().error("Could not instantiate annotated item translator " + clazz.getCanonicalName());
} }
} }
@ -302,13 +303,16 @@ public abstract class ItemTranslator {
return new ItemStack(mapping.getJavaId(), itemData.getCount(), this.translateToJavaNBT("", itemData.getTag())); return new ItemStack(mapping.getJavaId(), itemData.getCount(), this.translateToJavaNBT("", itemData.getTag()));
} }
/**
* Used for initialization only and only called once.
*/
public abstract List<ItemMapping> getAppliedItems(); public abstract List<ItemMapping> getAppliedItems();
protected ItemMapping getItemMapping(int javaId, CompoundTag nbt, ItemMappings mappings) { protected ItemMapping getItemMapping(int javaId, CompoundTag nbt, ItemMappings mappings) {
return mappings.getMapping(javaId); return mappings.getMapping(javaId);
} }
public NbtMap translateNbtToBedrock(CompoundTag tag) { protected NbtMap translateNbtToBedrock(CompoundTag tag) {
NbtMapBuilder builder = NbtMap.builder(); NbtMapBuilder builder = NbtMap.builder();
if (tag.getValue() != null && !tag.getValue().isEmpty()) { if (tag.getValue() != null && !tag.getValue().isEmpty()) {
for (String str : tag.getValue().keySet()) { for (String str : tag.getValue().keySet()) {
@ -387,7 +391,7 @@ public abstract class ItemTranslator {
return null; return null;
} }
public CompoundTag translateToJavaNBT(String name, NbtMap tag) { private CompoundTag translateToJavaNBT(String name, NbtMap tag) {
CompoundTag javaTag = new CompoundTag(name); CompoundTag javaTag = new CompoundTag(name);
Map<String, Tag> javaValue = javaTag.getValue(); Map<String, Tag> javaValue = javaTag.getValue();
if (tag != null && !tag.isEmpty()) { if (tag != null && !tag.isEmpty()) {

Datei anzeigen

@ -29,12 +29,12 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMapping;
public class NbtItemStackTranslator { public abstract class NbtItemStackTranslator {
/** /**
* Translate the item NBT to Bedrock * Translate the item NBT to Bedrock
* @param session the client's current session * @param session the client's current session
* @param itemTag the item's CompoundTag * @param itemTag the item's CompoundTag (cloned from Geyser's cached copy)
* @param mapping Geyser's item mapping * @param mapping Geyser's item mapping
*/ */
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) { public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) {

Datei anzeigen

@ -36,23 +36,13 @@ import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.registry.type.ItemMappings;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ItemRemapper @ItemRemapper
public class PotionTranslator extends ItemTranslator { public class PotionTranslator extends ItemTranslator {
private final List<ItemMapping> appliedItems;
public PotionTranslator() {
appliedItems = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion())
.getItems()
.values()
.stream()
.filter(entry -> entry.getJavaIdentifier().endsWith("potion"))
.collect(Collectors.toList());
}
@Override @Override
protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) {
if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, mapping, mappings); if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, mapping, mappings);
@ -84,6 +74,9 @@ public class PotionTranslator extends ItemTranslator {
@Override @Override
public List<ItemMapping> getAppliedItems() { public List<ItemMapping> getAppliedItems() {
return appliedItems; return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion())
.getItems())
.filter(entry -> entry.getJavaIdentifier().endsWith("potion"))
.collect(Collectors.toList());
} }
} }

Datei anzeigen

@ -36,28 +36,16 @@ import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.registry.type.ItemMappings;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ItemRemapper @ItemRemapper
public class TippedArrowTranslator extends ItemTranslator { public class TippedArrowTranslator extends ItemTranslator {
private final List<ItemMapping> appliedItems;
private static final int TIPPED_ARROW_JAVA_ID = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) private static final int TIPPED_ARROW_JAVA_ID = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion())
.getMapping("minecraft:tipped_arrow") .getMapping("minecraft:tipped_arrow")
.getJavaId(); .getJavaId();
public TippedArrowTranslator() {
appliedItems = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion())
.getItems()
.values()
.stream()
.filter(entry -> entry.getJavaIdentifier().contains("arrow")
&& !entry.getJavaIdentifier().contains("spectral"))
.collect(Collectors.toList());
}
@Override @Override
protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) {
if (!mapping.getJavaIdentifier().equals("minecraft:tipped_arrow") || itemStack.getNbt() == null) { if (!mapping.getJavaIdentifier().equals("minecraft:tipped_arrow") || itemStack.getNbt() == null) {
@ -93,6 +81,10 @@ public class TippedArrowTranslator extends ItemTranslator {
@Override @Override
public List<ItemMapping> getAppliedItems() { public List<ItemMapping> getAppliedItems() {
return appliedItems; return Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion())
.getItems())
.filter(entry -> entry.getJavaIdentifier().contains("arrow")
&& !entry.getJavaIdentifier().contains("spectral"))
.collect(Collectors.toList());
} }
} }

Datei anzeigen

@ -23,29 +23,25 @@
* @link https://github.com/GeyserMC/Geyser * @link https://github.com/GeyserMC/Geyser
*/ */
package org.geysermc.geyser.translator.inventory.item; package org.geysermc.geyser.translator.inventory.item.nbt;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.opennbt.tag.builtin.*; import com.github.steveice10.opennbt.tag.builtin.*;
import com.nukkitx.nbt.NbtList; import com.nukkitx.nbt.NbtList;
import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtMapBuilder;
import com.nukkitx.nbt.NbtType; import com.nukkitx.nbt.NbtType;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.inventory.item.ItemRemapper;
import org.geysermc.geyser.translator.inventory.item.NbtItemStackTranslator;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.ArrayList; import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ItemRemapper @ItemRemapper
public class BannerTranslator extends ItemTranslator { public class BannerTranslator extends NbtItemStackTranslator {
/** /**
* Holds what a Java ominous banner pattern looks like. * Holds what a Java ominous banner pattern looks like.
* *
@ -80,10 +76,8 @@ public class BannerTranslator extends ItemTranslator {
} }
public BannerTranslator() { public BannerTranslator() {
appliedItems = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) appliedItems = Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion())
.getItems() .getItems())
.values()
.stream()
.filter(entry -> entry.getJavaIdentifier().endsWith("banner")) .filter(entry -> entry.getJavaIdentifier().endsWith("banner"))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@ -117,21 +111,6 @@ public class BannerTranslator extends ItemTranslator {
.build(); .build();
} }
/**
* Convert a list of patterns from Bedrock nbt to Java nbt
*
* @param patterns The patterns to convert
* @return The new converted patterns
*/
public static ListTag convertBannerPattern(List<NbtMap> patterns) {
List<Tag> tagsList = new ArrayList<>();
for (NbtMap patternTag : patterns) {
tagsList.add(getJavaBannerPattern(patternTag));
}
return new ListTag("Patterns", tagsList);
}
/** /**
* Convert the Bedrock edition banner pattern nbt to Java edition * Convert the Bedrock edition banner pattern nbt to Java edition
* *
@ -146,62 +125,54 @@ public class BannerTranslator extends ItemTranslator {
return new CompoundTag("", tags); return new CompoundTag("", tags);
} }
@Override /**
protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) { * Convert a list of patterns from Java nbt to Bedrock nbt, or vice versa (we just need to invert the color)
if (itemStack.getNbt() == null) { *
return super.translateToBedrock(itemStack, mapping, mappings); * @param patterns The patterns to convert
*/
private void invertBannerColors(ListTag patterns) {
for (Tag patternTag : patterns.getValue()) {
IntTag color = ((CompoundTag) patternTag).get("Color");
color.setValue(15 - color.getValue());
} }
ItemData.Builder builder = super.translateToBedrock(itemStack, mapping, mappings);
CompoundTag blockEntityTag = itemStack.getNbt().get("BlockEntityTag");
if (blockEntityTag != null && blockEntityTag.get("Patterns") instanceof ListTag patterns) {
NbtMapBuilder nbtBuilder = builder.build().getTag().toBuilder(); //TODO fix ugly hack
if (patterns.equals(OMINOUS_BANNER_PATTERN)) {
// Remove the current patterns and set the ominous banner type
nbtBuilder.remove("Patterns");
nbtBuilder.putInt("Type", 1);
} else {
nbtBuilder.put("Patterns", convertBannerPattern(patterns));
}
builder.tag(nbtBuilder.build());
}
return builder;
} }
@Override @Override
public ItemStack translateToJava(ItemData itemData, ItemMapping mapping, ItemMappings mappings) { public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) {
if (itemData.getTag() == null) { CompoundTag blockEntityTag = itemTag.get("BlockEntityTag");
return super.translateToJava(itemData, mapping, mappings); if (blockEntityTag != null && blockEntityTag.get("Patterns") instanceof ListTag patterns) {
if (patterns.equals(OMINOUS_BANNER_PATTERN)) {
// Remove the current patterns and set the ominous banner type
itemTag.put(new IntTag("Type", 1));
} else {
invertBannerColors(patterns);
itemTag.put(patterns);
}
itemTag.remove("BlockEntityTag");
} }
}
ItemStack itemStack = super.translateToJava(itemData, mapping, mappings); @Override
public void translateToJava(CompoundTag itemTag, ItemMapping mapping) {
NbtMap nbtTag = itemData.getTag(); if (itemTag.get("Type") instanceof IntTag type && type.getValue() == 1) {
if (nbtTag.containsKey("Type", NbtType.INT) && nbtTag.getInt("Type") == 1) {
// Ominous banner pattern // Ominous banner pattern
itemStack.getNbt().remove("Type"); itemTag.remove("Type");
CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag"); CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
blockEntityTag.put(OMINOUS_BANNER_PATTERN); blockEntityTag.put(OMINOUS_BANNER_PATTERN);
itemStack.getNbt().put(blockEntityTag); itemTag.put(blockEntityTag);
} else if (nbtTag.containsKey("Patterns", NbtType.LIST)) { } else if (itemTag.get("Patterns") instanceof ListTag patterns) {
List<NbtMap> patterns = nbtTag.getList("Patterns", NbtType.COMPOUND);
CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag"); CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
blockEntityTag.put(convertBannerPattern(patterns)); invertBannerColors(patterns);
blockEntityTag.put(patterns);
itemStack.getNbt().put(blockEntityTag); itemTag.put(blockEntityTag);
itemStack.getNbt().remove("Patterns"); // Remove the old Bedrock patterns list itemTag.remove("Patterns"); // Remove the old Bedrock patterns list
} }
return itemStack;
} }
@Override @Override
public List<ItemMapping> getAppliedItems() { public boolean acceptItem(ItemMapping mapping) {
return appliedItems; return appliedItems.contains(mapping);
} }
} }

Datei anzeigen

@ -121,10 +121,6 @@ public class EnchantmentTranslator extends NbtItemStackTranslator {
private CompoundTag remapEnchantment(CompoundTag tag) { private CompoundTag remapEnchantment(CompoundTag tag) {
Tag javaEnchLvl = tag.get("lvl");
if (!(javaEnchLvl instanceof ShortTag || javaEnchLvl instanceof IntTag))
return null;
Tag javaEnchId = tag.get("id"); Tag javaEnchId = tag.get("id");
if (!(javaEnchId instanceof StringTag)) if (!(javaEnchId instanceof StringTag))
return null; return null;
@ -135,9 +131,12 @@ public class EnchantmentTranslator extends NbtItemStackTranslator {
return null; return null;
} }
Tag javaEnchLvl = tag.get("lvl");
CompoundTag bedrockTag = new CompoundTag(""); CompoundTag bedrockTag = new CompoundTag("");
bedrockTag.put(new ShortTag("id", (short) enchantment.ordinal())); bedrockTag.put(new ShortTag("id", (short) enchantment.ordinal()));
bedrockTag.put(new ShortTag("lvl", ((Number) javaEnchLvl.getValue()).shortValue())); // If the tag cannot parse, Java Edition 1.18.2 sets to 0
bedrockTag.put(new ShortTag("lvl", javaEnchLvl != null && javaEnchLvl.getValue() instanceof Number lvl ? lvl.shortValue() : (short) 0));
return bedrockTag; return bedrockTag;
} }

Datei anzeigen

@ -28,9 +28,10 @@ package org.geysermc.geyser.translator.level.block.entity;
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType; import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtMapBuilder;
import org.geysermc.geyser.translator.inventory.item.BannerTranslator;
import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.translator.inventory.item.nbt.BannerTranslator;
@BlockEntity(type = BlockEntityType.BANNER) @BlockEntity(type = BlockEntityType.BANNER)
public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@ -45,8 +46,7 @@ public class BannerBlockEntityTranslator extends BlockEntityTranslator implement
return; return;
} }
if (tag.contains("Patterns")) { if (tag.get("Patterns") instanceof ListTag patterns) {
ListTag patterns = tag.get("Patterns");
if (patterns.equals(BannerTranslator.OMINOUS_BANNER_PATTERN)) { if (patterns.equals(BannerTranslator.OMINOUS_BANNER_PATTERN)) {
// This is an ominous banner; don't try to translate the raw patterns (it doesn't translate correctly) // This is an ominous banner; don't try to translate the raw patterns (it doesn't translate correctly)
// and tell the Bedrock client that this is an ominous banner // and tell the Bedrock client that this is an ominous banner
@ -56,8 +56,9 @@ public class BannerBlockEntityTranslator extends BlockEntityTranslator implement
} }
} }
if (tag.contains("CustomName")) { Tag customName = tag.get("CustomName");
builder.put("CustomName", tag.get("CustomName").getValue()); if (customName != null) {
builder.put("CustomName", customName.getValue());
} }
} }
} }

Datei anzeigen

@ -1,48 +0,0 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.translator.level.block.entity;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.packet.BlockEventPacket;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.level.block.BlockStateValues;
/**
* Does not implement BlockEntityTranslator because it's only a block entity in Bedrock
*/
public class NoteblockBlockEntityTranslator {
public static void translate(GeyserSession session, Position position) {
int blockState = session.getGeyser().getWorldManager().getBlockAt(session, position);
BlockEventPacket blockEventPacket = new BlockEventPacket();
blockEventPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ()));
blockEventPacket.setEventType(0);
blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState));
session.sendUpstreamPacket(blockEventPacket);
}
}

Datei anzeigen

@ -25,7 +25,6 @@
package org.geysermc.geyser.translator.level.block.entity; package org.geysermc.geyser.translator.level.block.entity;
import com.github.steveice10.mc.auth.data.GameProfile;
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType; import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityType;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.ListTag;
@ -35,15 +34,12 @@ import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.nbt.NbtMapBuilder;
import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.type.player.SkullPlayerEntity; import org.geysermc.geyser.entity.type.player.SkullPlayerEntity;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.level.block.BlockStateValues; import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.skin.SkinProvider; import org.geysermc.geyser.skin.SkinProvider;
import org.geysermc.geyser.skin.SkullSkinManager; import org.geysermc.geyser.skin.SkullSkinManager;
import java.util.ArrayList;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -62,7 +58,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
builder.put("SkullType", skullVariant); builder.put("SkullType", skullVariant);
} }
public static CompletableFuture<GameProfile> getProfile(CompoundTag tag) { private static CompletableFuture<String> getTextures(CompoundTag tag) {
CompoundTag owner = tag.get("SkullOwner"); CompoundTag owner = tag.get("SkullOwner");
if (owner != null) { if (owner != null) {
CompoundTag properties = owner.get("Properties"); CompoundTag properties = owner.get("Properties");
@ -73,13 +69,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
ListTag textures = properties.get("textures"); ListTag textures = properties.get("textures");
LinkedHashMap<?,?> tag1 = (LinkedHashMap<?,?>) textures.get(0).getValue(); LinkedHashMap<?,?> tag1 = (LinkedHashMap<?,?>) textures.get(0).getValue();
StringTag texture = (StringTag) tag1.get("Value"); StringTag texture = (StringTag) tag1.get("Value");
return CompletableFuture.completedFuture(texture.getValue());
List<GameProfile.Property> profileProperties = new ArrayList<>();
GameProfile gameProfile = new GameProfile(UUID.randomUUID(), "");
profileProperties.add(new GameProfile.Property("textures", texture.getValue()));
gameProfile.setProperties(profileProperties);
return CompletableFuture.completedFuture(gameProfile);
} }
return CompletableFuture.completedFuture(null); return CompletableFuture.completedFuture(null);
} }
@ -108,21 +98,21 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
Vector3i blockPosition = Vector3i.from(posX, posY, posZ); Vector3i blockPosition = Vector3i.from(posX, posY, posZ);
Vector3f entityPosition = Vector3f.from(x, y, z); Vector3f entityPosition = Vector3f.from(x, y, z);
getProfile(tag).whenComplete((gameProfile, throwable) -> { getTextures(tag).whenComplete((texturesProperty, throwable) -> {
if (gameProfile == null) { if (texturesProperty == null) {
session.getGeyser().getLogger().debug("Custom skull with invalid SkullOwner tag: " + blockPosition + " " + tag); session.getGeyser().getLogger().debug("Custom skull with invalid SkullOwner tag: " + blockPosition + " " + tag);
return; return;
} }
if (session.getEventLoop().inEventLoop()) { if (session.getEventLoop().inEventLoop()) {
spawnPlayer(session, gameProfile, blockPosition, entityPosition, rotation, blockState); spawnPlayer(session, texturesProperty, blockPosition, entityPosition, rotation, blockState);
} else { } else {
session.executeInEventLoop(() -> spawnPlayer(session, gameProfile, blockPosition, entityPosition, rotation, blockState)); session.executeInEventLoop(() -> spawnPlayer(session, texturesProperty, blockPosition, entityPosition, rotation, blockState));
} }
}); });
} }
private static void spawnPlayer(GeyserSession session, GameProfile profile, Vector3i blockPosition, private static void spawnPlayer(GeyserSession session, String texturesProperty, Vector3i blockPosition,
Vector3f entityPosition, float rotation, int blockState) { Vector3f entityPosition, float rotation, int blockState) {
long geyserId = session.getEntityCache().getNextEntityId().incrementAndGet(); long geyserId = session.getEntityCache().getNextEntityId().incrementAndGet();
@ -132,7 +122,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
existingSkull.despawnEntity(blockPosition); existingSkull.despawnEntity(blockPosition);
} }
SkullPlayerEntity player = new SkullPlayerEntity(session, geyserId, profile, entityPosition, rotation, blockState); SkullPlayerEntity player = new SkullPlayerEntity(session, geyserId, entityPosition, rotation, blockState, texturesProperty);
// Cache entity // Cache entity
session.getSkullCache().put(blockPosition, player); session.getSkullCache().put(blockPosition, player);

Datei anzeigen

@ -139,7 +139,8 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
case ITEM_USE: case ITEM_USE:
switch (packet.getActionType()) { switch (packet.getActionType()) {
case 0 -> { case 0 -> {
Vector3i blockPos = BlockUtils.getBlockPosition(packet.getBlockPosition(), packet.getBlockFace()); final Vector3i packetBlockPosition = packet.getBlockPosition();
Vector3i blockPos = BlockUtils.getBlockPosition(packetBlockPosition, packet.getBlockFace());
if (session.getGeyser().getConfig().isDisableBedrockScaffolding()) { if (session.getGeyser().getConfig().isDisableBedrockScaffolding()) {
float yaw = session.getPlayerEntity().getYaw(); float yaw = session.getPlayerEntity().getYaw();
@ -159,8 +160,8 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
// Check to make sure the client isn't spamming interaction // Check to make sure the client isn't spamming interaction
// Based on Nukkit 1.0, with changes to ensure holding down still works // Based on Nukkit 1.0, with changes to ensure holding down still works
boolean hasAlreadyClicked = System.currentTimeMillis() - session.getLastInteractionTime() < 110.0 && boolean hasAlreadyClicked = System.currentTimeMillis() - session.getLastInteractionTime() < 110.0 &&
packet.getBlockPosition().distanceSquared(session.getLastInteractionBlockPosition()) < 0.00001; packetBlockPosition.distanceSquared(session.getLastInteractionBlockPosition()) < 0.00001;
session.setLastInteractionBlockPosition(packet.getBlockPosition()); session.setLastInteractionBlockPosition(packetBlockPosition);
session.setLastInteractionPlayerPosition(session.getPlayerEntity().getPosition()); session.setLastInteractionPlayerPosition(session.getPlayerEntity().getPosition());
if (hasAlreadyClicked) { if (hasAlreadyClicked) {
break; break;
@ -204,22 +205,48 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
playerPosition = playerPosition.sub(0, (EntityDefinitions.PLAYER.offset() - 0.2f), 0); playerPosition = playerPosition.sub(0, (EntityDefinitions.PLAYER.offset() - 0.2f), 0);
} // else, we don't have to modify the position } // else, we don't have to modify the position
float diffX = playerPosition.getX() - packet.getBlockPosition().getX(); boolean creative = session.getGameMode() == GameMode.CREATIVE;
float diffY = playerPosition.getY() - packet.getBlockPosition().getY();
float diffZ = playerPosition.getZ() - packet.getBlockPosition().getZ(); float diffX = playerPosition.getX() - packetBlockPosition.getX();
float diffY = playerPosition.getY() - packetBlockPosition.getY();
float diffZ = playerPosition.getZ() - packetBlockPosition.getZ();
if (((diffX * diffX) + (diffY * diffY) + (diffZ * diffZ)) > if (((diffX * diffX) + (diffY * diffY) + (diffZ * diffZ)) >
(session.getGameMode().equals(GameMode.CREATIVE) ? CREATIVE_EYE_HEIGHT_PLACE_DISTANCE : SURVIVAL_EYE_HEIGHT_PLACE_DISTANCE)) { (creative ? CREATIVE_EYE_HEIGHT_PLACE_DISTANCE : SURVIVAL_EYE_HEIGHT_PLACE_DISTANCE)) {
restoreCorrectBlock(session, blockPos, packet); restoreCorrectBlock(session, blockPos, packet);
return; return;
} }
double clickPositionFullX = (double) packetBlockPosition.getX() + (double) packet.getClickPosition().getX();
double clickPositionFullY = (double) packetBlockPosition.getY() + (double) packet.getClickPosition().getY();
double clickPositionFullZ = (double) packetBlockPosition.getZ() + (double) packet.getClickPosition().getZ();
// More recent Paper check - https://github.com/PaperMC/Paper/blob/87e11bf7fdf48ecdf3e1cae383c368b9b61d7df9/patches/server/0470-Move-range-check-for-block-placing-up.patch
double clickDiffX = playerPosition.getX() - clickPositionFullX;
double clickDiffY = playerPosition.getY() - clickPositionFullY;
double clickDiffZ = playerPosition.getZ() - clickPositionFullZ;
if (((clickDiffX * clickDiffX) + (clickDiffY * clickDiffY) + (clickDiffZ * clickDiffZ)) >
(creative ? CREATIVE_EYE_HEIGHT_PLACE_DISTANCE : SURVIVAL_EYE_HEIGHT_PLACE_DISTANCE)) {
restoreCorrectBlock(session, blockPos, packet);
return;
}
Vector3f blockCenter = Vector3f.from(packetBlockPosition.getX() + 0.5f, packetBlockPosition.getY() + 0.5f, packetBlockPosition.getZ() + 0.5f);
// Vanilla check // Vanilla check
if (!(session.getPlayerEntity().getPosition().sub(0, EntityDefinitions.PLAYER.offset(), 0) if (!(session.getPlayerEntity().getPosition().sub(0, EntityDefinitions.PLAYER.offset(), 0)
.distanceSquared(packet.getBlockPosition().toFloat().add(0.5f, 0.5f, 0.5f)) < MAXIMUM_BLOCK_PLACING_DISTANCE)) { .distanceSquared(blockCenter) < MAXIMUM_BLOCK_PLACING_DISTANCE)) {
// The client thinks that its blocks have been successfully placed. Restore the server's blocks instead. // The client thinks that its blocks have been successfully placed. Restore the server's blocks instead.
restoreCorrectBlock(session, blockPos, packet); restoreCorrectBlock(session, blockPos, packet);
return; return;
} }
// More recent vanilla check (as of 1.18.2)
double clickDistanceX = clickPositionFullX - blockCenter.getX();
double clickDistanceY = clickPositionFullY - blockCenter.getY();
double clickDistanceZ = clickPositionFullZ - blockCenter.getZ();
if (!(Math.abs(clickDistanceX) < 1.0000001D && Math.abs(clickDistanceY) < 1.0000001D && Math.abs(clickDistanceZ) < 1.0000001D)) {
restoreCorrectBlock(session, blockPos, packet);
return;
}
/* /*
Block place checks end - client is good to go Block place checks end - client is good to go
*/ */
@ -284,7 +311,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
} }
} }
} }
ItemMapping handItem = mappings.getMapping(packet.getItemInHand()); ItemMapping handItem = session.getPlayerInventory().getItemInHand().getMapping(session);
if (handItem.isBlock()) { if (handItem.isBlock()) {
session.setLastBlockPlacePosition(blockPos); session.setLastBlockPlacePosition(blockPos);
session.setLastBlockPlacedId(handItem.getJavaIdentifier()); session.setLastBlockPlacedId(handItem.getJavaIdentifier());

Datei anzeigen

@ -30,6 +30,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.Server
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundUseItemPacket; import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundUseItemPacket;
import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; import com.nukkitx.protocol.bedrock.data.inventory.ContainerId;
import com.nukkitx.protocol.bedrock.packet.MobEquipmentPacket; import com.nukkitx.protocol.bedrock.packet.MobEquipmentPacket;
import org.geysermc.geyser.inventory.GeyserItemStack;
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;
@ -42,8 +43,9 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
@Override @Override
public void translate(GeyserSession session, MobEquipmentPacket packet) { public void translate(GeyserSession session, MobEquipmentPacket packet) {
if (!session.isSpawned() || packet.getHotbarSlot() > 8 || int newSlot = packet.getHotbarSlot();
packet.getContainerId() != ContainerId.INVENTORY || session.getPlayerInventory().getHeldItemSlot() == packet.getHotbarSlot()) { if (!session.isSpawned() || newSlot > 8 || packet.getContainerId() != ContainerId.INVENTORY
|| session.getPlayerInventory().getHeldItemSlot() == newSlot) {
// For the last condition - Don't update the slot if the slot is the same - not Java Edition behavior and messes with plugins such as Grief Prevention // For the last condition - Don't update the slot if the slot is the same - not Java Edition behavior and messes with plugins such as Grief Prevention
return; return;
} }
@ -51,12 +53,15 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
// Send book update before switching hotbar slot // Send book update before switching hotbar slot
session.getBookEditCache().checkForSend(); session.getBookEditCache().checkForSend();
session.getPlayerInventory().setHeldItemSlot(packet.getHotbarSlot()); GeyserItemStack oldItem = session.getPlayerInventory().getItemInHand();
session.getPlayerInventory().setHeldItemSlot(newSlot);
ServerboundSetCarriedItemPacket setCarriedItemPacket = new ServerboundSetCarriedItemPacket(packet.getHotbarSlot()); ServerboundSetCarriedItemPacket setCarriedItemPacket = new ServerboundSetCarriedItemPacket(newSlot);
session.sendDownstreamPacket(setCarriedItemPacket); session.sendDownstreamPacket(setCarriedItemPacket);
if (session.isSneaking() && session.getPlayerInventory().getItemInHand().getJavaId() == session.getItemMappings().getStoredItems().shield().getJavaId()) { GeyserItemStack newItem = session.getPlayerInventory().getItemInHand();
if (session.isSneaking() && newItem.getJavaId() == session.getItemMappings().getStoredItems().shield().getJavaId()) {
// Activate shield since we are already sneaking // Activate shield since we are already sneaking
// (No need to send a release item packet - Java doesn't do this when swapping items) // (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 // Required to do it a tick later or else it doesn't register
@ -64,8 +69,10 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
50, TimeUnit.MILLISECONDS); 50, TimeUnit.MILLISECONDS);
} }
// Java sends a cooldown indicator whenever you switch an item if (oldItem.getJavaId() != newItem.getJavaId()) {
CooldownUtils.sendCooldown(session); // Java sends a cooldown indicator whenever you switch to a new item type
CooldownUtils.sendCooldown(session);
}
// Update the interactive tag, if an entity is present // Update the interactive tag, if an entity is present
if (session.getMouseoverEntity() != null) { if (session.getMouseoverEntity() != null) {

Datei anzeigen

@ -172,8 +172,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
// Sort the list by each output item's Java identifier - this is how it's sorted on Java, and therefore // Sort the list by each output item's Java identifier - this is how it's sorted on Java, and therefore
// 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 ->
session.getItemMappings().getItems() session.getItemMappings().getMapping(stoneCuttingRecipeData.getResult())
.getOrDefault(stoneCuttingRecipeData.getResult().getId(), ItemMapping.AIR)
.getJavaIdentifier()))); .getJavaIdentifier())));
// Now that it's sorted, let's translate these recipes // Now that it's sorted, let's translate these recipes
@ -229,7 +228,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
GroupedItem groupedItem = entry.getKey(); GroupedItem groupedItem = entry.getKey();
int idCount = 0; int idCount = 0;
//not optimal //not optimal
for (ItemMapping mapping : session.getItemMappings().getItems().values()) { for (ItemMapping mapping : session.getItemMappings().getItems()) {
if (mapping.getBedrockId() == groupedItem.id) { if (mapping.getBedrockId() == groupedItem.id) {
idCount++; idCount++;
} }

Datei anzeigen

@ -25,6 +25,7 @@
package org.geysermc.geyser.translator.protocol.java.entity.player; package org.geysermc.geyser.translator.protocol.java.entity.player;
import com.github.steveice10.mc.auth.data.GameProfile;
import com.github.steveice10.mc.protocol.data.game.PlayerListEntry; import com.github.steveice10.mc.protocol.data.game.PlayerListEntry;
import com.github.steveice10.mc.protocol.data.game.PlayerListEntryAction; import com.github.steveice10.mc.protocol.data.game.PlayerListEntryAction;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundPlayerInfoPacket; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundPlayerInfoPacket;
@ -50,31 +51,38 @@ public class JavaPlayerInfoTranslator extends PacketTranslator<ClientboundPlayer
for (PlayerListEntry entry : packet.getEntries()) { for (PlayerListEntry entry : packet.getEntries()) {
switch (packet.getAction()) { switch (packet.getAction()) {
case ADD_PLAYER -> { case ADD_PLAYER -> {
GameProfile profile = entry.getProfile();
PlayerEntity playerEntity; PlayerEntity playerEntity;
boolean self = entry.getProfile().getId().equals(session.getPlayerEntity().getUuid()); boolean self = profile.getId().equals(session.getPlayerEntity().getUuid());
if (self) { if (self) {
// Entity is ourself // Entity is ourself
playerEntity = session.getPlayerEntity(); playerEntity = session.getPlayerEntity();
} else { } else {
playerEntity = session.getEntityCache().getPlayerEntity(entry.getProfile().getId()); playerEntity = session.getEntityCache().getPlayerEntity(profile.getId());
} }
GameProfile.Property textures = profile.getProperty("textures");
String texturesProperty = textures == null ? null : textures.getValue();
if (playerEntity == null) { if (playerEntity == null) {
// It's a new player // It's a new player
playerEntity = new PlayerEntity( playerEntity = new PlayerEntity(
session, session,
-1, -1,
session.getEntityCache().getNextEntityId().incrementAndGet(), session.getEntityCache().getNextEntityId().incrementAndGet(),
entry.getProfile(), profile.getId(),
Vector3f.ZERO, Vector3f.ZERO,
Vector3f.ZERO, Vector3f.ZERO,
0, 0, 0 0, 0, 0,
profile.getName(),
texturesProperty
); );
session.getEntityCache().addPlayerEntity(playerEntity); session.getEntityCache().addPlayerEntity(playerEntity);
} else { } else {
playerEntity.setProfile(entry.getProfile()); playerEntity.setUsername(profile.getName());
playerEntity.setTexturesProperty(texturesProperty);
} }
playerEntity.setPlayerList(true); playerEntity.setPlayerList(true);

Datei anzeigen

@ -48,7 +48,9 @@ public class JavaAddPlayerTranslator extends PacketTranslator<ClientboundAddPlay
PlayerEntity entity; PlayerEntity entity;
if (packet.getUuid().equals(session.getPlayerEntity().getUuid())) { if (packet.getUuid().equals(session.getPlayerEntity().getUuid())) {
// Server is sending a fake version of the current player // Server is sending a fake version of the current player
entity = new PlayerEntity(session, packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(), session.getPlayerEntity().getProfile(), position, Vector3f.ZERO, yaw, pitch, headYaw); entity = new PlayerEntity(session, packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
session.getPlayerEntity().getUuid(), position, Vector3f.ZERO, yaw, pitch, headYaw, session.getPlayerEntity().getUsername(),
session.getPlayerEntity().getTexturesProperty());
} else { } else {
entity = session.getEntityCache().getPlayerEntity(packet.getUuid()); entity = session.getEntityCache().getPlayerEntity(packet.getUuid());
if (entity == null) { if (entity == null) {

Datei anzeigen

@ -35,14 +35,13 @@ import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
import com.nukkitx.protocol.bedrock.packet.BlockEventPacket; import com.nukkitx.protocol.bedrock.packet.BlockEventPacket;
import it.unimi.dsi.fastutil.objects.Object2IntMaps; import it.unimi.dsi.fastutil.objects.Object2IntMaps;
import org.geysermc.common.PlatformType; import org.geysermc.common.PlatformType;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.level.physics.Direction;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.PistonCache; import org.geysermc.geyser.session.cache.PistonCache;
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity;
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.level.block.BlockStateValues;
import org.geysermc.geyser.translator.level.block.entity.NoteblockBlockEntityTranslator;
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity;
import org.geysermc.geyser.level.physics.Direction;
@Translator(packet = ClientboundBlockEventPacket.class) @Translator(packet = ClientboundBlockEventPacket.class)
public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockEventPacket> { public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockEventPacket> {
@ -50,8 +49,9 @@ public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockE
@Override @Override
public void translate(GeyserSession session, ClientboundBlockEventPacket packet) { public void translate(GeyserSession session, ClientboundBlockEventPacket packet) {
BlockEventPacket blockEventPacket = new BlockEventPacket(); BlockEventPacket blockEventPacket = new BlockEventPacket();
blockEventPacket.setBlockPosition(Vector3i.from(packet.getPosition().getX(), Position position = packet.getPosition();
packet.getPosition().getY(), packet.getPosition().getZ())); Vector3i vector = Vector3i.from(position.getX(), position.getY(), position.getZ());
blockEventPacket.setBlockPosition(vector);
if (packet.getValue() instanceof ChestValue value) { if (packet.getValue() instanceof ChestValue value) {
blockEventPacket.setEventType(1); blockEventPacket.setEventType(1);
blockEventPacket.setEventData(value.getViewers() > 0 ? 1 : 0); blockEventPacket.setEventData(value.getViewers() > 0 ? 1 : 0);
@ -60,11 +60,12 @@ public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockE
blockEventPacket.setEventType(1); blockEventPacket.setEventType(1);
session.sendUpstreamPacket(blockEventPacket); session.sendUpstreamPacket(blockEventPacket);
} else if (packet.getValue() instanceof NoteBlockValue) { } else if (packet.getValue() instanceof NoteBlockValue) {
NoteblockBlockEntityTranslator.translate(session, packet.getPosition()); int blockState = session.getGeyser().getWorldManager().getBlockAt(session, position);
blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState));
session.sendUpstreamPacket(blockEventPacket);
} else if (packet.getValue() instanceof PistonValue pistonValue) { } else if (packet.getValue() instanceof PistonValue pistonValue) {
PistonValueType action = (PistonValueType) packet.getType(); PistonValueType action = (PistonValueType) packet.getType();
Direction direction = Direction.fromPistonValue(pistonValue); Direction direction = Direction.fromPistonValue(pistonValue);
Vector3i position = Vector3i.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ());
PistonCache pistonCache = session.getPistonCache(); PistonCache pistonCache = session.getPistonCache();
if (session.getGeyser().getPlatformType() == PlatformType.SPIGOT) { if (session.getGeyser().getPlatformType() == PlatformType.SPIGOT) {
@ -77,20 +78,20 @@ public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockE
return; return;
} }
if (action != PistonValueType.CANCELLED_MID_PUSH) { if (action != PistonValueType.CANCELLED_MID_PUSH) {
Vector3i blockInFrontPos = position.add(direction.getUnitVector()); Vector3i blockInFrontPos = vector.add(direction.getUnitVector());
int blockInFront = session.getGeyser().getWorldManager().getBlockAt(session, blockInFrontPos); int blockInFront = session.getGeyser().getWorldManager().getBlockAt(session, blockInFrontPos);
if (blockInFront != BlockStateValues.JAVA_AIR_ID) { if (blockInFront != BlockStateValues.JAVA_AIR_ID) {
// Piston pulled something // Piston pulled something
return; return;
} }
} }
PistonBlockEntity blockEntity = pistonCache.getPistons().computeIfAbsent(position, pos -> new PistonBlockEntity(session, pos, direction, true, true)); PistonBlockEntity blockEntity = pistonCache.getPistons().computeIfAbsent(vector, pos -> new PistonBlockEntity(session, pos, direction, true, true));
if (blockEntity.getAction() != action) { if (blockEntity.getAction() != action) {
blockEntity.setAction(action, Object2IntMaps.emptyMap()); blockEntity.setAction(action, Object2IntMaps.emptyMap());
} }
} }
} else { } else {
PistonBlockEntity blockEntity = pistonCache.getPistons().computeIfAbsent(position, pos -> { PistonBlockEntity blockEntity = pistonCache.getPistons().computeIfAbsent(vector, pos -> {
int blockId = session.getGeyser().getWorldManager().getBlockAt(session, position); int blockId = session.getGeyser().getWorldManager().getBlockAt(session, position);
boolean sticky = BlockStateValues.isStickyPiston(blockId); boolean sticky = BlockStateValues.isStickyPiston(blockId);
boolean extended = action != PistonValueType.PUSHING; boolean extended = action != PistonValueType.PUSHING;
@ -106,10 +107,8 @@ public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockE
session.sendUpstreamPacket(blockEventPacket); session.sendUpstreamPacket(blockEventPacket);
} else if (packet.getValue() instanceof GenericBlockValue bellValue && packet.getBlockId() == BlockStateValues.JAVA_BELL_ID) { } else if (packet.getValue() instanceof GenericBlockValue bellValue && packet.getBlockId() == BlockStateValues.JAVA_BELL_ID) {
// Bells - needed to show ring from other players // Bells - needed to show ring from other players
Position position = packet.getPosition();
BlockEntityDataPacket blockEntityPacket = new BlockEntityDataPacket(); BlockEntityDataPacket blockEntityPacket = new BlockEntityDataPacket();
blockEntityPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ())); blockEntityPacket.setBlockPosition(vector);
NbtMapBuilder builder = NbtMap.builder(); NbtMapBuilder builder = NbtMap.builder();
builder.putInt("x", position.getX()); builder.putInt("x", position.getX());

Datei anzeigen

@ -37,11 +37,14 @@ public class JavaClearTitlesTranslator extends PacketTranslator<ClientboundClear
@Override @Override
public void translate(GeyserSession session, ClientboundClearTitlesPacket packet) { public void translate(GeyserSession session, ClientboundClearTitlesPacket packet) {
SetTitlePacket titlePacket = new SetTitlePacket(); SetTitlePacket titlePacket = new SetTitlePacket();
// TODO handle packet.isResetTimes()
titlePacket.setType(SetTitlePacket.Type.CLEAR); titlePacket.setType(SetTitlePacket.Type.CLEAR);
titlePacket.setText(""); titlePacket.setText("");
titlePacket.setXuid(""); titlePacket.setXuid("");
titlePacket.setPlatformOnlineId(""); titlePacket.setPlatformOnlineId("");
session.sendUpstreamPacket(titlePacket); session.sendUpstreamPacket(titlePacket);
if (packet.isResetTimes()) {
session.getWorldCache().resetTitleTimes(true);
}
} }
} }

Datei anzeigen

@ -38,6 +38,8 @@ public class JavaSetTitleTextTranslator extends PacketTranslator<ClientboundSetT
@Override @Override
public void translate(GeyserSession session, ClientboundSetTitleTextPacket packet) { public void translate(GeyserSession session, ClientboundSetTitleTextPacket packet) {
session.getWorldCache().synchronizeCorrectTitleTimes();
String text; String text;
if (packet.getText() == null || Component.empty().equals(packet.getText())) { // This can happen, see https://github.com/KyoriPowered/adventure/issues/447 if (packet.getText() == null || Component.empty().equals(packet.getText())) { // This can happen, see https://github.com/KyoriPowered/adventure/issues/447
text = " "; text = " ";

Datei anzeigen

@ -36,12 +36,17 @@ public class JavaSetTitlesAnimationTranslator extends PacketTranslator<Clientbou
@Override @Override
public void translate(GeyserSession session, ClientboundSetTitlesAnimationPacket packet) { public void translate(GeyserSession session, ClientboundSetTitlesAnimationPacket packet) {
int fadeInTime = packet.getFadeIn();
int stayTime = packet.getStay();
int fadeOutTime = packet.getFadeOut();
session.getWorldCache().setTitleTimes(fadeInTime, stayTime, fadeOutTime);
SetTitlePacket titlePacket = new SetTitlePacket(); SetTitlePacket titlePacket = new SetTitlePacket();
titlePacket.setType(SetTitlePacket.Type.TIMES); titlePacket.setType(SetTitlePacket.Type.TIMES);
titlePacket.setText(""); titlePacket.setText("");
titlePacket.setFadeInTime(packet.getFadeIn()); titlePacket.setFadeInTime(fadeInTime);
titlePacket.setFadeOutTime(packet.getFadeOut()); titlePacket.setFadeOutTime(fadeOutTime);
titlePacket.setStayTime(packet.getStay()); titlePacket.setStayTime(stayTime);
titlePacket.setXuid(""); titlePacket.setXuid("");
titlePacket.setPlatformOnlineId(""); titlePacket.setPlatformOnlineId("");
session.sendUpstreamPacket(titlePacket); session.sendUpstreamPacket(titlePacket);

Datei anzeigen

@ -57,8 +57,19 @@ public class CooldownUtils {
if (sessionPreference == CooldownType.DISABLED) return; if (sessionPreference == CooldownType.DISABLED) return;
if (session.getAttackSpeed() == 0.0 || session.getAttackSpeed() > 20) return; // 0.0 usually happens on login and causes issues with visuals; anything above 20 means a plugin like OldCombatMechanics is being used if (session.getAttackSpeed() == 0.0 || session.getAttackSpeed() > 20) return; // 0.0 usually happens on login and causes issues with visuals; anything above 20 means a plugin like OldCombatMechanics is being used
// Needs to be sent or no subtitle packet is recognized by the client // Set the times to stay a bit with no fade in nor out
SetTitlePacket titlePacket = new SetTitlePacket(); SetTitlePacket titlePacket = new SetTitlePacket();
titlePacket.setType(SetTitlePacket.Type.TIMES);
titlePacket.setStayTime(1000);
titlePacket.setText("");
titlePacket.setXuid("");
titlePacket.setPlatformOnlineId("");
session.sendUpstreamPacket(titlePacket);
session.getWorldCache().markTitleTimesAsIncorrect();
// Needs to be sent or no subtitle packet is recognized by the client
titlePacket = new SetTitlePacket();
titlePacket.setType(SetTitlePacket.Type.TITLE); titlePacket.setType(SetTitlePacket.Type.TITLE);
titlePacket.setText(" "); titlePacket.setText(" ");
titlePacket.setXuid(""); titlePacket.setXuid("");
@ -85,9 +96,6 @@ public class CooldownUtils {
titlePacket.setType(SetTitlePacket.Type.SUBTITLE); titlePacket.setType(SetTitlePacket.Type.SUBTITLE);
} }
titlePacket.setText(getTitle(session)); titlePacket.setText(getTitle(session));
titlePacket.setFadeInTime(0);
titlePacket.setFadeOutTime(5);
titlePacket.setStayTime(2);
titlePacket.setXuid(""); titlePacket.setXuid("");
titlePacket.setPlatformOnlineId(""); titlePacket.setPlatformOnlineId("");
session.sendUpstreamPacket(titlePacket); session.sendUpstreamPacket(titlePacket);
@ -96,11 +104,7 @@ public class CooldownUtils {
computeCooldown(session, sessionPreference, lastHitTime), 50, TimeUnit.MILLISECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50 computeCooldown(session, sessionPreference, lastHitTime), 50, TimeUnit.MILLISECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50
} else { } else {
SetTitlePacket removeTitlePacket = new SetTitlePacket(); SetTitlePacket removeTitlePacket = new SetTitlePacket();
if (sessionPreference == CooldownType.ACTIONBAR) { removeTitlePacket.setType(SetTitlePacket.Type.CLEAR);
removeTitlePacket.setType(SetTitlePacket.Type.ACTIONBAR);
} else {
removeTitlePacket.setType(SetTitlePacket.Type.SUBTITLE);
}
removeTitlePacket.setText(" "); removeTitlePacket.setText(" ");
removeTitlePacket.setXuid(""); removeTitlePacket.setXuid("");
removeTitlePacket.setPlatformOnlineId(""); removeTitlePacket.setPlatformOnlineId("");

Datei anzeigen

@ -29,21 +29,27 @@ import com.github.steveice10.opennbt.tag.builtin.*;
import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntMap;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import javax.annotation.Nullable;
public class ItemUtils { public class ItemUtils {
private static Int2IntMap DYE_COLORS = null; private static Int2IntMap DYE_COLORS = null;
public static int getEnchantmentLevel(CompoundTag itemNBTData, String enchantmentId) { public static int getEnchantmentLevel(@Nullable CompoundTag itemNBTData, String enchantmentId) {
ListTag enchantments = (itemNBTData == null ? null : itemNBTData.get("Enchantments")); if (itemNBTData == null) {
return 0;
}
ListTag enchantments = itemNBTData.get("Enchantments");
if (enchantments != null) { if (enchantments != null) {
int enchantmentLevel = 0;
for (Tag tag : enchantments) { for (Tag tag : enchantments) {
CompoundTag enchantment = (CompoundTag) tag; CompoundTag enchantment = (CompoundTag) tag;
StringTag enchantId = enchantment.get("id"); StringTag enchantId = enchantment.get("id");
if (enchantId.getValue().equals(enchantmentId)) { if (enchantId.getValue().equals(enchantmentId)) {
enchantmentLevel = (int) ((ShortTag) enchantment.get("lvl")).getValue(); Tag lvl = enchantment.get("lvl");
if (lvl != null && lvl.getValue() instanceof Number number) {
return number.intValue();
}
} }
} }
return enchantmentLevel;
} }
return 0; return 0;
} }

Datei anzeigen

@ -62,7 +62,7 @@ public class SettingsUtils {
// 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 (session.getPreferencesCache().isAllowShowCoordinates()) { if (session.getPreferencesCache().isAllowShowCoordinates()) {
builder.toggle("geyser.settings.option.coordinates", session.getPreferencesCache().isPrefersShowCoordinates()); builder.toggle("%createWorldScreen.showCoordinates", session.getPreferencesCache().isPrefersShowCoordinates());
} }
if (CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED) { if (CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED) {
@ -175,6 +175,10 @@ public class SettingsUtils {
} }
private static String translateEntry(String key, String locale) { private static String translateEntry(String key, String locale) {
if (key.startsWith("%")) {
// Bedrock will translate
return key;
}
if (key.startsWith("geyser.")) { if (key.startsWith("geyser.")) {
return GeyserLocale.getPlayerLocaleString(key, locale); return GeyserLocale.getPlayerLocaleString(key, locale);
} }