diff --git a/Jenkinsfile b/Jenkinsfile index 7dfdaf30452..e7f2ec4e2c1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -66,7 +66,7 @@ pipeline { } deleteDir() withCredentials([string(credentialsId: 'geyser-discord-webhook', variable: 'DISCORD_WEBHOOK')]) { - discordSend description: "**Build:** [${currentBuild.id}](${env.BUILD_URL})\n**Status:** [${currentBuild.currentResult}](${env.BUILD_URL})\n${changes}\n\n[**Artifacts on Jenkins**](https://ci.nukkitx.com/job/Geyser)", footer: 'Cloudburst Jenkins', link: env.BUILD_URL, successful: currentBuild.resultIsBetterOrEqualTo('SUCCESS'), title: "${env.JOB_NAME} #${currentBuild.id}", webhookURL: DISCORD_WEBHOOK + discordSend description: "**Build:** [${currentBuild.id}](${env.BUILD_URL})\n**Status:** [${currentBuild.currentResult}](${env.BUILD_URL})\n${changes}\n\n[**Artifacts on Jenkins**](https://ci.opencollab.dev/job/GeyserMC/job/Geyser)", footer: 'Open Collaboration Jenkins', link: env.BUILD_URL, successful: currentBuild.resultIsBetterOrEqualTo('SUCCESS'), title: "${env.JOB_NAME} #${currentBuild.id}", webhookURL: DISCORD_WEBHOOK } } } diff --git a/README.md b/README.md index 2033a66ed13..816f765d6b2 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![forthebadge made-with-java](http://ForTheBadge.com/images/badges/made-with-java.svg)](https://java.com/) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) -[![Build Status](https://ci.nukkitx.com/job/Geyser/job/master/badge/icon)](https://ci.nukkitx.com/job/GeyserMC/job/Geyser/job/master/) +[![Build Status](https://ci.opencollab.dev/job/Geyser/job/master/badge/icon)](https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/master/) [![Discord](https://img.shields.io/discord/613163671870242838.svg?color=%237289da&label=discord)](http://discord.geysermc.org/) [![HitCount](http://hits.dwyl.io/Geyser/GeyserMC.svg)](http://hits.dwyl.io/Geyser/GeyserMC) [![Crowdin](https://badges.crowdin.net/geyser/localized.svg)](https://translate.geysermc.org/) @@ -52,6 +52,8 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set The following things can't be fixed because of Bedrock limitations. They might be fixable in the future, but not as of now. - Custom heads in inventories +- Clickable links in chat +- Glowing effect ## Compiling 1. Clone the repo to your computer diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java index b00ddafaa2b..bae9ca07559 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/manager/GeyserSpigot1_12WorldManager.java @@ -125,7 +125,7 @@ public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk for (int blockY = 0; blockY < 16; blockY++) { // Cache-friendly iteration order for (int blockZ = 0; blockZ < 16; blockZ++) { for (int blockX = 0; blockX < 16; blockX++) { - Block block = world.getBlockAt(x, y, z); + Block block = world.getBlockAt((x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ); // Black magic that gets the old block state ID int blockId = (block.getType().getId() << 4) | (block.getData() & 0xF); chunk.set(blockX, blockY, blockZ, getLegacyBlock(storage, blockId, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ)); diff --git a/connector/pom.xml b/connector/pom.xml index 5f578039427..60879effe4e 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -32,7 +32,7 @@ com.github.CloudburstMC.Protocol bedrock-v422 - 87d862d69d + d41b84e86c compile @@ -124,14 +124,26 @@ 26201a4 compile - - io.netty - netty-all - net.kyori adventure-text-serializer-gson + + com.github.steveice10 + packetlib + + + + + com.github.steveice10 + PacketLib + 54f761c + compile + + + io.netty + netty-all + @@ -140,6 +152,12 @@ 4.1.43.Final compile + + io.netty + netty-codec-haproxy + 4.1.56.Final + compile + org.reflections reflections diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 977c8aa910a..55af05f964b 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -46,6 +46,7 @@ import org.geysermc.connector.network.translators.BiomeTranslator; import org.geysermc.connector.network.translators.EntityIdentifierRegistry; import org.geysermc.connector.network.translators.PacketTranslatorRegistry; +import org.geysermc.connector.network.translators.collision.CollisionTranslator; import org.geysermc.connector.network.translators.effect.EffectRegistry; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; @@ -56,7 +57,6 @@ import org.geysermc.connector.network.translators.world.WorldManager; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator; -import org.geysermc.connector.network.translators.collision.CollisionTranslator; import org.geysermc.connector.network.translators.world.block.entity.SkullBlockEntityTranslator; import org.geysermc.connector.event.events.geyser.GeyserStopEvent; import org.geysermc.connector.utils.DimensionUtils; @@ -70,10 +70,7 @@ import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -346,6 +343,38 @@ public void removePlayer(GeyserSession player) { players.remove(player); } + /** + * Gets a player by their current UUID + * + * @param uuid the uuid + * @return the player or null if there is no player online with this UUID + */ + public GeyserSession getPlayerByUuid(UUID uuid) { + for (GeyserSession session : players) { + if (session.getPlayerEntity().getUuid().equals(uuid)) { + return session; + } + } + + return null; + } + + /** + * Gets a player by their Xbox user identifier + * + * @param xuid the Xbox user identifier + * @return the player or null if there is no player online with this xuid + */ + public GeyserSession getPlayerByXuid(String xuid) { + for (GeyserSession session : players) { + if (session.getAuthData() != null && session.getAuthData().getXboxUUID().equals(xuid)) { + return session; + } + } + + return null; + } + public static GeyserConnector start(PlatformType platformType, GeyserBootstrap bootstrap) { return new GeyserConnector(platformType, bootstrap); } diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/VersionCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/VersionCommand.java index 562bc9fb594..88fab368399 100644 --- a/connector/src/main/java/org/geysermc/connector/command/defaults/VersionCommand.java +++ b/connector/src/main/java/org/geysermc/connector/command/defaults/VersionCommand.java @@ -71,7 +71,7 @@ public void execute(CommandSender sender, String[] args) { Properties gitProp = new Properties(); gitProp.load(FileUtils.getResource("git.properties")); - String buildXML = WebUtils.getBody("https://ci.nukkitx.com/job/GeyserMC/job/Geyser/job/" + URLEncoder.encode(gitProp.getProperty("git.branch"), StandardCharsets.UTF_8.toString()) + "/lastSuccessfulBuild/api/xml?xpath=//buildNumber"); + String buildXML = WebUtils.getBody("https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/" + URLEncoder.encode(gitProp.getProperty("git.branch"), StandardCharsets.UTF_8.toString()) + "/lastSuccessfulBuild/api/xml?xpath=//buildNumber"); if (buildXML.startsWith("")) { int latestBuildNum = Integer.parseInt(buildXML.replaceAll("<(\\\\)?(/)?buildNumber>", "").trim()); int buildNum = Integer.parseInt(gitProp.getProperty("git.build.number")); diff --git a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java index dfa30ae92cf..a8044ef3209 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java @@ -34,7 +34,7 @@ public interface GeyserConfiguration { - // Modify this when you update the config + // Modify this when you introduce breaking changes into the config int CURRENT_CONFIG_VERSION = 4; IBedrockConfiguration getBedrock(); @@ -117,6 +117,8 @@ interface IRemoteConfiguration { void setPort(int port); String getAuthType(); + + boolean isUseProxyProtocol(); } interface IUserAuthenticationInfo { diff --git a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java index 67fa04492ad..c403bdf1182 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java @@ -148,6 +148,9 @@ public static class RemoteConfiguration implements IRemoteConfiguration { @Setter @JsonProperty("auth-type") private String authType = "online"; + + @JsonProperty("use-proxy-protocol") + private boolean useProxyProtocol = false; } @Getter diff --git a/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java b/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java index 308d2121a37..bffba186e69 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java @@ -29,6 +29,7 @@ import com.github.steveice10.mc.protocol.data.game.world.particle.Particle; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.effect.EffectRegistry; @@ -45,6 +46,8 @@ public AreaEffectCloudEntity(long entityId, long geyserId, EntityType entityType metadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS, 0.0f); metadata.put(EntityData.AREA_EFFECT_CLOUD_CHANGE_RATE, -0.005f); metadata.put(EntityData.AREA_EFFECT_CLOUD_CHANGE_ON_PICKUP, -0.5f); + + metadata.getFlags().setFlag(EntityFlag.FIRE_IMMUNE, true); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/entity/ThrownPotionEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ThrownPotionEntity.java new file mode 100644 index 00000000000..e9ea5fd7f43 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/ThrownPotionEntity.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2019-2020 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.connector.entity; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType; +import com.github.steveice10.opennbt.tag.builtin.StringTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.ItemEntry; +import org.geysermc.connector.network.translators.item.ItemRegistry; +import org.geysermc.connector.network.translators.item.Potion; + +import java.util.EnumSet; + +public class ThrownPotionEntity extends ThrowableEntity { + private static final EnumSet NON_ENCHANTED_POTIONS = EnumSet.of(Potion.WATER, Potion.MUNDANE, Potion.THICK, Potion.AWKWARD); + + public ThrownPotionEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 7 && entityMetadata.getType() == MetadataType.ITEM) { + ItemStack itemStack = (ItemStack) entityMetadata.getValue(); + ItemEntry itemEntry = ItemRegistry.getItem(itemStack); + if (itemEntry.getJavaIdentifier().endsWith("potion") && itemStack.getNbt() != null) { + Tag potionTag = itemStack.getNbt().get("Potion"); + if (potionTag instanceof StringTag) { + Potion potion = Potion.getByJavaIdentifier(((StringTag) potionTag).getValue()); + if (potion != null) { + metadata.put(EntityData.POTION_AUX_VALUE, potion.getBedrockId()); + metadata.getFlags().setFlag(EntityFlag.ENCHANTED, !NON_ENCHANTED_POTIONS.contains(potion)); + } else { + metadata.put(EntityData.POTION_AUX_VALUE, 0); + GeyserConnector.getInstance().getLogger().debug("Unknown java potion: " + potionTag.getValue()); + } + } + + boolean isLingering = itemEntry.getJavaIdentifier().equals("minecraft:lingering_potion"); + metadata.getFlags().setFlag(EntityFlag.LINGERING, isLingering); + } + } + + super.updateBedrockMetadata(entityMetadata, session); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java index 3e6b6c72d10..79a69d6481a 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java +++ b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java @@ -123,7 +123,7 @@ public enum EntityType { PAINTING(PaintingEntity.class, 83, 0f), MINECART(MinecartEntity.class, 84, 0.7f, 0.98f, 0.98f, 0.35f), FIREBALL(ItemedFireballEntity.class, 85, 1.0f), - THROWN_POTION(ThrowableEntity.class, 86, 0.25f, 0.25f, 0.25f, 0f, "minecraft:splash_potion"), + THROWN_POTION(ThrownPotionEntity.class, 86, 0.25f, 0.25f, 0.25f, 0f, "minecraft:splash_potion"), THROWN_ENDERPEARL(ThrowableEntity.class, 87, 0.25f, 0.25f, 0.25f, 0f, "minecraft:ender_pearl"), LEASH_KNOT(LeashKnotEntity.class, 88, 0.5f, 0.375f), WITHER_SKULL(WitherSkullEntity.class, 89, 0.3125f), diff --git a/connector/src/main/java/org/geysermc/connector/network/LoggingPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/LoggingPacketHandler.java index 7f52eb2d920..bfff1b3d3b1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/LoggingPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/LoggingPacketHandler.java @@ -132,11 +132,6 @@ public boolean handle(EntityEventPacket packet) { return defaultHandler(packet); } - @Override - public boolean handle(EntityFallPacket packet) { - return defaultHandler(packet); - } - @Override public boolean handle(EntityPickRequestPacket packet) { return defaultHandler(packet); @@ -826,4 +821,43 @@ public boolean handle(PositionTrackingDBClientRequestPacket packet) { public boolean handle(PositionTrackingDBServerBroadcastPacket packet) { return defaultHandler(packet); } + + // 1.16.100 new packets + + @Override + public boolean handle(MotionPredictionHintsPacket packet) { + return defaultHandler(packet); + } + + @Override + public boolean handle(AnimateEntityPacket packet) { + return defaultHandler(packet); + } + + @Override + public boolean handle(CameraShakePacket packet) { + return defaultHandler(packet); + } + + @Override + public boolean handle(PlayerFogPacket packet) { + return defaultHandler(packet); + } + + @Override + public boolean handle(CorrectPlayerMovePredictionPacket packet) { + return defaultHandler(packet); + } + + @Override + public boolean handle(ItemComponentPacket packet) { + return defaultHandler(packet); + } + + // 1.16.200 new packet + + @Override + public boolean handle(FilterTextPacket packet) { + return defaultHandler(packet); + } } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 7e2bce109a6..188fb95f6f5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -40,6 +40,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket; import com.github.steveice10.mc.protocol.packet.login.server.LoginSuccessPacket; +import com.github.steveice10.packetlib.BuiltinFlags; import com.github.steveice10.packetlib.Client; import com.github.steveice10.packetlib.event.session.*; import com.github.steveice10.packetlib.packet.Packet; @@ -260,6 +261,12 @@ public class GeyserSession implements CommandSender { @Setter private ScheduledFuture movementSendIfIdle; + /** + * Controls whether the daylight cycle gamerule has been sent to the client, so the sun/moon remain motionless. + */ + @Setter + private boolean daylightCycle = true; + private boolean reducedDebugInfo = false; @Setter @@ -479,6 +486,10 @@ public void authenticate(String username, String password) { } downstream = new Client(remoteServer.getAddress(), remoteServer.getPort(), protocol, new TcpSessionFactory()); + if (connector.getConfig().getRemote().isUseProxyProtocol()) { + downstream.getSession().setFlag(BuiltinFlags.ENABLE_CLIENT_PROXY_PROTOCOL, true); + downstream.getSession().setFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS, upstream.getAddress()); + } // Let Geyser handle sending the keep alive downstream.getSession().setFlag(MinecraftConstants.AUTOMATIC_KEEP_ALIVE_MANAGEMENT, false); downstream.getSession().addListener(new SessionAdapter() { @@ -685,7 +696,6 @@ public void sendForm(FormWindow window, int id) { public void setRenderDistance(int renderDistance) { renderDistance = GenericMath.ceil(++renderDistance * MathUtils.SQRT_OF_TWO); //square to circle - if (renderDistance > 32) renderDistance = 32; // <3 u ViaVersion but I don't like crashing clients x) this.renderDistance = renderDistance; ChunkRadiusUpdatedPacket chunkRadiusUpdatedPacket = new ChunkRadiusUpdatedPacket(); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java index 3e40ddd6f4c..f7e3fbd9c3f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java @@ -46,7 +46,6 @@ public void translate(BlockPickRequestPacket packet, GeyserSession session) { return; } - String targetIdentifier = BlockTranslator.getJavaIdBlockMap().inverse().get(blockToPick).split("\\[")[0]; - InventoryUtils.findOrCreatePickedBlock(session, targetIdentifier); + InventoryUtils.findOrCreateItem(session, BlockTranslator.getPickItem(blockToPick)); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEntityPickRequestTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEntityPickRequestTranslator.java index 4aa044ee452..d03aa7b738b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEntityPickRequestTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEntityPickRequestTranslator.java @@ -110,6 +110,6 @@ public void translate(EntityPickRequestPacket packet, GeyserSession session) { // Verify it is, indeed, an item if (entry == null) return; - InventoryUtils.findOrCreatePickedBlock(session, fullItemName); + InventoryUtils.findOrCreateItem(session, fullItemName); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java new file mode 100644 index 00000000000..8c963076483 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockFilterTextTranslator.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019-2020 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.connector.network.translators.bedrock; + +import com.nukkitx.protocol.bedrock.packet.FilterTextPacket; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.Translator; + +/** + * Used to send strings to the server and filter out unwanted words. + * Java doesn't care, so we don't care, and we approve all strings. + */ +@Translator(packet = FilterTextPacket.class) +public class BedrockFilterTextTranslator extends PacketTranslator { + + @Override + public void translate(FilterTextPacket packet, GeyserSession session) { + packet.setFromServer(true); + session.sendUpstreamPacket(packet); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockTextTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockTextTranslator.java index e4a76569445..955a9a539ec 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockTextTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockTextTranslator.java @@ -38,7 +38,7 @@ public class BedrockTextTranslator extends PacketTranslator { @Override public void translate(TextPacket packet, GeyserSession session) { - String message = packet.getMessage().replaceAll("^\\.", "/").trim(); + String message = packet.getMessage(); if (MessageTranslator.isTooLong(message, session)) { return; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java index 14b9343622f..5e5bc354224 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/BannerTranslator.java @@ -45,8 +45,39 @@ @ItemRemapper public class BannerTranslator extends ItemTranslator { + /** + * Holds what a Java ominous banner pattern looks like. + * + * Translating the patterns over to Bedrock does not work effectively, but Bedrock has a dedicated type for + * ominous banners that we set instead. This variable is used to detect Java ominous banner patterns, and apply + * the correct ominous banner pattern if Bedrock pulls the item from creative. + */ + public static final ListTag OMINOUS_BANNER_PATTERN; + private final List appliedItems; + static { + OMINOUS_BANNER_PATTERN = new ListTag("Patterns"); + // Construct what an ominous banner is supposed to look like + OMINOUS_BANNER_PATTERN.add(getPatternTag("mr", 9)); + OMINOUS_BANNER_PATTERN.add(getPatternTag("bs", 8)); + OMINOUS_BANNER_PATTERN.add(getPatternTag("cs", 7)); + OMINOUS_BANNER_PATTERN.add(getPatternTag("bo", 8)); + OMINOUS_BANNER_PATTERN.add(getPatternTag("ms", 15)); + OMINOUS_BANNER_PATTERN.add(getPatternTag("hh", 8)); + OMINOUS_BANNER_PATTERN.add(getPatternTag("mc", 8)); + OMINOUS_BANNER_PATTERN.add(getPatternTag("bo", 15)); + } + + private static CompoundTag getPatternTag(String pattern, int color) { + StringTag patternType = new StringTag("Pattern", pattern); + IntTag colorTag = new IntTag("Color", color); + CompoundTag tag = new CompoundTag(""); + tag.put(patternType); + tag.put(colorTag); + return tag; + } + public BannerTranslator() { appliedItems = ItemRegistry.ITEM_ENTRIES.values() .stream() @@ -62,7 +93,7 @@ public BannerTranslator() { */ public static NbtList convertBannerPattern(ListTag patterns) { List tagsList = new ArrayList<>(); - for (com.github.steveice10.opennbt.tag.builtin.Tag patternTag : patterns.getValue()) { + for (Tag patternTag : patterns.getValue()) { NbtMap newPatternTag = getBedrockBannerPattern((CompoundTag) patternTag); if (newPatternTag != null) { tagsList.add(newPatternTag); @@ -134,7 +165,13 @@ public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) { ListTag patterns = blockEntityTag.get("Patterns"); NbtMapBuilder builder = itemData.getTag().toBuilder(); - builder.put("Patterns", convertBannerPattern(patterns)); + if (patterns.equals(OMINOUS_BANNER_PATTERN)) { + // Remove the current patterns and set the ominous banner type + builder.remove("Patterns"); + builder.putInt("Type", 1); + } else { + builder.put("Patterns", convertBannerPattern(patterns)); + } itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), builder.build()); } @@ -151,7 +188,14 @@ public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) { ItemStack itemStack = super.translateToJava(itemData, itemEntry); NbtMap nbtTag = itemData.getTag(); - if (nbtTag.containsKey("Patterns", NbtType.COMPOUND)) { + if (nbtTag.containsKey("Type", NbtType.INT) && nbtTag.getInt("Type") == 1) { + // Ominous banner pattern + itemStack.getNbt().remove("Type"); + CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag"); + blockEntityTag.put(OMINOUS_BANNER_PATTERN); + + itemStack.getNbt().put(blockEntityTag); + } else if (nbtTag.containsKey("Patterns", NbtType.COMPOUND)) { List patterns = nbtTag.getList("Patterns", NbtType.COMPOUND); CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag"); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java index 8f52433665f..2247b55be15 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java @@ -52,9 +52,14 @@ public class JavaDeclareCommandsTranslator extends PacketTranslator commandData = new ArrayList<>(); Int2ObjectMap commands = new Int2ObjectOpenHashMap<>(); Int2ObjectMap> commandArgs = new Int2ObjectOpenHashMap<>(); @@ -83,14 +88,14 @@ public void translate(ServerDeclareCommandsPacket packet, GeyserSession session) } // The command flags, not sure what these do apart from break things - List flags = new ArrayList<>(); + List flags = Collections.emptyList(); // Loop through all the found commands for (int commandID : commands.keySet()) { String commandName = commands.get(commandID); // Create a basic alias - CommandEnumData aliases = new CommandEnumData( commandName + "Aliases", new String[] { commandName.toLowerCase() }, false); + CommandEnumData aliases = new CommandEnumData(commandName + "Aliases", new String[] { commandName.toLowerCase() }, false); // Get and parse all params CommandParamData[][] params = getParams(packet.getNodes()[commandID], packet.getNodes()); @@ -102,9 +107,7 @@ public void translate(ServerDeclareCommandsPacket packet, GeyserSession session) // Add our commands to the AvailableCommandsPacket for the bedrock client AvailableCommandsPacket availableCommandsPacket = new AvailableCommandsPacket(); - for (CommandData data : commandData) { - availableCommandsPacket.getCommands().add(data); - } + availableCommandsPacket.getCommands().addAll(commandData); GeyserConnector.getInstance().getLogger().debug("Sending command packet of " + commandData.size() + " commands"); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java index 8c84a5c9772..8b68bf61fbb 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java @@ -89,6 +89,7 @@ public void translate(ServerEntityStatusPacket packet, GeyserSession session) { case LIVING_DROWN: case LIVING_HURT: case LIVING_HURT_SWEET_BERRY_BUSH: + case LIVING_HURT_THORNS: entityEventPacket.setType(EntityEventType.HURT); break; case LIVING_DEATH: diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaPlayBuiltinSoundTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaPlayBuiltinSoundTranslator.java index d849c917935..1c057f45f3e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaPlayBuiltinSoundTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaPlayBuiltinSoundTranslator.java @@ -34,8 +34,8 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.sound.SoundRegistry; +import org.geysermc.connector.network.translators.world.block.BlockTranslator; @Translator(packet = ServerPlayBuiltinSoundPacket.class) public class JavaPlayBuiltinSoundTranslator extends PacketTranslator { @@ -82,7 +82,11 @@ public void translate(ServerPlayBuiltinSoundPacket packet, GeyserSession session // Bedrock has a number for each type of note, then proceeds up the scale by adding to that number soundPacket.setExtraData(soundMapping.getExtraData() + (int)(Math.round((Math.log10(packet.getPitch()) / Math.log10(2)) * 12)) + 12); } else if (sound == SoundEvent.PLACE && soundMapping.getExtraData() == -1) { - soundPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaBlockState(soundMapping.getIdentifier()))); + if (!soundMapping.getIdentifier().equals(":")) { + soundPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaBlockState(soundMapping.getIdentifier()))); + } else { + session.getConnector().getLogger().debug("PLACE sound mapping identifier was invalid! Please report: " + packet.toString()); + } soundPacket.setIdentifier(":"); } else { soundPacket.setExtraData(soundMapping.getExtraData()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTimeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTimeTranslator.java index 8dc68918550..dff78697f74 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTimeTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUpdateTimeTranslator.java @@ -25,49 +25,38 @@ package org.geysermc.connector.network.translators.java.world; -import com.nukkitx.protocol.bedrock.data.GameRuleData; -import com.nukkitx.protocol.bedrock.packet.GameRulesChangedPacket; -import it.unimi.dsi.fastutil.longs.Long2LongMap; -import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; +import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdateTimePacket; +import com.nukkitx.protocol.bedrock.packet.SetTimePacket; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; -import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdateTimePacket; -import com.nukkitx.protocol.bedrock.packet.SetTimePacket; - @Translator(packet = ServerUpdateTimePacket.class) public class JavaUpdateTimeTranslator extends PacketTranslator { - // If negative, the last time is stored so we know it's not some plugin behavior doing weird things. - // Per-player for multi-world support - private static final Long2LongMap LAST_RECORDED_TIMES = new Long2LongOpenHashMap(); - @Override public void translate(ServerUpdateTimePacket packet, GeyserSession session) { - // Bedrock sends a GameRulesChangedPacket if there is no daylight cycle // Java just sends a negative long if there is no daylight cycle - long lastTime = LAST_RECORDED_TIMES.getOrDefault(session.getPlayerEntity().getEntityId(), 0); long time = packet.getTime(); - if (lastTime != time) { - // https://minecraft.gamepedia.com/Day-night_cycle#24-hour_Minecraft_day - SetTimePacket setTimePacket = new SetTimePacket(); - setTimePacket.setTime((int) Math.abs(time) % 24000); - session.sendUpstreamPacket(setTimePacket); - // TODO: Performance efficient to always do this? - LAST_RECORDED_TIMES.put(session.getPlayerEntity().getEntityId(), time); - } - if (lastTime < 0 && time >= 0) { + // https://minecraft.gamepedia.com/Day-night_cycle#24-hour_Minecraft_day + SetTimePacket setTimePacket = new SetTimePacket(); + setTimePacket.setTime((int) Math.abs(time) % 24000); + session.sendUpstreamPacket(setTimePacket); + if (!session.isDaylightCycle() && time >= 0) { + // Client thinks there is no daylight cycle but there is setDoDaylightCycleGamerule(session, true); - } else if (lastTime != time && time < 0) { + } else if (session.isDaylightCycle() && time < 0) { + // Client thinks there is daylight cycle but there isn't setDoDaylightCycleGamerule(session, false); } } private void setDoDaylightCycleGamerule(GeyserSession session, boolean doCycle) { session.sendGameRule("dodaylightcycle", doCycle); + // Save the value so we don't have to constantly send a daylight cycle gamerule update + session.setDaylightCycle(doCycle); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java index e84dccf0483..c32cbf527aa 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockTranslator.java @@ -30,7 +30,10 @@ import com.google.common.collect.HashBiMap; import com.nukkitx.nbt.*; import it.unimi.dsi.fastutil.ints.*; -import it.unimi.dsi.fastutil.objects.*; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.event.EventManager; import org.geysermc.connector.event.events.registry.BlockEntityRegistryEvent; @@ -68,8 +71,6 @@ public class BlockTranslator { // Bedrock carpet ID, used in LlamaEntity.java for decoration public static final int CARPET = 171; - public static final Int2ObjectMap JAVA_ID_TO_BLOCK_ENTITY_MAP = new Int2ObjectOpenHashMap<>(); - public static final Int2DoubleMap JAVA_RUNTIME_ID_TO_HARDNESS = new Int2DoubleOpenHashMap(); public static final Int2BooleanMap JAVA_RUNTIME_ID_TO_CAN_HARVEST_WITH_HAND = new Int2BooleanOpenHashMap(); public static final Int2ObjectMap JAVA_RUNTIME_ID_TO_TOOL_TYPE = new Int2ObjectOpenHashMap<>(); @@ -77,6 +78,8 @@ public class BlockTranslator { // The index of the collision data in collision.json public static final Int2IntMap JAVA_RUNTIME_ID_TO_COLLISION_INDEX = new Int2IntOpenHashMap(); + private static final Int2ObjectMap JAVA_RUNTIME_ID_TO_PICK_ITEM = new Int2ObjectOpenHashMap<>(); + /** * Java numeric ID to java unique identifier, used for block names in the statistics screen */ @@ -143,11 +146,6 @@ public class BlockTranslator { addedStatesMap.defaultReturnValue(-1); List paletteList = new ArrayList<>(); - Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections("org.geysermc.connector.network.translators.world.block.entity") : new Reflections("org.geysermc.connector.network.translators.world.block.entity"); - Set> blockEntityClasses = EventManager.getInstance().triggerEvent(new BlockEntityRegistryEvent( - ref.getTypesAnnotatedWith(BlockEntity.class) - )).getEvent().getRegisteredTranslators(); - int waterRuntimeId = -1; int javaRuntimeId = -1; int airRuntimeId = -1; @@ -196,20 +194,13 @@ public class BlockTranslator { JAVA_RUNTIME_ID_TO_COLLISION_INDEX.put(javaRuntimeId, collisionIndexNode.intValue()); } - JAVA_ID_BLOCK_MAP.put(javaId, javaRuntimeId); - - // Used for adding all "special" Java block states to block state map - String identifier; - String bedrockIdentifier = entry.getValue().get("bedrock_identifier").asText(); - for (Class clazz : blockEntityClasses) { - identifier = clazz.getAnnotation(BlockEntity.class).regex(); - // Endswith, or else the block bedrock gets picked up for bed - if (bedrockIdentifier.endsWith(identifier) && !identifier.equals("")) { - JAVA_ID_TO_BLOCK_ENTITY_MAP.put(javaRuntimeId, clazz.getAnnotation(BlockEntity.class).name()); - break; - } + JsonNode pickItemNode = entry.getValue().get("pick_item"); + if (pickItemNode != null) { + JAVA_RUNTIME_ID_TO_PICK_ITEM.put(javaRuntimeId, pickItemNode.textValue()); } + JAVA_ID_BLOCK_MAP.put(javaId, javaRuntimeId); + BlockStateValues.storeBlockStateValues(entry, javaRuntimeId); String cleanJavaIdentifier = entry.getKey().split("\\[")[0]; @@ -219,6 +210,8 @@ public class BlockTranslator { JAVA_ID_TO_JAVA_IDENTIFIER_MAP.put(uniqueJavaId, cleanJavaIdentifier); } + String bedrockIdentifier = entry.getValue().get("bedrock_identifier").asText(); + if (!cleanJavaIdentifier.equals(bedrockIdentifier)) { JAVA_TO_BEDROCK_IDENTIFIERS.put(cleanJavaIdentifier, bedrockIdentifier); } @@ -375,12 +368,12 @@ public static int getBlockStateVersion() { return BLOCK_STATE_VERSION; } + /** + * @param javaId the Java string identifier to search for + * @return the Java block state integer, or {@link #JAVA_AIR_ID} if there is no valid entry. + */ public static int getJavaBlockState(String javaId) { - return JAVA_ID_BLOCK_MAP.get(javaId); - } - - public static String getBlockEntityString(int javaId) { - return JAVA_ID_TO_BLOCK_ENTITY_MAP.get(javaId); + return JAVA_ID_BLOCK_MAP.getOrDefault(javaId, JAVA_AIR_ID); } public static boolean isWaterlogged(int state) { @@ -394,4 +387,19 @@ public static BiMap getJavaIdBlockMap() { public static int getJavaWaterloggedState(int bedrockId) { return BEDROCK_TO_JAVA_BLOCK_MAP.get(1 << 31 | bedrockId); } + + /** + * Get the item a Java client would receive when pressing + * the Pick Block key on a specific Java block state. + * + * @param javaId The Java runtime id of the block + * @return The Java identifier of the item + */ + public static String getPickItem(int javaId) { + String itemIdentifier = JAVA_RUNTIME_ID_TO_PICK_ITEM.get(javaId); + if (itemIdentifier == null) { + return JAVA_ID_BLOCK_MAP.inverse().get(javaId).split("\\[")[0]; + } + return itemIdentifier; + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java index f5e1d5948c5..b59794796f4 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BannerBlockEntityTranslator.java @@ -47,7 +47,13 @@ public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) if (tag.contains("Patterns")) { ListTag patterns = tag.get("Patterns"); - builder.put("Patterns", BannerTranslator.convertBannerPattern(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 + builder.putInt("Type", 1); + } else { + builder.put("Patterns", BannerTranslator.convertBannerPattern(patterns)); + } } if (tag.contains("CustomName")) { diff --git a/connector/src/main/java/org/geysermc/connector/skin/SkinManager.java b/connector/src/main/java/org/geysermc/connector/skin/SkinManager.java index 70b5a29856d..e23b4b52386 100644 --- a/connector/src/main/java/org/geysermc/connector/skin/SkinManager.java +++ b/connector/src/main/java/org/geysermc/connector/skin/SkinManager.java @@ -83,11 +83,10 @@ public static PlayerListPacket.Entry buildEntryManually(GeyserSession session, U // This attempts to find the xuid of the player so profile images show up for xbox accounts String xuid = ""; - for (GeyserSession player : GeyserConnector.getInstance().getPlayers()) { - if (player.getPlayerEntity().getUuid() != null && player.getPlayerEntity().getUuid().equals(uuid)) { - xuid = player.getAuthData().getXboxUUID(); - break; - } + GeyserSession player = GeyserConnector.getInstance().getPlayerByUuid(uuid); + + if (player.getPlayerEntity().getUuid() != null && player != null) { + xuid = player.getAuthData().getXboxUUID(); } PlayerListPacket.Entry entry; @@ -274,11 +273,10 @@ public static GameProfileData from(GameProfile profile) { // return default skin with default cape when texture data is invalid String skinUrl = isAlex ? SkinProvider.EMPTY_SKIN_ALEX.getTextureUrl() : SkinProvider.EMPTY_SKIN.getTextureUrl(); if ("steve".equals(skinUrl) || "alex".equals(skinUrl)) { - for (GeyserSession session : GeyserConnector.getInstance().getPlayers()) { - if (session.getPlayerEntity().getUuid() != null && session.getPlayerEntity().getUuid().equals(profile.getId())) { - skinUrl = session.getClientData().getSkinId(); - break; - } + GeyserSession session = GeyserConnector.getInstance().getPlayerByUuid(profile.getId()); + + if (session.getPlayerEntity().getUuid() != null && session != null) { + skinUrl = session.getClientData().getSkinId(); } } return new GameProfileData(skinUrl, SkinProvider.EMPTY_CAPE.getTextureUrl(), isAlex); diff --git a/connector/src/main/java/org/geysermc/connector/skin/SkinProvider.java b/connector/src/main/java/org/geysermc/connector/skin/SkinProvider.java index 117198685a5..948e4b37447 100644 --- a/connector/src/main/java/org/geysermc/connector/skin/SkinProvider.java +++ b/connector/src/main/java/org/geysermc/connector/skin/SkinProvider.java @@ -144,12 +144,10 @@ public static CompletableFuture requestSkinAndCape(UUID playerId, S String newSkinUrl = skinUrl; if ("steve".equals(skinUrl) || "alex".equals(skinUrl)) { - // TODO: Don't have a for loop for this? Have a proper map? - for (GeyserSession session : GeyserConnector.getInstance().getPlayers()) { - if (session.getPlayerEntity().getUuid().equals(playerId)) { - newSkinUrl = session.getClientData().getSkinId(); - break; - } + GeyserSession session = GeyserConnector.getInstance().getPlayerByUuid(playerId); + + if (session != null) { + newSkinUrl = session.getClientData().getSkinId(); } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java index c1224e6e255..75bd7c94ef7 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java @@ -46,6 +46,7 @@ import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; +import org.geysermc.connector.network.translators.world.block.BlockTranslator; import java.util.Collections; import java.util.Objects; @@ -168,13 +169,17 @@ public static ItemData createUnusableSpaceBlock(String description) { * @param session the Bedrock client's session * @param itemName the Java identifier of the item to search/select */ - public static void findOrCreatePickedBlock(GeyserSession session, String itemName) { + public static void findOrCreateItem(GeyserSession session, String itemName) { // Get the inventory to choose a slot to pick Inventory inventory = session.getInventoryCache().getOpenInventory(); if (inventory == null) { inventory = session.getInventory(); } + if (itemName.equals("minecraft:air")) { + return; + } + // Check hotbar for item for (int i = 36; i < 45; i++) { if (inventory.getItem(i) == null) { @@ -219,12 +224,17 @@ public static void findOrCreatePickedBlock(GeyserSession session, String itemNam } } - ClientCreativeInventoryActionPacket actionPacket = new ClientCreativeInventoryActionPacket(slot, - new ItemStack(ItemRegistry.getItemEntry(itemName).getJavaId())); - if ((slot - 36) != session.getInventory().getHeldItemSlot()) { - setHotbarItem(session, slot); + ItemEntry entry = ItemRegistry.getItemEntry(itemName); + if (entry != null) { + ClientCreativeInventoryActionPacket actionPacket = new ClientCreativeInventoryActionPacket(slot, + new ItemStack(entry.getJavaId())); + if ((slot - 36) != session.getInventory().getHeldItemSlot()) { + setHotbarItem(session, slot); + } + session.sendDownstreamPacket(actionPacket); + } else { + session.getConnector().getLogger().debug("Cannot find item for block " + itemName); } - session.sendDownstreamPacket(actionPacket); } } diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index bdb212160de..ac9ec753d83 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -32,6 +32,11 @@ remote: port: 25565 # Authentication type. Can be offline, online, or floodgate (see https://github.com/GeyserMC/Geyser/wiki/Floodgate). auth-type: online + # Whether to enable PROXY protocol or not while connecting to the server. + # This is useful only when: + # 1) Your server supports PROXY protocol (it probably doesn't) + # 2) You run Velocity or BungeeCord with respective option enabled. + use-proxy-protocol: false # Floodgate uses encryption to ensure use from authorised sources. # This should point to the public key generated by Floodgate (Bungee or CraftBukkit)