mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Merge branch 'master' of https://github.com/GeyserMC/Geyser into feature/extensions
This commit is contained in:
commit
1ebc3fd8f6
44 changed files with 611 additions and 432 deletions
|
@ -1,20 +1,25 @@
|
|||
val paperVersion = "1.17.1-R0.1-SNAPSHOT" // Needed because we do not support Java 17 yet
|
||||
val viaVersion = "4.0.0"
|
||||
val adaptersVersion = "1.4-SNAPSHOT"
|
||||
val commodoreVersion = "1.13"
|
||||
|
||||
dependencies {
|
||||
api(projects.core)
|
||||
|
||||
implementation("org.geysermc.geyser.adapters", "spigot-all", adaptersVersion)
|
||||
|
||||
implementation("me.lucko", "commodore", commodoreVersion)
|
||||
}
|
||||
|
||||
platformRelocate("it.unimi.dsi.fastutil")
|
||||
platformRelocate("com.fasterxml.jackson")
|
||||
platformRelocate("net.kyori")
|
||||
platformRelocate("org.objectweb.asm")
|
||||
platformRelocate("me.lucko.commodore")
|
||||
|
||||
// These dependencies are already present on the platform
|
||||
provided("io.papermc.paper", "paper-api", paperVersion)
|
||||
provided("io.papermc.paper", "paper-mojangapi", paperVersion)
|
||||
provided("com.viaversion", "viaversion", viaVersion)
|
||||
|
||||
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-resolver-dns:.*"))
|
||||
exclude(dependency("io.netty:netty-resolver-dns-native-macos:.*"))
|
||||
|
||||
// Commodore includes Brigadier
|
||||
exclude(dependency("com.mojang:.*"))
|
||||
}
|
||||
}
|
|
@ -29,7 +29,9 @@ import com.viaversion.viaversion.api.Via;
|
|||
import com.viaversion.viaversion.api.data.MappingData;
|
||||
import com.viaversion.viaversion.api.protocol.ProtocolPathEntry;
|
||||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import me.lucko.commodore.CommodoreProvider;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.geysermc.common.PlatformType;
|
||||
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.ping.GeyserLegacyPingPassthrough;
|
||||
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.GeyserSpigotCommandManager;
|
||||
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);
|
||||
|
||||
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
|
||||
GeyserSpigotVersionChecker.checkForSupportedProtocol(geyserLogger, isViaVersion);
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@
|
|||
package org.geysermc.geyser.platform.spigot.command;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandMap;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
|
@ -35,16 +36,24 @@ import java.lang.reflect.Field;
|
|||
|
||||
public class GeyserSpigotCommandManager extends GeyserCommandManager {
|
||||
|
||||
private static CommandMap COMMAND_MAP;
|
||||
private static final CommandMap COMMAND_MAP;
|
||||
|
||||
static {
|
||||
CommandMap commandMap = null;
|
||||
try {
|
||||
Field cmdMapField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
|
||||
cmdMapField.setAccessible(true);
|
||||
COMMAND_MAP = (CommandMap) cmdMapField.get(Bukkit.getServer());
|
||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||
ex.printStackTrace();
|
||||
// Paper-only
|
||||
Server.class.getMethod("getCommandMap");
|
||||
commandMap = Bukkit.getServer().getCommandMap();
|
||||
} catch (NoSuchMethodException e) {
|
||||
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) {
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
*/
|
||||
|
||||
object Versions {
|
||||
const val jacksonVersion = "2.12.4"
|
||||
const val jacksonVersion = "2.13.2"
|
||||
const val fastutilVersion = "8.5.2"
|
||||
const val nettyVersion = "4.1.66.Final"
|
||||
const val guavaVersion = "29.0-jre"
|
||||
|
|
|
@ -12,7 +12,7 @@ dependencies {
|
|||
|
||||
// Jackson JSON and YAML serialization
|
||||
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.google.guava", "guava", Versions.guavaVersion)
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
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.Pose;
|
||||
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 java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Getter @Setter
|
||||
public class PlayerEntity extends LivingEntity {
|
||||
public static final float SNEAKING_POSE_HEIGHT = 1.5f;
|
||||
|
||||
private GameProfile profile;
|
||||
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;
|
||||
|
||||
|
@ -82,11 +87,12 @@ public class PlayerEntity extends LivingEntity {
|
|||
*/
|
||||
private ParrotEntity rightParrot;
|
||||
|
||||
public PlayerEntity(GeyserSession session, int entityId, long geyserId, GameProfile gameProfile, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
||||
super(session, entityId, geyserId, gameProfile.getId(), EntityDefinitions.PLAYER, position, motion, yaw, pitch, headYaw);
|
||||
public PlayerEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, Vector3f position,
|
||||
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;
|
||||
username = gameProfile.getName();
|
||||
this.username = username;
|
||||
this.texturesProperty = texturesProperty;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
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.AttributeType;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
|
||||
|
@ -71,7 +70,7 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
private int fakeTradeXp;
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
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.Vector3i;
|
||||
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
|
||||
|
@ -36,6 +35,8 @@ import com.nukkitx.protocol.bedrock.packet.AddPlayerPacket;
|
|||
import lombok.Getter;
|
||||
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
|
||||
* custom player skulls in Bedrock.
|
||||
|
@ -48,8 +49,8 @@ public class SkullPlayerEntity extends PlayerEntity {
|
|||
@Getter
|
||||
private final int blockState;
|
||||
|
||||
public SkullPlayerEntity(GeyserSession session, long geyserId, GameProfile gameProfile, Vector3f position, float rotation, int blockState) {
|
||||
super(session, 0, geyserId, gameProfile, position, Vector3f.ZERO, rotation, 0, rotation);
|
||||
public SkullPlayerEntity(GeyserSession session, long geyserId, Vector3f position, float rotation, int blockState, String texturesProperty) {
|
||||
super(session, 0, geyserId, UUID.randomUUID(), position, Vector3f.ZERO, rotation, 0, rotation, "", texturesProperty);
|
||||
this.blockState = blockState;
|
||||
setPlayerList(false);
|
||||
}
|
||||
|
|
|
@ -391,7 +391,7 @@ public final class ClickPlan {
|
|||
public IntSet getAffectedSlots() {
|
||||
IntSet affectedSlots = new IntOpenHashSet();
|
||||
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);
|
||||
if (action.click.actionType == ContainerActionType.MOVE_TO_HOTBAR_SLOT) {
|
||||
//TODO won't work if offhand is added
|
||||
|
|
|
@ -52,7 +52,6 @@ public class StoredItemMappings {
|
|||
private final int goldIngot;
|
||||
private final int ironIngot;
|
||||
private final int lead;
|
||||
private final ItemMapping lodestoneCompass;
|
||||
private final ItemMapping milkBucket;
|
||||
private final int nameTag;
|
||||
private final ItemMapping powderSnowBucket;
|
||||
|
@ -80,7 +79,6 @@ public class StoredItemMappings {
|
|||
this.goldIngot = load(itemMappings, "gold_ingot").getJavaId();
|
||||
this.ironIngot = load(itemMappings, "iron_ingot").getJavaId();
|
||||
this.lead = load(itemMappings, "lead").getJavaId();
|
||||
this.lodestoneCompass = load(itemMappings, "lodestone_compass");
|
||||
this.milkBucket = load(itemMappings, "milk_bucket");
|
||||
this.nameTag = load(itemMappings, "name_tag").getJavaId();
|
||||
this.powderSnowBucket = load(itemMappings, "powder_snow_bucket");
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -128,7 +128,7 @@ public class ItemRegistryPopulator {
|
|||
IntList spawnEggs = new IntArrayList();
|
||||
List<ItemData> carpets = new ObjectArrayList<>();
|
||||
|
||||
Int2ObjectMap<ItemMapping> mappings = new Int2ObjectOpenHashMap<>();
|
||||
List<ItemMapping> mappings = new ObjectArrayList<>();
|
||||
// Temporary mapping to create stored items
|
||||
Map<String, ItemMapping> identifierToMapping = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
|
@ -166,6 +166,9 @@ public class ItemRegistryPopulator {
|
|||
if (identifier.equals("minecraft:debug_stick")) {
|
||||
// Just shows an empty texture; either way it doesn't exist in the creative menu on Java
|
||||
continue;
|
||||
} else if (identifier.equals("minecraft:empty_map") && damage == 2) {
|
||||
// Bedrock-only as its own item
|
||||
continue;
|
||||
}
|
||||
StartGamePacket.ItemEntry entry = entries.get(identifier);
|
||||
int id = -1;
|
||||
|
@ -240,6 +243,8 @@ public class ItemRegistryPopulator {
|
|||
if (usingFurnaceMinecart && javaIdentifier.equals("minecraft:furnace_minecart")) {
|
||||
javaFurnaceMinecartId = itemIndex;
|
||||
itemIndex++;
|
||||
// Will be added later
|
||||
mappings.add(null);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -416,7 +421,7 @@ public class ItemRegistryPopulator {
|
|||
spawnEggs.add(mapping.getBedrockId());
|
||||
}
|
||||
|
||||
mappings.put(itemIndex, mapping);
|
||||
mappings.add(mapping);
|
||||
identifierToMapping.put(javaIdentifier, mapping);
|
||||
|
||||
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
|
||||
ItemMapping lodestoneEntry = ItemMapping.builder()
|
||||
.javaIdentifier("minecraft:lodestone_compass")
|
||||
.javaIdentifier("")
|
||||
.bedrockIdentifier("minecraft:lodestone_compass")
|
||||
.javaId(itemIndex)
|
||||
.javaId(-1)
|
||||
.bedrockId(lodestoneCompassId)
|
||||
.bedrockData(0)
|
||||
.bedrockBlockId(-1)
|
||||
.stackSize(1)
|
||||
.build();
|
||||
mappings.put(itemIndex, lodestoneEntry);
|
||||
identifierToMapping.put(lodestoneEntry.getJavaIdentifier(), lodestoneEntry);
|
||||
|
||||
ComponentItemData furnaceMinecartData = null;
|
||||
if (usingFurnaceMinecart) {
|
||||
|
@ -455,7 +458,7 @@ public class ItemRegistryPopulator {
|
|||
|
||||
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")
|
||||
.bedrockIdentifier("geysermc:furnace_minecart")
|
||||
.javaId(javaFurnaceMinecartId)
|
||||
|
@ -506,9 +509,9 @@ public class ItemRegistryPopulator {
|
|||
}
|
||||
|
||||
ItemMappings itemMappings = ItemMappings.builder()
|
||||
.items(mappings)
|
||||
.items(mappings.toArray(new ItemMapping[0]))
|
||||
.creativeItems(creativeItems.toArray(new ItemData[0]))
|
||||
.itemEntries(new ArrayList<>(entries.values()))
|
||||
.itemEntries(List.copyOf(entries.values()))
|
||||
.itemNames(itemNames.toArray(new String[0]))
|
||||
.storedItems(new StoredItemMappings(identifierToMapping))
|
||||
.javaOnlyItems(javaOnlyItems)
|
||||
|
@ -517,6 +520,7 @@ public class ItemRegistryPopulator {
|
|||
.spawnEggIds(spawnEggs)
|
||||
.carpets(carpets)
|
||||
.furnaceMinecartData(furnaceMinecartData)
|
||||
.lodestoneCompass(lodestoneEntry)
|
||||
.build();
|
||||
|
||||
Registries.ITEMS.register(palette.getValue().protocolVersion(), itemMappings);
|
||||
|
|
|
@ -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.ItemData;
|
||||
import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import lombok.Builder;
|
||||
import lombok.Value;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.inventory.item.StoredItemMappings;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -48,7 +48,12 @@ public class ItemMappings {
|
|||
|
||||
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;
|
||||
List<StartGamePacket.ItemEntry> itemEntries;
|
||||
|
@ -70,6 +75,7 @@ public class ItemMappings {
|
|||
* @param itemStack the itemstack
|
||||
* @return an item entry from the given java edition identifier
|
||||
*/
|
||||
@Nonnull
|
||||
public ItemMapping getMapping(ItemStack itemStack) {
|
||||
return this.getMapping(itemStack.getId());
|
||||
}
|
||||
|
@ -81,8 +87,9 @@ public class ItemMappings {
|
|||
* @param javaId the id
|
||||
* @return an item entry from the given java edition identifier
|
||||
*/
|
||||
@Nonnull
|
||||
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) {
|
||||
return this.cachedJavaMappings.computeIfAbsent(javaIdentifier, key -> {
|
||||
for (ItemMapping mapping : this.items.values()) {
|
||||
for (ItemMapping mapping : this.items) {
|
||||
if (mapping.getJavaIdentifier().equals(key)) {
|
||||
return mapping;
|
||||
}
|
||||
|
@ -110,11 +117,18 @@ public class ItemMappings {
|
|||
* @return an item entry from the given item 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 hasDamage = data.getDamage() != 0;
|
||||
|
||||
for (ItemMapping mapping : this.items.values()) {
|
||||
if (mapping.getBedrockId() == data.getId()) {
|
||||
for (ItemMapping mapping : this.items) {
|
||||
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 (data.getBlockRuntimeId() != mapping.getBedrockBlockId()) {
|
||||
continue;
|
||||
|
@ -135,7 +149,7 @@ public class ItemMappings {
|
|||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
return ItemMapping.AIR;
|
||||
|
|
|
@ -26,24 +26,36 @@
|
|||
package org.geysermc.geyser.session.cache;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetTitlePacket;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.scoreboard.Scoreboard;
|
||||
import org.geysermc.geyser.scoreboard.ScoreboardUpdater.ScoreboardSession;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
@Getter
|
||||
public class WorldCache {
|
||||
public final class WorldCache {
|
||||
private final GeyserSession session;
|
||||
@Getter
|
||||
private final ScoreboardSession scoreboardSession;
|
||||
@Getter
|
||||
private Scoreboard scoreboard;
|
||||
@Getter
|
||||
@Setter
|
||||
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) {
|
||||
this.session = session;
|
||||
this.scoreboard = new Scoreboard(session);
|
||||
scoreboardSession = new ScoreboardSession(session);
|
||||
resetTitleTimes(false);
|
||||
}
|
||||
|
||||
public void removeScoreboard() {
|
||||
|
@ -58,4 +70,55 @@ public class WorldCache {
|
|||
int pps = scoreboardSession.getPacketsPerSecond();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
package org.geysermc.geyser.skin;
|
||||
|
||||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
|
@ -106,7 +105,7 @@ public class FakeHeadProvider {
|
|||
|
||||
session.getPlayerWithCustomHeads().add(entity.getUuid());
|
||||
|
||||
GameProfile.Property texturesProperty = entity.getProfile().getProperty("textures");
|
||||
String texturesProperty = entity.getTexturesProperty();
|
||||
|
||||
SkinProvider.EXECUTOR_SERVICE.execute(() -> {
|
||||
try {
|
||||
|
@ -182,7 +181,7 @@ public class FakeHeadProvider {
|
|||
@Getter
|
||||
@Setter
|
||||
private static class FakeHeadEntry {
|
||||
private final GameProfile.Property texturesProperty;
|
||||
private final String texturesProperty;
|
||||
private final String fakeHeadSkinUrl;
|
||||
private PlayerEntity entity;
|
||||
|
||||
|
@ -192,18 +191,7 @@ public class FakeHeadProvider {
|
|||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
FakeHeadEntry that = (FakeHeadEntry) o;
|
||||
return 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());
|
||||
return Objects.equals(texturesProperty, that.texturesProperty) && Objects.equals(fakeHeadSkinUrl, that.fakeHeadSkinUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
package org.geysermc.geyser.skin;
|
||||
|
||||
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.ListTag;
|
||||
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
|
||||
*/
|
||||
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.SkinGeometry geometry = SkinProvider.SkinGeometry.getLegacy(data.isAlex());
|
||||
|
||||
|
@ -65,8 +64,8 @@ public class SkinManager {
|
|||
|
||||
return buildEntryManually(
|
||||
session,
|
||||
playerEntity.getProfile().getId(),
|
||||
playerEntity.getProfile().getName(),
|
||||
playerEntity.getUuid(),
|
||||
playerEntity.getUsername(),
|
||||
playerEntity.getGeyserId(),
|
||||
skin.getTextureUrl(),
|
||||
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
|
||||
*/
|
||||
public static GameProfileData from(GameProfile profile) {
|
||||
public static GameProfileData from(PlayerEntity entity) {
|
||||
try {
|
||||
GameProfile.Property skinProperty = profile.getProperty("textures");
|
||||
String texturesProperty = entity.getTexturesProperty();
|
||||
|
||||
if (skinProperty == null) {
|
||||
if (texturesProperty == null) {
|
||||
// Likely offline mode
|
||||
return loadBedrockOrOfflineSkin(profile);
|
||||
return loadBedrockOrOfflineSkin(entity);
|
||||
}
|
||||
GameProfileData data = loadFromJson(skinProperty.getValue());
|
||||
GameProfileData data = loadFromJson(texturesProperty);
|
||||
if (data != null) {
|
||||
return data;
|
||||
} else {
|
||||
return loadBedrockOrOfflineSkin(profile);
|
||||
return loadBedrockOrOfflineSkin(entity);
|
||||
}
|
||||
} 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()) {
|
||||
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
|
||||
* 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
|
||||
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 capeUrl = SkinProvider.EMPTY_CAPE.getTextureUrl();
|
||||
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) {
|
||||
skinUrl = session.getClientData().getSkinId();
|
||||
|
|
|
@ -27,7 +27,6 @@ package org.geysermc.geyser.skin;
|
|||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
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.IntArrayTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
|
@ -53,7 +52,6 @@ import java.io.IOException;
|
|||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.function.Predicate;
|
||||
|
@ -157,7 +155,7 @@ public class SkinProvider {
|
|||
}
|
||||
|
||||
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())
|
||||
.thenApplyAsync(skinAndCape -> {
|
||||
|
@ -546,12 +544,11 @@ public class SkinProvider {
|
|||
* @param skullOwner the CompoundTag of the skull with no textures
|
||||
* @return a completable GameProfile with textures included
|
||||
*/
|
||||
public static CompletableFuture<GameProfile> requestTexturesFromUsername(CompoundTag skullOwner) {
|
||||
public static CompletableFuture<String> requestTexturesFromUsername(CompoundTag skullOwner) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
Tag uuidTag = skullOwner.get("Id");
|
||||
String uuidToString = "";
|
||||
JsonNode node;
|
||||
GameProfile gameProfile = new GameProfile(UUID.randomUUID(), "");
|
||||
boolean retrieveUuidFromInternet = !(uuidTag instanceof IntArrayTag); // also covers null check
|
||||
|
||||
if (!retrieveUuidFromInternet) {
|
||||
|
@ -577,15 +574,12 @@ public class SkinProvider {
|
|||
|
||||
// Get textures from UUID
|
||||
node = WebUtils.getJson("https://sessionserver.mojang.com/session/minecraft/profile/" + uuidToString);
|
||||
List<GameProfile.Property> profileProperties = new ArrayList<>();
|
||||
JsonNode properties = node.get("properties");
|
||||
if (properties == null) {
|
||||
GeyserImpl.getInstance().getLogger().debug("No properties found in Mojang response for " + uuidToString);
|
||||
return null;
|
||||
}
|
||||
profileProperties.add(new GameProfile.Property("textures", node.get("properties").get(0).get("value").asText()));
|
||||
gameProfile.setProperties(profileProperties);
|
||||
return gameProfile;
|
||||
return node.get("properties").get(0).get("value").asText();
|
||||
} catch (Exception e) {
|
||||
if (GeyserImpl.getInstance().getConfig().isDebugMode()) {
|
||||
e.printStackTrace();
|
||||
|
|
|
@ -50,7 +50,7 @@ public class SkullSkinManager extends SkinManager {
|
|||
|
||||
public static void requestAndHandleSkin(PlayerEntity entity, GeyserSession session,
|
||||
Consumer<SkinProvider.Skin> skinConsumer) {
|
||||
GameProfileData data = GameProfileData.from(entity.getProfile());
|
||||
GameProfileData data = GameProfileData.from(entity);
|
||||
|
||||
SkinProvider.requestSkin(entity.getUuid(), data.skinUrl(), true)
|
||||
.whenCompleteAsync((skin, throwable) -> {
|
||||
|
|
|
@ -41,13 +41,13 @@ import com.nukkitx.protocol.bedrock.data.inventory.stackrequestactions.StackRequ
|
|||
import com.nukkitx.protocol.bedrock.packet.ItemStackResponsePacket;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import org.geysermc.geyser.inventory.BedrockContainerSlot;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
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.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.List;
|
||||
|
|
|
@ -35,28 +35,18 @@ import org.geysermc.geyser.registry.Registries;
|
|||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.registry.type.ItemMappings;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ItemRemapper
|
||||
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
|
||||
protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) {
|
||||
if (isLodestoneCompass(itemStack.getNbt())) {
|
||||
// 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);
|
||||
}
|
||||
|
@ -64,7 +54,7 @@ public class CompassTranslator extends ItemTranslator {
|
|||
@Override
|
||||
protected ItemMapping getItemMapping(int javaId, CompoundTag nbt, ItemMappings mappings) {
|
||||
if (isLodestoneCompass(nbt)) {
|
||||
return mappings.getStoredItems().lodestoneCompass();
|
||||
return mappings.getLodestoneCompass();
|
||||
}
|
||||
return super.getItemMapping(javaId, nbt, mappings);
|
||||
}
|
||||
|
@ -89,6 +79,9 @@ public class CompassTranslator extends ItemTranslator {
|
|||
|
||||
@Override
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
);
|
||||
}
|
||||
}
|
|
@ -47,6 +47,7 @@ import org.geysermc.geyser.translator.text.MessageTranslator;
|
|||
import org.geysermc.geyser.util.FileUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -71,11 +72,11 @@ public abstract class ItemTranslator {
|
|||
|
||||
try {
|
||||
if (NbtItemStackTranslator.class.isAssignableFrom(clazz)) {
|
||||
NbtItemStackTranslator nbtItemTranslator = (NbtItemStackTranslator) clazz.newInstance();
|
||||
NbtItemStackTranslator nbtItemTranslator = (NbtItemStackTranslator) clazz.getDeclaredConstructor().newInstance();
|
||||
loadedNbtItemTranslators.put(nbtItemTranslator, priority);
|
||||
continue;
|
||||
}
|
||||
ItemTranslator itemStackTranslator = (ItemTranslator) clazz.newInstance();
|
||||
ItemTranslator itemStackTranslator = (ItemTranslator) clazz.getDeclaredConstructor().newInstance();
|
||||
List<ItemMapping> appliedItems = itemStackTranslator.getAppliedItems();
|
||||
for (ItemMapping item : appliedItems) {
|
||||
ItemTranslator registered = ITEM_STACK_TRANSLATORS.get(item.getJavaId());
|
||||
|
@ -87,7 +88,7 @@ public abstract class ItemTranslator {
|
|||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
@ -302,13 +303,16 @@ public abstract class ItemTranslator {
|
|||
return new ItemStack(mapping.getJavaId(), itemData.getCount(), this.translateToJavaNBT("", itemData.getTag()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for initialization only and only called once.
|
||||
*/
|
||||
public abstract List<ItemMapping> getAppliedItems();
|
||||
|
||||
protected ItemMapping getItemMapping(int javaId, CompoundTag nbt, ItemMappings mappings) {
|
||||
return mappings.getMapping(javaId);
|
||||
}
|
||||
|
||||
public NbtMap translateNbtToBedrock(CompoundTag tag) {
|
||||
protected NbtMap translateNbtToBedrock(CompoundTag tag) {
|
||||
NbtMapBuilder builder = NbtMap.builder();
|
||||
if (tag.getValue() != null && !tag.getValue().isEmpty()) {
|
||||
for (String str : tag.getValue().keySet()) {
|
||||
|
@ -387,7 +391,7 @@ public abstract class ItemTranslator {
|
|||
return null;
|
||||
}
|
||||
|
||||
public CompoundTag translateToJavaNBT(String name, NbtMap tag) {
|
||||
private CompoundTag translateToJavaNBT(String name, NbtMap tag) {
|
||||
CompoundTag javaTag = new CompoundTag(name);
|
||||
Map<String, Tag> javaValue = javaTag.getValue();
|
||||
if (tag != null && !tag.isEmpty()) {
|
||||
|
|
|
@ -29,12 +29,12 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
|||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
|
||||
public class NbtItemStackTranslator {
|
||||
public abstract class NbtItemStackTranslator {
|
||||
|
||||
/**
|
||||
* Translate the item NBT to Bedrock
|
||||
* @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
|
||||
*/
|
||||
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) {
|
||||
|
|
|
@ -36,23 +36,13 @@ import org.geysermc.geyser.registry.Registries;
|
|||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.registry.type.ItemMappings;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ItemRemapper
|
||||
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
|
||||
protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) {
|
||||
if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, mapping, mappings);
|
||||
|
@ -84,6 +74,9 @@ public class PotionTranslator extends ItemTranslator {
|
|||
|
||||
@Override
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,28 +36,16 @@ import org.geysermc.geyser.registry.Registries;
|
|||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.registry.type.ItemMappings;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ItemRemapper
|
||||
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())
|
||||
.getMapping("minecraft:tipped_arrow")
|
||||
.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
|
||||
protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) {
|
||||
if (!mapping.getJavaIdentifier().equals("minecraft:tipped_arrow") || itemStack.getNbt() == null) {
|
||||
|
@ -93,6 +81,10 @@ public class TippedArrowTranslator extends ItemTranslator {
|
|||
|
||||
@Override
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,29 +23,25 @@
|
|||
* @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.nukkitx.nbt.NbtList;
|
||||
import com.nukkitx.nbt.NbtMap;
|
||||
import com.nukkitx.nbt.NbtMapBuilder;
|
||||
import com.nukkitx.nbt.NbtType;
|
||||
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 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 java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ItemRemapper
|
||||
public class BannerTranslator extends ItemTranslator {
|
||||
public class BannerTranslator extends NbtItemStackTranslator {
|
||||
/**
|
||||
* Holds what a Java ominous banner pattern looks like.
|
||||
*
|
||||
|
@ -80,10 +76,8 @@ public class BannerTranslator extends ItemTranslator {
|
|||
}
|
||||
|
||||
public BannerTranslator() {
|
||||
appliedItems = Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion())
|
||||
.getItems()
|
||||
.values()
|
||||
.stream()
|
||||
appliedItems = Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion())
|
||||
.getItems())
|
||||
.filter(entry -> entry.getJavaIdentifier().endsWith("banner"))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
@ -117,21 +111,6 @@ public class BannerTranslator extends ItemTranslator {
|
|||
.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
|
||||
*
|
||||
|
@ -146,62 +125,54 @@ public class BannerTranslator extends ItemTranslator {
|
|||
return new CompoundTag("", tags);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ItemData.Builder translateToBedrock(ItemStack itemStack, ItemMapping mapping, ItemMappings mappings) {
|
||||
if (itemStack.getNbt() == null) {
|
||||
return super.translateToBedrock(itemStack, mapping, mappings);
|
||||
/**
|
||||
* Convert a list of patterns from Java nbt to Bedrock nbt, or vice versa (we just need to invert the color)
|
||||
*
|
||||
* @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
|
||||
public ItemStack translateToJava(ItemData itemData, ItemMapping mapping, ItemMappings mappings) {
|
||||
if (itemData.getTag() == null) {
|
||||
return super.translateToJava(itemData, mapping, mappings);
|
||||
public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemMapping mapping) {
|
||||
CompoundTag blockEntityTag = itemTag.get("BlockEntityTag");
|
||||
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);
|
||||
|
||||
NbtMap nbtTag = itemData.getTag();
|
||||
if (nbtTag.containsKey("Type", NbtType.INT) && nbtTag.getInt("Type") == 1) {
|
||||
@Override
|
||||
public void translateToJava(CompoundTag itemTag, ItemMapping mapping) {
|
||||
if (itemTag.get("Type") instanceof IntTag type && type.getValue() == 1) {
|
||||
// Ominous banner pattern
|
||||
itemStack.getNbt().remove("Type");
|
||||
itemTag.remove("Type");
|
||||
CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
|
||||
blockEntityTag.put(OMINOUS_BANNER_PATTERN);
|
||||
|
||||
itemStack.getNbt().put(blockEntityTag);
|
||||
} else if (nbtTag.containsKey("Patterns", NbtType.LIST)) {
|
||||
List<NbtMap> patterns = nbtTag.getList("Patterns", NbtType.COMPOUND);
|
||||
|
||||
itemTag.put(blockEntityTag);
|
||||
} else if (itemTag.get("Patterns") instanceof ListTag patterns) {
|
||||
CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
|
||||
blockEntityTag.put(convertBannerPattern(patterns));
|
||||
invertBannerColors(patterns);
|
||||
blockEntityTag.put(patterns);
|
||||
|
||||
itemStack.getNbt().put(blockEntityTag);
|
||||
itemStack.getNbt().remove("Patterns"); // Remove the old Bedrock patterns list
|
||||
itemTag.put(blockEntityTag);
|
||||
itemTag.remove("Patterns"); // Remove the old Bedrock patterns list
|
||||
}
|
||||
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ItemMapping> getAppliedItems() {
|
||||
return appliedItems;
|
||||
public boolean acceptItem(ItemMapping mapping) {
|
||||
return appliedItems.contains(mapping);
|
||||
}
|
||||
}
|
|
@ -121,10 +121,6 @@ public class EnchantmentTranslator extends NbtItemStackTranslator {
|
|||
|
||||
|
||||
private CompoundTag remapEnchantment(CompoundTag tag) {
|
||||
Tag javaEnchLvl = tag.get("lvl");
|
||||
if (!(javaEnchLvl instanceof ShortTag || javaEnchLvl instanceof IntTag))
|
||||
return null;
|
||||
|
||||
Tag javaEnchId = tag.get("id");
|
||||
if (!(javaEnchId instanceof StringTag))
|
||||
return null;
|
||||
|
@ -135,9 +131,12 @@ public class EnchantmentTranslator extends NbtItemStackTranslator {
|
|||
return null;
|
||||
}
|
||||
|
||||
Tag javaEnchLvl = tag.get("lvl");
|
||||
|
||||
CompoundTag bedrockTag = new CompoundTag("");
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
import com.nukkitx.nbt.NbtMapBuilder;
|
||||
import org.geysermc.geyser.translator.inventory.item.BannerTranslator;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.translator.inventory.item.nbt.BannerTranslator;
|
||||
|
||||
@BlockEntity(type = BlockEntityType.BANNER)
|
||||
public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
|
||||
|
@ -45,8 +46,7 @@ public class BannerBlockEntityTranslator extends BlockEntityTranslator implement
|
|||
return;
|
||||
}
|
||||
|
||||
if (tag.contains("Patterns")) {
|
||||
ListTag patterns = tag.get("Patterns");
|
||||
if (tag.get("Patterns") instanceof ListTag patterns) {
|
||||
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)
|
||||
// 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")) {
|
||||
builder.put("CustomName", tag.get("CustomName").getValue());
|
||||
Tag customName = tag.get("CustomName");
|
||||
if (customName != null) {
|
||||
builder.put("CustomName", customName.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -25,7 +25,6 @@
|
|||
|
||||
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.opennbt.tag.builtin.CompoundTag;
|
||||
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.protocol.bedrock.data.entity.EntityFlag;
|
||||
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.session.GeyserSession;
|
||||
import org.geysermc.geyser.skin.SkinProvider;
|
||||
import org.geysermc.geyser.skin.SkullSkinManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
@ -62,7 +58,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
|
|||
builder.put("SkullType", skullVariant);
|
||||
}
|
||||
|
||||
public static CompletableFuture<GameProfile> getProfile(CompoundTag tag) {
|
||||
private static CompletableFuture<String> getTextures(CompoundTag tag) {
|
||||
CompoundTag owner = tag.get("SkullOwner");
|
||||
if (owner != null) {
|
||||
CompoundTag properties = owner.get("Properties");
|
||||
|
@ -73,13 +69,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
|
|||
ListTag textures = properties.get("textures");
|
||||
LinkedHashMap<?,?> tag1 = (LinkedHashMap<?,?>) textures.get(0).getValue();
|
||||
StringTag texture = (StringTag) tag1.get("Value");
|
||||
|
||||
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(texture.getValue());
|
||||
}
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
@ -108,21 +98,21 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
|
|||
Vector3i blockPosition = Vector3i.from(posX, posY, posZ);
|
||||
Vector3f entityPosition = Vector3f.from(x, y, z);
|
||||
|
||||
getProfile(tag).whenComplete((gameProfile, throwable) -> {
|
||||
if (gameProfile == null) {
|
||||
getTextures(tag).whenComplete((texturesProperty, throwable) -> {
|
||||
if (texturesProperty == null) {
|
||||
session.getGeyser().getLogger().debug("Custom skull with invalid SkullOwner tag: " + blockPosition + " " + tag);
|
||||
return;
|
||||
}
|
||||
|
||||
if (session.getEventLoop().inEventLoop()) {
|
||||
spawnPlayer(session, gameProfile, blockPosition, entityPosition, rotation, blockState);
|
||||
spawnPlayer(session, texturesProperty, blockPosition, entityPosition, rotation, blockState);
|
||||
} 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) {
|
||||
long geyserId = session.getEntityCache().getNextEntityId().incrementAndGet();
|
||||
|
||||
|
@ -132,7 +122,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
|
|||
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
|
||||
session.getSkullCache().put(blockPosition, player);
|
||||
|
|
|
@ -139,7 +139,8 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
case ITEM_USE:
|
||||
switch (packet.getActionType()) {
|
||||
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()) {
|
||||
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
|
||||
// Based on Nukkit 1.0, with changes to ensure holding down still works
|
||||
boolean hasAlreadyClicked = System.currentTimeMillis() - session.getLastInteractionTime() < 110.0 &&
|
||||
packet.getBlockPosition().distanceSquared(session.getLastInteractionBlockPosition()) < 0.00001;
|
||||
session.setLastInteractionBlockPosition(packet.getBlockPosition());
|
||||
packetBlockPosition.distanceSquared(session.getLastInteractionBlockPosition()) < 0.00001;
|
||||
session.setLastInteractionBlockPosition(packetBlockPosition);
|
||||
session.setLastInteractionPlayerPosition(session.getPlayerEntity().getPosition());
|
||||
if (hasAlreadyClicked) {
|
||||
break;
|
||||
|
@ -204,22 +205,48 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
playerPosition = playerPosition.sub(0, (EntityDefinitions.PLAYER.offset() - 0.2f), 0);
|
||||
} // else, we don't have to modify the position
|
||||
|
||||
float diffX = playerPosition.getX() - packet.getBlockPosition().getX();
|
||||
float diffY = playerPosition.getY() - packet.getBlockPosition().getY();
|
||||
float diffZ = playerPosition.getZ() - packet.getBlockPosition().getZ();
|
||||
boolean creative = session.getGameMode() == GameMode.CREATIVE;
|
||||
|
||||
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)) >
|
||||
(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);
|
||||
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
|
||||
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.
|
||||
restoreCorrectBlock(session, blockPos, packet);
|
||||
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
|
||||
*/
|
||||
|
@ -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()) {
|
||||
session.setLastBlockPlacePosition(blockPos);
|
||||
session.setLastBlockPlacedId(handItem.getJavaIdentifier());
|
||||
|
|
|
@ -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.nukkitx.protocol.bedrock.data.inventory.ContainerId;
|
||||
import com.nukkitx.protocol.bedrock.packet.MobEquipmentPacket;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
|
@ -42,8 +43,9 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
|
|||
|
||||
@Override
|
||||
public void translate(GeyserSession session, MobEquipmentPacket packet) {
|
||||
if (!session.isSpawned() || packet.getHotbarSlot() > 8 ||
|
||||
packet.getContainerId() != ContainerId.INVENTORY || session.getPlayerInventory().getHeldItemSlot() == packet.getHotbarSlot()) {
|
||||
int newSlot = 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
|
||||
return;
|
||||
}
|
||||
|
@ -51,12 +53,15 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
|
|||
// Send book update before switching hotbar slot
|
||||
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);
|
||||
|
||||
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
|
||||
// (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
|
||||
|
@ -64,8 +69,10 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
|
|||
50, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
// Java sends a cooldown indicator whenever you switch an item
|
||||
CooldownUtils.sendCooldown(session);
|
||||
if (oldItem.getJavaId() != newItem.getJavaId()) {
|
||||
// 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
|
||||
if (session.getMouseoverEntity() != null) {
|
||||
|
|
|
@ -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
|
||||
// We can get the correct order for button pressing
|
||||
data.getValue().sort(Comparator.comparing((stoneCuttingRecipeData ->
|
||||
session.getItemMappings().getItems()
|
||||
.getOrDefault(stoneCuttingRecipeData.getResult().getId(), ItemMapping.AIR)
|
||||
session.getItemMappings().getMapping(stoneCuttingRecipeData.getResult())
|
||||
.getJavaIdentifier())));
|
||||
|
||||
// Now that it's sorted, let's translate these recipes
|
||||
|
@ -229,7 +228,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
|
|||
GroupedItem groupedItem = entry.getKey();
|
||||
int idCount = 0;
|
||||
//not optimal
|
||||
for (ItemMapping mapping : session.getItemMappings().getItems().values()) {
|
||||
for (ItemMapping mapping : session.getItemMappings().getItems()) {
|
||||
if (mapping.getBedrockId() == groupedItem.id) {
|
||||
idCount++;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
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.PlayerListEntryAction;
|
||||
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()) {
|
||||
switch (packet.getAction()) {
|
||||
case ADD_PLAYER -> {
|
||||
GameProfile profile = entry.getProfile();
|
||||
PlayerEntity playerEntity;
|
||||
boolean self = entry.getProfile().getId().equals(session.getPlayerEntity().getUuid());
|
||||
boolean self = profile.getId().equals(session.getPlayerEntity().getUuid());
|
||||
|
||||
if (self) {
|
||||
// Entity is ourself
|
||||
playerEntity = session.getPlayerEntity();
|
||||
} 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) {
|
||||
// It's a new player
|
||||
playerEntity = new PlayerEntity(
|
||||
session,
|
||||
-1,
|
||||
session.getEntityCache().getNextEntityId().incrementAndGet(),
|
||||
entry.getProfile(),
|
||||
profile.getId(),
|
||||
Vector3f.ZERO,
|
||||
Vector3f.ZERO,
|
||||
0, 0, 0
|
||||
0, 0, 0,
|
||||
profile.getName(),
|
||||
texturesProperty
|
||||
);
|
||||
|
||||
session.getEntityCache().addPlayerEntity(playerEntity);
|
||||
} else {
|
||||
playerEntity.setProfile(entry.getProfile());
|
||||
playerEntity.setUsername(profile.getName());
|
||||
playerEntity.setTexturesProperty(texturesProperty);
|
||||
}
|
||||
|
||||
playerEntity.setPlayerList(true);
|
||||
|
|
|
@ -48,7 +48,9 @@ public class JavaAddPlayerTranslator extends PacketTranslator<ClientboundAddPlay
|
|||
PlayerEntity entity;
|
||||
if (packet.getUuid().equals(session.getPlayerEntity().getUuid())) {
|
||||
// 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 {
|
||||
entity = session.getEntityCache().getPlayerEntity(packet.getUuid());
|
||||
if (entity == null) {
|
||||
|
|
|
@ -35,14 +35,13 @@ import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket;
|
|||
import com.nukkitx.protocol.bedrock.packet.BlockEventPacket;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMaps;
|
||||
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.cache.PistonCache;
|
||||
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
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)
|
||||
public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockEventPacket> {
|
||||
|
@ -50,8 +49,9 @@ public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockE
|
|||
@Override
|
||||
public void translate(GeyserSession session, ClientboundBlockEventPacket packet) {
|
||||
BlockEventPacket blockEventPacket = new BlockEventPacket();
|
||||
blockEventPacket.setBlockPosition(Vector3i.from(packet.getPosition().getX(),
|
||||
packet.getPosition().getY(), packet.getPosition().getZ()));
|
||||
Position position = packet.getPosition();
|
||||
Vector3i vector = Vector3i.from(position.getX(), position.getY(), position.getZ());
|
||||
blockEventPacket.setBlockPosition(vector);
|
||||
if (packet.getValue() instanceof ChestValue value) {
|
||||
blockEventPacket.setEventType(1);
|
||||
blockEventPacket.setEventData(value.getViewers() > 0 ? 1 : 0);
|
||||
|
@ -60,11 +60,12 @@ public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockE
|
|||
blockEventPacket.setEventType(1);
|
||||
session.sendUpstreamPacket(blockEventPacket);
|
||||
} 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) {
|
||||
PistonValueType action = (PistonValueType) packet.getType();
|
||||
Direction direction = Direction.fromPistonValue(pistonValue);
|
||||
Vector3i position = Vector3i.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ());
|
||||
PistonCache pistonCache = session.getPistonCache();
|
||||
|
||||
if (session.getGeyser().getPlatformType() == PlatformType.SPIGOT) {
|
||||
|
@ -77,20 +78,20 @@ public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockE
|
|||
return;
|
||||
}
|
||||
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);
|
||||
if (blockInFront != BlockStateValues.JAVA_AIR_ID) {
|
||||
// Piston pulled something
|
||||
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) {
|
||||
blockEntity.setAction(action, Object2IntMaps.emptyMap());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PistonBlockEntity blockEntity = pistonCache.getPistons().computeIfAbsent(position, pos -> {
|
||||
PistonBlockEntity blockEntity = pistonCache.getPistons().computeIfAbsent(vector, pos -> {
|
||||
int blockId = session.getGeyser().getWorldManager().getBlockAt(session, position);
|
||||
boolean sticky = BlockStateValues.isStickyPiston(blockId);
|
||||
boolean extended = action != PistonValueType.PUSHING;
|
||||
|
@ -106,10 +107,8 @@ public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockE
|
|||
session.sendUpstreamPacket(blockEventPacket);
|
||||
} else if (packet.getValue() instanceof GenericBlockValue bellValue && packet.getBlockId() == BlockStateValues.JAVA_BELL_ID) {
|
||||
// Bells - needed to show ring from other players
|
||||
Position position = packet.getPosition();
|
||||
|
||||
BlockEntityDataPacket blockEntityPacket = new BlockEntityDataPacket();
|
||||
blockEntityPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ()));
|
||||
blockEntityPacket.setBlockPosition(vector);
|
||||
|
||||
NbtMapBuilder builder = NbtMap.builder();
|
||||
builder.putInt("x", position.getX());
|
||||
|
|
|
@ -37,11 +37,14 @@ public class JavaClearTitlesTranslator extends PacketTranslator<ClientboundClear
|
|||
@Override
|
||||
public void translate(GeyserSession session, ClientboundClearTitlesPacket packet) {
|
||||
SetTitlePacket titlePacket = new SetTitlePacket();
|
||||
// TODO handle packet.isResetTimes()
|
||||
titlePacket.setType(SetTitlePacket.Type.CLEAR);
|
||||
titlePacket.setText("");
|
||||
titlePacket.setXuid("");
|
||||
titlePacket.setPlatformOnlineId("");
|
||||
session.sendUpstreamPacket(titlePacket);
|
||||
|
||||
if (packet.isResetTimes()) {
|
||||
session.getWorldCache().resetTitleTimes(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,8 @@ public class JavaSetTitleTextTranslator extends PacketTranslator<ClientboundSetT
|
|||
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundSetTitleTextPacket packet) {
|
||||
session.getWorldCache().synchronizeCorrectTitleTimes();
|
||||
|
||||
String text;
|
||||
if (packet.getText() == null || Component.empty().equals(packet.getText())) { // This can happen, see https://github.com/KyoriPowered/adventure/issues/447
|
||||
text = " ";
|
||||
|
|
|
@ -36,12 +36,17 @@ public class JavaSetTitlesAnimationTranslator extends PacketTranslator<Clientbou
|
|||
|
||||
@Override
|
||||
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();
|
||||
titlePacket.setType(SetTitlePacket.Type.TIMES);
|
||||
titlePacket.setText("");
|
||||
titlePacket.setFadeInTime(packet.getFadeIn());
|
||||
titlePacket.setFadeOutTime(packet.getFadeOut());
|
||||
titlePacket.setStayTime(packet.getStay());
|
||||
titlePacket.setFadeInTime(fadeInTime);
|
||||
titlePacket.setFadeOutTime(fadeOutTime);
|
||||
titlePacket.setStayTime(stayTime);
|
||||
titlePacket.setXuid("");
|
||||
titlePacket.setPlatformOnlineId("");
|
||||
session.sendUpstreamPacket(titlePacket);
|
||||
|
|
|
@ -57,8 +57,19 @@ public class CooldownUtils {
|
|||
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
|
||||
// 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();
|
||||
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.setText(" ");
|
||||
titlePacket.setXuid("");
|
||||
|
@ -85,9 +96,6 @@ public class CooldownUtils {
|
|||
titlePacket.setType(SetTitlePacket.Type.SUBTITLE);
|
||||
}
|
||||
titlePacket.setText(getTitle(session));
|
||||
titlePacket.setFadeInTime(0);
|
||||
titlePacket.setFadeOutTime(5);
|
||||
titlePacket.setStayTime(2);
|
||||
titlePacket.setXuid("");
|
||||
titlePacket.setPlatformOnlineId("");
|
||||
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
|
||||
} else {
|
||||
SetTitlePacket removeTitlePacket = new SetTitlePacket();
|
||||
if (sessionPreference == CooldownType.ACTIONBAR) {
|
||||
removeTitlePacket.setType(SetTitlePacket.Type.ACTIONBAR);
|
||||
} else {
|
||||
removeTitlePacket.setType(SetTitlePacket.Type.SUBTITLE);
|
||||
}
|
||||
removeTitlePacket.setType(SetTitlePacket.Type.CLEAR);
|
||||
removeTitlePacket.setText(" ");
|
||||
removeTitlePacket.setXuid("");
|
||||
removeTitlePacket.setPlatformOnlineId("");
|
||||
|
|
|
@ -29,21 +29,27 @@ import com.github.steveice10.opennbt.tag.builtin.*;
|
|||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ItemUtils {
|
||||
private static Int2IntMap DYE_COLORS = null;
|
||||
|
||||
public static int getEnchantmentLevel(CompoundTag itemNBTData, String enchantmentId) {
|
||||
ListTag enchantments = (itemNBTData == null ? null : itemNBTData.get("Enchantments"));
|
||||
public static int getEnchantmentLevel(@Nullable CompoundTag itemNBTData, String enchantmentId) {
|
||||
if (itemNBTData == null) {
|
||||
return 0;
|
||||
}
|
||||
ListTag enchantments = itemNBTData.get("Enchantments");
|
||||
if (enchantments != null) {
|
||||
int enchantmentLevel = 0;
|
||||
for (Tag tag : enchantments) {
|
||||
CompoundTag enchantment = (CompoundTag) tag;
|
||||
StringTag enchantId = enchantment.get("id");
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ public class SettingsUtils {
|
|||
|
||||
// Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config.
|
||||
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) {
|
||||
|
@ -175,6 +175,10 @@ public class SettingsUtils {
|
|||
}
|
||||
|
||||
private static String translateEntry(String key, String locale) {
|
||||
if (key.startsWith("%")) {
|
||||
// Bedrock will translate
|
||||
return key;
|
||||
}
|
||||
if (key.startsWith("geyser.")) {
|
||||
return GeyserLocale.getPlayerLocaleString(key, locale);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue