From 3b8d1758b3333780a050cceaa5e4fb6cb830dc05 Mon Sep 17 00:00:00 2001 From: DoctorMacc Date: Mon, 20 Jul 2020 19:02:18 -0400 Subject: [PATCH 001/140] Initial update for 20w29a --- connector/pom.xml | 2 +- .../java/org/geysermc/connector/GeyserConnector.java | 7 +++---- .../translators/bedrock/BedrockActionTranslator.java | 5 +---- .../bedrock/BedrockAdventureSettingsTranslator.java | 11 ++--------- connector/src/main/resources/languages | 2 +- connector/src/main/resources/mappings | 2 +- 6 files changed, 9 insertions(+), 20 deletions(-) diff --git a/connector/pom.xml b/connector/pom.xml index f58fb0be8..b595ed327 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -105,7 +105,7 @@ com.github.steveice10 mcprotocollib - 46b46001f6 + 20w29a-SNAPSHOT compile diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 89eccda5c..bdaabc52f 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -32,10 +32,10 @@ import com.nukkitx.protocol.bedrock.BedrockServer; import com.nukkitx.protocol.bedrock.v407.Bedrock_v407; import lombok.Getter; import lombok.Setter; -import org.geysermc.connector.common.AuthType; -import org.geysermc.connector.common.PlatformType; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.command.CommandManager; +import org.geysermc.connector.common.AuthType; +import org.geysermc.connector.common.PlatformType; import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.metrics.Metrics; import org.geysermc.connector.network.ConnectorServerEventHandler; @@ -51,17 +51,16 @@ import org.geysermc.connector.network.translators.item.PotionMixRegistry; import org.geysermc.connector.network.translators.sound.SoundHandlerRegistry; import org.geysermc.connector.network.translators.sound.SoundRegistry; import org.geysermc.connector.network.translators.world.WorldManager; -import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator; import org.geysermc.connector.utils.DimensionUtils; import org.geysermc.connector.utils.DockerCheck; +import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.connector.utils.LocaleUtils; import java.net.InetSocketAddress; import java.text.DecimalFormat; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java index e7139353c..118a8cbf3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java @@ -26,7 +26,6 @@ package org.geysermc.connector.network.translators.bedrock; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; -import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerState; import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; @@ -73,9 +72,7 @@ public class BedrockActionTranslator extends PacketTranslator Date: Tue, 21 Jul 2020 00:45:38 +0100 Subject: [PATCH 002/140] Add piglin brutes (#989) --- .../entity/living/monster/BasePiglinEntity.java | 11 +++++++++++ .../connector/entity/living/monster/PiglinEntity.java | 2 +- .../geysermc/connector/entity/type/EntityType.java | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 connector/src/main/java/org/geysermc/connector/entity/living/monster/BasePiglinEntity.java diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/BasePiglinEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/BasePiglinEntity.java new file mode 100644 index 000000000..830c7ea3d --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/BasePiglinEntity.java @@ -0,0 +1,11 @@ +package org.geysermc.connector.entity.living.monster; + +import com.nukkitx.math.vector.Vector3f; +import org.geysermc.connector.entity.type.EntityType; + +public class BasePiglinEntity extends MonsterEntity { + + public BasePiglinEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + } +} \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java index 83027e30e..7b0d71e17 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/PiglinEntity.java @@ -33,7 +33,7 @@ import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.item.ItemRegistry; -public class PiglinEntity extends MonsterEntity { +public class PiglinEntity extends BasePiglinEntity { public PiglinEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); 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 e32de0568..87f4c8b50 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 @@ -155,6 +155,7 @@ public enum EntityType { HOGLIN(AnimalEntity.class, 124, 1.4f, 1.3965f, 1.3965f, 0f, "minecraft:hoglin"), ZOGLIN(ZoglinEntity.class, 126, 1.4f, 1.3965f, 1.3965f, 0f, "minecraft:zoglin"), PIGLIN(PiglinEntity.class, 123, 1.95f, 0.6f, 0.6f, 0f, "minecraft:piglin"), + PIGLIN_BRUTE(BasePiglinEntity.class, 127, 1.95f, 0.6f, 0.6f, 0f, "minecraft:piglin_brute"), /** * Item frames are handled differently since they are a block in Bedrock. From 6b7dad148375361ec24a4f6284df398369c063d9 Mon Sep 17 00:00:00 2001 From: DoctorMacc Date: Fri, 24 Jul 2020 10:39:10 -0400 Subject: [PATCH 003/140] Update for protocol 408 and 20w30a --- connector/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/connector/pom.xml b/connector/pom.xml index b595ed327..93501d01b 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -32,7 +32,7 @@ com.nukkitx.protocol - bedrock-v407 + bedrock-v408 2.6.0-SNAPSHOT compile @@ -105,7 +105,7 @@ com.github.steveice10 mcprotocollib - 20w29a-SNAPSHOT + 20w30a-SNAPSHOT compile From ae77388b2e0df0e68375ac95dbd945c67f5e738e Mon Sep 17 00:00:00 2001 From: DoctorMacc Date: Fri, 24 Jul 2020 10:45:36 -0400 Subject: [PATCH 004/140] Allow compilation; update GeyserConnector --- connector/pom.xml | 6 +++--- .../main/java/org/geysermc/connector/GeyserConnector.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/connector/pom.xml b/connector/pom.xml index 93501d01b..b999d842e 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -103,9 +103,9 @@ compile - com.github.steveice10 - mcprotocollib - 20w30a-SNAPSHOT + com.github.GeyserMC + MCProtocolLib + da45dc9165 compile diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index bdaabc52f..de465637b 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -29,7 +29,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import com.nukkitx.protocol.bedrock.BedrockServer; -import com.nukkitx.protocol.bedrock.v407.Bedrock_v407; +import com.nukkitx.protocol.bedrock.v408.Bedrock_v408; import lombok.Getter; import lombok.Setter; import org.geysermc.connector.bootstrap.GeyserBootstrap; @@ -72,7 +72,7 @@ public class GeyserConnector { public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES); - public static final BedrockPacketCodec BEDROCK_PACKET_CODEC = Bedrock_v407.V407_CODEC; + public static final BedrockPacketCodec BEDROCK_PACKET_CODEC = Bedrock_v408.V408_CODEC; public static final String NAME = "Geyser"; public static final String VERSION = "DEV"; // A fallback for running in IDEs From 523e30429027d7cabaf8c88687790e1252b931f3 Mon Sep 17 00:00:00 2001 From: DoctorMacc Date: Fri, 24 Jul 2020 16:45:44 -0400 Subject: [PATCH 005/140] Update mappings --- connector/src/main/resources/mappings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index 6bb50495f..536cde02e 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 6bb50495ffc9c3ef74c90838ed2f6a4f784c0be4 +Subproject commit 536cde02e47416a341c6de4635a3e5a400d4b9a3 From bf07f1a9ba8e967617cd9e478f6235906c375efe Mon Sep 17 00:00:00 2001 From: DoctorMacc Date: Wed, 29 Jul 2020 21:05:18 -0400 Subject: [PATCH 006/140] Update to 1.16.2-pre1 --- bootstrap/spigot/pom.xml | 2 +- .../platform/spigot/world/GeyserSpigotWorldManager.java | 7 ++++--- connector/pom.xml | 2 +- connector/src/main/resources/mappings | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml index e05ad7f03..854ce9fa5 100644 --- a/bootstrap/spigot/pom.xml +++ b/bootstrap/spigot/pom.xml @@ -26,7 +26,7 @@ us.myles viaversion - 3.0.1 + 3.1.0-1.16.2-pre1 provided diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java index d785fabca..23caf5d99 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java @@ -33,7 +33,7 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.WorldManager; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import us.myles.ViaVersion.protocols.protocol1_13_1to1_13.Protocol1_13_1To1_13; -import us.myles.ViaVersion.protocols.protocol1_16to1_15_2.data.MappingData; +import us.myles.ViaVersion.protocols.protocol1_16_2to1_16_1.data.MappingData; @AllArgsConstructor public class GeyserSpigotWorldManager extends WorldManager { @@ -60,12 +60,13 @@ public class GeyserSpigotWorldManager extends WorldManager { Block block = Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getBlockAt(x, y, z); // Black magic that gets the old block state ID int oldBlockId = (block.getType().getId() << 4) | (block.getData() & 0xF); - // Convert block state from old version -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 + // Convert block state from old version -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2 int thirteenBlockId = us.myles.ViaVersion.protocols.protocol1_13to1_12_2.data.MappingData.blockMappings.getNewId(oldBlockId); int thirteenPointOneBlockId = Protocol1_13_1To1_13.getNewBlockStateId(thirteenBlockId); int fourteenBlockId = us.myles.ViaVersion.protocols.protocol1_14to1_13_2.data.MappingData.blockStateMappings.getNewId(thirteenPointOneBlockId); int fifteenBlockId = us.myles.ViaVersion.protocols.protocol1_15to1_14_4.data.MappingData.blockStateMappings.getNewId(fourteenBlockId); - return MappingData.blockStateMappings.getNewId(fifteenBlockId); + int sixteenBlockId = us.myles.ViaVersion.protocols.protocol1_16to1_15_2.data.MappingData.blockStateMappings.getNewId(fifteenBlockId); + return MappingData.blockStateMappings.getNewId(sixteenBlockId); } else { return BlockTranslator.AIR; } diff --git a/connector/pom.xml b/connector/pom.xml index b999d842e..e2e49df5d 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -105,7 +105,7 @@ com.github.GeyserMC MCProtocolLib - da45dc9165 + ba0c4e9ecd compile diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index 536cde02e..46538b603 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 536cde02e47416a341c6de4635a3e5a400d4b9a3 +Subproject commit 46538b6036f02b3fe855ff75d82646ed74d46156 From 098a0e7993b81cd28f38b54de76d169666a804e8 Mon Sep 17 00:00:00 2001 From: DoctorMacc Date: Wed, 5 Aug 2020 18:57:41 -0400 Subject: [PATCH 007/140] Update to 1.16.2-pre2 --- connector/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/pom.xml b/connector/pom.xml index e2e49df5d..f78cc8750 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -105,7 +105,7 @@ com.github.GeyserMC MCProtocolLib - ba0c4e9ecd + da148409ff compile From 7df476183abe005b93593e9ac774b015f2909d20 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Sat, 8 Aug 2020 17:50:32 -0400 Subject: [PATCH 008/140] Implement proper mappings for pistons, dropper, dispenser (#1103) This commit gets rid of the hacky workaround implemented for pistons, droppers and dispensers and actually implements the vanilla data values. --- .../connector/network/translators/item/ItemRegistry.java | 7 ------- connector/src/main/resources/mappings | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java index 6fc564729..4828fbf27 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java @@ -214,13 +214,6 @@ public class ItemRegistry { return itemEntry; } } - // If item find was unsuccessful first time, we try again while ignoring damage - // Fixes piston, sticky pistons, dispensers and droppers turning into air from creative inventory - for (ItemEntry itemEntry : ITEM_ENTRIES.values()) { - if (itemEntry.getBedrockId() == data.getId()) { - return itemEntry; - } - } // This will hide the message when the player clicks with an empty hand if (data.getId() != 0 && data.getDamage() != 0) { diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index 83a346876..a222d85dc 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 83a3468766b82ea97bd1ae8a1e92bffcc2745349 +Subproject commit a222d85dc0b8e8c95a150f2d488a6a25390b8c2f From 0fde30fc78f05ad97d9aa278ebc4283187f10e22 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Sat, 8 Aug 2020 17:50:49 -0400 Subject: [PATCH 009/140] GeyserSession: always send naturalRegeneration=false gamerule (#1097) This essentially gives the server full control over the health visual. --- .../geysermc/connector/network/session/GeyserSession.java | 6 ++++++ 1 file changed, 6 insertions(+) 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 5378900d0..340292e25 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 @@ -249,6 +249,12 @@ public class GeyserSession implements CommandSender { attributes.add(new AttributeData("minecraft:movement", 0.0f, 1024f, 0.1f, 0.1f)); attributesPacket.setAttributes(attributes); upstream.sendPacket(attributesPacket); + + // Only allow the server to send health information + // Setting this to false allows natural regeneration to work false but doesn't break it being true + GameRulesChangedPacket gamerulePacket = new GameRulesChangedPacket(); + gamerulePacket.getGameRules().add(new GameRuleData<>("naturalregeneration", false)); + upstream.sendPacket(gamerulePacket); } public void login() { From 0a5048232f8d994537190674e1b4f6c09729ff1f Mon Sep 17 00:00:00 2001 From: rtm516 Date: Sat, 8 Aug 2020 23:41:12 +0100 Subject: [PATCH 010/140] Add support for client side settings (#1035) * Port code from #486 Co-authored-by: Luke <32024335+lukeeey@users.noreply.github.com> * Fix and clean code and add default gamemode changing * Clean copyright * Remove direct modification of server, clean up code and add player list xuid fetching. * Move to custom settings menu * Move sendAdventureSettings to GeyserSession * Add javadoc comments * Add translation support * Remove updated copyright * Clean up * Clarify some javadoc comments * Remove obsolete code * Update languages submodule * Fix javadoc comments * Fix compile Co-authored-by: Luke <32024335+lukeeey@users.noreply.github.com> Co-authored-by: Redned --- .../world/GeyserSpigotWorldManager.java | 21 ++- connector/pom.xml | 6 + .../connector/bootstrap/GeyserBootstrap.java | 4 +- .../connector/entity/PlayerEntity.java | 12 +- .../network/UpstreamPacketHandler.java | 5 + .../network/session/GeyserSession.java | 109 +++++++++++- .../{ScoreboardCache.java => WorldCache.java} | 23 ++- ...drockServerSettingsRequestTranslator.java} | 20 ++- .../BedrockEntityEventTranslator.java | 2 +- .../player}/BedrockActionTranslator.java | 2 +- .../player}/BedrockEmoteTranslator.java | 2 +- .../player}/BedrockInteractTranslator.java | 2 +- .../player}/BedrockMovePlayerTranslator.java | 2 +- .../BedrockLevelSoundEventTranslator.java | 2 +- .../java/JavaDifficultyTranslator.java | 2 + .../java/JavaJoinGameTranslator.java | 2 +- .../entity/JavaEntityStatusTranslator.java | 28 +++ .../player/JavaPlayerAbilitiesTranslator.java | 21 +-- .../JavaDisplayScoreboardTranslator.java | 2 +- .../JavaScoreboardObjectiveTranslator.java | 4 +- .../java/scoreboard/JavaTeamTranslator.java | 2 +- .../scoreboard/JavaUpdateScoreTranslator.java | 2 +- .../world/JavaNotifyClientTranslator.java | 31 +--- .../java/world/JavaUpdateTimeTranslator.java | 4 +- .../translators/world/GeyserWorldManager.java | 86 +++++++++ .../translators/world/WorldManager.java | 55 ++++++ .../geysermc/connector/utils/GameRule.java | 123 +++++++++++++ .../connector/utils/SettingsUtils.java | 163 ++++++++++++++++++ .../geysermc/connector/utils/SkinUtils.java | 14 +- 29 files changed, 668 insertions(+), 83 deletions(-) rename connector/src/main/java/org/geysermc/connector/network/session/cache/{ScoreboardCache.java => WorldCache.java} (78%) rename connector/src/main/java/org/geysermc/connector/network/translators/{world/CachedChunkManager.java => bedrock/BedrockServerSettingsRequestTranslator.java} (55%) rename connector/src/main/java/org/geysermc/connector/network/translators/bedrock/{ => entity}/BedrockEntityEventTranslator.java (98%) rename connector/src/main/java/org/geysermc/connector/network/translators/bedrock/{ => entity/player}/BedrockActionTranslator.java (99%) rename connector/src/main/java/org/geysermc/connector/network/translators/bedrock/{ => entity/player}/BedrockEmoteTranslator.java (96%) rename connector/src/main/java/org/geysermc/connector/network/translators/bedrock/{ => entity/player}/BedrockInteractTranslator.java (99%) rename connector/src/main/java/org/geysermc/connector/network/translators/bedrock/{ => entity/player}/BedrockMovePlayerTranslator.java (98%) rename connector/src/main/java/org/geysermc/connector/network/translators/bedrock/{ => world}/BedrockLevelSoundEventTranslator.java (97%) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java create mode 100644 connector/src/main/java/org/geysermc/connector/utils/GameRule.java create mode 100644 connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java index 7871f0060..0e7eab351 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java @@ -25,17 +25,19 @@ package org.geysermc.platform.spigot.world; +import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import lombok.AllArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.block.Block; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.WorldManager; +import org.geysermc.connector.network.translators.world.GeyserWorldManager; import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.utils.GameRule; import us.myles.ViaVersion.protocols.protocol1_13_1to1_13.Protocol1_13_1To1_13; import us.myles.ViaVersion.protocols.protocol1_16to1_15_2.data.MappingData; @AllArgsConstructor -public class GeyserSpigotWorldManager extends WorldManager { +public class GeyserSpigotWorldManager extends GeyserWorldManager { private final boolean isLegacy; // You need ViaVersion to connect to an older server with Geyser. @@ -69,4 +71,19 @@ public class GeyserSpigotWorldManager extends WorldManager { return BlockTranslator.AIR; } } + + @Override + public Boolean getGameRuleBool(GeyserSession session, GameRule gameRule) { + return Boolean.parseBoolean(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getGameRuleValue(gameRule.getJavaID())); + } + + @Override + public int getGameRuleInt(GeyserSession session, GameRule gameRule) { + return Integer.parseInt(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getGameRuleValue(gameRule.getJavaID())); + } + + @Override + public boolean hasPermission(GeyserSession session, String permission) { + return Bukkit.getPlayer(session.getPlayerEntity().getUsername()).hasPermission(permission); + } } diff --git a/connector/pom.xml b/connector/pom.xml index 17c9711d9..ad8b82211 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -96,6 +96,12 @@ 8.3.1 compile + + com.nukkitx.fastutil + fastutil-object-object-maps + 8.3.1 + compile + com.google.guava guava diff --git a/connector/src/main/java/org/geysermc/connector/bootstrap/GeyserBootstrap.java b/connector/src/main/java/org/geysermc/connector/bootstrap/GeyserBootstrap.java index eb8bf967e..b6a766a3b 100644 --- a/connector/src/main/java/org/geysermc/connector/bootstrap/GeyserBootstrap.java +++ b/connector/src/main/java/org/geysermc/connector/bootstrap/GeyserBootstrap.java @@ -30,14 +30,14 @@ import org.geysermc.connector.ping.IGeyserPingPassthrough; import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.GeyserLogger; import org.geysermc.connector.command.CommandManager; -import org.geysermc.connector.network.translators.world.CachedChunkManager; +import org.geysermc.connector.network.translators.world.GeyserWorldManager; import org.geysermc.connector.network.translators.world.WorldManager; import java.nio.file.Path; public interface GeyserBootstrap { - CachedChunkManager DEFAULT_CHUNK_MANAGER = new CachedChunkManager(); + GeyserWorldManager DEFAULT_CHUNK_MANAGER = new GeyserWorldManager(); /** * Called when the GeyserBootstrap is enabled diff --git a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java index 424f51870..52b273513 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java @@ -30,6 +30,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadat import com.github.steveice10.mc.protocol.data.message.TextMessage; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.AdventureSetting; import com.nukkitx.protocol.bedrock.data.AttributeData; import com.nukkitx.protocol.bedrock.data.PlayerPermission; import com.nukkitx.protocol.bedrock.data.command.CommandPermission; @@ -47,10 +48,7 @@ import org.geysermc.connector.scoreboard.Team; import org.geysermc.connector.utils.AttributeUtils; import org.geysermc.connector.utils.MessageUtils; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; import java.util.concurrent.TimeUnit; @Getter @Setter @@ -95,7 +93,7 @@ public class PlayerEntity extends LivingEntity { addPlayerPacket.setMotion(motion); addPlayerPacket.setHand(hand); addPlayerPacket.getAdventureSettings().setCommandPermission(CommandPermission.NORMAL); - addPlayerPacket.getAdventureSettings().setPlayerPermission(PlayerPermission.VISITOR); + addPlayerPacket.getAdventureSettings().setPlayerPermission(PlayerPermission.MEMBER); addPlayerPacket.setDeviceId(""); addPlayerPacket.setPlatformChatId(""); addPlayerPacket.getMetadata().putAll(metadata); @@ -212,7 +210,7 @@ public class PlayerEntity extends LivingEntity { if (entityMetadata.getId() == 2) { // System.out.println(session.getScoreboardCache().getScoreboard().getObjectives().keySet()); - for (Team team : session.getScoreboardCache().getScoreboard().getTeams().values()) { + for (Team team : session.getWorldCache().getScoreboard().getTeams().values()) { // session.getConnector().getLogger().info("team name " + team.getName()); // session.getConnector().getLogger().info("team entities " + team.getEntities()); } @@ -221,7 +219,7 @@ public class PlayerEntity extends LivingEntity { if (name != null) { username = MessageUtils.getBedrockMessage(name); } - Team team = session.getScoreboardCache().getScoreboard().getTeamFor(username); + Team team = session.getWorldCache().getScoreboard().getTeamFor(username); if (team != null) { // session.getConnector().getLogger().info("team name es " + team.getName() + " with prefix " + team.getPrefix() + " and suffix " + team.getSuffix()); metadata.put(EntityData.NAMETAG, team.getPrefix() + MessageUtils.toChatColor(team.getColor()) + username + team.getSuffix()); diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index dd9f48d6a..357e870f6 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -34,6 +34,7 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslatorRegistry; import org.geysermc.connector.utils.LoginEncryptionUtils; import org.geysermc.connector.utils.LanguageUtils; +import org.geysermc.connector.utils.SettingsUtils; public class UpstreamPacketHandler extends LoggingPacketHandler { @@ -91,6 +92,10 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { @Override public boolean handle(ModalFormResponsePacket packet) { + if (packet.getFormId() == SettingsUtils.SETTINGS_FORM_ID) { + return SettingsUtils.handleSettingsForm(session, packet.getFormData()); + } + return LoginEncryptionUtils.authenticateFromForm(session, connector, packet.getFormId(), packet.getFormData()); } 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 340292e25..ac186a799 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 @@ -46,6 +46,7 @@ import com.nukkitx.math.vector.*; import com.nukkitx.protocol.bedrock.BedrockPacket; import com.nukkitx.protocol.bedrock.BedrockServerSession; import com.nukkitx.protocol.bedrock.data.*; +import com.nukkitx.protocol.bedrock.data.command.CommandPermission; import com.nukkitx.protocol.bedrock.packet.*; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; @@ -54,6 +55,7 @@ import it.unimi.dsi.fastutil.objects.Object2LongMap; import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap; import lombok.Getter; import lombok.Setter; +import org.geysermc.common.window.CustomFormWindow; import org.geysermc.common.window.FormWindow; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; @@ -79,9 +81,7 @@ import java.net.InetSocketAddress; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; +import java.util.*; import java.util.concurrent.atomic.AtomicInteger; @Getter @@ -102,7 +102,7 @@ public class GeyserSession implements CommandSender { private ChunkCache chunkCache; private EntityCache entityCache; private InventoryCache inventoryCache; - private ScoreboardCache scoreboardCache; + private WorldCache worldCache; private WindowCache windowCache; @Setter private TeleportCache teleportCache; @@ -191,6 +191,41 @@ public class GeyserSession implements CommandSender { private MinecraftProtocol protocol; + private boolean reducedDebugInfo = false; + + @Setter + private CustomFormWindow settingsForm; + + /** + * The op permission level set by the server + */ + @Setter + private int opPermissionLevel = 0; + + /** + * If the current player can fly + */ + @Setter + private boolean canFly = false; + + /** + * If the current player is flying + */ + @Setter + private boolean flying = false; + + /** + * If the current player is in noclip + */ + @Setter + private boolean noClip = false; + + /** + * If the current player can not interact with the world + */ + @Setter + private boolean worldImmutable = false; + public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) { this.connector = connector; this.upstream = new UpstreamSession(bedrockServerSession); @@ -198,7 +233,7 @@ public class GeyserSession implements CommandSender { this.chunkCache = new ChunkCache(this); this.entityCache = new EntityCache(this); this.inventoryCache = new InventoryCache(this); - this.scoreboardCache = new ScoreboardCache(this); + this.worldCache = new WorldCache(this); this.windowCache = new WindowCache(this); this.playerEntity = new PlayerEntity(new GameProfile(UUID.randomUUID(), "unknown"), 1, 1, Vector3f.ZERO, Vector3f.ZERO, Vector3f.ZERO); @@ -440,7 +475,7 @@ public class GeyserSession implements CommandSender { this.chunkCache = null; this.entityCache = null; - this.scoreboardCache = null; + this.worldCache = null; this.inventoryCache = null; this.windowCache = null; @@ -605,4 +640,66 @@ public class GeyserSession implements CommandSender { connector.getLogger().debug("Tried to send downstream packet " + packet.getClass().getSimpleName() + " before connected to the server"); } } + + /** + * Update the cached value for the reduced debug info gamerule. + * This also toggles the coordinates display + * + * @param value The new value for reducedDebugInfo + */ + public void setReducedDebugInfo(boolean value) { + worldCache.setShowCoordinates(!value); + reducedDebugInfo = value; + } + + /** + * Send a gamerule value to the client + * + * @param gameRule The gamerule to send + * @param value The value of the gamerule + */ + public void sendGameRule(String gameRule, Object value) { + GameRulesChangedPacket gameRulesChangedPacket = new GameRulesChangedPacket(); + gameRulesChangedPacket.getGameRules().add(new GameRuleData<>(gameRule, value)); + upstream.sendPacket(gameRulesChangedPacket); + } + + /** + * @see org.geysermc.connector.network.translators.world.WorldManager#hasPermission(GeyserSession, String) + */ + public Boolean hasPermission(String permission) { + return connector.getWorldManager().hasPermission(this, permission); + } + + /** + * Send an AdventureSettingsPacket to the client with the latest flags + */ + public void sendAdventureSettings() { + AdventureSettingsPacket adventureSettingsPacket = new AdventureSettingsPacket(); + adventureSettingsPacket.setUniqueEntityId(playerEntity.getGeyserId()); + adventureSettingsPacket.setCommandPermission(CommandPermission.NORMAL); + adventureSettingsPacket.setPlayerPermission(PlayerPermission.MEMBER); + + Set flags = new HashSet<>(); + if (canFly) { + flags.add(AdventureSetting.MAY_FLY); + } + + if (flying) { + flags.add(AdventureSetting.FLYING); + } + + if (worldImmutable) { + flags.add(AdventureSetting.WORLD_IMMUTABLE); + } + + if (noClip) { + flags.add(AdventureSetting.NO_CLIP); + } + + flags.add(AdventureSetting.AUTO_JUMP); + + adventureSettingsPacket.getSettings().addAll(flags); + sendUpstreamPacket(adventureSettingsPacket); + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/ScoreboardCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java similarity index 78% rename from connector/src/main/java/org/geysermc/connector/network/session/cache/ScoreboardCache.java rename to connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java index 9a6924075..310e5f9d7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/ScoreboardCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java @@ -25,7 +25,9 @@ package org.geysermc.connector.network.session.cache; +import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; import lombok.Getter; +import lombok.Setter; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.scoreboard.Objective; import org.geysermc.connector.scoreboard.Scoreboard; @@ -33,11 +35,18 @@ import org.geysermc.connector.scoreboard.Scoreboard; import java.util.Collection; @Getter -public class ScoreboardCache { +public class WorldCache { + private GeyserSession session; + + @Setter + private Difficulty difficulty = Difficulty.EASY; + + private boolean showCoordinates = true; + private Scoreboard scoreboard; - public ScoreboardCache(GeyserSession session) { + public WorldCache(GeyserSession session) { this.session = session; this.scoreboard = new Scoreboard(session); } @@ -52,4 +61,14 @@ public class ScoreboardCache { } } } + + /** + * Tell the client to hide or show the coordinates + * + * @param value True to show, false to hide + */ + public void setShowCoordinates(boolean value) { + showCoordinates = value; + session.sendGameRule("showcoordinates", value); + } } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/CachedChunkManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockServerSettingsRequestTranslator.java similarity index 55% rename from connector/src/main/java/org/geysermc/connector/network/translators/world/CachedChunkManager.java rename to connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockServerSettingsRequestTranslator.java index 0580fcffd..a8591cd7f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/CachedChunkManager.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockServerSettingsRequestTranslator.java @@ -23,15 +23,25 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.world; +package org.geysermc.connector.network.translators.bedrock; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; +import com.nukkitx.protocol.bedrock.packet.ServerSettingsRequestPacket; +import com.nukkitx.protocol.bedrock.packet.ServerSettingsResponsePacket; 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.utils.SettingsUtils; -public class CachedChunkManager extends WorldManager { +@Translator(packet = ServerSettingsRequestPacket.class) +public class BedrockServerSettingsRequestTranslator extends PacketTranslator { @Override - public int getBlockAt(GeyserSession session, int x, int y, int z) { - return session.getChunkCache().getBlockAt(new Position(x, y, z)); + public void translate(ServerSettingsRequestPacket packet, GeyserSession session) { + SettingsUtils.buildForm(session); + + ServerSettingsResponsePacket serverSettingsResponsePacket = new ServerSettingsResponsePacket(); + serverSettingsResponsePacket.setFormData(session.getSettingsForm().getJSONData()); + serverSettingsResponsePacket.setFormId(SettingsUtils.SETTINGS_FORM_ID); + session.sendUpstreamPacket(serverSettingsResponsePacket); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEntityEventTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/BedrockEntityEventTranslator.java similarity index 98% rename from connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEntityEventTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/BedrockEntityEventTranslator.java index 620e2b8a5..18fd6614e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEntityEventTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/BedrockEntityEventTranslator.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.bedrock; +package org.geysermc.connector.network.translators.bedrock.entity; import com.github.steveice10.mc.protocol.data.game.window.VillagerTrade; import com.github.steveice10.mc.protocol.data.game.window.WindowType; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java similarity index 99% rename from connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java index 2e1a122ec..f4365f796 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockActionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.bedrock; +package org.geysermc.connector.network.translators.bedrock.entity.player; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEmoteTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockEmoteTranslator.java similarity index 96% rename from connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEmoteTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockEmoteTranslator.java index f07016e70..e76fece0b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEmoteTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockEmoteTranslator.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.bedrock; +package org.geysermc.connector.network.translators.bedrock.entity.player; import com.nukkitx.protocol.bedrock.packet.EmotePacket; import org.geysermc.connector.GeyserConnector; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java similarity index 99% rename from connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java index 856b01eec..c5d6f2dda 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInteractTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockInteractTranslator.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.bedrock; +package org.geysermc.connector.network.translators.bedrock.entity.player; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityDataMap; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java similarity index 98% rename from connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java index 0abf81505..be918ba74 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMovePlayerTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.bedrock; +package org.geysermc.connector.network.translators.bedrock.entity.player; import com.nukkitx.math.vector.Vector3d; import org.geysermc.connector.common.ChatColor; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLevelSoundEventTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/world/BedrockLevelSoundEventTranslator.java similarity index 97% rename from connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLevelSoundEventTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/bedrock/world/BedrockLevelSoundEventTranslator.java index 08ad10bfb..44553e82e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLevelSoundEventTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/world/BedrockLevelSoundEventTranslator.java @@ -23,7 +23,7 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.bedrock; +package org.geysermc.connector.network.translators.bedrock.world; import com.nukkitx.protocol.bedrock.data.SoundEvent; import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDifficultyTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDifficultyTranslator.java index 7e4d9ca86..601b0fc48 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDifficultyTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDifficultyTranslator.java @@ -40,5 +40,7 @@ public class JavaDifficultyTranslator extends PacketTranslator playerFlags = new ObjectOpenHashSet<>(); - playerFlags.add(AdventureSetting.AUTO_JUMP); - if (packet.isCanFly()) - playerFlags.add(AdventureSetting.MAY_FLY); - - if (packet.isFlying()) - playerFlags.add(AdventureSetting.FLYING); - - AdventureSettingsPacket adventureSettingsPacket = new AdventureSettingsPacket(); - adventureSettingsPacket.setPlayerPermission(PlayerPermission.MEMBER); - // Required or the packet simply is not sent - adventureSettingsPacket.setCommandPermission(CommandPermission.NORMAL); - adventureSettingsPacket.setUniqueEntityId(entity.getGeyserId()); - adventureSettingsPacket.getSettings().addAll(playerFlags); - session.sendUpstreamPacket(adventureSettingsPacket); + session.setCanFly(packet.isCanFly()); + session.setFlying(packet.isFlying()); + session.sendAdventureSettings(); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaDisplayScoreboardTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaDisplayScoreboardTranslator.java index 5a722953a..3ee174d7a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaDisplayScoreboardTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaDisplayScoreboardTranslator.java @@ -36,7 +36,7 @@ public class JavaDisplayScoreboardTranslator extends PacketTranslator { public void translate(ServerTeamPacket packet, GeyserSession session) { GeyserConnector.getInstance().getLogger().debug("Team packet " + packet.getTeamName() + " " + packet.getAction() + " " + Arrays.toString(packet.getPlayers())); - Scoreboard scoreboard = session.getScoreboardCache().getScoreboard(); + Scoreboard scoreboard = session.getWorldCache().getScoreboard(); Team team = scoreboard.getTeam(packet.getTeamName()); switch (packet.getAction()) { case CREATE: diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java index 827e4c7f4..8d7d59a89 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java @@ -42,7 +42,7 @@ public class JavaUpdateScoreTranslator extends PacketTranslator playerFlags = new ObjectOpenHashSet<>(); GameMode gameMode = (GameMode) packet.getValue(); - if (gameMode == GameMode.ADVENTURE) - playerFlags.add(AdventureSetting.WORLD_IMMUTABLE); - if (gameMode == GameMode.CREATIVE) - playerFlags.add(AdventureSetting.MAY_FLY); - - if (gameMode == GameMode.SPECTATOR) { - playerFlags.add(AdventureSetting.MAY_FLY); - playerFlags.add(AdventureSetting.NO_CLIP); - playerFlags.add(AdventureSetting.FLYING); - playerFlags.add(AdventureSetting.WORLD_IMMUTABLE); - gameMode = GameMode.CREATIVE; // spectator doesnt exist on bedrock - } - - playerFlags.add(AdventureSetting.AUTO_JUMP); + session.setNoClip(gameMode == GameMode.SPECTATOR); + session.setWorldImmutable(gameMode == GameMode.ADVENTURE || gameMode == GameMode.SPECTATOR); + session.sendAdventureSettings(); SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket(); playerGameTypePacket.setGamemode(gameMode.ordinal()); session.sendUpstreamPacket(playerGameTypePacket); session.setGameMode(gameMode); - // We need to delay this because otherwise it's overridden by the adventure settings from the abilities packet - session.getConnector().getGeneralThreadPool().schedule(() -> { - AdventureSettingsPacket adventureSettingsPacket = new AdventureSettingsPacket(); - adventureSettingsPacket.setPlayerPermission(PlayerPermission.MEMBER); - adventureSettingsPacket.setCommandPermission(CommandPermission.NORMAL); - adventureSettingsPacket.setUniqueEntityId(entity.getGeyserId()); - adventureSettingsPacket.getSettings().addAll(playerFlags); - session.sendUpstreamPacket(adventureSettingsPacket); - }, 50, TimeUnit.MILLISECONDS); - // Update the crafting grid to add/remove barriers for creative inventory PlayerInventoryTranslator.updateCraftingGrid(session, session.getInventory()); break; 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 188e960d6..8dc689185 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 @@ -67,9 +67,7 @@ public class JavaUpdateTimeTranslator extends PacketTranslator("dodaylightcycle", doCycle)); - session.sendUpstreamPacket(gameRulesChangedPacket); + session.sendGameRule("dodaylightcycle", doCycle); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java new file mode 100644 index 000000000..83f1a7783 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java @@ -0,0 +1,86 @@ +/* + * 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.world; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; +import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; +import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; +import com.github.steveice10.mc.protocol.packet.ingame.client.ClientChatPacket; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.GameRule; + +public class GeyserWorldManager extends WorldManager { + + private static final Object2ObjectMap gameruleCache = new Object2ObjectOpenHashMap<>(); + + @Override + public int getBlockAt(GeyserSession session, int x, int y, int z) { + return session.getChunkCache().getBlockAt(new Position(x, y, z)); + } + + @Override + public void setGameRule(GeyserSession session, String name, Object value) { + session.sendDownstreamPacket(new ClientChatPacket("/gamerule " + name + " " + value)); + gameruleCache.put(name, String.valueOf(value)); + } + + @Override + public Boolean getGameRuleBool(GeyserSession session, GameRule gameRule) { + String value = gameruleCache.get(gameRule.getJavaID()); + if (value != null) { + return Boolean.parseBoolean(value); + } + + return gameRule.getDefaultValue() != null ? (Boolean) gameRule.getDefaultValue() : false; + } + + @Override + public int getGameRuleInt(GeyserSession session, GameRule gameRule) { + String value = gameruleCache.get(gameRule.getJavaID()); + if (value != null) { + return Integer.parseInt(value); + } + + return gameRule.getDefaultValue() != null ? (int) gameRule.getDefaultValue() : 0; + } + + @Override + public void setPlayerGameMode(GeyserSession session, GameMode gameMode) { + session.sendDownstreamPacket(new ClientChatPacket("/gamemode " + gameMode.name().toLowerCase())); + } + + @Override + public void setDifficulty(GeyserSession session, Difficulty difficulty) { + session.sendDownstreamPacket(new ClientChatPacket("/difficulty " + difficulty.name().toLowerCase())); + } + + @Override + public boolean hasPermission(GeyserSession session, String permission) { + return false; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java index 325e68609..326012277 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java @@ -26,8 +26,11 @@ package org.geysermc.connector.network.translators.world; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; +import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; +import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; import com.nukkitx.math.vector.Vector3i; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.GameRule; /** * Class that manages or retrieves various information @@ -70,4 +73,56 @@ public abstract class WorldManager { * @return the block state at the specified location */ public abstract int getBlockAt(GeyserSession session, int x, int y, int z); + + /** + * Updates a gamerule value on the Java server + * + * @param session The session of the user that requested the change + * @param name The gamerule to change + * @param value The new value for the gamerule + */ + public abstract void setGameRule(GeyserSession session, String name, Object value); + + /** + * Get a gamerule value as a boolean + * + * @param session The session of the user that requested the value + * @param gameRule The gamerule to fetch the value of + * @return The boolean representation of the value + */ + public abstract Boolean getGameRuleBool(GeyserSession session, GameRule gameRule); + + /** + * Get a gamerule value as an integer + * + * @param session The session of the user that requested the value + * @param gameRule The gamerule to fetch the value of + * @return The integer representation of the value + */ + public abstract int getGameRuleInt(GeyserSession session, GameRule gameRule); + + /** + * Change the game mode of the given session + * + * @param session The session of the player to change the game mode of + * @param gameMode The game mode to change the player to + */ + public abstract void setPlayerGameMode(GeyserSession session, GameMode gameMode); + + /** + * Change the difficulty of the Java server + * + * @param session The session of the user that requested the change + * @param difficulty The difficulty to change to + */ + public abstract void setDifficulty(GeyserSession session, Difficulty difficulty); + + /** + * Checks if the given session's player has a permission + * + * @param session The session of the player to check the permission of + * @param permission The permission node to check + * @return True if the player has the requested permission, false if not + */ + public abstract boolean hasPermission(GeyserSession session, String permission); } diff --git a/connector/src/main/java/org/geysermc/connector/utils/GameRule.java b/connector/src/main/java/org/geysermc/connector/utils/GameRule.java new file mode 100644 index 000000000..350337f3d --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/utils/GameRule.java @@ -0,0 +1,123 @@ +/* + * 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.utils; + +import lombok.Getter; + +/** + * This enum stores each gamerule along with the value type and the default. + * It is used to construct the list for the settings menu + */ +public enum GameRule { + ANNOUNCEADVANCEMENTS("announceAdvancements", Boolean.class, true), // JE only + COMMANDBLOCKOUTPUT("commandBlockOutput", Boolean.class, true), + DISABLEELYTRAMOVEMENTCHECK("disableElytraMovementCheck", Boolean.class, false), // JE only + DISABLERAIDS("disableRaids", Boolean.class, false), // JE only + DODAYLIGHTCYCLE("doDaylightCycle", Boolean.class, true), + DOENTITYDROPS("doEntityDrops", Boolean.class, true), + DOFIRETICK("doFireTick", Boolean.class, true), + DOIMMEDIATERESPAWN("doImmediateRespawn", Boolean.class, false), + DOINSOMNIA("doInsomnia", Boolean.class, true), + DOLIMITEDCRAFTING("doLimitedCrafting", Boolean.class, false), // JE only + DOMOBLOOT("doMobLoot", Boolean.class, true), + DOMOBSPAWNING("doMobSpawning", Boolean.class, true), + DOPATROLSPAWNING("doPatrolSpawning", Boolean.class, true), // JE only + DOTILEDROPS("doTileDrops", Boolean.class, true), + DOTRADERSPAWNING("doTraderSpawning", Boolean.class, true), // JE only + DOWEATHERCYCLE("doWeatherCycle", Boolean.class, true), + DROWNINGDAMAGE("drowningDamage", Boolean.class, true), + FALLDAMAGE("fallDamage", Boolean.class, true), + FIREDAMAGE("fireDamage", Boolean.class, true), + FORGIVEDEADPLAYERS("forgiveDeadPlayers", Boolean.class, true), // JE only + KEEPINVENTORY("keepInventory", Boolean.class, false), + LOGADMINCOMMANDS("logAdminCommands", Boolean.class, true), // JE only + MAXCOMMANDCHAINLENGTH("maxCommandChainLength", Integer.class, 65536), + MAXENTITYCRAMMING("maxEntityCramming", Integer.class, 24), // JE only + MOBGRIEFING("mobGriefing", Boolean.class, true), + NATURALREGENERATION("naturalRegeneration", Boolean.class, true), + RANDOMTICKSPEED("randomTickSpeed", Integer.class, 3), + REDUCEDDEBUGINFO("reducedDebugInfo", Boolean.class, false), // JE only + SENDCOMMANDFEEDBACK("sendCommandFeedback", Boolean.class, true), + SHOWDEATHMESSAGES("showDeathMessages", Boolean.class, true), + SPAWNRADIUS("spawnRadius", Integer.class, 10), + SPECTATORSGENERATECHUNKS("spectatorsGenerateChunks", Boolean.class, true), // JE only + UNIVERSALANGER("universalAnger", Boolean.class, false), // JE only + + UNKNOWN("unknown", Object.class); + + private static final GameRule[] VALUES = values(); + + @Getter + private String javaID; + + @Getter + private Class type; + + @Getter + private Object defaultValue; + + GameRule(String javaID, Class type) { + this(javaID, type, null); + } + + GameRule(String javaID, Class type, Object defaultValue) { + this.javaID = javaID; + this.type = type; + this.defaultValue = defaultValue; + } + + /** + * Convert a string to an object of the correct type for the current gamerule + * + * @param value The string value to convert + * @return The converted and formatted value + */ + public Object convertValue(String value) { + if (type.equals(Boolean.class)) { + return Boolean.parseBoolean(value); + } else if (type.equals(Integer.class)) { + return Integer.parseInt(value); + } + + return null; + } + + /** + * Fetch a game rule by the given Java ID + * + * @param id The ID of the gamerule + * @return A {@link GameRule} object representing the requested ID or {@link GameRule.UNKNOWN} + */ + public static GameRule fromJavaID(String id) { + for (GameRule gamerule : VALUES) { + if (gamerule.javaID.equals(id)) { + return gamerule; + } + } + + return UNKNOWN; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java new file mode 100644 index 000000000..89e9fe67b --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java @@ -0,0 +1,163 @@ +/* + * 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.utils; + +import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; +import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; +import org.geysermc.common.window.CustomFormBuilder; +import org.geysermc.common.window.CustomFormWindow; +import org.geysermc.common.window.button.FormImage; +import org.geysermc.common.window.component.DropdownComponent; +import org.geysermc.common.window.component.InputComponent; +import org.geysermc.common.window.component.LabelComponent; +import org.geysermc.common.window.component.ToggleComponent; +import org.geysermc.common.window.response.CustomFormResponse; +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.network.session.GeyserSession; + +import java.util.ArrayList; + +public class SettingsUtils { + + // Used in UpstreamPacketHandler.java + public static final int SETTINGS_FORM_ID = 1338; + + /** + * Build a settings form for the given session and store it for later + * + * @param session The session to build the form for + */ + public static void buildForm(GeyserSession session) { + // Cache the language for cleaner access + String language = session.getClientData().getLanguageCode(); + + CustomFormBuilder builder = new CustomFormBuilder(LanguageUtils.getPlayerLocaleString("geyser.settings.title.main", language)); + builder.setIcon(new FormImage(FormImage.FormImageType.PATH, "textures/ui/settings_glyph_color_2x.png")); + + builder.addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.title.client", language))); + builder.addComponent(new ToggleComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.option.coordinates", language, session.getWorldCache().isShowCoordinates()))); + + + if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) { + builder.addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.title.server", language))); + + DropdownComponent gamemodeDropdown = new DropdownComponent(); + gamemodeDropdown.setText("%createWorldScreen.gameMode.personal"); + gamemodeDropdown.setOptions(new ArrayList<>()); + for (GameMode gamemode : GameMode.values()) { + gamemodeDropdown.addOption(LocaleUtils.getLocaleString("selectWorld.gameMode." + gamemode.name().toLowerCase(), language), session.getGameMode() == gamemode); + } + builder.addComponent(gamemodeDropdown); + + DropdownComponent difficultyDropdown = new DropdownComponent(); + difficultyDropdown.setText("%options.difficulty"); + difficultyDropdown.setOptions(new ArrayList<>()); + for (Difficulty difficulty : Difficulty.values()) { + difficultyDropdown.addOption("%options.difficulty." + difficulty.name().toLowerCase(), session.getWorldCache().getDifficulty() == difficulty); + } + builder.addComponent(difficultyDropdown); + } + + if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.gamerules")) { + builder.addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.title.game_rules", language))); + for (GameRule gamerule : GameRule.values()) { + if (gamerule.equals(GameRule.UNKNOWN)) { + continue; + } + + // Add the relevant form item based on the gamerule type + if (Boolean.class.equals(gamerule.getType())) { + builder.addComponent(new ToggleComponent(LocaleUtils.getLocaleString("gamerule." + gamerule.getJavaID(), language), GeyserConnector.getInstance().getWorldManager().getGameRuleBool(session, gamerule))); + } else if (Integer.class.equals(gamerule.getType())) { + builder.addComponent(new InputComponent(LocaleUtils.getLocaleString("gamerule." + gamerule.getJavaID(), language), "", String.valueOf(GeyserConnector.getInstance().getWorldManager().getGameRuleInt(session, gamerule)))); + } + } + } + + session.setSettingsForm(builder.build()); + } + + /** + * Handle the settings form response + * + * @param session The session that sent the response + * @param response The response string to parse + * @return True if the form was parsed correctly, false if not + */ + public static boolean handleSettingsForm(GeyserSession session, String response) { + CustomFormWindow settingsForm = session.getSettingsForm(); + settingsForm.setResponse(response); + + CustomFormResponse settingsResponse = (CustomFormResponse) settingsForm.getResponse(); + int offset = 0; + + offset++; // Client settings title + + session.getWorldCache().setShowCoordinates(settingsResponse.getToggleResponses().get(offset)); + offset++; + + if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) { + offset++; // Server settings title + + GameMode gameMode = GameMode.values()[settingsResponse.getDropdownResponses().get(offset).getElementID()]; + if (gameMode != null && gameMode != session.getGameMode()) { + session.getConnector().getWorldManager().setPlayerGameMode(session, gameMode); + } + offset++; + + Difficulty difficulty = Difficulty.values()[settingsResponse.getDropdownResponses().get(offset).getElementID()]; + if (difficulty != null && difficulty != session.getWorldCache().getDifficulty()) { + session.getConnector().getWorldManager().setDifficulty(session, difficulty); + } + offset++; + } + + if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.gamerules")) { + offset++; // Game rule title + + for (GameRule gamerule : GameRule.values()) { + if (gamerule.equals(GameRule.UNKNOWN)) { + continue; + } + + if (Boolean.class.equals(gamerule.getType())) { + Boolean value = settingsResponse.getToggleResponses().get(offset).booleanValue(); + if (value != session.getConnector().getWorldManager().getGameRuleBool(session, gamerule)) { + session.getConnector().getWorldManager().setGameRule(session, gamerule.getJavaID(), value); + } + } else if (Integer.class.equals(gamerule.getType())) { + int value = Integer.parseInt(settingsResponse.getInputResponses().get(offset)); + if (value != session.getConnector().getWorldManager().getGameRuleInt(session, gamerule)) { + session.getConnector().getWorldManager().setGameRule(session, gamerule.getJavaID(), value); + } + } + offset++; + } + } + + return true; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java index 77ab7f939..1d5e4073d 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java @@ -35,6 +35,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; import org.geysermc.connector.common.AuthType; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.auth.BedrockClientData; @@ -93,6 +94,15 @@ public class SkinUtils { ImageData.of(capeData), geometryData, "", true, false, !capeId.equals(SkinProvider.EMPTY_CAPE.getCapeId()), capeId, skinId ); + // 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().equals(uuid)) { + xuid = player.getAuthData().getXboxUUID(); + break; + } + } + PlayerListPacket.Entry entry; // If we are building a PlayerListEntry for our own session we use our AuthData UUID instead of the Java UUID @@ -102,11 +112,11 @@ public class SkinUtils { } else { entry = new PlayerListPacket.Entry(uuid); } - + entry.setName(username); entry.setEntityId(geyserId); entry.setSkin(serializedSkin); - entry.setXuid(""); + entry.setXuid(xuid); entry.setPlatformChatId(""); entry.setTeacher(false); entry.setTrustedSkin(true); From 04cf8b2a995fb2c352bcf7a289eaee6435efcacc Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 8 Aug 2020 17:56:15 -0500 Subject: [PATCH 011/140] Fix javadoc errors --- .../main/java/org/geysermc/connector/GeyserLogger.java | 6 ++++++ .../connector/network/session/GeyserSession.java | 5 ++++- .../java/org/geysermc/connector/utils/FileUtils.java | 1 + .../java/org/geysermc/connector/utils/GameRule.java | 2 +- .../org/geysermc/connector/utils/InventoryUtils.java | 3 +++ .../org/geysermc/connector/utils/MessageUtils.java | 10 +++++----- 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/GeyserLogger.java b/connector/src/main/java/org/geysermc/connector/GeyserLogger.java index 2ea45a495..0ba2a689f 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserLogger.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserLogger.java @@ -36,6 +36,9 @@ public interface GeyserLogger { /** * Logs a severe message and an exception to console + * + * @param message the message to log + * @param error the error to throw */ void severe(String message, Throwable error); @@ -48,6 +51,9 @@ public interface GeyserLogger { /** * Logs an error message and an exception to console + * + * @param message the message to log + * @param error the error to throw */ void error(String message, Throwable error); 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 ac186a799..05465c46b 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 @@ -665,7 +665,10 @@ public class GeyserSession implements CommandSender { } /** - * @see org.geysermc.connector.network.translators.world.WorldManager#hasPermission(GeyserSession, String) + * Checks if the given session's player has a permission + * + * @param permission The permission node to check + * @return true if the player has the requested permission, false if not */ public Boolean hasPermission(String permission) { return connector.getWorldManager().hasPermission(this, permission); diff --git a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java index 0b7b5c5cf..38369d6c8 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java @@ -42,6 +42,7 @@ public class FileUtils { * * @param src File to load * @param valueType Class to load file into + * @param the type * @return The data as the given class * @throws IOException if the config could not be loaded */ diff --git a/connector/src/main/java/org/geysermc/connector/utils/GameRule.java b/connector/src/main/java/org/geysermc/connector/utils/GameRule.java index 350337f3d..48feb1c18 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/GameRule.java +++ b/connector/src/main/java/org/geysermc/connector/utils/GameRule.java @@ -109,7 +109,7 @@ public enum GameRule { * Fetch a game rule by the given Java ID * * @param id The ID of the gamerule - * @return A {@link GameRule} object representing the requested ID or {@link GameRule.UNKNOWN} + * @return A {@link GameRule} object representing the requested ID or {@link GameRule#UNKNOWN} */ public static GameRule fromJavaID(String id) { for (GameRule gamerule : VALUES) { 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 6d83da0ad..cb51e2f3b 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java @@ -136,6 +136,9 @@ public class InventoryUtils { /** * Returns a barrier block with custom name and lore to explain why * part of the inventory is unusable. + * + * @param description the description + * @return the unusable space block */ public static ItemData createUnusableSpaceBlock(String description) { NbtMapBuilder root = NbtMap.builder(); diff --git a/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java b/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java index 0b4958950..36cdbc422 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java @@ -95,7 +95,7 @@ public class MessageUtils { * @param messages A {@link List} of {@link Message} to parse * @param locale A locale loaded to get the message for * @param parent A {@link Message} to use as the parent (can be null) - * @return + * @return the translation parameters */ public static List getTranslationParams(List messages, String locale, Message parent) { List strings = new ArrayList<>(); @@ -160,10 +160,10 @@ public class MessageUtils { * Translate a given {@link TranslationMessage} to the given locale * * @param message The {@link Message} to send - * @param locale - * @param shouldTranslate - * @param parent - * @return + * @param locale the locale + * @param shouldTranslate if the message should be translated + * @param parent the parent message + * @return the given translation message translated from the given locale */ public static String getTranslatedBedrockMessage(Message message, String locale, boolean shouldTranslate, Message parent) { JsonParser parser = new JsonParser(); From 3ef7e30230bf82a1f3989fb53016ae1bb979e2c0 Mon Sep 17 00:00:00 2001 From: Savagetechguy Date: Sat, 8 Aug 2020 17:59:03 -0500 Subject: [PATCH 012/140] Fix fire not extinguishing on server side when on bedrock Fixes #875 Fixes #906 --- .../entity/player/BedrockActionTranslator.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java index f4365f796..536bad988 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockActionTranslator.java @@ -119,6 +119,18 @@ public class BedrockActionTranslator extends PacketTranslator Date: Sun, 9 Aug 2020 22:43:57 -0400 Subject: [PATCH 013/140] Update to 1.16.2-rc1 --- connector/pom.xml | 2 +- .../java/JavaJoinGameTranslator.java | 7 ++--- .../java/JavaRespawnTranslator.java | 7 ++--- .../connector/utils/DimensionUtils.java | 26 ++++++++++++++++++- 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/connector/pom.xml b/connector/pom.xml index f78cc8750..7997efade 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -105,7 +105,7 @@ com.github.GeyserMC MCProtocolLib - da148409ff + 2ee53b72d1 compile diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java index 94b5bed38..06fa2016f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java @@ -51,10 +51,11 @@ public class JavaJoinGameTranslator extends PacketTranslator stopRainPacket.setPosition(Vector3f.ZERO); session.sendUpstreamPacket(stopRainPacket); - if (!entity.getDimension().equals(packet.getDimension())) { - DimensionUtils.switchDimension(session, packet.getDimension()); + String newDimension = DimensionUtils.getNewDimension(packet.getDimension()); + if (!entity.getDimension().equals(newDimension)) { + DimensionUtils.switchDimension(session, newDimension); } else { if (session.isManyDimPackets()) { //reloading world String fakeDim = entity.getDimension().equals(DimensionUtils.OVERWORLD) ? DimensionUtils.NETHER : DimensionUtils.OVERWORLD; DimensionUtils.switchDimension(session, fakeDim); - DimensionUtils.switchDimension(session, packet.getDimension()); + DimensionUtils.switchDimension(session, newDimension); } else { // Handled in JavaPlayerPositionRotationTranslator session.setSpawned(false); diff --git a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java index 74db16bb5..bcbc4ce0d 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java @@ -26,8 +26,13 @@ package org.geysermc.connector.utils; import com.github.steveice10.mc.protocol.data.game.entity.Effect; +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.nukkitx.math.vector.Vector3i; -import com.nukkitx.protocol.bedrock.packet.*; +import com.nukkitx.protocol.bedrock.packet.ChangeDimensionPacket; +import com.nukkitx.protocol.bedrock.packet.MobEffectPacket; +import com.nukkitx.protocol.bedrock.packet.StopSoundPacket; +import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.network.session.GeyserSession; @@ -99,6 +104,25 @@ public class DimensionUtils { } } + /** + * Determines the new dimension based on the {@link CompoundTag} sent by either the {@link com.github.steveice10.mc.protocol.packet.ingame.server.ServerJoinGamePacket} + * or {@link com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket}. + * @param dimensionTag the packet's dimension tag. + * @return the dimension identifier. + */ + public static String getNewDimension(CompoundTag dimensionTag) { + if (dimensionTag == null || dimensionTag.isEmpty()) { + GeyserConnector.getInstance().getLogger().debug("Dimension tag was null or empty."); + return "minecraft:overworld"; + } + if (dimensionTag.getValue().get("effects") != null) { + System.out.println(((StringTag) dimensionTag.getValue().get("effects")).getValue()); + return ((StringTag) dimensionTag.getValue().get("effects")).getValue(); + } + GeyserConnector.getInstance().getLogger().debug("Effects portion of the tag was null or empty."); + return "minecraft:overworld"; + } + public static void changeBedrockNetherId() { // Change dimension ID to the End to allow for building above Bedrock BEDROCK_NETHER_ID = 2; From 439027d510146caef6ebdece92b9caa3f794cca3 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Mon, 10 Aug 2020 15:31:08 +0100 Subject: [PATCH 014/140] Fix Shulker color and open state (#1113) --- .../entity/living/monster/ShulkerEntity.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java index 8728547fc..a0bd5bc2b 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/ShulkerEntity.java @@ -53,14 +53,21 @@ public class ShulkerEntity extends GolemEntity { metadata.put(EntityData.SHULKER_ATTACH_POS, Vector3i.from(position.getX(), position.getY(), position.getZ())); } } - //TODO Outdated metadata flag SHULKER_PEAK_HEIGHT -// if (entityMetadata.getId() == 17) { -// int height = (byte) entityMetadata.getValue(); -// metadata.put(EntityData.SHULKER_PEAK_HEIGHT, height); -// } + + if (entityMetadata.getId() == 17) { + int height = (byte) entityMetadata.getValue(); + metadata.put(EntityData.SHULKER_PEEK_ID, height); + } + if (entityMetadata.getId() == 18) { - int color = Math.abs((byte) entityMetadata.getValue() - 15); - metadata.put(EntityData.VARIANT, color); + byte color = (byte) entityMetadata.getValue(); + if (color == 16) { + // 16 is default on both editions + metadata.put(EntityData.VARIANT, 16); + } else { + // Every other shulker color is offset 15 in bedrock edition + metadata.put(EntityData.VARIANT, Math.abs(color - 15)); + } } super.updateBedrockMetadata(entityMetadata, session); } From 9ac13f37b7c4458ccf342b0f9c664588d841f936 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 10 Aug 2020 10:31:39 -0400 Subject: [PATCH 015/140] Update submodules (#1109) --- connector/src/main/resources/languages | 2 +- connector/src/main/resources/mappings | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/connector/src/main/resources/languages b/connector/src/main/resources/languages index 7cc503e2f..bd69044f8 160000 --- a/connector/src/main/resources/languages +++ b/connector/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 7cc503e2f7c0871a24beb3a114726d764a4836f1 +Subproject commit bd69044f8f69783bd9a7d8b4922b9122044a2a1e diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index a222d85dc..e7c7f5f52 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit a222d85dc0b8e8c95a150f2d488a6a25390b8c2f +Subproject commit e7c7f5f52d11537c234e63dd46009f68a9b8d9a1 From c7958af1dbbb4a3f5a81d8530b6566b1c69fa994 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Mon, 10 Aug 2020 15:31:49 +0100 Subject: [PATCH 016/140] Fix dust particles type (#1108) --- .../translators/java/world/JavaSpawnParticleTranslator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnParticleTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnParticleTranslator.java index 323f57f07..453e08445 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnParticleTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnParticleTranslator.java @@ -78,7 +78,7 @@ public class JavaSpawnParticleTranslator extends PacketTranslator Date: Mon, 10 Aug 2020 11:22:59 -0400 Subject: [PATCH 017/140] Remove debug line --- .../main/java/org/geysermc/connector/utils/DimensionUtils.java | 1 - 1 file changed, 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java index bcbc4ce0d..de09ed8c6 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java @@ -116,7 +116,6 @@ public class DimensionUtils { return "minecraft:overworld"; } if (dimensionTag.getValue().get("effects") != null) { - System.out.println(((StringTag) dimensionTag.getValue().get("effects")).getValue()); return ((StringTag) dimensionTag.getValue().get("effects")).getValue(); } GeyserConnector.getInstance().getLogger().debug("Effects portion of the tag was null or empty."); From b84986a502df07052612aa8564504db3e657bf1c Mon Sep 17 00:00:00 2001 From: rtm516 Date: Mon, 10 Aug 2020 21:44:20 +0100 Subject: [PATCH 018/140] Fix quotes breaking formatted message strings (#1118) --- .../main/java/org/geysermc/connector/utils/LanguageUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java index 61e23470d..de6796a26 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LanguageUtils.java @@ -122,7 +122,7 @@ public class LanguageUtils { formatString = key; } - return MessageFormat.format(formatString.replace("&", "\u00a7"), values); + return MessageFormat.format(formatString.replace("'", "''").replace("&", "\u00a7"), values); } /** From afc7dfeb4518442351844807ae3a0c9115e45b82 Mon Sep 17 00:00:00 2001 From: EasyClifton <63668444+EasyClifton@users.noreply.github.com> Date: Tue, 11 Aug 2020 12:57:49 +0300 Subject: [PATCH 019/140] Add Command Block and Structure block to the fix list (#1119) * Add Command Block and Structure block to the fix list * Add Horse Inventory to the fix list Added Horse to the What's left to be added/fixed list. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 0d474d990..1f4562258 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,9 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set - [ ] Beacon - [ ] Cartography Table - [ ] Stonecutter + - [ ] Command Block + - [ ] Structure Block + - [ ] Horse Inventory - Some Entity Flags ## Compiling From 009381d9c77cd2586d10432a70f154937f16243e Mon Sep 17 00:00:00 2001 From: DoctorMacc Date: Tue, 11 Aug 2020 09:06:28 -0400 Subject: [PATCH 020/140] Update for protocol v409 --- connector/pom.xml | 2 +- .../src/main/java/org/geysermc/connector/GeyserConnector.java | 4 ++-- connector/src/main/resources/languages | 2 +- connector/src/main/resources/mappings | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/connector/pom.xml b/connector/pom.xml index 7997efade..ff6101b32 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -32,7 +32,7 @@ com.nukkitx.protocol - bedrock-v408 + bedrock-v409 2.6.0-SNAPSHOT compile diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 4de375100..693244799 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -30,7 +30,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.nukkitx.network.raknet.RakNetConstants; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import com.nukkitx.protocol.bedrock.BedrockServer; -import com.nukkitx.protocol.bedrock.v408.Bedrock_v408; +import com.nukkitx.protocol.bedrock.v409.Bedrock_v409; import lombok.Getter; import lombok.Setter; import org.geysermc.connector.bootstrap.GeyserBootstrap; @@ -75,7 +75,7 @@ public class GeyserConnector { public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES); - public static final BedrockPacketCodec BEDROCK_PACKET_CODEC = Bedrock_v408.V408_CODEC; + public static final BedrockPacketCodec BEDROCK_PACKET_CODEC = Bedrock_v409.V409_CODEC; public static final String NAME = "Geyser"; public static final String VERSION = "DEV"; // A fallback for running in IDEs diff --git a/connector/src/main/resources/languages b/connector/src/main/resources/languages index 7cc503e2f..bd69044f8 160000 --- a/connector/src/main/resources/languages +++ b/connector/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 7cc503e2f7c0871a24beb3a114726d764a4836f1 +Subproject commit bd69044f8f69783bd9a7d8b4922b9122044a2a1e diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index 16eedef89..e7c7f5f52 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 16eedef89e2a28ca5a1edbfb068008740efa7286 +Subproject commit e7c7f5f52d11537c234e63dd46009f68a9b8d9a1 From 953fe8fec3f3b6b92e8f6c1b155a85d2912ab354 Mon Sep 17 00:00:00 2001 From: DoctorMacc Date: Tue, 11 Aug 2020 09:09:29 -0400 Subject: [PATCH 021/140] Update mappings --- connector/src/main/resources/mappings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index e7c7f5f52..94d30c1c6 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit e7c7f5f52d11537c234e63dd46009f68a9b8d9a1 +Subproject commit 94d30c1c6c4518932b813826bfcef680575a3c9c From 6ccf629a8ae9f5c573a60379c775f7282ead4951 Mon Sep 17 00:00:00 2001 From: DoctorMacc Date: Tue, 11 Aug 2020 10:00:14 -0400 Subject: [PATCH 022/140] Update to 1.16.2-rc2; add multiversion support --- connector/pom.xml | 2 +- .../geysermc/connector/GeyserConnector.java | 4 -- .../command/defaults/VersionCommand.java | 3 +- .../org/geysermc/connector/dump/DumpInfo.java | 5 +- .../connector/network/BedrockProtocol.java | 66 +++++++++++++++++++ .../network/ConnectorServerEventHandler.java | 3 +- .../connector/network/QueryPacketHandler.java | 2 +- .../network/UpstreamPacketHandler.java | 20 ++++-- 8 files changed, 87 insertions(+), 18 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java diff --git a/connector/pom.xml b/connector/pom.xml index 3d5c098ae..dd1ad1921 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -111,7 +111,7 @@ com.github.GeyserMC MCProtocolLib - 2ee53b72d1 + 82dcf31d03 compile diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 693244799..103faf791 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -28,9 +28,7 @@ package org.geysermc.connector; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.nukkitx.network.raknet.RakNetConstants; -import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import com.nukkitx.protocol.bedrock.BedrockServer; -import com.nukkitx.protocol.bedrock.v409.Bedrock_v409; import lombok.Getter; import lombok.Setter; import org.geysermc.connector.bootstrap.GeyserBootstrap; @@ -75,8 +73,6 @@ public class GeyserConnector { public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES); - public static final BedrockPacketCodec BEDROCK_PACKET_CODEC = Bedrock_v409.V409_CODEC; - public static final String NAME = "Geyser"; public static final String VERSION = "DEV"; // A fallback for running in IDEs 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 50527968d..681474a98 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 @@ -30,6 +30,7 @@ import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.command.GeyserCommand; import org.geysermc.connector.common.ChatColor; +import org.geysermc.connector.network.BedrockProtocol; import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.connector.utils.WebUtils; @@ -50,7 +51,7 @@ public class VersionCommand extends GeyserCommand { @Override public void execute(CommandSender sender, String[] args) { - sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.version.version", GeyserConnector.NAME, GeyserConnector.VERSION, MinecraftConstants.GAME_VERSION, GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion())); + sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.version.version", GeyserConnector.NAME, GeyserConnector.VERSION, MinecraftConstants.GAME_VERSION, BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion())); // Disable update checking in dev mode //noinspection ConstantConditions - changes in production diff --git a/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java b/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java index 9c4928414..9d91cde6b 100644 --- a/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java +++ b/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java @@ -32,6 +32,7 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.Getter; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.configuration.GeyserConfiguration; +import org.geysermc.connector.network.BedrockProtocol; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.utils.DockerCheck; import org.geysermc.connector.utils.FileUtils; @@ -136,8 +137,8 @@ public class DumpInfo { private final int javaProtocol; MCInfo() { - this.bedrockVersion = GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion(); - this.bedrockProtocol = GeyserConnector.BEDROCK_PACKET_CODEC.getProtocolVersion(); + this.bedrockVersion = BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion(); + this.bedrockProtocol = BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion(); this.javaVersion = MinecraftConstants.GAME_VERSION; this.javaProtocol = MinecraftConstants.PROTOCOL_VERSION; } diff --git a/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java b/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java new file mode 100644 index 000000000..734bea436 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java @@ -0,0 +1,66 @@ +/* + * 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; + +import com.nukkitx.protocol.bedrock.BedrockPacketCodec; +import com.nukkitx.protocol.bedrock.v407.Bedrock_v407; +import com.nukkitx.protocol.bedrock.v409.Bedrock_v409; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Contains information about the supported Bedrock protocols in Geyser. + */ +public class BedrockProtocol { + /** + * Default Bedrock codec that should act as a fallback and as the version shown in /geyser version + */ + public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v409.V409_CODEC; + /** + * A list of all supported Bedrock versions that can join Geyser + */ + public static final Set SUPPORTED_BEDROCK_CODECS = ConcurrentHashMap.newKeySet(); + + static { + SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); + SUPPORTED_BEDROCK_CODECS.add(Bedrock_v407.V407_CODEC); + } + + /** + * Gets the {@link BedrockPacketCodec} of the given protocol version. + * @param protocolVersion The protocol version to attempt to find + * @return The packet codec, or null if the client's protocol is unsupported + */ + public static BedrockPacketCodec getBedrockCodec(int protocolVersion) { + for (BedrockPacketCodec packetCodec : SUPPORTED_BEDROCK_CODECS) { + if (packetCodec.getProtocolVersion() == protocolVersion) { + return packetCodec; + } + } + return null; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java b/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java index 6ca9063c2..c850fc4ab 100644 --- a/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java @@ -71,7 +71,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { pong.setEdition("MCPE"); pong.setGameType("Default"); pong.setNintendoLimited(false); - pong.setProtocolVersion(GeyserConnector.BEDROCK_PACKET_CODEC.getProtocolVersion()); + pong.setProtocolVersion(BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()); pong.setVersion(null); // Server tries to connect either way and it looks better pong.setIpv4Port(config.getBedrock().getPort()); @@ -108,7 +108,6 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { public void onSessionCreation(BedrockServerSession bedrockServerSession) { bedrockServerSession.setLogging(true); bedrockServerSession.setPacketHandler(new UpstreamPacketHandler(connector, new GeyserSession(connector, bedrockServerSession))); - bedrockServerSession.setPacketCodec(GeyserConnector.BEDROCK_PACKET_CODEC); } @Override diff --git a/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java index 7854f14c0..bd67fd1b5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java @@ -167,7 +167,7 @@ public class QueryPacketHandler { gameData.put("hostname", motd); gameData.put("gametype", "SMP"); gameData.put("game_id", "MINECRAFT"); - gameData.put("version", GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion()); + gameData.put("version", BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()); gameData.put("plugins", ""); gameData.put("map", GeyserConnector.NAME); gameData.put("numplayers", currentPlayerCount); diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index 357e870f6..f65b3ef1a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -26,6 +26,7 @@ package org.geysermc.connector.network; import com.nukkitx.protocol.bedrock.BedrockPacket; +import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import com.nukkitx.protocol.bedrock.packet.*; import org.geysermc.connector.common.AuthType; import org.geysermc.connector.configuration.GeyserConfiguration; @@ -48,15 +49,20 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { @Override public boolean handle(LoginPacket loginPacket) { - if (loginPacket.getProtocolVersion() > GeyserConnector.BEDROCK_PACKET_CODEC.getProtocolVersion()) { - // Too early to determine session locale - session.disconnect(LanguageUtils.getLocaleStringLog("geyser.network.outdated.server", GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion())); - return true; - } else if (loginPacket.getProtocolVersion() < GeyserConnector.BEDROCK_PACKET_CODEC.getProtocolVersion()) { - session.disconnect(LanguageUtils.getLocaleStringLog("geyser.network.outdated.client", GeyserConnector.BEDROCK_PACKET_CODEC.getMinecraftVersion())); - return true; + BedrockPacketCodec packetCodec = BedrockProtocol.getBedrockCodec(loginPacket.getProtocolVersion()); + if (packetCodec == null) { + if (loginPacket.getProtocolVersion() > BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { + // Too early to determine session locale + session.disconnect(LanguageUtils.getLocaleStringLog("geyser.network.outdated.server", BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion())); + return true; + } else if (loginPacket.getProtocolVersion() < BedrockProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { + session.disconnect(LanguageUtils.getLocaleStringLog("geyser.network.outdated.client", BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion())); + return true; + } } + session.getUpstream().getSession().setPacketCodec(packetCodec); + LoginEncryptionUtils.encryptPlayerConnection(connector, session, loginPacket); PlayStatusPacket playStatus = new PlayStatusPacket(); From 8b691d22d53fc33c7699406135343fbabc5d2508 Mon Sep 17 00:00:00 2001 From: DoctorMacc Date: Tue, 11 Aug 2020 12:16:18 -0400 Subject: [PATCH 023/140] Add v408 as the default protocol smh mojang --- .../java/org/geysermc/connector/network/BedrockProtocol.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java b/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java index 734bea436..904f70a0a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java +++ b/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java @@ -27,6 +27,7 @@ package org.geysermc.connector.network; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import com.nukkitx.protocol.bedrock.v407.Bedrock_v407; +import com.nukkitx.protocol.bedrock.v408.Bedrock_v408; import com.nukkitx.protocol.bedrock.v409.Bedrock_v409; import java.util.Set; @@ -39,7 +40,7 @@ public class BedrockProtocol { /** * Default Bedrock codec that should act as a fallback and as the version shown in /geyser version */ - public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v409.V409_CODEC; + public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v408.V408_CODEC; /** * A list of all supported Bedrock versions that can join Geyser */ @@ -48,6 +49,7 @@ public class BedrockProtocol { static { SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); SUPPORTED_BEDROCK_CODECS.add(Bedrock_v407.V407_CODEC); + SUPPORTED_BEDROCK_CODECS.add(Bedrock_v409.V409_CODEC); } /** From 31fec1d4bf88fa9b33b85fd028abc082e5f64415 Mon Sep 17 00:00:00 2001 From: DoctorMacc Date: Tue, 11 Aug 2020 12:35:45 -0400 Subject: [PATCH 024/140] Update to 1.16.2 --- bootstrap/spigot/pom.xml | 2 +- connector/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml index 854ce9fa5..ca9958d0a 100644 --- a/bootstrap/spigot/pom.xml +++ b/bootstrap/spigot/pom.xml @@ -26,7 +26,7 @@ us.myles viaversion - 3.1.0-1.16.2-pre1 + 3.1.0 provided diff --git a/connector/pom.xml b/connector/pom.xml index dd1ad1921..711e74bb1 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -111,7 +111,7 @@ com.github.GeyserMC MCProtocolLib - 82dcf31d03 + b835436e20 compile From 016a5c04ea57decfcaaaccfd08f7e7cfdbdd9995 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Tue, 11 Aug 2020 18:45:14 +0100 Subject: [PATCH 025/140] Fix chat translation parameters not having color sometimes --- .../connector/utils/MessageUtils.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java b/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java index 36cdbc422..a127fd8db 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/MessageUtils.java @@ -110,14 +110,6 @@ public class MessageUtils { strings.add(builder); } - if (translation.getKey().equals("commands.gamemode.success.other")) { - strings.add(""); - } - - if (translation.getKey().equals("command.context.here")) { - strings.add(" - no permission or invalid command!"); - } - // Collect all params and add format corrections to the end of them List furtherParams = new ArrayList<>(); for (String param : getTranslationParams(translation.getWith(), locale, message)) { @@ -133,9 +125,16 @@ public class MessageUtils { } if (locale != null) { - strings.add(insertParams(LocaleUtils.getLocaleString(translation.getKey(), locale), furtherParams)); + String builder = getFormat(message.getStyle().getFormats()) + + getColor(message.getStyle().getColor()); + builder += insertParams(LocaleUtils.getLocaleString(translation.getKey(), locale), furtherParams); + strings.add(builder); } else { - strings.addAll(furtherParams); + String format = getFormat(message.getStyle().getFormats()) + + getColor(message.getStyle().getColor()); + for (String param : furtherParams) { + strings.add(format + param); + } } } else { String builder = getFormat(message.getStyle().getFormats()) + From 41d299fae59399f29d5d05281e6f5b57c41130e4 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Tue, 11 Aug 2020 19:10:48 +0100 Subject: [PATCH 026/140] Change version number to 1.1.0 --- README.md | 2 +- bootstrap/bungeecord/pom.xml | 4 ++-- bootstrap/pom.xml | 2 +- bootstrap/spigot/pom.xml | 4 ++-- bootstrap/sponge/pom.xml | 4 ++-- bootstrap/standalone/pom.xml | 4 ++-- bootstrap/velocity/pom.xml | 4 ++-- common/pom.xml | 2 +- connector/pom.xml | 4 ++-- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 1f4562258..64ff22522 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have now joined us here! -### Currently supporting Minecraft Bedrock v1.16.0/1 and Minecraft Java v1.16.1. +### Currently supporting Minecraft Bedrock v1.16.x and Minecraft Java v1.16.2. ## Setting Up Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set up Geyser. diff --git a/bootstrap/bungeecord/pom.xml b/bootstrap/bungeecord/pom.xml index 565264f0f..8497b9684 100644 --- a/bootstrap/bungeecord/pom.xml +++ b/bootstrap/bungeecord/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 1.0.0 + 1.1.0 ../ bootstrap-bungeecord @@ -14,7 +14,7 @@ org.geysermc connector - 1.0.0 + 1.1.0 compile diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml index 85ede3466..d9bac67d1 100644 --- a/bootstrap/pom.xml +++ b/bootstrap/pom.xml @@ -10,7 +10,7 @@ ../ bootstrap-parent - 1.0.0 + 1.1.0 pom diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml index ca9958d0a..422b2769b 100644 --- a/bootstrap/spigot/pom.xml +++ b/bootstrap/spigot/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 1.0.0 + 1.1.0 ../ bootstrap-spigot @@ -14,7 +14,7 @@ org.geysermc connector - 1.0.0 + 1.1.0 compile diff --git a/bootstrap/sponge/pom.xml b/bootstrap/sponge/pom.xml index cca3fcaae..f3c898084 100644 --- a/bootstrap/sponge/pom.xml +++ b/bootstrap/sponge/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 1.0.0 + 1.1.0 ../ bootstrap-sponge @@ -14,7 +14,7 @@ org.geysermc connector - 1.0.0 + 1.1.0 compile diff --git a/bootstrap/standalone/pom.xml b/bootstrap/standalone/pom.xml index 468042b8f..07458f730 100644 --- a/bootstrap/standalone/pom.xml +++ b/bootstrap/standalone/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 1.0.0 + 1.1.0 ../ bootstrap-standalone @@ -14,7 +14,7 @@ org.geysermc connector - 1.0.0 + 1.1.0 compile diff --git a/bootstrap/velocity/pom.xml b/bootstrap/velocity/pom.xml index 7c42ba336..b08e5cbc5 100644 --- a/bootstrap/velocity/pom.xml +++ b/bootstrap/velocity/pom.xml @@ -6,7 +6,7 @@ org.geysermc bootstrap-parent - 1.0.0 + 1.1.0 ../ bootstrap-velocity @@ -14,7 +14,7 @@ org.geysermc connector - 1.0.0 + 1.1.0 compile diff --git a/common/pom.xml b/common/pom.xml index 0df8ef4bf..85dde12c6 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -10,7 +10,7 @@ ../ common - 1.0.0 + 1.1.0 com.google.code.gson diff --git a/connector/pom.xml b/connector/pom.xml index 711e74bb1..ff5bc6174 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -10,12 +10,12 @@ ../ connector - 1.0.0 + 1.1.0 org.geysermc common - 1.0.0 + 1.1.0 compile From 5db0e7898faee12e729511be50e5e6abd0026f77 Mon Sep 17 00:00:00 2001 From: DoctorMacc Date: Tue, 11 Aug 2020 14:33:37 -0400 Subject: [PATCH 027/140] Bump MCProtocolLib --- connector/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/pom.xml b/connector/pom.xml index ff5bc6174..6cc46f003 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -111,7 +111,7 @@ com.github.GeyserMC MCProtocolLib - b835436e20 + 82c20c954c compile From fbf30a605922fd3224e3c1aeaceefdb0df8ec708 Mon Sep 17 00:00:00 2001 From: bundabrg Date: Wed, 12 Aug 2020 11:06:32 +0800 Subject: [PATCH 028/140] Fix Skin Selfie (#1131) Co-authored-by: bundabrg --- .../src/main/java/org/geysermc/connector/utils/SkinUtils.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java index 1d5e4073d..e9e9995ef 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java @@ -264,8 +264,6 @@ public class SkinUtils { } public static void handleBedrockSkin(PlayerEntity playerEntity, BedrockClientData clientData) { - GameProfileData data = GameProfileData.from(playerEntity.getProfile()); - GeyserConnector.getInstance().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.skin.bedrock.register", playerEntity.getUsername(), playerEntity.getUuid())); try { @@ -276,7 +274,7 @@ public class SkinUtils { byte[] geometryBytes = Base64.getDecoder().decode(clientData.getGeometryData().getBytes("UTF-8")); if (skinBytes.length <= (128 * 128 * 4) && !clientData.isPersonaSkin()) { - SkinProvider.storeBedrockSkin(playerEntity.getUuid(), data.getSkinUrl(), skinBytes); + SkinProvider.storeBedrockSkin(playerEntity.getUuid(), clientData.getSkinId(), skinBytes); SkinProvider.storeBedrockGeometry(playerEntity.getUuid(), geometryNameBytes, geometryBytes); } else { GeyserConnector.getInstance().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.skin.bedrock.fail", playerEntity.getUsername())); From fb5a89459540473abf8bf528ccf119e42c549570 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Wed, 12 Aug 2020 09:22:13 -0400 Subject: [PATCH 029/140] EntityUtils: Properly map 1.14 entity status effects (#1133) Previously, Hero of the Village and Bad Omen effects were mapped to 0. This commit updates them to their proper Bedrock values. Fixes #1129 --- .../java/org/geysermc/connector/utils/EntityUtils.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java b/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java index b5033c947..51102202d 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/EntityUtils.java @@ -42,8 +42,7 @@ public class EntityUtils { case LUCK: case UNLUCK: case DOLPHINS_GRACE: - case BAD_OMEN: - case HERO_OF_THE_VILLAGE: + // All Java-exclusive effects as of 1.16.2 return 0; case LEVITATION: return 24; @@ -51,6 +50,10 @@ public class EntityUtils { return 26; case SLOW_FALLING: return 27; + case BAD_OMEN: + return 28; + case HERO_OF_THE_VILLAGE: + return 29; default: return effect.ordinal() + 1; } From e02495ca7f12f2cf18bbeafe38c454a9c2115605 Mon Sep 17 00:00:00 2001 From: bundabrg Date: Wed, 12 Aug 2020 21:23:11 +0800 Subject: [PATCH 030/140] Fix Alex/Steve skins being sent incorrectly (#1135) * Return permanent skins (alex/steve) when queried instead of returning an empty skin due to invalid lookup * Fix Alex/Steve being shown incorrectly due to java signed integers Co-authored-by: bundabrg --- .../org/geysermc/connector/utils/SkinProvider.java | 10 +++++++--- .../java/org/geysermc/connector/utils/SkinUtils.java | 5 ++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java index 352a0b0f9..5551230b9 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java @@ -45,6 +45,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.util.Arrays; import java.util.Base64; +import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.UUID; @@ -58,6 +59,10 @@ public class SkinProvider { public static final Skin EMPTY_SKIN = new Skin(-1, "steve", STEVE_SKIN); public static final byte[] ALEX_SKIN = new ProvidedSkin("bedrock/skin/skin_alex.png").getSkin(); public static final Skin EMPTY_SKIN_ALEX = new Skin(-1, "alex", ALEX_SKIN); + private static final Map permanentSkins = new HashMap() {{ + put("steve", EMPTY_SKIN); + put("alex", EMPTY_SKIN_ALEX); + }}; private static final Cache cachedSkins = CacheBuilder.newBuilder() .expireAfterAccess(1, TimeUnit.HOURS) .build(); @@ -141,8 +146,7 @@ public class SkinProvider { } public static Skin getCachedSkin(String skinUrl) { - Skin skin = cachedSkins.getIfPresent(skinUrl); - return skin != null ? skin : EMPTY_SKIN; + return permanentSkins.getOrDefault(skinUrl, cachedSkins.getIfPresent(skinUrl)); } public static Cape getCachedCape(String capeUrl) { @@ -169,7 +173,7 @@ public class SkinProvider { if (textureUrl == null || textureUrl.isEmpty()) return CompletableFuture.completedFuture(EMPTY_SKIN); if (requestedSkins.containsKey(textureUrl)) return requestedSkins.get(textureUrl); // already requested - Skin cachedSkin = cachedSkins.getIfPresent(textureUrl); + Skin cachedSkin = getCachedSkin(textureUrl); if (cachedSkin != null) { return CompletableFuture.completedFuture(cachedSkin); } diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java index e9e9995ef..f0b88c435 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java @@ -55,6 +55,9 @@ public class SkinUtils { SkinProvider.SkinGeometry geometry = SkinProvider.SkinGeometry.getLegacy(data.isAlex()); SkinProvider.Skin skin = SkinProvider.getCachedSkin(data.getSkinUrl()); + if (skin == null) { + skin = SkinProvider.EMPTY_SKIN; + } return buildEntryManually( session, @@ -138,7 +141,7 @@ public class SkinUtils { */ public static GameProfileData from(GameProfile profile) { // Fallback to the offline mode of working it out - boolean isAlex = ((profile.getId().hashCode() % 2) == 1); + boolean isAlex = (Math.abs(profile.getId().hashCode() % 2) == 1); try { GameProfile.Property skinProperty = profile.getProperty("textures"); From e2a956692620dd09dc46afe9c0774bb26c4fc3ca Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Wed, 12 Aug 2020 10:42:02 -0500 Subject: [PATCH 031/140] Kick player with invalid chain data for additional security The client should disallow players to join servers if they're not logged in, however this just adds a second layer of security in the event that it's somehow bypassed. --- .../org/geysermc/connector/utils/LoginEncryptionUtils.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java index 3d4dd506a..8a13d0549 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java @@ -105,6 +105,10 @@ public class LoginEncryptionUtils { connector.getLogger().debug(String.format("Is player data valid? %s", validChain)); + if (!validChain) { + session.disconnect(LanguageUtils.getLocaleStringLog("geyser.auth.login.form.notice.desc")); + return; + } JWSObject jwt = JWSObject.parse(certChainData.get(certChainData.size() - 1).asText()); JsonNode payload = JSON_MAPPER.readTree(jwt.getPayload().toBytes()); From 1fb68dc9e3f7202d1f2eec9f2e8e362a6afc46bc Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Wed, 12 Aug 2020 14:48:40 -0400 Subject: [PATCH 032/140] LoginEncryptionUtils: Add proper string for no Xbox account (#1139) --- .../java/org/geysermc/connector/utils/LoginEncryptionUtils.java | 2 +- connector/src/main/resources/languages | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java index 8a13d0549..7b41ae3de 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java @@ -106,7 +106,7 @@ public class LoginEncryptionUtils { connector.getLogger().debug(String.format("Is player data valid? %s", validChain)); if (!validChain) { - session.disconnect(LanguageUtils.getLocaleStringLog("geyser.auth.login.form.notice.desc")); + session.disconnect(LanguageUtils.getLocaleStringLog("geyser.network.remote.invalid_xbox_account")); return; } JWSObject jwt = JWSObject.parse(certChainData.get(certChainData.size() - 1).asText()); diff --git a/connector/src/main/resources/languages b/connector/src/main/resources/languages index bd69044f8..57e5986bd 160000 --- a/connector/src/main/resources/languages +++ b/connector/src/main/resources/languages @@ -1 +1 @@ -Subproject commit bd69044f8f69783bd9a7d8b4922b9122044a2a1e +Subproject commit 57e5986bd99bf3d81d67c75299ea3dde67d53554 From bf238f52c72a65f63c65210eae7b7941fd54739d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Wed, 12 Aug 2020 16:27:13 -0400 Subject: [PATCH 033/140] Update to latest MCProtocolLib (#1140) --- connector/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/pom.xml b/connector/pom.xml index 6cc46f003..a3748044a 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -111,7 +111,7 @@ com.github.GeyserMC MCProtocolLib - 82c20c954c + f37c98dc70 compile From 4bcf44638e69b8ba268a968709aae88de6e79bda Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Thu, 13 Aug 2020 10:56:03 -0400 Subject: [PATCH 034/140] ConnectorServerEventHandler: set default packet handler (#1148) This allows disconnect packets to be sent and kick unsupported versions of the game. --- .../geysermc/connector/network/ConnectorServerEventHandler.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java b/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java index c850fc4ab..9fb4ad9e1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java @@ -108,6 +108,8 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { public void onSessionCreation(BedrockServerSession bedrockServerSession) { bedrockServerSession.setLogging(true); bedrockServerSession.setPacketHandler(new UpstreamPacketHandler(connector, new GeyserSession(connector, bedrockServerSession))); + // Set the packet codec to default just in case we need to send disconnect packets. + bedrockServerSession.setPacketCodec(BedrockProtocol.DEFAULT_BEDROCK_CODEC); } @Override From 4af17df46f69095658005ce0b71a348d3962e669 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Sat, 15 Aug 2020 21:06:50 +0100 Subject: [PATCH 035/140] Add support for sensitive data in dumps (#1149) * Add sensitive dumps * Add better arg handling and offline dumps * Add sensitive parameters for plugin IPs * Add sensitive property to the Bedrock remote address Co-authored-by: Camotoy <20743703+DoctorMacc@users.noreply.github.com> --- .../bungeecord/GeyserBungeeDumpInfo.java | 9 +- .../platform/spigot/GeyserSpigotDumpInfo.java | 7 +- .../velocity/GeyserVelocityDumpInfo.java | 7 +- .../command/defaults/DumpCommand.java | 83 ++++++++++++++----- .../common/serializer/AsteriskSerializer.java | 16 +++- .../GeyserJacksonConfiguration.java | 2 + connector/src/main/resources/languages | 2 +- 7 files changed, 98 insertions(+), 28 deletions(-) diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeDumpInfo.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeDumpInfo.java index d81b663e7..ce2b1fc30 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeDumpInfo.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeDumpInfo.java @@ -28,6 +28,7 @@ package org.geysermc.platform.bungeecord; import lombok.Getter; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.plugin.Plugin; +import org.geysermc.connector.common.serializer.AsteriskSerializer; import org.geysermc.connector.dump.BootstrapDumpInfo; import java.util.ArrayList; @@ -52,7 +53,13 @@ public class GeyserBungeeDumpInfo extends BootstrapDumpInfo { this.plugins = new ArrayList<>(); for (net.md_5.bungee.api.config.ListenerInfo listener : proxy.getConfig().getListeners()) { - this.listeners.add(new ListenerInfo(listener.getHost().getHostString(), listener.getHost().getPort())); + String hostname; + if (AsteriskSerializer.showSensitive || (listener.getHost().getHostString().equals("") || listener.getHost().getHostString().equals("0.0.0.0"))) { + hostname = listener.getHost().getHostString(); + } else { + hostname = "***"; + } + this.listeners.add(new ListenerInfo(hostname, listener.getHost().getPort())); } for (Plugin plugin : proxy.getPluginManager().getPlugins()) { diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotDumpInfo.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotDumpInfo.java index 03d648084..71134b6b4 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotDumpInfo.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotDumpInfo.java @@ -28,6 +28,7 @@ package org.geysermc.platform.spigot; import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; +import org.geysermc.connector.common.serializer.AsteriskSerializer; import org.geysermc.connector.dump.BootstrapDumpInfo; import java.util.ArrayList; @@ -50,7 +51,11 @@ public class GeyserSpigotDumpInfo extends BootstrapDumpInfo { this.platformVersion = Bukkit.getVersion(); this.platformAPIVersion = Bukkit.getBukkitVersion(); this.onlineMode = Bukkit.getOnlineMode(); - this.serverIP = Bukkit.getIp(); + if (AsteriskSerializer.showSensitive || (Bukkit.getIp().equals("") || Bukkit.getIp().equals("0.0.0.0"))) { + this.serverIP = Bukkit.getIp(); + } else { + this.serverIP = "***"; + } this.serverPort = Bukkit.getPort(); this.plugins = new ArrayList<>(); diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityDumpInfo.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityDumpInfo.java index 9c21db9e1..f44086c59 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityDumpInfo.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityDumpInfo.java @@ -28,6 +28,7 @@ package org.geysermc.platform.velocity; import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.proxy.ProxyServer; import lombok.Getter; +import org.geysermc.connector.common.serializer.AsteriskSerializer; import org.geysermc.connector.dump.BootstrapDumpInfo; import java.util.ArrayList; @@ -50,7 +51,11 @@ public class GeyserVelocityDumpInfo extends BootstrapDumpInfo { this.platformVersion = proxy.getVersion().getVersion(); this.platformVendor = proxy.getVersion().getVendor(); this.onlineMode = proxy.getConfiguration().isOnlineMode(); - this.serverIP = proxy.getBoundAddress().getHostString(); + if (AsteriskSerializer.showSensitive || (proxy.getBoundAddress().getHostString().equals("") || proxy.getBoundAddress().getHostString().equals("0.0.0.0"))) { + this.serverIP = proxy.getBoundAddress().getHostString(); + } else { + this.serverIP = "***"; + } this.serverPort = proxy.getBoundAddress().getPort(); this.plugins = new ArrayList<>(); diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/DumpCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/DumpCommand.java index 6566ecc1c..9ad0d23dd 100644 --- a/connector/src/main/java/org/geysermc/connector/command/defaults/DumpCommand.java +++ b/connector/src/main/java/org/geysermc/connector/command/defaults/DumpCommand.java @@ -33,10 +33,12 @@ import org.geysermc.connector.common.ChatColor; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.command.GeyserCommand; +import org.geysermc.connector.common.serializer.AsteriskSerializer; import org.geysermc.connector.dump.DumpInfo; import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.connector.utils.WebUtils; +import java.io.FileOutputStream; import java.io.IOException; public class DumpCommand extends GeyserCommand { @@ -49,43 +51,80 @@ public class DumpCommand extends GeyserCommand { super(name, description, permission); this.connector = connector; - - final SimpleFilterProvider filter = new SimpleFilterProvider(); - filter.addFilter("dump_user_auth", SimpleBeanPropertyFilter.serializeAllExcept(new String[] {"password"})); - - MAPPER.setFilterProvider(filter); } @Override public void execute(CommandSender sender, String[] args) { + boolean showSensitive = false; + boolean offlineDump = false; + if (args.length >= 1) { + for (String arg : args) { + switch (arg) { + case "full": + showSensitive = true; + break; + case "offline": + offlineDump = true; + break; + + } + } + } + + AsteriskSerializer.showSensitive = showSensitive; + sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.dump.collecting")); String dumpData = ""; try { - dumpData = MAPPER.writeValueAsString(new DumpInfo()); + if (offlineDump) { + dumpData = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(new DumpInfo()); + } else { + dumpData = MAPPER.writeValueAsString(new DumpInfo()); + } } catch (IOException e) { sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.collect_error")); connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.dump.collect_error_short"), e); return; } - sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.dump.uploading")); - String response; - JsonNode responseNode; - try { - response = WebUtils.post(DUMP_URL + "documents", dumpData); - responseNode = MAPPER.readTree(response); - } catch (IOException e) { - sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error")); - connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error_short"), e); - return; + String uploadedDumpUrl = ""; + + if (offlineDump) { + sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.dump.writing")); + + try { + FileOutputStream outputStream = new FileOutputStream(GeyserConnector.getInstance().getBootstrap().getConfigFolder().resolve("dump.json").toFile()); + outputStream.write(dumpData.getBytes()); + outputStream.close(); + } catch (IOException e) { + sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.write_error")); + connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.dump.write_error_short"), e); + return; + } + + uploadedDumpUrl = "dump.json"; + } else { + sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.dump.uploading")); + + String response; + JsonNode responseNode; + try { + response = WebUtils.post(DUMP_URL + "documents", dumpData); + responseNode = MAPPER.readTree(response); + } catch (IOException e) { + sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error")); + connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error_short"), e); + return; + } + + if (!responseNode.has("key")) { + sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error_short") + ": " + (responseNode.has("message") ? responseNode.get("message").asText() : response)); + return; + } + + uploadedDumpUrl = DUMP_URL + responseNode.get("key").asText(); } - if (!responseNode.has("key")) { - sender.sendMessage(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.commands.dump.upload_error_short") + ": " + (responseNode.has("message") ? responseNode.get("message").asText() : response)); - return; - } - - String uploadedDumpUrl = DUMP_URL + responseNode.get("key").asText(); sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.dump.message") + " " + ChatColor.DARK_AQUA + uploadedDumpUrl); if (!sender.isConsole()) { connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.commands.dump.created", sender.getName(), uploadedDumpUrl)); diff --git a/connector/src/main/java/org/geysermc/connector/common/serializer/AsteriskSerializer.java b/connector/src/main/java/org/geysermc/connector/common/serializer/AsteriskSerializer.java index d05b9def3..d91034bd6 100644 --- a/connector/src/main/java/org/geysermc/connector/common/serializer/AsteriskSerializer.java +++ b/connector/src/main/java/org/geysermc/connector/common/serializer/AsteriskSerializer.java @@ -42,34 +42,46 @@ import java.lang.annotation.Target; import java.util.Optional; public class AsteriskSerializer extends StdSerializer implements ContextualSerializer { + + public static boolean showSensitive = false; + @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @JacksonAnnotationsInside @JsonSerialize(using = AsteriskSerializer.class) public @interface Asterisk { String value() default "***"; + boolean sensitive() default false; } String asterisk; + boolean sensitive; public AsteriskSerializer() { super(Object.class); } - public AsteriskSerializer(String asterisk) { + public AsteriskSerializer(String asterisk, boolean sensitive) { super(Object.class); this.asterisk = asterisk; + this.sensitive = sensitive; } @Override public JsonSerializer createContextual(SerializerProvider serializerProvider, BeanProperty property) { Optional anno = Optional.ofNullable(property) .map(prop -> prop.getAnnotation(Asterisk.class)); - return new AsteriskSerializer(anno.map(Asterisk::value).orElse(null)); + + return new AsteriskSerializer(anno.map(Asterisk::value).orElse(null), anno.map(Asterisk::sensitive).orElse(null)); } @Override public void serialize(Object obj, JsonGenerator gen, SerializerProvider prov) throws IOException { + if (sensitive && showSensitive) { + gen.writeObject(obj); + return; + } + gen.writeString(asterisk); } } 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 3873db3cc..7f2dafa01 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java @@ -98,6 +98,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @Getter public static class BedrockConfiguration implements IBedrockConfiguration { + @AsteriskSerializer.Asterisk(sensitive = true) private String address; @Setter @@ -114,6 +115,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration public static class RemoteConfiguration implements IRemoteConfiguration { @Setter + @AsteriskSerializer.Asterisk(sensitive = true) private String address; @Setter diff --git a/connector/src/main/resources/languages b/connector/src/main/resources/languages index 57e5986bd..2641db5aa 160000 --- a/connector/src/main/resources/languages +++ b/connector/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 57e5986bd99bf3d81d67c75299ea3dde67d53554 +Subproject commit 2641db5aa9100cdbe21b4493489e9be19092a600 From 1ead2900a3733fb2802adc0f0bef31d61aab7104 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Sun, 16 Aug 2020 13:43:16 -0400 Subject: [PATCH 036/140] Translate RAIN_STRENGTH to Bedrock client (#1151) * Translate RAIN_STRENGTH to Bedrock client Previously Geyser ignored RAIN_STRENGTH and instead relied on START_RAIN and STOP_RAIN only. This is unreliable on a vanilla server as these values are swapped around. This commit also implements thunder strength which was untranslated. * Update rain code in JavaRespawnTranslator --- .../network/session/GeyserSession.java | 12 +++++ .../java/JavaRespawnTranslator.java | 15 +++--- .../world/JavaNotifyClientTranslator.java | 46 ++++++++++++++----- 3 files changed, 55 insertions(+), 18 deletions(-) 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 05465c46b..cea17fdfc 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 @@ -226,6 +226,18 @@ public class GeyserSession implements CommandSender { @Setter private boolean worldImmutable = false; + /** + * Caches current rain status. + */ + @Setter + private boolean raining = false; + + /** + * Caches current thunder status. + */ + @Setter + private boolean thunder = false; + public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) { this.connector = connector; this.upstream = new UpstreamSession(bedrockServerSession); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaRespawnTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaRespawnTranslator.java index 2f7dead77..11e7744d9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaRespawnTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaRespawnTranslator.java @@ -37,8 +37,6 @@ import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.utils.DimensionUtils; -import java.util.concurrent.ThreadLocalRandom; - @Translator(packet = ServerRespawnPacket.class) public class JavaRespawnTranslator extends PacketTranslator { @@ -59,11 +57,14 @@ public class JavaRespawnTranslator extends PacketTranslator session.sendUpstreamPacket(playerGameTypePacket); session.setGameMode(packet.getGamemode()); - LevelEventPacket stopRainPacket = new LevelEventPacket(); - stopRainPacket.setType(LevelEventType.STOP_RAINING); - stopRainPacket.setData(ThreadLocalRandom.current().nextInt(50000) + 10000); - stopRainPacket.setPosition(Vector3f.ZERO); - session.sendUpstreamPacket(stopRainPacket); + if (session.isRaining()) { + LevelEventPacket stopRainPacket = new LevelEventPacket(); + stopRainPacket.setType(LevelEventType.STOP_RAINING); + stopRainPacket.setData(0); + stopRainPacket.setPosition(Vector3f.ZERO); + session.sendUpstreamPacket(stopRainPacket); + session.setRaining(false); + } String newDimension = DimensionUtils.getNewDimension(packet.getDimension()); if (!entity.getDimension().equals(newDimension)) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java index 7260c0f0a..33e602eb3 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java @@ -28,29 +28,22 @@ package org.geysermc.connector.network.translators.java.world; import com.github.steveice10.mc.protocol.data.game.ClientRequest; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.world.notify.EnterCreditsValue; +import com.github.steveice10.mc.protocol.data.game.world.notify.RainStrengthValue; import com.github.steveice10.mc.protocol.data.game.world.notify.RespawnScreenValue; +import com.github.steveice10.mc.protocol.data.game.world.notify.ThunderStrengthValue; import com.github.steveice10.mc.protocol.packet.ingame.client.ClientRequestPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerNotifyClientPacket; import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.protocol.bedrock.data.AdventureSetting; import com.nukkitx.protocol.bedrock.data.GameRuleData; import com.nukkitx.protocol.bedrock.data.LevelEventType; -import com.nukkitx.protocol.bedrock.data.PlayerPermission; -import com.nukkitx.protocol.bedrock.data.command.CommandPermission; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; import com.nukkitx.protocol.bedrock.packet.*; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.PlayerEntity; 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.inventory.PlayerInventoryTranslator; -import java.util.Set; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.TimeUnit; - @Translator(packet = ServerNotifyClientPacket.class) public class JavaNotifyClientTranslator extends PacketTranslator { @@ -64,16 +57,47 @@ public class JavaNotifyClientTranslator extends PacketTranslator 0f; + // Java sends the rain level. Bedrock doesn't care, so we don't care if it's already raining. + if (isCurrentlyRaining != session.isRaining()) { + LevelEventPacket changeRainPacket = new LevelEventPacket(); + changeRainPacket.setType(isCurrentlyRaining ? LevelEventType.START_RAINING : LevelEventType.STOP_RAINING); + changeRainPacket.setData(Integer.MAX_VALUE); // Dunno what this does; used to be implemented with ThreadLocalRandom + changeRainPacket.setPosition(Vector3f.ZERO); + session.sendUpstreamPacket(changeRainPacket); + session.setRaining(isCurrentlyRaining); + } + break; + case THUNDER_STRENGTH: + // See above, same process + ThunderStrengthValue thunderValue = (ThunderStrengthValue) packet.getValue(); + boolean isCurrentlyThundering = thunderValue.getStrength() > 0f; + if (isCurrentlyThundering != session.isThunder()) { + LevelEventPacket changeThunderPacket = new LevelEventPacket(); + changeThunderPacket.setType(isCurrentlyThundering ? LevelEventType.START_THUNDERSTORM : LevelEventType.STOP_THUNDERSTORM); + changeThunderPacket.setData(Integer.MAX_VALUE); + changeThunderPacket.setPosition(Vector3f.ZERO); + session.sendUpstreamPacket(changeThunderPacket); + session.setThunder(isCurrentlyThundering); + } break; case CHANGE_GAMEMODE: GameMode gameMode = (GameMode) packet.getValue(); From 0e91475c624e5b9ee38946a1eb5f5bf808edfe86 Mon Sep 17 00:00:00 2001 From: R-Josef <460257626@qq.com> Date: Sun, 16 Aug 2020 12:45:52 -0500 Subject: [PATCH 037/140] Follows specified address/port in remote config for plugin versions: fix #1110 (#1145) * fix #1110 * updating comments in config.yml * Fix indentation * Centralize localhost retrieval; remove unnecessary Docker check * Add config.yml Co-authored-by: DoctorMacc --- .../bungeecord/GeyserBungeePlugin.java | 9 +++---- .../platform/spigot/GeyserSpigotPlugin.java | 9 +++---- .../platform/sponge/GeyserSpongePlugin.java | 6 ++--- .../standalone/GeyserStandaloneBootstrap.java | 3 +++ .../velocity/GeyserVelocityPlugin.java | 9 +++---- .../geysermc/connector/GeyserConnector.java | 15 ++++++++--- .../geysermc/connector/utils/DockerCheck.java | 27 ++----------------- connector/src/main/resources/config.yml | 5 +++- 8 files changed, 32 insertions(+), 51 deletions(-) diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java index 4e3ad5b90..059e1dfd8 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java @@ -81,17 +81,14 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { InetSocketAddress javaAddr = listener.getHost(); - // Don't change the ip if its listening on all interfaces - // By default this should be 127.0.0.1 but may need to be changed in some circumstances - if (!javaAddr.getHostString().equals("0.0.0.0") && !javaAddr.getHostString().equals("")) { - this.geyserConfig.getRemote().setAddress(javaAddr.getHostString()); + // By default this should be localhost but may need to be changed in some circumstances + if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + this.geyserConfig.getRemote().setPort(javaAddr.getPort()); } if (geyserConfig.getBedrock().isCloneRemotePort()) { geyserConfig.getBedrock().setPort(javaAddr.getPort()); } - - this.geyserConfig.getRemote().setPort(javaAddr.getPort()); } this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode()); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java index 759246df3..496681d33 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java @@ -81,18 +81,15 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { ex.printStackTrace(); } - // Don't change the ip if its listening on all interfaces - // By default this should be 127.0.0.1 but may need to be changed in some circumstances - if (!Bukkit.getIp().equals("0.0.0.0") && !Bukkit.getIp().equals("")) { - geyserConfig.getRemote().setAddress(Bukkit.getIp()); + // By default this should be localhost but may need to be changed in some circumstances + if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + geyserConfig.getRemote().setPort(Bukkit.getPort()); } if (geyserConfig.getBedrock().isCloneRemotePort()) { geyserConfig.getBedrock().setPort(Bukkit.getPort()); } - geyserConfig.getRemote().setPort(Bukkit.getPort()); - this.geyserLogger = new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode()); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java index cfead6fdd..e57e383b2 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java @@ -104,11 +104,9 @@ public class GeyserSpongePlugin implements GeyserBootstrap { // Don't change the ip if its listening on all interfaces // By default this should be 127.0.0.1 but may need to be changed in some circumstances - if (!javaAddr.getHostString().equals("0.0.0.0") && !javaAddr.getHostString().equals("")) { - serverIP.setValue("127.0.0.1"); + if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + serverPort.setValue(javaAddr.getPort()); } - - serverPort.setValue(javaAddr.getPort()); } ConfigurationNode bedrockPort = config.getNode("bedrock").getNode("port"); diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java index e9576b19a..e4b70133f 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java @@ -108,6 +108,9 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { try { File configFile = FileUtils.fileOrCopiedFromResource("config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString())); geyserConfig = FileUtils.loadConfig(configFile, GeyserStandaloneConfiguration.class); + if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + geyserConfig.getRemote().setAddress("127.0.0.1"); + } } catch (IOException ex) { geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.config.failed"), ex); System.exit(0); diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java index 974d4c4c9..6d8032343 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java @@ -92,18 +92,15 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { InetSocketAddress javaAddr = proxyServer.getBoundAddress(); - // Don't change the ip if its listening on all interfaces - // By default this should be 127.0.0.1 but may need to be changed in some circumstances - if (!javaAddr.getHostString().equals("0.0.0.0") && !javaAddr.getHostString().equals("")) { - geyserConfig.getRemote().setAddress(javaAddr.getHostString()); + // By default this should be localhost but may need to be changed in some circumstances + if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + geyserConfig.getRemote().setPort(javaAddr.getPort()); } if (geyserConfig.getBedrock().isCloneRemotePort()) { geyserConfig.getBedrock().setPort(javaAddr.getPort()); } - geyserConfig.getRemote().setPort(javaAddr.getPort()); - this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode()); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 103faf791..8203a1689 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -53,13 +53,14 @@ 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.utils.DimensionUtils; -import org.geysermc.connector.utils.DockerCheck; import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.connector.utils.LocaleUtils; import javax.naming.directory.Attribute; import javax.naming.directory.InitialDirContext; +import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.UnknownHostException; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; @@ -133,8 +134,16 @@ public class GeyserConnector { SoundRegistry.init(); SoundHandlerRegistry.init(); - if (platformType != PlatformType.STANDALONE) { - DockerCheck.check(bootstrap); + if (platformType != PlatformType.STANDALONE && config.getRemote().getAddress().equals("auto")) { + // Set the remote address to localhost since that is where we are always connecting + try { + config.getRemote().setAddress(InetAddress.getLocalHost().getHostAddress()); + } catch (UnknownHostException ex) { + logger.debug("Unknown host when trying to find localhost."); + if (config.isDebugMode()) { + ex.printStackTrace(); + } + } } String remoteAddress = config.getRemote().getAddress(); int remotePort = config.getRemote().getPort(); diff --git a/connector/src/main/java/org/geysermc/connector/utils/DockerCheck.java b/connector/src/main/java/org/geysermc/connector/utils/DockerCheck.java index 74d020bca..59a039887 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/DockerCheck.java +++ b/connector/src/main/java/org/geysermc/connector/utils/DockerCheck.java @@ -25,36 +25,13 @@ package org.geysermc.connector.utils; -import org.geysermc.connector.bootstrap.GeyserBootstrap; - -import java.net.InetAddress; import java.nio.file.Files; import java.nio.file.Paths; public class DockerCheck { - public static void check(GeyserBootstrap bootstrap) { - try { - String OS = System.getProperty("os.name").toLowerCase(); - String ipAddress = InetAddress.getLocalHost().getHostAddress(); - - // Check if the user is already using the recommended IP - if (ipAddress.equals(bootstrap.getGeyserConfig().getRemote().getAddress())) { - return; - } - - if (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0 || OS.indexOf("aix") > 0) { - bootstrap.getGeyserLogger().debug("We are on a Unix system, checking for Docker..."); - - String output = new String(Files.readAllBytes(Paths.get("/proc/1/cgroup"))); - - if (output.contains("docker")) { - bootstrap.getGeyserLogger().warning(LanguageUtils.getLocaleStringLog("geyser.bootstrap.docker_warn.line1")); - bootstrap.getGeyserLogger().warning(LanguageUtils.getLocaleStringLog("geyser.bootstrap.docker_warn.line2", ipAddress)); - } - } - } catch (Exception e) { } // Ignore any errors, inc ip failed to fetch, process could not run or access denied - } + // By default, Geyser now sets the IP to the local IP in all cases on plugin versions so we don't notify the user of anything + // However we still have this check for the potential future bug public static boolean checkBasic() { try { String OS = System.getProperty("os.name").toLowerCase(); diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index 6ccaa3065..b4d437276 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -22,8 +22,11 @@ bedrock: motd2: "Another GeyserMC forced host." remote: # The IP address of the remote (Java Edition) server - address: 127.0.0.1 + # If it is "auto", for standalone version the remote address will be set to 127.0.0.1, + # for plugin versions, Geyser will attempt to find the best address to connect to. + address: auto # The port of the remote (Java Edition) server + # For plugin versions, if address has been set to "auto", the port will also follow the server's listening port. port: 25565 # Authentication type. Can be offline, online, or floodgate (see https://github.com/GeyserMC/Geyser/wiki/Floodgate). auth-type: online From 78e8792a2d05e5cf4d310342e443ee550a9c7e67 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Sun, 16 Aug 2020 15:25:35 -0400 Subject: [PATCH 038/140] FireworkEntity: don't process if item is null (#1160) --- .../java/org/geysermc/connector/entity/FireworkEntity.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java index 3e1d05f44..5e47cb0f2 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java @@ -55,6 +55,9 @@ public class FireworkEntity extends Entity { public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { if (entityMetadata.getId() == 7) { ItemStack item = (ItemStack) entityMetadata.getValue(); + if (item == null) { + return; + } CompoundTag tag = item.getNbt(); if (tag == null) { From b07433698a8e84bba4ef5d3d15917a8d6769ae93 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Sun, 16 Aug 2020 19:02:59 -0400 Subject: [PATCH 039/140] Translate specific messages (#1161) --- .../java/org/geysermc/connector/entity/Entity.java | 12 ++++-------- .../translators/java/JavaTitleTranslator.java | 7 ++++--- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/entity/Entity.java b/connector/src/main/java/org/geysermc/connector/entity/Entity.java index 1db5d2529..5e825e892 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/Entity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/Entity.java @@ -32,8 +32,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.player.Hand; import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; -import com.github.steveice10.mc.protocol.data.message.TextMessage; -import com.github.steveice10.mc.protocol.data.message.TranslationMessage; +import com.github.steveice10.mc.protocol.data.message.Message; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerUseItemPacket; import com.nukkitx.math.vector.Vector3f; @@ -318,13 +317,10 @@ public class Entity { } break; case 2: // custom name - if (entityMetadata.getValue() instanceof TextMessage) { - TextMessage name = (TextMessage) entityMetadata.getValue(); - if (name != null) - metadata.put(EntityData.NAMETAG, MessageUtils.getBedrockMessage(name)); - } else if (entityMetadata.getValue() instanceof TranslationMessage) { - TranslationMessage message = (TranslationMessage) entityMetadata.getValue(); + if (entityMetadata.getValue() instanceof Message) { + Message message = (Message) entityMetadata.getValue(); if (message != null) + // Always translate even if it's a TextMessage since there could be translatable parameters metadata.put(EntityData.NAMETAG, MessageUtils.getTranslatedBedrockMessage(message, session.getClientData().getLanguageCode(), true)); } break; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaTitleTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaTitleTranslator.java index 214413ab7..6a885e901 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaTitleTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaTitleTranslator.java @@ -39,15 +39,16 @@ public class JavaTitleTranslator extends PacketTranslator { @Override public void translate(ServerTitlePacket packet, GeyserSession session) { SetTitlePacket titlePacket = new SetTitlePacket(); + String locale = session.getClientData().getLanguageCode(); switch (packet.getAction()) { case TITLE: titlePacket.setType(SetTitlePacket.Type.TITLE); - titlePacket.setText(MessageUtils.getBedrockMessage(packet.getTitle())); + titlePacket.setText(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), locale)); break; case SUBTITLE: titlePacket.setType(SetTitlePacket.Type.SUBTITLE); - titlePacket.setText(MessageUtils.getBedrockMessage(packet.getTitle())); + titlePacket.setText(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), locale)); break; case CLEAR: case RESET: @@ -56,7 +57,7 @@ public class JavaTitleTranslator extends PacketTranslator { break; case ACTION_BAR: titlePacket.setType(SetTitlePacket.Type.ACTIONBAR); - titlePacket.setText(MessageUtils.getBedrockMessage(packet.getTitle())); + titlePacket.setText(MessageUtils.getTranslatedBedrockMessage(packet.getTitle(), locale)); break; case TIMES: titlePacket.setFadeInTime(packet.getFadeIn()); From 8c514d9feb8c024d5bc0186a6bb11fb4af0aff98 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 17 Aug 2020 12:04:09 -0400 Subject: [PATCH 040/140] Fix Xbox authentication and add support for proxies (#1162) Waterdog and ProxyPass will work when `enable-proxy-connections` is set to true at the expense of security. --- .../platform/sponge/GeyserSpongeConfiguration.java | 5 +++++ .../connector/configuration/GeyserConfiguration.java | 5 ++++- .../configuration/GeyserJacksonConfiguration.java | 3 +++ .../geysermc/connector/utils/LoginEncryptionUtils.java | 4 ++-- connector/src/main/resources/config.yml | 9 +++++++-- connector/src/main/resources/mappings | 2 +- 6 files changed, 22 insertions(+), 6 deletions(-) diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java index 734fcca67..40fe106ce 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java @@ -258,6 +258,11 @@ public class GeyserSpongeConfiguration implements GeyserConfiguration { } } + @Override + public boolean isEnableProxyConnections() { + return node.getNode("enable-proxy-connections").getBoolean(false); + } + @Override public int getMtu() { return node.getNode("mtu").getInt(1400); 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 4d9933ff5..be4182306 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java @@ -36,7 +36,7 @@ import java.util.Map; public interface GeyserConfiguration { // Modify this when you update the config - int CURRENT_CONFIG_VERSION = 3; + int CURRENT_CONFIG_VERSION = 4; IBedrockConfiguration getBedrock(); @@ -120,6 +120,9 @@ public interface GeyserConfiguration { String getUniqueId(); } + // if u have offline mode enabled pls be safe + boolean isEnableProxyConnections(); + int getMtu(); int getConfigVersion(); 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 7f2dafa01..1a22b0357 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java @@ -143,6 +143,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration private String uniqueId; } + @JsonProperty("enable-proxy-connections") + private boolean enableProxyConnections = false; + @JsonProperty("mtu") private int mtu = 1400; diff --git a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java index 7b41ae3de..4bc997bdf 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LoginEncryptionUtils.java @@ -72,7 +72,7 @@ public class LoginEncryptionUtils { } if (lastKey != null) { - EncryptionUtils.verifyJwt(jwt, lastKey); + if (!EncryptionUtils.verifyJwt(jwt, lastKey)) return false; } JsonNode payloadNode = JSON_MAPPER.readTree(jwt.getPayload().toString()); @@ -105,7 +105,7 @@ public class LoginEncryptionUtils { connector.getLogger().debug(String.format("Is player data valid? %s", validChain)); - if (!validChain) { + if (!validChain && !session.getConnector().getConfig().isEnableProxyConnections()) { session.disconnect(LanguageUtils.getLocaleStringLog("geyser.network.remote.invalid_xbox_account")); return; } diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index b4d437276..d2bca8234 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -117,9 +117,14 @@ metrics: # UUID of server, don't change! uuid: generateduuid -# ADVANCED OPTIONS - DO NOT TOUCH UNLESS YOU KNOW WHAT YOU ARE DOING! +# ADVANCED OPTIONS - DO NOT TOUCH UNLESS YOU KNOW WHAT YOU ARE DOING! + +# Allow connections from ProxyPass and Waterdog. +# See https://www.spigotmc.org/wiki/firewall-guide/ for assistance - use UDP instead of TCP. +enable-proxy-connections: false + # The internet supports a maximum MTU of 1492 but could cause issues with packet fragmentation. # 1400 is the default. # mtu: 1400 -config-version: 3 +config-version: 4 diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index 94d30c1c6..88678e69b 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 94d30c1c6c4518932b813826bfcef680575a3c9c +Subproject commit 88678e69bf358cd562bd410a2459384aeb7ba482 From 6db56fd68b79e865b4ae3334ba4ba193e683617e Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Mon, 17 Aug 2020 20:04:12 -0500 Subject: [PATCH 041/140] Disable fireworks for consoles (Addresses #1083, #1164) Not ideal, but there isn't a whole lot we can do as this is a game bug within console versions. --- .../java/org/geysermc/connector/entity/FireworkEntity.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java index 5e47cb0f2..b940b87b9 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java @@ -40,6 +40,7 @@ import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.utils.FireworkColor; import org.geysermc.connector.utils.MathUtils; +import org.geysermc.floodgate.util.DeviceOS; import java.util.ArrayList; import java.util.List; @@ -64,6 +65,12 @@ public class FireworkEntity extends Entity { return; } + // TODO: Remove once Mojang fixes bugs with fireworks crashing clients on these specific devices. + // https://bugs.mojang.com/browse/MCPE-89115 + if (session.getClientData().getDeviceOS() == DeviceOS.XBOX_ONE || session.getClientData().getDeviceOS() == DeviceOS.ORBIS) { + return; + } + CompoundTag fireworks = tag.get("Fireworks"); NbtMapBuilder fireworksBuilder = NbtMap.builder(); From 44f521ed0455a6505d1afb7625651d473b373340 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Mon, 17 Aug 2020 20:40:57 -0500 Subject: [PATCH 042/140] Set player motion when explosion takes place --- .../translators/java/world/JavaExplosionTranslator.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaExplosionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaExplosionTranslator.java index 1b6273c92..1eae0b316 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaExplosionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaExplosionTranslator.java @@ -32,6 +32,7 @@ import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.SoundEvent; import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket; +import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; @@ -64,5 +65,12 @@ public class JavaExplosionTranslator extends PacketTranslator 0f && packet.getPushY() > 0f && packet.getPushZ() > 0f) { + SetEntityMotionPacket motionPacket = new SetEntityMotionPacket(); + motionPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId()); + motionPacket.setMotion(Vector3f.from(packet.getPushX(), packet.getPushY(), packet.getPushZ())); + session.sendUpstreamPacket(motionPacket); + } } } From 80a36344eb78ff620c5ee4a565540b257b51d361 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Mon, 17 Aug 2020 20:46:03 -0500 Subject: [PATCH 043/140] Only one of the values here needs to be greater than 0 --- .../network/translators/java/world/JavaExplosionTranslator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaExplosionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaExplosionTranslator.java index 1eae0b316..efed6ba45 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaExplosionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaExplosionTranslator.java @@ -66,7 +66,7 @@ public class JavaExplosionTranslator extends PacketTranslator 0f && packet.getPushY() > 0f && packet.getPushZ() > 0f) { + if (packet.getPushX() > 0f || packet.getPushY() > 0f || packet.getPushZ() > 0f) { SetEntityMotionPacket motionPacket = new SetEntityMotionPacket(); motionPacket.setRuntimeEntityId(session.getPlayerEntity().getGeyserId()); motionPacket.setMotion(Vector3f.from(packet.getPushX(), packet.getPushY(), packet.getPushZ())); From e7363b4e9fb50743f7e8f5c9e71c0fa08ada3537 Mon Sep 17 00:00:00 2001 From: James Cahill Date: Tue, 18 Aug 2020 04:36:15 +0100 Subject: [PATCH 044/140] Add 'passthrough-protocol-name' config option (#1124) * Initial version (tested) * Don't bump config version * Misc changes * Add punctuation to config --- .../platform/sponge/GeyserSpongeConfiguration.java | 5 +++++ .../connector/configuration/GeyserConfiguration.java | 3 +++ .../configuration/GeyserJacksonConfiguration.java | 3 +++ .../geysermc/connector/network/QueryPacketHandler.java | 10 +++++++++- connector/src/main/resources/config.yml | 5 ++++- 5 files changed, 24 insertions(+), 2 deletions(-) diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java index 40fe106ce..7f08a0950 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java @@ -89,6 +89,11 @@ public class GeyserSpongeConfiguration implements GeyserConfiguration { return node.getNode("passthrough-motd").getBoolean(false); } + @Override + public boolean isPassthroughProtocolName() { + return node.getNode("passthrough-protocol-name").getBoolean(false); + } + @Override public boolean isPassthroughPlayerCounts() { return node.getNode("passthrough-player-counts").getBoolean(false); 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 be4182306..2cf9a181e 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java @@ -49,6 +49,9 @@ public interface GeyserConfiguration { @JsonIgnore boolean isPassthroughMotd(); + @JsonIgnore + boolean isPassthroughProtocolName(); + @JsonIgnore boolean isPassthroughPlayerCounts(); 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 1a22b0357..f322b4363 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java @@ -57,6 +57,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonProperty("passthrough-player-counts") private boolean isPassthroughPlayerCounts; + @JsonProperty("passthrough-protocol-name") + private boolean isPassthroughProtocolName; + @JsonProperty("legacy-ping-passthrough") private boolean isLegacyPingPassthrough; diff --git a/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java index bd67fd1b5..7faf36bdd 100644 --- a/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java @@ -141,6 +141,7 @@ public class QueryPacketHandler { String motd; String currentPlayerCount; String maxPlayerCount; + String map; if (connector.getConfig().isPassthroughMotd() || connector.getConfig().isPassthroughPlayerCounts()) { pingInfo = connector.getBootstrap().getGeyserPingPassthrough().getPingInformation(); @@ -162,6 +163,13 @@ public class QueryPacketHandler { maxPlayerCount = String.valueOf(connector.getConfig().getMaxPlayers()); } + // If passthrough protocol name is enabled let's get the protocol name from the ping response. + if (connector.getConfig().isPassthroughProtocolName() && pingInfo != null) { + map = String.valueOf((pingInfo.getVersion().getName())); + } else { + map = GeyserConnector.NAME; + } + // Create a hashmap of all game data needed in the query Map gameData = new HashMap(); gameData.put("hostname", motd); @@ -169,7 +177,7 @@ public class QueryPacketHandler { gameData.put("game_id", "MINECRAFT"); gameData.put("version", BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()); gameData.put("plugins", ""); - gameData.put("map", GeyserConnector.NAME); + gameData.put("map", map); gameData.put("numplayers", currentPlayerCount); gameData.put("maxplayers", maxPlayerCount); gameData.put("hostport", String.valueOf(connector.getConfig().getBedrock().getPort())); diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index d2bca8234..f59939b6a 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -53,9 +53,12 @@ floodgate-key-file: public-key.pem # Disabling this will prevent command suggestions from being sent and solve freezing for Bedrock clients. command-suggestions: true -# The following two options enable "ping passthrough" - the MOTD and/or player count gets retrieved from the Java server. +# The following three options enable "ping passthrough" -the MOTD, player count and/or protocol name gets retrieved from the Java server. # Relay the MOTD from the remote server to Bedrock players. passthrough-motd: false +# Relay the protocol name (e.g. BungeeCord [X.X], Paper 1.X) - only really useful when using a custom protocol name! +# This will also show up on sites like MCSrvStatus. +passthrough-protocol-name: false # Relay the player count and max players from the remote server to Bedrock players. passthrough-player-counts: false # Enable LEGACY ping passthrough. There is no need to enable this unless your MOTD or player count does not appear properly. From d6290ccb6640317aeb932e7aca91f8dfaa72837e Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Wed, 19 Aug 2020 13:14:17 -0400 Subject: [PATCH 045/140] Auto-configure more if setting is enabled (#1168) * Auto-configure more if setting is enabled - Geyser dumps now show if the config was automatic - Floodgate is now automatically detected if the address is also automatically found - If the plugin versions' servers have the listening address set to something different, set our remote address to that * Fix Sponge config * Remove redundant Getter --- .../platform/bungeecord/GeyserBungeePlugin.java | 11 ++++++++++- .../platform/spigot/GeyserSpigotPlugin.java | 11 ++++++++++- .../sponge/GeyserSpongeConfiguration.java | 11 +++++++++-- .../platform/sponge/GeyserSpongePlugin.java | 5 +++-- .../standalone/GeyserStandaloneBootstrap.java | 3 ++- .../platform/velocity/GeyserVelocityPlugin.java | 15 +++++++++++---- .../configuration/GeyserJacksonConfiguration.java | 7 +++++++ 7 files changed, 52 insertions(+), 11 deletions(-) diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java index 059e1dfd8..e80207d3f 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java @@ -30,10 +30,10 @@ import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.config.Configuration; import net.md_5.bungee.config.ConfigurationProvider; import net.md_5.bungee.config.YamlConfiguration; -import org.geysermc.connector.common.PlatformType; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.command.CommandManager; +import org.geysermc.connector.common.PlatformType; import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.dump.BootstrapDumpInfo; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; @@ -83,6 +83,11 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { // By default this should be localhost but may need to be changed in some circumstances if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + this.geyserConfig.setAutoconfiguredRemote(true); + // Don't use localhost if not listening on all interfaces + if (!javaAddr.getHostString().equals("0.0.0.0") && !javaAddr.getHostString().equals("")) { + this.geyserConfig.getRemote().setAddress(javaAddr.getHostString()); + } this.geyserConfig.getRemote().setPort(javaAddr.getPort()); } @@ -97,6 +102,10 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { if (geyserConfig.getRemote().getAuthType().equals("floodgate") && getProxy().getPluginManager().getPlugin("floodgate-bungee") == null) { geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); return; + } else if (geyserConfig.isAutoconfiguredRemote() && getProxy().getPluginManager().getPlugin("floodgate-bungee") != null) { + // Floodgate installed means that the user wants Floodgate authentication + geyserLogger.debug("Auto-setting to Floodgate authentication."); + geyserConfig.getRemote().setAuthType("floodgate"); } geyserConfig.loadFloodgate(this, configuration); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java index 496681d33..d51cf21a5 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java @@ -27,10 +27,10 @@ package org.geysermc.platform.spigot; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; -import org.geysermc.connector.common.PlatformType; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.command.CommandManager; +import org.geysermc.connector.common.PlatformType; import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.dump.BootstrapDumpInfo; import org.geysermc.connector.network.translators.world.WorldManager; @@ -83,6 +83,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { // By default this should be localhost but may need to be changed in some circumstances if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + geyserConfig.setAutoconfiguredRemote(true); + // Don't use localhost if not listening on all interfaces + if (!Bukkit.getIp().equals("0.0.0.0") && !Bukkit.getIp().equals("")) { + geyserConfig.getRemote().setAddress(Bukkit.getIp()); + } geyserConfig.getRemote().setPort(Bukkit.getPort()); } @@ -97,6 +102,10 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); this.getPluginLoader().disablePlugin(this); return; + } else if (geyserConfig.isAutoconfiguredRemote() && Bukkit.getPluginManager().getPlugin("floodgate-bukkit") != null) { + // Floodgate installed means that the user wants Floodgate authentication + geyserLogger.debug("Auto-setting to Floodgate authentication."); + geyserConfig.getRemote().setAuthType("floodgate"); } geyserConfig.loadFloodgate(this); diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java index 7f08a0950..35bbf97d5 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java @@ -26,9 +26,7 @@ package org.geysermc.platform.sponge; import lombok.AllArgsConstructor; - import ninja.leaping.configurate.ConfigurationNode; - import org.geysermc.connector.configuration.GeyserConfiguration; import java.io.File; @@ -41,6 +39,11 @@ public class GeyserSpongeConfiguration implements GeyserConfiguration { private File dataFolder; private ConfigurationNode node; + /** + * If the config was originally 'auto' before the values changed + */ + private boolean autoconfiguredRemote = false; + private SpongeBedrockConfiguration bedrockConfig; private SpongeRemoteConfiguration remoteConfig; private SpongeMetricsInfo metricsInfo; @@ -64,6 +67,10 @@ public class GeyserSpongeConfiguration implements GeyserConfiguration { } } + public void setAutoconfiguredRemote(boolean autoconfiguredRemote) { + this.autoconfiguredRemote = autoconfiguredRemote; + } + @Override public SpongeBedrockConfiguration getBedrock() { return bedrockConfig; diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java index e57e383b2..0f1b72533 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java @@ -29,11 +29,11 @@ import com.google.inject.Inject; import ninja.leaping.configurate.ConfigurationNode; import ninja.leaping.configurate.loader.ConfigurationLoader; import ninja.leaping.configurate.yaml.YAMLConfigurationLoader; -import org.geysermc.connector.common.PlatformType; -import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.command.CommandManager; +import org.geysermc.connector.common.PlatformType; +import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.dump.BootstrapDumpInfo; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; import org.geysermc.connector.ping.IGeyserPingPassthrough; @@ -105,6 +105,7 @@ public class GeyserSpongePlugin implements GeyserBootstrap { // Don't change the ip if its listening on all interfaces // By default this should be 127.0.0.1 but may need to be changed in some circumstances if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + this.geyserConfig.setAutoconfiguredRemote(true); serverPort.setValue(javaAddr.getPort()); } } diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java index e4b70133f..35cc48610 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java @@ -33,8 +33,8 @@ import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.core.appender.ConsoleAppender; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.bootstrap.GeyserBootstrap; -import org.geysermc.connector.common.PlatformType; import org.geysermc.connector.command.CommandManager; +import org.geysermc.connector.common.PlatformType; import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.dump.BootstrapDumpInfo; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; @@ -109,6 +109,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { File configFile = FileUtils.fileOrCopiedFromResource("config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString())); geyserConfig = FileUtils.loadConfig(configFile, GeyserStandaloneConfiguration.class); if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + geyserConfig.setAutoconfiguredRemote(true); // Doesn't really need to be set but /shrug geyserConfig.getRemote().setAddress("127.0.0.1"); } } catch (IOException ex) { diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java index 6d8032343..291d7a001 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityPlugin.java @@ -26,19 +26,17 @@ package org.geysermc.platform.velocity; import com.google.inject.Inject; - import com.velocitypowered.api.command.CommandManager; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; import com.velocitypowered.api.plugin.Plugin; - import com.velocitypowered.api.proxy.ProxyServer; import lombok.Getter; -import org.geysermc.connector.common.PlatformType; -import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.bootstrap.GeyserBootstrap; +import org.geysermc.connector.common.PlatformType; +import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.dump.BootstrapDumpInfo; import org.geysermc.connector.ping.GeyserLegacyPingPassthrough; import org.geysermc.connector.ping.IGeyserPingPassthrough; @@ -94,6 +92,11 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { // By default this should be localhost but may need to be changed in some circumstances if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { + this.geyserConfig.setAutoconfiguredRemote(true); + // Don't use localhost if not listening on all interfaces + if (!javaAddr.getHostString().equals("0.0.0.0") && !javaAddr.getHostString().equals("")) { + this.geyserConfig.getRemote().setAddress(javaAddr.getHostString()); + } geyserConfig.getRemote().setPort(javaAddr.getPort()); } @@ -107,6 +110,10 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { if (geyserConfig.getRemote().getAuthType().equals("floodgate") && !proxyServer.getPluginManager().getPlugin("floodgate").isPresent()) { geyserLogger.severe(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); return; + } else if (geyserConfig.isAutoconfiguredRemote() && proxyServer.getPluginManager().getPlugin("floodgate").isPresent()) { + // Floodgate installed means that the user wants Floodgate authentication + geyserLogger.debug("Auto-setting to Floodgate authentication."); + geyserConfig.getRemote().setAuthType("floodgate"); } geyserConfig.loadFloodgate(this, proxyServer, configFolder.toFile()); 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 f322b4363..8390640f9 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java @@ -38,6 +38,12 @@ import java.util.Map; @JsonIgnoreProperties(ignoreUnknown = true) public abstract class GeyserJacksonConfiguration implements GeyserConfiguration { + /** + * If the config was originally 'auto' before the values changed + */ + @Setter + private boolean autoconfiguredRemote = false; + private BedrockConfiguration bedrock; private RemoteConfiguration remote; @@ -124,6 +130,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @Setter private int port; + @Setter @JsonProperty("auth-type") private String authType; } From 7fcfa7d54d4fc3b0e6d4416047b0394eb48e962a Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Thu, 20 Aug 2020 20:53:47 -0400 Subject: [PATCH 046/140] Implement an enchantment table GUI (#1177) Until 1.16, enchantment tables were impossible to implement properly in Geyser. When a user selects an enchantment in Bedrock, the client creates the book on its end and assumes the server is OK with it. Java requires a button to be pressed to select the enchantment. With 1.16, server authoritative inventories remove that on Bedrock. However, until our inventory rewrite is finished we are still stuck without enchantment table support. This commit serves as an alternative as we wait. Enchantment table GUI support is still impossible since we are using the pre-1.16 inventory system. To solve this, this commit replaces the enchantment table GUI with a hopper GUI. The first slot serves as the spot you place the weapon. The second slot acts as the lapis slot - Geyser prevents any item from going in there that is not lapis. The final three slots act as the buttons; an enchanted book acts as each button, with the ability to show the translated text of each enchantment. https://cdn.discordapp.com/attachments/613194828359925800/746164042359504927/unknown.png --- README.md | 2 +- .../network/session/GeyserSession.java | 10 +- .../EnchantmentInventoryTranslator.java | 231 +++++++++++++++++- .../inventory/InventoryTranslator.java | 2 +- 4 files changed, 238 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 64ff22522..92462e7ee 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set ## What's Left to be Added/Fixed - The Following Inventories - - [ ] Enchantment Table + - [ ] Enchantment Table (as a proper GUI) - [ ] Beacon - [ ] Cartography Table - [ ] Stonecutter 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 cea17fdfc..a385c21c8 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 @@ -70,6 +70,7 @@ import org.geysermc.connector.network.session.cache.*; 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.inventory.EnchantmentInventoryTranslator; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.*; @@ -177,6 +178,11 @@ public class GeyserSession implements CommandSender { @Setter private long lastInteractedVillagerEid; + /** + * Stores the enchantment information the client has received if they are in an enchantment table GUI + */ + private final EnchantmentInventoryTranslator.EnchantmentSlotData[] enchantmentSlotData = new EnchantmentInventoryTranslator.EnchantmentSlotData[3]; + /** * The current attack speed of the player. Used for sending proper cooldown timings. */ @@ -189,8 +195,6 @@ public class GeyserSession implements CommandSender { @Setter private long lastHitTime; - private MinecraftProtocol protocol; - private boolean reducedDebugInfo = false; @Setter @@ -238,6 +242,8 @@ public class GeyserSession implements CommandSender { @Setter private boolean thunder = false; + private MinecraftProtocol protocol; + public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) { this.connector = connector; this.upstream = new UpstreamSession(bedrockServerSession); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java index c8e9ed186..cbcdce10b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/EnchantmentInventoryTranslator.java @@ -25,18 +25,243 @@ package org.geysermc.connector.network.translators.inventory; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientClickWindowButtonPacket; +import com.nukkitx.nbt.NbtMap; +import com.nukkitx.nbt.NbtMapBuilder; +import com.nukkitx.nbt.NbtType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData; +import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import com.nukkitx.protocol.bedrock.packet.InventoryContentPacket; +import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import lombok.ToString; +import org.geysermc.connector.common.ChatColor; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.inventory.updater.ContainerInventoryUpdater; +import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; +import org.geysermc.connector.network.translators.item.ItemTranslator; +import org.geysermc.connector.utils.InventoryUtils; +import org.geysermc.connector.utils.LocaleUtils; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * A temporary reconstruction of the enchantment table UI until our inventory rewrite is complete. + * The enchantment table on Bedrock without server authoritative inventories doesn't tell us which button is pressed + * when selecting an enchantment. + */ public class EnchantmentInventoryTranslator extends BlockInventoryTranslator { - public EnchantmentInventoryTranslator() { - super(2, "minecraft:enchanting_table", ContainerType.ENCHANTMENT, new ContainerInventoryUpdater()); + + private static final int DYE_ID = 351; + private static final short LAPIS_DAMAGE = 4; + private static final int ENCHANTED_BOOK_ID = 403; + + public EnchantmentInventoryTranslator(InventoryUpdater updater) { + super(2, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, updater); + } + + @Override + public void translateActions(GeyserSession session, Inventory inventory, List actions) { + for (InventoryActionData action : actions) { + if (action.getSource().getContainerId() == inventory.getId()) { + // This is the hopper UI + switch (action.getSlot()) { + case 1: + // Don't allow the slot to be put through if the item isn't lapis + if ((action.getToItem().getId() != DYE_ID + && action.getToItem().getDamage() != LAPIS_DAMAGE) && action.getToItem() != ItemData.AIR) { + updateInventory(session, inventory); + InventoryUtils.updateCursor(session); + return; + } + break; + case 2: + case 3: + case 4: + // The books here act as buttons + ClientClickWindowButtonPacket packet = new ClientClickWindowButtonPacket(inventory.getId(), action.getSlot() - 2); + session.sendDownstreamPacket(packet); + updateInventory(session, inventory); + InventoryUtils.updateCursor(session); + return; + default: + break; + } + } + } + + super.translateActions(session, inventory, actions); + } + + @Override + public void updateInventory(GeyserSession session, Inventory inventory) { + super.updateInventory(session, inventory); + ItemData[] items = new ItemData[5]; + items[0] = ItemTranslator.translateToBedrock(session, inventory.getItem(0)); + items[1] = ItemTranslator.translateToBedrock(session, inventory.getItem(1)); + for (int i = 0; i < 3; i++) { + items[i + 2] = session.getEnchantmentSlotData()[i].getItem() != null ? session.getEnchantmentSlotData()[i].getItem() : createEnchantmentBook(); + } + + InventoryContentPacket contentPacket = new InventoryContentPacket(); + contentPacket.setContainerId(inventory.getId()); + contentPacket.setContents(items); + session.sendUpstreamPacket(contentPacket); } @Override public void updateProperty(GeyserSession session, Inventory inventory, int key, int value) { + int bookSlotToUpdate; + switch (key) { + case 0: + case 1: + case 2: + // Experience required + bookSlotToUpdate = key; + session.getEnchantmentSlotData()[bookSlotToUpdate].setExperienceRequired(value); + break; + case 4: + case 5: + case 6: + // Enchantment name + bookSlotToUpdate = key - 4; + if (value != -1) { + session.getEnchantmentSlotData()[bookSlotToUpdate].setEnchantmentType(EnchantmentTableEnchantments.values()[value - 1]); + } else { + // -1 means no enchantment specified + session.getEnchantmentSlotData()[bookSlotToUpdate].setEnchantmentType(null); + } + break; + case 7: + case 8: + case 9: + // Enchantment level + bookSlotToUpdate = key - 7; + session.getEnchantmentSlotData()[bookSlotToUpdate].setEnchantmentLevel(value); + break; + default: + return; + } + updateEnchantmentBook(session, inventory, bookSlotToUpdate); + } + @Override + public void openInventory(GeyserSession session, Inventory inventory) { + super.openInventory(session, inventory); + for (int i = 0; i < session.getEnchantmentSlotData().length; i++) { + session.getEnchantmentSlotData()[i] = new EnchantmentSlotData(); + } + } + + @Override + public void closeInventory(GeyserSession session, Inventory inventory) { + super.closeInventory(session, inventory); + Arrays.fill(session.getEnchantmentSlotData(), null); + } + + private ItemData createEnchantmentBook() { + NbtMapBuilder root = NbtMap.builder(); + NbtMapBuilder display = NbtMap.builder(); + + display.putString("Name", ChatColor.RESET + "No Enchantment"); + + root.put("display", display.build()); + return ItemData.of(ENCHANTED_BOOK_ID, (short) 0, 1, root.build()); + } + + private void updateEnchantmentBook(GeyserSession session, Inventory inventory, int slot) { + NbtMapBuilder root = NbtMap.builder(); + NbtMapBuilder display = NbtMap.builder(); + EnchantmentSlotData data = session.getEnchantmentSlotData()[slot]; + if (data.getEnchantmentType() != null) { + display.putString("Name", ChatColor.ITALIC + data.getEnchantmentType().toString(session) + + (data.getEnchantmentLevel() != -1 ? " " + toRomanNumeral(session, data.getEnchantmentLevel()) : "") + "?"); + } else { + display.putString("Name", ChatColor.RESET + "No Enchantment"); + } + + display.putList("Lore", NbtType.STRING, Collections.singletonList(ChatColor.DARK_GRAY + data.getExperienceRequired() + "xp")); + root.put("display", display.build()); + ItemData book = ItemData.of(ENCHANTED_BOOK_ID, (short) 0, 1, root.build()); + + InventorySlotPacket slotPacket = new InventorySlotPacket(); + slotPacket.setContainerId(inventory.getId()); + slotPacket.setSlot(slot + 2); + slotPacket.setItem(book); + session.sendUpstreamPacket(slotPacket); + data.setItem(book); + } + + private String toRomanNumeral(GeyserSession session, int level) { + return LocaleUtils.getLocaleString("enchantment.level." + level, + session.getClientData().getLanguageCode()); + } + + /** + * Stores the data of each slot in an enchantment table + */ + @NoArgsConstructor + @Getter + @Setter + @ToString + public static class EnchantmentSlotData { + private EnchantmentTableEnchantments enchantmentType = null; + private int enchantmentLevel = 0; + private int experienceRequired = 0; + private ItemData item; + } + + /** + * Classifies enchantments by Java order + */ + public enum EnchantmentTableEnchantments { + PROTECTION, + FIRE_PROTECTION, + FEATHER_FALLING, + BLAST_PROTECTION, + PROJECTILE_PROTECTION, + RESPIRATION, + AQUA_AFFINITY, + THORNS, + DEPTH_STRIDER, + FROST_WALKER, + BINDING_CURSE, + SHARPNESS, + SMITE, + BANE_OF_ARTHROPODS, + KNOCKBACK, + FIRE_ASPECT, + LOOTING, + SWEEPING, + EFFICIENCY, + SILK_TOUCH, + UNBREAKING, + FORTUNE, + POWER, + PUNCH, + FLAME, + INFINITY, + LUCK_OF_THE_SEA, + LURE, + LOYALTY, + IMPALING, + RIPTIDE, + CHANNELING, + MENDING, + VANISHING_CURSE, // After this is not documented + MULTISHOT, + PIERCING, + QUICK_CHARGE, + SOUL_SPEED; + + public String toString(GeyserSession session) { + return LocaleUtils.getLocaleString("enchantment.minecraft." + this.toString().toLowerCase(), + session.getClientData().getLanguageCode()); + } } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java index 7d06aed14..e44e4bd0b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/InventoryTranslator.java @@ -56,7 +56,6 @@ public abstract class InventoryTranslator { put(WindowType.GRINDSTONE, new GrindstoneInventoryTranslator()); put(WindowType.MERCHANT, new MerchantInventoryTranslator()); put(WindowType.SMITHING, new SmithingInventoryTranslator()); - //put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator()); //TODO InventoryTranslator furnace = new FurnaceInventoryTranslator(); put(WindowType.FURNACE, furnace); @@ -64,6 +63,7 @@ public abstract class InventoryTranslator { put(WindowType.SMOKER, furnace); InventoryUpdater containerUpdater = new ContainerInventoryUpdater(); + put(WindowType.ENCHANTMENT, new EnchantmentInventoryTranslator(containerUpdater)); //TODO put(WindowType.GENERIC_3X3, new BlockInventoryTranslator(9, "minecraft:dispenser[facing=north,triggered=false]", ContainerType.DISPENSER, containerUpdater)); put(WindowType.HOPPER, new BlockInventoryTranslator(5, "minecraft:hopper[enabled=false,facing=down]", ContainerType.HOPPER, containerUpdater)); put(WindowType.SHULKER_BOX, new BlockInventoryTranslator(27, "minecraft:shulker_box[facing=north]", ContainerType.CONTAINER, containerUpdater)); From ee42067d87fe3592d79bb3b2c682347dfa834ec4 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Fri, 21 Aug 2020 13:01:38 -0400 Subject: [PATCH 047/140] Don't play a sound when falling onto a block (#1175) --- .../translators/java/world/JavaSpawnParticleTranslator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnParticleTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnParticleTranslator.java index 453e08445..52d3f6493 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnParticleTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnParticleTranslator.java @@ -50,7 +50,7 @@ public class JavaSpawnParticleTranslator extends PacketTranslator Date: Fri, 21 Aug 2020 13:01:50 -0400 Subject: [PATCH 048/140] Prevent a comma from appearing if Geyser fails to bind (#1174) --- .../src/main/java/org/geysermc/connector/GeyserConnector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 8203a1689..19b2fbac8 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -184,7 +184,7 @@ public class GeyserConnector { if (throwable == null) { logger.info(LanguageUtils.getLocaleStringLog("geyser.core.start", config.getBedrock().getAddress(), String.valueOf(config.getBedrock().getPort()))); } else { - logger.severe(LanguageUtils.getLocaleStringLog("geyser.core.fail", config.getBedrock().getAddress(), config.getBedrock().getPort())); + logger.severe(LanguageUtils.getLocaleStringLog("geyser.core.fail", config.getBedrock().getAddress(), String.valueOf(config.getBedrock().getPort()))); throwable.printStackTrace(); } }).join(); From 713085adf24b2b90875e1ae148666acfaa803100 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Fri, 21 Aug 2020 13:02:09 -0400 Subject: [PATCH 049/140] Fix NPE if block data string isn't in mappings (#1172) This happens when the server version is below the current version and the block state changed. A better solution would be to use ViaVersion to translate the block state strings but this would require getting the server version and figuring out mappings from there. --- .../spigot/world/GeyserSpigotBlockPlaceListener.java | 2 +- .../platform/spigot/world/GeyserSpigotWorldManager.java | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java index 63cb3b4ab..cb59e202b 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java @@ -59,7 +59,7 @@ public class GeyserSpigotBlockPlaceListener implements Listener { } else { javaBlockId = event.getBlockPlaced().getBlockData().getAsString(); } - placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().get(javaBlockId))); + placeBlockSoundPacket.setExtraData(BlockTranslator.getBedrockBlockId(BlockTranslator.getJavaIdBlockMap().getOrDefault(javaBlockId, 0))); placeBlockSoundPacket.setIdentifier(":"); session.sendUpstreamPacket(placeBlockSoundPacket); session.setLastBlockPlacePosition(null); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java index c43d9eaba..4873a1750 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java @@ -25,7 +25,6 @@ package org.geysermc.platform.spigot.world; -import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import lombok.AllArgsConstructor; import org.bukkit.Bukkit; import org.bukkit.block.Block; @@ -49,10 +48,14 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { if (session.getPlayerEntity() == null) { return BlockTranslator.AIR; } + if (Bukkit.getPlayer(session.getPlayerEntity().getUsername()) == null) { + return BlockTranslator.AIR; + } if (isLegacy) { return getLegacyBlock(session, x, y, z, isViaVersion); } - return BlockTranslator.getJavaIdBlockMap().get(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getBlockAt(x, y, z).getBlockData().getAsString()); + //TODO possibly: detect server version for all versions and use ViaVersion for block state mappings like below + return BlockTranslator.getJavaIdBlockMap().getOrDefault(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getBlockAt(x, y, z).getBlockData().getAsString(), 0); } @SuppressWarnings("deprecation") From 2d6264d7c1f44ea04add9a2f8b5f7881dbe3b93a Mon Sep 17 00:00:00 2001 From: Jordie <30464310+jordieh@users.noreply.github.com> Date: Sat, 22 Aug 2020 22:39:40 +0200 Subject: [PATCH 050/140] Add visual support for signs colored with dye (#1180) * Fix dyed signs in Bedrock Edition Add visual support (in Bedrock Edition) for signs colored with dye (in Java Edition) * Javadoc for getBedrockSignColor(string) * Simplified getBedrockSignColor(string) --- .../entity/SignBlockEntityTranslator.java | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java index a95c853e7..ab9ba3065 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java @@ -51,6 +51,11 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator { signLine = signLine.substring(0, 14); } + // Java Edition 1.14 added the ability to change the text color of the whole sign using dye + if (tag.contains("Color")) { + signText.append(getBedrockSignColor(tag.get("Color").getValue().toString())); + } + signText.append(signLine); signText.append("\n"); } @@ -75,4 +80,66 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator { .putString("Text", "") .build(); } + + /** + * Maps a color stored in a sign's Color tag to a Bedrock Edition formatting code. + *
+ * The color names correspond to dye names, because of this we can't use {@link MessageUtils#getColor(String)}. + * + * @param javaColor The dye color stored in the sign's Color tag. + * @return A Bedrock Edition formatting code for valid dye colors, otherwise an empty string. + */ + private static String getBedrockSignColor(String javaColor) { + String base = "\u00a7"; + switch (javaColor) { + case "white": + base += 'f'; + break; + case "orange": + base += '6'; + break; + case "magenta": + case "purple": + base += '5'; + break; + case "light_blue": + base += 'b'; + break; + case "yellow": + base += 'e'; + break; + case "lime": + base += 'a'; + break; + case "pink": + base += 'd'; + break; + case "gray": + base += '8'; + break; + case "light_gray": + base += '7'; + break; + case "cyan": + base += '3'; + break; + case "blue": + base += '9'; + break; + case "brown": // Brown does not have a bedrock counterpart. + case "red": // In Java Edition light red (&c) can only be applied using commands. Red dye gives &4. + base += '4'; + break; + case "green": + base += '2'; + break; + case "black": + base += '0'; + break; + default: + return ""; + } + return base; + } + } From 73e09def06d06edd7cf7dd4893e5c10787e3974e Mon Sep 17 00:00:00 2001 From: EasyClifton <63668444+EasyClifton@users.noreply.github.com> Date: Sun, 23 Aug 2020 18:24:31 +0300 Subject: [PATCH 051/140] Add Loom to fix list (#1185) Added loom to the What's Left to be Added/Fixed list. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 92462e7ee..a34edb4fd 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set - [ ] Command Block - [ ] Structure Block - [ ] Horse Inventory + - [ ] Loom - Some Entity Flags ## Compiling From a306e9d35b0aa8b43efe01bba6749c3d7b9544af Mon Sep 17 00:00:00 2001 From: Comstepr <32700815+Comstepr@users.noreply.github.com> Date: Sun, 23 Aug 2020 23:29:02 +0800 Subject: [PATCH 052/140] Add smithing table to what's left to be added/fixed (#1187) Co-authored-by: Camotoy <20743703+DoctorMacc@users.noreply.github.com> --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a34edb4fd..ead5b3b70 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set - [ ] Structure Block - [ ] Horse Inventory - [ ] Loom + - [ ] Smithing Table - Some Entity Flags ## Compiling From 1d5b453595422ffa06bcef5bab6a5b06d9c62a70 Mon Sep 17 00:00:00 2001 From: E404NNF <37207067+E404NNF@users.noreply.github.com> Date: Sun, 23 Aug 2020 17:34:09 +0200 Subject: [PATCH 053/140] Add a warning at start about movement translation (#1069) Co-authored-by: Redned Co-authored-by: Camotoy <20743703+DoctorMacc@users.noreply.github.com> --- .../src/main/java/org/geysermc/connector/GeyserConnector.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 19b2fbac8..fb0840d3f 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -216,6 +216,10 @@ public class GeyserConnector { message += LanguageUtils.getLocaleStringLog("geyser.core.finish.console"); } logger.info(message); + + if (platformType == PlatformType.STANDALONE) { + logger.warning(LanguageUtils.getLocaleStringLog("geyser.core.movement_warn")); + } } public void shutdown() { From 8b7165a5645630b735db8713b220cc2560c5ae79 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 24 Aug 2020 09:31:21 -0400 Subject: [PATCH 054/140] Implement (hopefully) temporary dimension switching fix (#1188) This fixes rare (?) instances where dimension switching doesn't finish loading on the client. Ideally a proper fix would send the finishing packets in the correct order but I didn't get far in this regard. Fixes #1154 and #1072. Other miscellaeous chunk-related fixes have also been included here. --- .../connector/network/session/GeyserSession.java | 2 -- .../java/world/JavaChunkDataTranslator.java | 13 +------------ .../java/world/JavaSpawnPositionTranslator.java | 2 ++ .../org/geysermc/connector/utils/ChunkUtils.java | 2 -- .../geysermc/connector/utils/DimensionUtils.java | 4 ++++ 5 files changed, 7 insertions(+), 16 deletions(-) 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 a385c21c8..72dc0dac2 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 @@ -159,8 +159,6 @@ public class GeyserSession implements CommandSender { @Setter private Vector3i lastInteractionPosition; - @Setter - private boolean switchingDimension = false; private boolean manyDimPackets = false; private ServerRespawnPacket lastDimPacket = null; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java index 556d0eab3..5170929a9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java @@ -25,25 +25,22 @@ package org.geysermc.connector.network.translators.java.world; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerChunkDataPacket; import com.nukkitx.nbt.NBTOutputStream; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtUtils; import com.nukkitx.network.VarInts; import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket; - import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufOutputStream; import io.netty.buffer.Unpooled; -import it.unimi.dsi.fastutil.objects.Object2IntMap; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.BiomeTranslator; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; -import org.geysermc.connector.utils.ChunkUtils; import org.geysermc.connector.network.translators.world.chunk.ChunkSection; +import org.geysermc.connector.utils.ChunkUtils; @Translator(packet = ServerChunkDataPacket.class) public class JavaChunkDataTranslator extends PacketTranslator { @@ -99,14 +96,6 @@ public class JavaChunkDataTranslator extends PacketTranslator blockEntityEntry : chunkData.getLoadBlockEntitiesLater().object2IntEntrySet()) { - int x = blockEntityEntry.getKey().getInt("x"); - int y = blockEntityEntry.getKey().getInt("y"); - int z = blockEntityEntry.getKey().getInt("z"); - ChunkUtils.updateBlock(session, blockEntityEntry.getIntValue(), new Position(x, y, z)); - } - chunkData.getLoadBlockEntitiesLater().clear(); session.getChunkCache().addToCache(packet.getColumn()); } catch (Exception ex) { ex.printStackTrace(); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnPositionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnPositionTranslator.java index a59c71ea3..6120c737c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnPositionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaSpawnPositionTranslator.java @@ -32,6 +32,7 @@ import org.geysermc.connector.network.translators.Translator; import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerSpawnPositionPacket; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.packet.SetSpawnPositionPacket; +import org.geysermc.connector.utils.DimensionUtils; @Translator(packet = ServerSpawnPositionPacket.class) public class JavaSpawnPositionTranslator extends PacketTranslator { @@ -41,6 +42,7 @@ public class JavaSpawnPositionTranslator extends PacketTranslator loadBlockEntitiesLater = new Object2IntOpenHashMap<>(); } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java index de09ed8c6..7b283e9cb 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/DimensionUtils.java @@ -85,6 +85,10 @@ public class DimensionUtils { stopSoundPacket.setStoppingAllSound(true); stopSoundPacket.setSoundName(""); session.sendUpstreamPacket(stopSoundPacket); + + // TODO - fix this hack of a fix by sending the final dimension switching logic after chunks have been sent. + // The client wants chunks sent to it before it can successfully respawn. + ChunkUtils.sendEmptyChunks(session, player.getPosition().toInt(), 3, true); } /** From aaa3d7238defb95b7d1a11f3d181dc4c1e62ccc0 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 24 Aug 2020 10:26:37 -0400 Subject: [PATCH 055/140] BedrockEmoteTranslator: ensure sending player is valid for all other sessions (#1194) --- .../bedrock/entity/player/BedrockEmoteTranslator.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockEmoteTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockEmoteTranslator.java index e76fece0b..813c5594c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockEmoteTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockEmoteTranslator.java @@ -26,7 +26,7 @@ package org.geysermc.connector.network.translators.bedrock.entity.player; import com.nukkitx.protocol.bedrock.packet.EmotePacket; -import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.entity.Entity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; @@ -37,9 +37,12 @@ public class BedrockEmoteTranslator extends PacketTranslator { @Override public void translate(EmotePacket packet, GeyserSession session) { long javaId = session.getPlayerEntity().getEntityId(); - for (GeyserSession otherSession : GeyserConnector.getInstance().getPlayers()) { + for (GeyserSession otherSession : session.getConnector().getPlayers()) { if (otherSession != session) { - packet.setRuntimeEntityId(otherSession.getEntityCache().getEntityByJavaId(javaId).getGeyserId()); + if (otherSession.isClosed()) continue; + Entity otherEntity = otherSession.getEntityCache().getEntityByJavaId(javaId); + if (otherEntity == null) continue; + packet.setRuntimeEntityId(otherEntity.getGeyserId()); otherSession.sendUpstreamPacket(packet); } } From 65c45386b9d162ae6e93374514d7696ea67364ef Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 24 Aug 2020 22:04:25 -0400 Subject: [PATCH 056/140] Update mappings (#1196) --- connector/src/main/resources/languages | 2 +- connector/src/main/resources/mappings | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/connector/src/main/resources/languages b/connector/src/main/resources/languages index 2641db5aa..93b2caed3 160000 --- a/connector/src/main/resources/languages +++ b/connector/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 2641db5aa9100cdbe21b4493489e9be19092a600 +Subproject commit 93b2caed3c4ecd94b3c77a87f1b2304a7bf4f062 diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index 88678e69b..ec8b68297 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 88678e69bf358cd562bd410a2459384aeb7ba482 +Subproject commit ec8b68297c4d62a9e4d640a8c7e77977062628ee From 6e8106eeec7b4d68f5eb7a75df098e1fee90026e Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 24 Aug 2020 22:05:39 -0400 Subject: [PATCH 057/140] Add shulker box item tooltip translating (#1189) * Add shulker box item tooltip translating This commit adds support for previewing the items inside of a shulker box. This does not do a full translation, and only does enough to translate the item information to the client, so as to prevent any accidental item modifying/removing on creative mode. * Swap values --- .../translators/item/ItemTranslator.java | 69 +++++++------- .../item/NbtItemStackTranslator.java | 18 +++- .../translators/nbt/BasicItemTranslator.java | 3 +- .../translators/nbt/BookPagesTranslator.java | 3 +- .../translators/nbt/CrossbowTranslator.java | 3 +- .../nbt/EnchantedBookTranslator.java | 3 +- .../nbt/EnchantmentTranslator.java | 3 +- .../translators/nbt/FireworkTranslator.java | 3 +- .../nbt/LeatherArmorTranslator.java | 3 +- .../translators/nbt/MapItemTranslator.java | 3 +- .../nbt/ShulkerBoxItemTranslator.java | 90 +++++++++++++++++++ 11 files changed, 158 insertions(+), 43 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java index 37dd066ed..dce0f4b4d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java @@ -138,11 +138,13 @@ public abstract class ItemTranslator { if (nbt != null) { for (NbtItemStackTranslator translator : NBT_TRANSLATORS) { if (translator.acceptItem(bedrockItem)) { - translator.translateToBedrock(nbt, bedrockItem); + translator.translateToBedrock(session, nbt, bedrockItem); } } } + translateDisplayProperties(session, nbt); + ItemData itemData; ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS.get(bedrockItem.getJavaId()); if (itemStackTranslator != null) { @@ -151,39 +153,6 @@ public abstract class ItemTranslator { itemData = DEFAULT_TRANSLATOR.translateToBedrock(itemStack, bedrockItem); } - - // Get the display name of the item - NbtMap tag = itemData.getTag(); - if (tag != null) { - NbtMap display = tag.getCompound("display"); - if (display != null && !display.isEmpty() && display.containsKey("Name")) { - String name = display.getString("Name"); - - // If its not a message convert it - if (!MessageUtils.isMessage(name)) { - TextComponent component = LegacyComponentSerializer.legacySection().deserialize(name); - name = GsonComponentSerializer.gson().serialize(component); - } - - // Check if its a message to translate - if (MessageUtils.isMessage(name)) { - // Get the translated name - name = MessageUtils.getTranslatedBedrockMessage(MessageSerializer.fromString(name), session.getClientData().getLanguageCode()); - - // Build the new display tag - NbtMapBuilder displayBuilder = display.toBuilder(); - displayBuilder.putString("Name", name); - - // Build the new root tag - NbtMapBuilder builder = tag.toBuilder(); - builder.put("display", displayBuilder.build()); - - // Create a new item with the original data + updated name - itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), builder.build()); - } - } - } - return itemData; } @@ -375,6 +344,38 @@ public abstract class ItemTranslator { return null; } + /** + * Translates the display name of the item + * @param session the Bedrock client's session + * @param tag the tag to translate + */ + public static void translateDisplayProperties(GeyserSession session, CompoundTag tag) { + if (tag != null) { + CompoundTag display = tag.get("display"); + if (display != null && !display.isEmpty() && display.contains("Name")) { + String name = ((StringTag) display.get("Name")).getValue(); + + // If its not a message convert it + if (!MessageUtils.isMessage(name)) { + TextComponent component = LegacyComponentSerializer.legacySection().deserialize(name); + name = GsonComponentSerializer.gson().serialize(component); + } + + // Check if its a message to translate + if (MessageUtils.isMessage(name)) { + // Get the translated name + name = MessageUtils.getTranslatedBedrockMessage(MessageSerializer.fromString(name), session.getClientData().getLanguageCode()); + + // Add the new name tag + display.put(new StringTag("Name", name)); + + // Add to the new root tag + tag.put(display); + } + } + } + } + /** * Checks if an {@link ItemStack} is equal to another item stack * diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/NbtItemStackTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/NbtItemStackTranslator.java index 3c0b75b75..89d41e98d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/NbtItemStackTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/NbtItemStackTranslator.java @@ -26,17 +26,33 @@ package org.geysermc.connector.network.translators.item; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import org.geysermc.connector.network.session.GeyserSession; public class NbtItemStackTranslator { - public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) { + /** + * Translate the item NBT to Bedrock + * @param session the client's current session + * @param itemTag the item's CompoundTag + * @param itemEntry Geyser's item entry + */ + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { } + /** + * Translate the item NBT to Java. + * @param itemTag the item's CompoundTag + * @param itemEntry Geyser's item entry + */ public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) { } + /** + * @param itemEntry Geyser's item entry + * @return if the item should be processed under this class + */ public boolean acceptItem(ItemEntry itemEntry) { return true; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java index b30031ebc..e45566264 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java @@ -33,6 +33,7 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; @@ -45,7 +46,7 @@ import java.util.List; public class BasicItemTranslator extends NbtItemStackTranslator { @Override - public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { if (!itemTag.contains("display")) { return; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java index e802f0174..41ee4fbca 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BookPagesTranslator.java @@ -29,6 +29,7 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; import com.github.steveice10.opennbt.tag.builtin.Tag; +import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; import org.geysermc.connector.network.translators.item.ItemEntry; @@ -41,7 +42,7 @@ import java.util.List; public class BookPagesTranslator extends NbtItemStackTranslator { @Override - public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { if (!itemTag.contains("pages")) { return; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java index 3c5271ebf..979c5a205 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java @@ -29,6 +29,7 @@ import com.github.steveice10.opennbt.tag.builtin.ByteTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; +import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; import org.geysermc.connector.network.translators.item.ItemEntry; @@ -37,7 +38,7 @@ import org.geysermc.connector.network.translators.item.ItemEntry; public class CrossbowTranslator extends NbtItemStackTranslator { @Override - public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { if (itemTag.get("ChargedProjectiles") != null) { ListTag chargedProjectiles = itemTag.get("ChargedProjectiles"); if (!chargedProjectiles.getValue().isEmpty()) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantedBookTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantedBookTranslator.java index 63ac6c510..990d5a7ad 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantedBookTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantedBookTranslator.java @@ -28,6 +28,7 @@ package org.geysermc.connector.network.translators.item.translators.nbt; 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 org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; import org.geysermc.connector.network.translators.item.ItemEntry; @@ -36,7 +37,7 @@ import org.geysermc.connector.network.translators.item.ItemEntry; public class EnchantedBookTranslator extends NbtItemStackTranslator { @Override - public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { if (!itemTag.contains("StoredEnchantments")) { return; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantmentTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantmentTranslator.java index 6bd9cc77e..6884c00ba 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantmentTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/EnchantmentTranslator.java @@ -27,6 +27,7 @@ package org.geysermc.connector.network.translators.item.translators.nbt; import com.github.steveice10.opennbt.tag.builtin.*; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; import org.geysermc.connector.network.translators.item.Enchantment; @@ -40,7 +41,7 @@ import java.util.Map; public class EnchantmentTranslator extends NbtItemStackTranslator { @Override - public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { List newTags = new ArrayList<>(); if (itemTag.contains("Enchantments")) { ListTag enchantmentTag = itemTag.get("Enchantments"); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java index 0e23169f8..6023d77de 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java @@ -26,6 +26,7 @@ package org.geysermc.connector.network.translators.item.translators.nbt; import com.github.steveice10.opennbt.tag.builtin.*; +import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; @@ -36,7 +37,7 @@ import org.geysermc.connector.utils.MathUtils; public class FireworkTranslator extends NbtItemStackTranslator { @Override - public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { if (!itemTag.contains("Fireworks")) { return; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java index 9f864ccf4..93af3e709 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/LeatherArmorTranslator.java @@ -27,6 +27,7 @@ package org.geysermc.connector.network.translators.item.translators.nbt; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.IntTag; +import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; import org.geysermc.connector.network.translators.item.ItemEntry; @@ -37,7 +38,7 @@ public class LeatherArmorTranslator extends NbtItemStackTranslator { private static final String[] ITEMS = new String[]{"minecraft:leather_helmet", "minecraft:leather_chestplate", "minecraft:leather_leggings", "minecraft:leather_boots"}; @Override - public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { if (!itemTag.contains("display")) { return; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java index 4a36880c9..c9b49efd4 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/MapItemTranslator.java @@ -29,6 +29,7 @@ import com.github.steveice10.opennbt.tag.builtin.ByteTag; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.IntTag; import com.github.steveice10.opennbt.tag.builtin.LongTag; +import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; @@ -37,7 +38,7 @@ import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; public class MapItemTranslator extends NbtItemStackTranslator { @Override - public void translateToBedrock(CompoundTag itemTag, ItemEntry itemEntry) { + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { IntTag mapId = itemTag.get("map"); if (mapId != null) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java new file mode 100644 index 000000000..1780537c5 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java @@ -0,0 +1,90 @@ +/* + * 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.item.translators.nbt; + +import com.github.steveice10.opennbt.tag.builtin.*; +import com.nukkitx.protocol.bedrock.packet.StartGamePacket; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.ItemRemapper; +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.item.NbtItemStackTranslator; + +@ItemRemapper +public class ShulkerBoxItemTranslator extends NbtItemStackTranslator { + + @Override + public void translateToBedrock(GeyserSession session, CompoundTag itemTag, ItemEntry itemEntry) { + if (!itemTag.contains("BlockEntityTag")) return; // Empty shulker box + + CompoundTag blockEntityTag = itemTag.get("BlockEntityTag"); + ListTag itemsList = new ListTag("Items"); + for (Tag item : (ListTag) blockEntityTag.get("Items")) { + CompoundTag itemData = (CompoundTag) item; // Information about the item + CompoundTag boxItemTag = new CompoundTag(""); // Final item tag to add to the list + boxItemTag.put(new ByteTag("Slot", ((ByteTag) itemData.get("Slot")).getValue())); + boxItemTag.put(new ByteTag("WasPickedUp", (byte) 0)); // ??? + + ItemEntry boxItemEntry = ItemRegistry.getItemEntry(((StringTag) itemData.get("id")).getValue()); + String blockName = ""; + for (StartGamePacket.ItemEntry startGamePacketItemEntry : ItemRegistry.ITEMS) { + if (startGamePacketItemEntry.getId() == (short) boxItemEntry.getBedrockId()) { + blockName = startGamePacketItemEntry.getIdentifier(); // Find the Bedrock string name + break; + } + } + + boxItemTag.put(new StringTag("Name", blockName)); + boxItemTag.put(new ShortTag("Damage", (short) boxItemEntry.getBedrockData())); + boxItemTag.put(new ByteTag("Count", ((ByteTag) itemData.get("Count")).getValue())); + if (itemData.contains("tag")) { + // Only the display name is what we have interest in, so just translate that if relevant + CompoundTag displayTag = itemData.get("tag"); + ItemTranslator.translateDisplayProperties(session, displayTag); + boxItemTag.put(displayTag); + } + + itemsList.add(boxItemTag); + } + itemTag.put(itemsList); + // Don't actually bother with removing the block entity tag. Too risky to translate + // if the user is on creative and messing with a shulker box + //itemTag.remove("BlockEntityTag"); + } + + @Override + public void translateToJava(CompoundTag itemTag, ItemEntry itemEntry) { + if (itemTag.contains("Items")) { // Remove any extraneous Bedrock tag and don't touch the Java one + itemTag.remove("Items"); + } + } + + @Override + public boolean acceptItem(ItemEntry itemEntry) { + return itemEntry.getJavaIdentifier().contains("shulker_box"); + } +} From c1a70c77545cac5ddc5b82dfc4422759cb7c3277 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 24 Aug 2020 22:14:44 -0400 Subject: [PATCH 058/140] Translate client-computed recipes (#1181) * Translate client-computed recipes A handful of recipes are complex enough on Java Edition that the client simply calculates them after getting an assurance that they are valid recipes. This PR stores those recipes in a Bedrock-compatible format in mappings, then generates the CraftingData information on startup to send to the Bedrock client when called. This fixes firework rocket and star crafting, and fixes leather armor and shulker box dyeing. The recipe information for everything except leather armor was taken right from the Bedrock server. The leather armor had to be created separately (see https://github.com/DoctorMacc/LeatherDyeingCreation). There will be a slight visual difference in the crafting result preview if the armor is not perfectly dyed to one of the sixteen colors, but this is a visual issue that will persist unless we calculate every single possbile combination. * Revert other changes * Register shulker box recipes properly * Add break * Update mappings --- .../geysermc/connector/GeyserConnector.java | 2 + .../translators/item/ItemRegistry.java | 44 ++++--- .../translators/item/RecipeRegistry.java | 117 ++++++++++++++++++ .../translators/nbt/FireworkTranslator.java | 3 + .../java/JavaDeclareRecipesTranslator.java | 23 +++- 5 files changed, 170 insertions(+), 19 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index fb0840d3f..ec332ed66 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -47,6 +47,7 @@ import org.geysermc.connector.network.translators.effect.EffectRegistry; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.network.translators.item.PotionMixRegistry; +import org.geysermc.connector.network.translators.item.RecipeRegistry; import org.geysermc.connector.network.translators.sound.SoundHandlerRegistry; import org.geysermc.connector.network.translators.sound.SoundRegistry; import org.geysermc.connector.network.translators.world.WorldManager; @@ -131,6 +132,7 @@ public class GeyserConnector { ItemTranslator.init(); LocaleUtils.init(); PotionMixRegistry.init(); + RecipeRegistry.init(); SoundRegistry.init(); SoundHandlerRegistry.init(); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java index 4828fbf27..56ed2d6e0 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java @@ -173,21 +173,8 @@ public class ItemRegistry { int netId = 1; List creativeItems = new ArrayList<>(); for (JsonNode itemNode : creativeItemEntries) { - try { - short damage = 0; - NbtMap tag = null; - if (itemNode.has("damage")) { - damage = itemNode.get("damage").numberValue().shortValue(); - } - if (itemNode.has("nbt_b64")) { - byte[] bytes = Base64.getDecoder().decode(itemNode.get("nbt_b64").asText()); - ByteArrayInputStream bais = new ByteArrayInputStream(bytes); - tag = (NbtMap) NbtUtils.createReaderLE(bais).readTag(); - } - creativeItems.add(ItemData.fromNet(netId++, itemNode.get("id").asInt(), damage, 1, tag)); - } catch (IOException e) { - e.printStackTrace(); - } + ItemData item = getBedrockItemFromJson(itemNode); + creativeItems.add(ItemData.fromNet(netId++, item.getId(), item.getDamage(), item.getCount(), item.getTag())); } CREATIVE_ITEMS = creativeItems.toArray(new ItemData[0]); } @@ -233,4 +220,31 @@ public class ItemRegistry { return JAVA_IDENTIFIER_MAP.computeIfAbsent(javaIdentifier, key -> ITEM_ENTRIES.values() .stream().filter(itemEntry -> itemEntry.getJavaIdentifier().equals(key)).findFirst().orElse(null)); } + + /** + * Gets a Bedrock {@link ItemData} from a {@link JsonNode} + * @param itemNode the JSON node that contains ProxyPass-compatible Bedrock item data + * @return + */ + public static ItemData getBedrockItemFromJson(JsonNode itemNode) { + int count = 1; + short damage = 0; + NbtMap tag = null; + if (itemNode.has("damage")) { + damage = itemNode.get("damage").numberValue().shortValue(); + } + if (itemNode.has("count")) { + count = itemNode.get("count").asInt(); + } + if (itemNode.has("nbt_b64")) { + byte[] bytes = Base64.getDecoder().decode(itemNode.get("nbt_b64").asText()); + ByteArrayInputStream bais = new ByteArrayInputStream(bytes); + try { + tag = (NbtMap) NbtUtils.createReaderLE(bais).readTag(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return ItemData.of(itemNode.get("id").asInt(), damage, count, tag); + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java new file mode 100644 index 000000000..191b285c6 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/RecipeRegistry.java @@ -0,0 +1,117 @@ +/* + * 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.item; + +import com.fasterxml.jackson.databind.JsonNode; +import com.nukkitx.protocol.bedrock.data.inventory.CraftingData; +import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.utils.FileUtils; +import org.geysermc.connector.utils.LanguageUtils; + +import java.io.InputStream; +import java.util.List; +import java.util.UUID; + +/** + * Manages any recipe-related storing + */ +public class RecipeRegistry { + + /** + * A list of all possible leather armor dyeing recipes. + * Created manually. + */ + public static List LEATHER_DYEING_RECIPES = new ObjectArrayList<>(); + /** + * A list of all possible firework rocket recipes, including the base rocket. + * Obtained from a ProxyPass dump of protocol v407 + */ + public static List FIREWORK_ROCKET_RECIPES = new ObjectArrayList<>(21); + /** + * A list of all possible firework star recipes. + * Obtained from a ProxyPass dump of protocol v407 + */ + public static List FIREWORK_STAR_RECIPES = new ObjectArrayList<>(40); + /** + * A list of all possible shulker box dyeing options. + * Obtained from a ProxyPass dump of protocol v407 + */ + public static List SHULKER_BOX_DYEING_RECIPES = new ObjectArrayList<>(); + + static { + // Get all recipes that are not directly sent from a Java server + InputStream stream = FileUtils.getResource("mappings/recipes.json"); + + JsonNode items; + try { + items = GeyserConnector.JSON_MAPPER.readTree(stream); + } catch (Exception e) { + throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.runtime_java"), e); + } + + for (JsonNode entry: items.get("leather_armor")) { + // This won't be perfect, as we can't possibly send every leather input for every kind of color + // But it does display the correct output from a base leather armor, and besides visuals everything works fine + LEATHER_DYEING_RECIPES.add(getCraftingDataFromJsonNode(entry)); + } + for (JsonNode entry : items.get("firework_rockets")) { + FIREWORK_ROCKET_RECIPES.add(getCraftingDataFromJsonNode(entry)); + } + for (JsonNode entry : items.get("firework_stars")) { + FIREWORK_STAR_RECIPES.add(getCraftingDataFromJsonNode(entry)); + } + for (JsonNode entry : items.get("shulker_boxes")) { + SHULKER_BOX_DYEING_RECIPES.add(getCraftingDataFromJsonNode(entry)); + } + } + + /** + * Computes a Bedrock crafting recipe from the given JSON data. + * @param node the JSON data to compute + * @return the {@link CraftingData} to send to the Bedrock client. + */ + private static CraftingData getCraftingDataFromJsonNode(JsonNode node) { + ItemData output = ItemRegistry.getBedrockItemFromJson(node.get("output").get(0)); + List inputs = new ObjectArrayList<>(); + for (JsonNode entry : node.get("input")) { + inputs.add(ItemRegistry.getBedrockItemFromJson(entry)); + } + UUID uuid = UUID.randomUUID(); + if (node.get("type").asInt() == 5) { + // Shulker box + return CraftingData.fromShulkerBox(uuid.toString(), + inputs.toArray(new ItemData[0]), new ItemData[]{output}, uuid, "crafting_table", 0); + } + return CraftingData.fromShapeless(uuid.toString(), + inputs.toArray(new ItemData[0]), new ItemData[]{output}, uuid, "crafting_table", 0); + } + + public static void init() { + // no-op + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java index 6023d77de..3b453ea18 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/FireworkTranslator.java @@ -107,6 +107,9 @@ public class FireworkTranslator extends NbtItemStackTranslator { fireworks.put(new ByteTag("Flight", MathUtils.convertByte(fireworks.get("Flight").getValue()))); } + if (!itemTag.contains("Explosions")) { + return; + } ListTag explosions = fireworks.get("Explosions"); for (Tag effect : explosions.getValue()) { CompoundTag effectData = (CompoundTag) effect; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java index 75ccc0a5a..9ffb4f0d9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareRecipesTranslator.java @@ -41,10 +41,7 @@ import lombok.EqualsAndHashCode; 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.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ItemRegistry; -import org.geysermc.connector.network.translators.item.ItemTranslator; -import org.geysermc.connector.network.translators.item.PotionMixRegistry; +import org.geysermc.connector.network.translators.item.*; import java.util.*; import java.util.stream.Collectors; @@ -83,6 +80,24 @@ public class JavaDeclareRecipesTranslator extends PacketTranslator Date: Tue, 25 Aug 2020 14:29:55 +0100 Subject: [PATCH 059/140] Add Server Name config option (#1170) * Add bedrock.server-name config option * Fix spelling mistake oops! * Remove trailing whitespace --- .../geysermc/platform/sponge/GeyserSpongeConfiguration.java | 5 +++++ .../connector/configuration/GeyserConfiguration.java | 2 ++ .../connector/configuration/GeyserJacksonConfiguration.java | 4 ++++ .../geysermc/connector/network/session/GeyserSession.java | 6 ++++-- connector/src/main/resources/config.yml | 2 ++ 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java index 35bbf97d5..ba4161105 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java @@ -205,6 +205,11 @@ public class GeyserSpongeConfiguration implements GeyserConfiguration { public String getMotd2() { return node.getNode("motd2").getString("GeyserMC"); } + + @Override + public String getServerName() { + return node.getNode("server-name").getString("Geyser"); + } } @AllArgsConstructor 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 2cf9a181e..5803ff13b 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java @@ -95,6 +95,8 @@ public interface GeyserConfiguration { String getMotd1(); String getMotd2(); + + String getServerName(); } interface IRemoteConfiguration { 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 8390640f9..378eba681 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java @@ -29,6 +29,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import lombok.Setter; +import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.common.serializer.AsteriskSerializer; import java.nio.file.Path; @@ -118,6 +119,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration private String motd1; private String motd2; + + @JsonProperty("server-name") + private String serverName = GeyserConnector.NAME; } @Getter 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 72dc0dac2..b208aaf3c 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 @@ -590,8 +590,10 @@ public class GeyserSession implements CommandSender { startGamePacket.setFromWorldTemplate(false); startGamePacket.setWorldTemplateOptionLocked(false); - startGamePacket.setLevelId("world"); - startGamePacket.setLevelName("world"); + String serverName = connector.getConfig().getBedrock().getServerName(); + startGamePacket.setLevelId(serverName); + startGamePacket.setLevelName(serverName); + startGamePacket.setPremiumWorldTemplateId("00000000-0000-0000-0000-000000000000"); // startGamePacket.setCurrentTick(0); startGamePacket.setEnchantmentSeed(0); diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index f59939b6a..af5881858 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -20,6 +20,8 @@ bedrock: # The MOTD that will be broadcasted to Minecraft: Bedrock Edition clients. Irrelevant if "passthrough-motd" is set to true motd1: "GeyserMC" motd2: "Another GeyserMC forced host." + # The Server Name that will be sent to Minecraft: Bedrock Edition clients. Visible in both the pause menu and the settings menu. + server-name: "Geyser" remote: # The IP address of the remote (Java Edition) server # If it is "auto", for standalone version the remote address will be set to 127.0.0.1, From 5458a85ed79f2980d79904416ef765852819e9dc Mon Sep 17 00:00:00 2001 From: abeshi-softwire <67909514+abeshi-softwire@users.noreply.github.com> Date: Tue, 25 Aug 2020 22:39:51 +0100 Subject: [PATCH 060/140] Adding option to use different config file for standalone (#1102) * Added config option to standalone Geyser * Cleanup * Added --gui, --nogui options * Made new options read default config.yml from correct place internally * Changed to locale strings rather than hardcoded English * Using separate options texts * Changed '-c' to be string parameter so it isn't translated --- .../standalone/GeyserStandaloneBootstrap.java | 58 ++++++++++++++++--- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java index 35cc48610..123a9a600 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneBootstrap.java @@ -49,6 +49,7 @@ import java.io.IOException; import java.lang.reflect.Method; import java.nio.file.Path; import java.nio.file.Paths; +import java.text.MessageFormat; import java.util.UUID; public class GeyserStandaloneBootstrap implements GeyserBootstrap { @@ -62,22 +63,61 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { @Getter private boolean useGui = System.console() == null && !isHeadless(); + private String configFilename = "config.yml"; private GeyserConnector connector; + public static void main(String[] args) { - for (String arg : args) { + GeyserStandaloneBootstrap bootstrap = new GeyserStandaloneBootstrap(); + // Set defaults + boolean useGuiOpts = bootstrap.useGui; + String configFilenameOpt = bootstrap.configFilename; + + for (int i = 0; i < args.length; i++) { // By default, standalone Geyser will check if it should open the GUI based on if the GUI is null // Optionally, you can force the use of a GUI or no GUI by specifying args - if (arg.equals("gui")) { - new GeyserStandaloneBootstrap().onEnable(true); - return; - } else if (arg.equals("nogui")) { - new GeyserStandaloneBootstrap().onEnable(false); - return; + // Allows gui and nogui without options, for backwards compatibility + String arg = args[i]; + switch (arg) { + case "--gui": + case "gui": + useGuiOpts = true; + break; + case "--nogui": + case "nogui": + useGuiOpts = false; + break; + case "--config": + case "-c": + if (i >= args.length - 1) { + System.err.println(MessageFormat.format(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.confignotspecified"), "-c")); + return; + } + configFilenameOpt = args[i+1]; i++; + System.out.println(MessageFormat.format(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.configspecified"), configFilenameOpt)); + break; + case "--help": + case "-h": + System.out.println(MessageFormat.format(LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.usage"), "[java -jar] Geyser.jar [opts]")); + System.out.println(" " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.options")); + System.out.println(" -c, --config [file] " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.config")); + System.out.println(" -h, --help " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.help")); + System.out.println(" --gui, --nogui " + LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.gui")); + return; + default: + String badArgMsg = LanguageUtils.getLocaleStringLog("geyser.bootstrap.args.unrecognised"); + System.err.println(MessageFormat.format(badArgMsg, arg)); + return; } } - new GeyserStandaloneBootstrap().onEnable(); + bootstrap.onEnable(useGuiOpts, configFilenameOpt); + } + + public void onEnable(boolean useGui, String configFilename) { + this.configFilename = configFilename; + this.useGui = useGui; + this.onEnable(); } public void onEnable(boolean useGui) { @@ -106,7 +146,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap { LoopbackUtil.checkLoopback(geyserLogger); try { - File configFile = FileUtils.fileOrCopiedFromResource("config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString())); + File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString())); geyserConfig = FileUtils.loadConfig(configFile, GeyserStandaloneConfiguration.class); if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { geyserConfig.setAutoconfiguredRemote(true); // Doesn't really need to be set but /shrug From 7cbfdcf52192479be2eda88cf6c3aae20b72fc05 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Fri, 28 Aug 2020 16:29:48 +0100 Subject: [PATCH 061/140] Fix reflections relocation (#1199) --- bootstrap/bungeecord/pom.xml | 2 +- bootstrap/spigot/pom.xml | 2 +- bootstrap/sponge/pom.xml | 2 +- bootstrap/velocity/pom.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bootstrap/bungeecord/pom.xml b/bootstrap/bungeecord/pom.xml index 8497b9684..682690a21 100644 --- a/bootstrap/bungeecord/pom.xml +++ b/bootstrap/bungeecord/pom.xml @@ -66,7 +66,7 @@ org.geysermc.platform.bungeecord.shaded.netty - org.reflections.reflections + org.reflections org.geysermc.platform.bungeecord.shaded.reflections diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml index 422b2769b..8b3ad7428 100644 --- a/bootstrap/spigot/pom.xml +++ b/bootstrap/spigot/pom.xml @@ -76,7 +76,7 @@ org.geysermc.platform.spigot.shaded.jackson - org.reflections.reflections + org.reflections org.geysermc.platform.spigot.shaded.reflections diff --git a/bootstrap/sponge/pom.xml b/bootstrap/sponge/pom.xml index f3c898084..c21a3a539 100644 --- a/bootstrap/sponge/pom.xml +++ b/bootstrap/sponge/pom.xml @@ -70,7 +70,7 @@ org.geysermc.platform.sponge.shaded.fastutil - org.reflections.reflections + org.reflections org.geysermc.platform.sponge.shaded.reflections diff --git a/bootstrap/velocity/pom.xml b/bootstrap/velocity/pom.xml index b08e5cbc5..0ebb70cab 100644 --- a/bootstrap/velocity/pom.xml +++ b/bootstrap/velocity/pom.xml @@ -62,7 +62,7 @@ org.geysermc.platform.velocity.shaded.fastutil - org.reflections.reflections + org.reflections org.geysermc.platform.velocity.shaded.reflections From 1c849938539ed919f184d4243fd674728ca65127 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Fri, 28 Aug 2020 17:47:52 +0200 Subject: [PATCH 062/140] Scoreboard improvements (#1166) * Added a way to check if debug logging is enabled * Improved scoreboard performance * Include Teams in pps and return pending pps instead when higher then pps Some servers have a huge amount of score packets when the player logs in, but before this commit, only after the first high pps (packets per second) the ScoreboardUpdater will be used (after pending packets per second have been moved to packets per second). But this commit fixes that the ScoreboardUpdater can be used on the second that the pps is getting high. * Fixed team pre + suffix "null" issue and added threshold config option Fixed team pre + suffix "null" issue. When the prefix and/or suffix of a Team is null, "null" will be returned instead of null (Due to the way that MCProtocolLib is made and designed). This is fixed by simply checking if the prefix and/or suffix equal "null" and if that is the case, replace it with "". Added threshold option. Gave the person who is running Geyser an option to specify the first Scoreboard packets per second threshold to further improve performance by lowering the setting or decrease performance by relaxing the setting a bit. The value can't be higher then 250 (the second threshold), because it'll always choose the lowest threshold. * Forgot to bump config version * Small changes * Reverted version bump, changed Sponge config, changed FloodgateKeyLoader Reverted version bump Camotoy said that you only need to bump the config version if the change is breaking, the config version bump has been reverted. Changed Sponge config The Sponge config has been modified to look like the other platform configurations. Changed FloodgateKeyLoader * Changed default-locale and (remote) address as requested by Camotoy * Reduce bandwidth and a few final tweaks * Made the scoreboard-packet-threshold a bit higher due to improvements --- .../bungeecord/GeyserBungeeConfiguration.java | 17 +- .../bungeecord/GeyserBungeeLogger.java | 20 +- .../bungeecord/GeyserBungeePlugin.java | 7 +- .../spigot/GeyserSpigotConfiguration.java | 18 +- .../platform/spigot/GeyserSpigotLogger.java | 17 +- .../sponge/GeyserSpongeConfiguration.java | 263 +----------------- .../platform/sponge/GeyserSpongeLogger.java | 17 +- .../platform/sponge/GeyserSpongePlugin.java | 16 +- .../GeyserStandaloneConfiguration.java | 11 +- .../standalone/GeyserStandaloneLogger.java | 12 +- .../velocity/GeyserVelocityConfiguration.java | 19 +- .../velocity/GeyserVelocityLogger.java | 17 +- .../connector/FloodgateKeyLoader.java | 8 +- .../org/geysermc/connector/GeyserLogger.java | 5 + .../configuration/GeyserConfiguration.java | 5 +- .../GeyserJacksonConfiguration.java | 60 ++-- .../connector/entity/PlayerEntity.java | 17 +- .../network/session/GeyserSession.java | 6 +- .../network/session/UpstreamSession.java | 14 +- .../network/session/cache/WorldCache.java | 23 +- .../JavaScoreboardObjectiveTranslator.java | 10 +- .../java/scoreboard/JavaTeamTranslator.java | 60 ++-- .../scoreboard/JavaUpdateScoreTranslator.java | 60 ++-- .../connector/scoreboard/Objective.java | 81 +++--- .../geysermc/connector/scoreboard/Score.java | 39 ++- .../connector/scoreboard/Scoreboard.java | 178 ++++++------ .../scoreboard/ScoreboardUpdater.java | 121 ++++++++ .../geysermc/connector/scoreboard/Team.java | 38 ++- connector/src/main/resources/config.yml | 6 + 29 files changed, 549 insertions(+), 616 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/scoreboard/ScoreboardUpdater.java diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java index d9b86a2e8..00b091d1f 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeConfiguration.java @@ -29,27 +29,22 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Getter; import net.md_5.bungee.api.plugin.Plugin; -import net.md_5.bungee.config.Configuration; import org.geysermc.connector.FloodgateKeyLoader; import org.geysermc.connector.configuration.GeyserJacksonConfiguration; import java.nio.file.Path; -import java.nio.file.Paths; @Getter @JsonIgnoreProperties(ignoreUnknown = true) -public class GeyserBungeeConfiguration extends GeyserJacksonConfiguration { - +public final class GeyserBungeeConfiguration extends GeyserJacksonConfiguration { @JsonIgnore - private Path floodgateKey; + private Path floodgateKeyPath; - public void loadFloodgate(GeyserBungeePlugin plugin, Configuration configuration) { + public void loadFloodgate(GeyserBungeePlugin plugin) { Plugin floodgate = plugin.getProxy().getPluginManager().getPlugin("floodgate-bungee"); - floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(plugin.getDataFolder().toString(), configuration.getString("floodgate-key-file"), "public-key.pem"), floodgate, floodgate != null ? floodgate.getDataFolder().toPath() : null); - } + Path geyserDataFolder = plugin.getDataFolder().toPath(); + Path floodgateDataFolder = floodgate != null ? floodgate.getDataFolder().toPath() : null; - @Override - public Path getFloodgateKeyFile() { - return floodgateKey; + floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgate, floodgateDataFolder, geyserDataFolder, plugin.getGeyserLogger()); } } diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeLogger.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeLogger.java index cd07b333d..e40f404c1 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeLogger.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeeLogger.java @@ -25,19 +25,21 @@ package org.geysermc.platform.bungeecord; +import lombok.Getter; +import lombok.Setter; import org.geysermc.connector.GeyserLogger; import java.util.logging.Level; import java.util.logging.Logger; public class GeyserBungeeLogger implements GeyserLogger { + private final Logger logger; + @Getter @Setter + private boolean debug; - private Logger logger; - private boolean debugMode; - - public GeyserBungeeLogger(Logger logger, boolean debugMode) { + public GeyserBungeeLogger(Logger logger, boolean debug) { this.logger = logger; - this.debugMode = debugMode; + this.debug = debug; } @Override @@ -72,12 +74,8 @@ public class GeyserBungeeLogger implements GeyserLogger { @Override public void debug(String message) { - if (debugMode) + if (debug) { info(message); - } - - @Override - public void setDebug(boolean debug) { - debugMode = debug; + } } } diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java index e80207d3f..abb9789e1 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/GeyserBungeePlugin.java @@ -27,9 +27,6 @@ package org.geysermc.platform.bungeecord; import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.api.plugin.Plugin; -import net.md_5.bungee.config.Configuration; -import net.md_5.bungee.config.ConfigurationProvider; -import net.md_5.bungee.config.YamlConfiguration; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.command.CommandManager; @@ -64,13 +61,11 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { if (!getDataFolder().exists()) getDataFolder().mkdir(); - Configuration configuration = null; try { if (!getDataFolder().exists()) getDataFolder().mkdir(); File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString())); this.geyserConfig = FileUtils.loadConfig(configFile, GeyserBungeeConfiguration.class); - configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(new File(getDataFolder(), "config.yml")); } catch (IOException ex) { getLogger().log(Level.WARNING, LanguageUtils.getLocaleStringLog("geyser.config.failed"), ex); ex.printStackTrace(); @@ -108,7 +103,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { geyserConfig.getRemote().setAuthType("floodgate"); } - geyserConfig.loadFloodgate(this, configuration); + geyserConfig.loadFloodgate(this); this.connector = GeyserConnector.start(PlatformType.BUNGEECORD, this); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotConfiguration.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotConfiguration.java index 380f70376..5c48efe88 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotConfiguration.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotConfiguration.java @@ -27,7 +27,6 @@ package org.geysermc.platform.spigot; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; @@ -35,26 +34,19 @@ import org.geysermc.connector.FloodgateKeyLoader; import org.geysermc.connector.configuration.GeyserJacksonConfiguration; import java.nio.file.Path; -import java.nio.file.Paths; @Getter @JsonIgnoreProperties(ignoreUnknown = true) -public class GeyserSpigotConfiguration extends GeyserJacksonConfiguration { - - @JsonProperty("floodgate-key-file") - private String floodgateKeyFile; - +public final class GeyserSpigotConfiguration extends GeyserJacksonConfiguration { @JsonIgnore - private Path floodgateKey; + private Path floodgateKeyPath; public void loadFloodgate(GeyserSpigotPlugin plugin) { Plugin floodgate = Bukkit.getPluginManager().getPlugin("floodgate-bukkit"); - floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(plugin.getDataFolder().toString(), plugin.getConfig().getString("floodgate-key-file", "public-key.pem")), floodgate, floodgate != null ? floodgate.getDataFolder().toPath() : null); - } + Path geyserDataFolder = plugin.getDataFolder().toPath(); + Path floodgateDataFolder = floodgate != null ? floodgate.getDataFolder().toPath() : null; - @Override - public Path getFloodgateKeyFile() { - return floodgateKey; + floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgate, floodgateDataFolder, geyserDataFolder, plugin.getGeyserLogger()); } @Override diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotLogger.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotLogger.java index 252d6bbed..b462f1f1c 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotLogger.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotLogger.java @@ -26,7 +26,8 @@ package org.geysermc.platform.spigot; import lombok.AllArgsConstructor; - +import lombok.Getter; +import lombok.Setter; import org.geysermc.connector.GeyserLogger; import java.util.logging.Level; @@ -34,9 +35,9 @@ import java.util.logging.Logger; @AllArgsConstructor public class GeyserSpigotLogger implements GeyserLogger { - - private Logger logger; - private boolean debugMode; + private final Logger logger; + @Getter @Setter + private boolean debug; @Override public void severe(String message) { @@ -70,12 +71,8 @@ public class GeyserSpigotLogger implements GeyserLogger { @Override public void debug(String message) { - if (debugMode) + if (debug) { info(message); - } - - @Override - public void setDebug(boolean debug) { - debugMode = debug; + } } } diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java index ba4161105..2d5eefebd 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeConfiguration.java @@ -25,268 +25,13 @@ package org.geysermc.platform.sponge; -import lombok.AllArgsConstructor; -import ninja.leaping.configurate.ConfigurationNode; -import org.geysermc.connector.configuration.GeyserConfiguration; +import org.geysermc.connector.configuration.GeyserJacksonConfiguration; -import java.io.File; import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.*; - -public class GeyserSpongeConfiguration implements GeyserConfiguration { - - private File dataFolder; - private ConfigurationNode node; - - /** - * If the config was originally 'auto' before the values changed - */ - private boolean autoconfiguredRemote = false; - - private SpongeBedrockConfiguration bedrockConfig; - private SpongeRemoteConfiguration remoteConfig; - private SpongeMetricsInfo metricsInfo; - - private Map userAuthInfo = new HashMap<>(); - - public GeyserSpongeConfiguration(File dataFolder, ConfigurationNode node) { - this.dataFolder = dataFolder; - this.node = node; - - this.bedrockConfig = new SpongeBedrockConfiguration(node.getNode("bedrock")); - this.remoteConfig = new SpongeRemoteConfiguration(node.getNode("remote")); - this.metricsInfo = new SpongeMetricsInfo(); - - if (node.getNode("userAuths").getValue() == null) - return; - - List userAuths = new ArrayList(((LinkedHashMap)node.getNode("userAuths").getValue()).keySet()); - for (String key : userAuths) { - userAuthInfo.put(key, new SpongeUserAuthenticationInfo(key)); - } - } - - public void setAutoconfiguredRemote(boolean autoconfiguredRemote) { - this.autoconfiguredRemote = autoconfiguredRemote; - } +public final class GeyserSpongeConfiguration extends GeyserJacksonConfiguration { @Override - public SpongeBedrockConfiguration getBedrock() { - return bedrockConfig; - } - - @Override - public SpongeRemoteConfiguration getRemote() { - return remoteConfig; - } - - @Override - public Map getUserAuths() { - return userAuthInfo; - } - - @Override - public boolean isCommandSuggestions() { - return node.getNode("command-suggestions").getBoolean(true); - } - - @Override - public boolean isPassthroughMotd() { - return node.getNode("passthrough-motd").getBoolean(false); - } - - @Override - public boolean isPassthroughProtocolName() { - return node.getNode("passthrough-protocol-name").getBoolean(false); - } - - @Override - public boolean isPassthroughPlayerCounts() { - return node.getNode("passthrough-player-counts").getBoolean(false); - } - - @Override - public boolean isLegacyPingPassthrough() { - return node.getNode("legacy-ping-passthrough").getBoolean(false); - } - - @Override - public int getPingPassthroughInterval() { - return node.getNode("ping-passthrough-interval").getInt(3); - } - - @Override - public int getMaxPlayers() { - return node.getNode("max-players").getInt(100); - } - - @Override - public boolean isDebugMode() { - return node.getNode("debug-mode").getBoolean(false); - } - - @Override - public int getGeneralThreadPool() { - return node.getNode("genereal-thread-pool").getInt(32); - } - - @Override - public boolean isAllowThirdPartyCapes() { - return node.getNode("allow-third-party-capes").getBoolean(true); - } - - @Override - public boolean isAllowThirdPartyEars() { - return node.getNode("allow-third-party-ears").getBoolean(false); - } - - @Override - public boolean isShowCooldown() { - return node.getNode("show-cooldown").getBoolean(true); - } - - @Override - public String getDefaultLocale() { - return node.getNode("default-locale").getString("en_us"); - } - - @Override - public Path getFloodgateKeyFile() { - return Paths.get(dataFolder.toString(), node.getNode("floodgate-key-file").getString("public-key.pem")); - } - - @Override - public boolean isCacheChunks() { - return node.getNode("cache-chunks").getBoolean(false); - } - - @Override - public int getCacheImages() { - return node.getNode("cache-skins").getInt(0); - } - - @Override - public boolean isAboveBedrockNetherBuilding() { - return node.getNode("above-bedrock-nether-building").getBoolean(false); - } - - @Override - public SpongeMetricsInfo getMetrics() { - return metricsInfo; - } - - @AllArgsConstructor - public class SpongeBedrockConfiguration implements IBedrockConfiguration { - - private ConfigurationNode node; - - @Override - public String getAddress() { - return node.getNode("address").getString("0.0.0.0"); - } - - @Override - public int getPort() { - return node.getNode("port").getInt(19132); - } - - @Override - public boolean isCloneRemotePort() { - return node.getNode("clone-remote-port").getBoolean(false); - } - - @Override - public String getMotd1() { - return node.getNode("motd1").getString("GeyserMC"); - } - - @Override - public String getMotd2() { - return node.getNode("motd2").getString("GeyserMC"); - } - - @Override - public String getServerName() { - return node.getNode("server-name").getString("Geyser"); - } - } - - @AllArgsConstructor - public class SpongeRemoteConfiguration implements IRemoteConfiguration { - - private ConfigurationNode node; - - @Override - public String getAddress() { - return node.getNode("address").getString("127.0.0.1"); - } - - @Override - public void setAddress(String address) { - node.getNode("address").setValue(address); - } - - @Override - public int getPort() { - return node.getNode("port").getInt(25565); - } - - @Override - public void setPort(int port) { - node.getNode("port").setValue(port); - } - - @Override - public String getAuthType() { - return node.getNode("auth-type").getString("online"); - } - } - - public class SpongeUserAuthenticationInfo implements IUserAuthenticationInfo { - - private String key; - - public SpongeUserAuthenticationInfo(String key) { - this.key = key; - } - - @Override - public String getEmail() { - return node.getNode("userAuths").getNode(key).getNode("email").getString(); - } - - @Override - public String getPassword() { - return node.getNode("userAuths").getNode(key).getNode("password").getString(); - } - } - - public class SpongeMetricsInfo implements IMetricsInfo { - - @Override - public boolean isEnabled() { - return node.getNode("metrics").getNode("enabled").getBoolean(true); - } - - @Override - public String getUniqueId() { - return node.getNode("metrics").getNode("uuid").getString("generateduuid"); - } - } - - @Override - public boolean isEnableProxyConnections() { - return node.getNode("enable-proxy-connections").getBoolean(false); - } - - @Override - public int getMtu() { - return node.getNode("mtu").getInt(1400); - } - - @Override - public int getConfigVersion() { - return node.getNode("config-version").getInt(0); + public Path getFloodgateKeyPath() { + return null; //floodgate isn't available for Sponge } } diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeLogger.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeLogger.java index fb7cb54bb..bdbd25311 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeLogger.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongeLogger.java @@ -26,15 +26,16 @@ package org.geysermc.platform.sponge; import lombok.AllArgsConstructor; - +import lombok.Getter; +import lombok.Setter; import org.geysermc.connector.GeyserLogger; import org.slf4j.Logger; @AllArgsConstructor public class GeyserSpongeLogger implements GeyserLogger { - - private Logger logger; - private boolean debugMode; + private final Logger logger; + @Getter @Setter + private boolean debug; @Override public void severe(String message) { @@ -68,12 +69,8 @@ public class GeyserSpongeLogger implements GeyserLogger { @Override public void debug(String message) { - if (debugMode) + if (debug) { info(message); - } - - @Override - public void setDebug(boolean debugMode) { - this.debugMode = debugMode; + } } } diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java index 0f1b72533..106d2b155 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/GeyserSpongePlugin.java @@ -26,9 +26,6 @@ package org.geysermc.platform.sponge; import com.google.inject.Inject; -import ninja.leaping.configurate.ConfigurationNode; -import ninja.leaping.configurate.loader.ConfigurationLoader; -import ninja.leaping.configurate.yaml.YAMLConfigurationLoader; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.command.CommandManager; @@ -85,20 +82,14 @@ public class GeyserSpongePlugin implements GeyserBootstrap { ex.printStackTrace(); } - ConfigurationLoader loader = YAMLConfigurationLoader.builder().setPath(configFile.toPath()).build(); - ConfigurationNode config; try { - config = loader.load(); - this.geyserConfig = new GeyserSpongeConfiguration(configDir, config); + this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpongeConfiguration.class); } catch (IOException ex) { logger.warn(LanguageUtils.getLocaleStringLog("geyser.config.failed")); ex.printStackTrace(); return; } - ConfigurationNode serverIP = config.getNode("remote").getNode("address"); - ConfigurationNode serverPort = config.getNode("remote").getNode("port"); - if (Sponge.getServer().getBoundAddress().isPresent()) { InetSocketAddress javaAddr = Sponge.getServer().getBoundAddress().get(); @@ -106,13 +97,12 @@ public class GeyserSpongePlugin implements GeyserBootstrap { // By default this should be 127.0.0.1 but may need to be changed in some circumstances if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) { this.geyserConfig.setAutoconfiguredRemote(true); - serverPort.setValue(javaAddr.getPort()); + geyserConfig.getRemote().setPort(javaAddr.getPort()); } } - ConfigurationNode bedrockPort = config.getNode("bedrock").getNode("port"); if (geyserConfig.getBedrock().isCloneRemotePort()){ - bedrockPort.setValue(serverPort.getValue()); + geyserConfig.getBedrock().setPort(geyserConfig.getRemote().getPort()); } this.geyserLogger = new GeyserSpongeLogger(logger, geyserConfig.isDebugMode()); diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneConfiguration.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneConfiguration.java index 29e18d08f..5707863c2 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneConfiguration.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneConfiguration.java @@ -26,7 +26,6 @@ package org.geysermc.platform.standalone; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Getter; import org.geysermc.connector.configuration.GeyserJacksonConfiguration; @@ -35,13 +34,9 @@ import java.nio.file.Paths; @Getter @JsonIgnoreProperties(ignoreUnknown = true) -public class GeyserStandaloneConfiguration extends GeyserJacksonConfiguration { - - @JsonProperty("floodgate-key-file") - private String floodgateKeyFile; - +public final class GeyserStandaloneConfiguration extends GeyserJacksonConfiguration { @Override - public Path getFloodgateKeyFile() { - return Paths.get(floodgateKeyFile); + public Path getFloodgateKeyPath() { + return Paths.get(getFloodgateKeyFile()); } } diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneLogger.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneLogger.java index 0d9ced951..f0f7156f9 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneLogger.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/GeyserStandaloneLogger.java @@ -26,18 +26,16 @@ package org.geysermc.platform.standalone; import lombok.extern.log4j.Log4j2; - import net.minecrell.terminalconsole.SimpleTerminalConsole; - import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configurator; -import org.geysermc.connector.common.ChatColor; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.GeyserLogger; import org.geysermc.connector.command.CommandSender; +import org.geysermc.connector.common.ChatColor; @Log4j2 -public class GeyserStandaloneLogger extends SimpleTerminalConsole implements org.geysermc.connector.GeyserLogger, CommandSender { - +public class GeyserStandaloneLogger extends SimpleTerminalConsole implements GeyserLogger, CommandSender { private boolean colored = true; @Override @@ -99,10 +97,6 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements org Configurator.setLevel(log.getName(), debug ? Level.DEBUG : Level.INFO); } - /** - * Used for setting debug mode in GUI mode - * @return if debug is enabled - */ public boolean isDebug() { return log.isDebugEnabled(); } diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityConfiguration.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityConfiguration.java index 075804437..7dc6746b0 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityConfiguration.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityConfiguration.java @@ -27,7 +27,6 @@ package org.geysermc.platform.velocity; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.proxy.ProxyServer; import lombok.Getter; @@ -37,25 +36,15 @@ import org.geysermc.connector.configuration.GeyserJacksonConfiguration; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Optional; @Getter @JsonIgnoreProperties(ignoreUnknown = true) -public class GeyserVelocityConfiguration extends GeyserJacksonConfiguration { - - @JsonProperty("floodgate-key-file") - private String floodgateKeyFile; - +public final class GeyserVelocityConfiguration extends GeyserJacksonConfiguration { @JsonIgnore - private Path floodgateKey; - - @Override - public Path getFloodgateKeyFile() { - return floodgateKey; - } + private Path floodgateKeyPath; public void loadFloodgate(GeyserVelocityPlugin plugin, ProxyServer proxyServer, File dataFolder) { - Optional floodgate = proxyServer.getPluginManager().getPlugin("floodgate"); - floodgate.ifPresent(it -> floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(dataFolder.toString(), floodgateKeyFile.isEmpty() ? floodgateKeyFile : "public-key.pem"), it, Paths.get("plugins/floodgate/"))); + PluginContainer floodgate = proxyServer.getPluginManager().getPlugin("floodgate").orElse(null); + floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgate, Paths.get("plugins/floodgate/"), dataFolder.toPath(), plugin.getGeyserLogger()); } } diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityLogger.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityLogger.java index a935d786c..cb98411c8 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityLogger.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/GeyserVelocityLogger.java @@ -26,15 +26,16 @@ package org.geysermc.platform.velocity; import lombok.AllArgsConstructor; - +import lombok.Getter; +import lombok.Setter; import org.geysermc.connector.GeyserLogger; import org.slf4j.Logger; @AllArgsConstructor public class GeyserVelocityLogger implements GeyserLogger { - - private Logger logger; - private boolean debugMode; + private final Logger logger; + @Getter @Setter + private boolean debug; @Override public void severe(String message) { @@ -68,12 +69,8 @@ public class GeyserVelocityLogger implements GeyserLogger { @Override public void debug(String message) { - if (debugMode) + if (debug) { info(message); - } - - @Override - public void setDebug(boolean debugMode) { - this.debugMode = debugMode; + } } } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java b/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java index a30a5f4d4..ec5dd349a 100644 --- a/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java +++ b/connector/src/main/java/org/geysermc/connector/FloodgateKeyLoader.java @@ -25,17 +25,19 @@ package org.geysermc.connector; +import org.geysermc.connector.configuration.GeyserJacksonConfiguration; import org.geysermc.connector.utils.LanguageUtils; -import org.geysermc.connector.configuration.GeyserConfiguration; import java.nio.file.Files; import java.nio.file.Path; public class FloodgateKeyLoader { - public static Path getKey(GeyserLogger logger, GeyserConfiguration config, Path floodgateKey, Object floodgate, Path floodgateFolder) { + public static Path getKeyPath(GeyserJacksonConfiguration config, Object floodgate, Path floodgateDataFolder, Path geyserDataFolder, GeyserLogger logger) { + Path floodgateKey = geyserDataFolder.resolve(config.getFloodgateKeyFile()); + if (!Files.exists(floodgateKey) && config.getRemote().getAuthType().equals("floodgate")) { if (floodgate != null) { - Path autoKey = floodgateFolder.resolve("public-key.pem"); + Path autoKey = floodgateDataFolder.resolve("public-key.pem"); if (Files.exists(autoKey)) { logger.info(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.auto_loaded")); floodgateKey = autoKey; diff --git a/connector/src/main/java/org/geysermc/connector/GeyserLogger.java b/connector/src/main/java/org/geysermc/connector/GeyserLogger.java index 0ba2a689f..138285eb1 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserLogger.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserLogger.java @@ -84,4 +84,9 @@ public interface GeyserLogger { * @param debug if the logger should print debug messages */ void setDebug(boolean debug); + + /** + * If debug is enabled for this logger + */ + boolean isDebug(); } 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 5803ff13b..2d76e5740 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java @@ -27,7 +27,6 @@ package org.geysermc.connector.configuration; import com.fasterxml.jackson.annotation.JsonIgnore; import org.geysermc.connector.GeyserLogger; - import org.geysermc.connector.utils.LanguageUtils; import java.nio.file.Path; @@ -74,7 +73,7 @@ public interface GeyserConfiguration { String getDefaultLocale(); - Path getFloodgateKeyFile(); + Path getFloodgateKeyPath(); boolean isAboveBedrockNetherBuilding(); @@ -125,6 +124,8 @@ public interface GeyserConfiguration { String getUniqueId(); } + int getScoreboardPacketThreshold(); + // if u have offline mode enabled pls be safe boolean isEnableProxyConnections(); 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 378eba681..6748ef7e7 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java @@ -49,76 +49,75 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration private RemoteConfiguration remote; @JsonProperty("floodgate-key-file") - private String floodgateKeyFile; + private String floodgateKeyFile = "public-key.pem"; - public abstract Path getFloodgateKeyFile(); + public abstract Path getFloodgateKeyPath(); private Map userAuths; @JsonProperty("command-suggestions") - private boolean commandSuggestions; + private boolean commandSuggestions = true; @JsonProperty("passthrough-motd") - private boolean isPassthroughMotd; + private boolean isPassthroughMotd = false; @JsonProperty("passthrough-player-counts") - private boolean isPassthroughPlayerCounts; + private boolean isPassthroughPlayerCounts = false; @JsonProperty("passthrough-protocol-name") - private boolean isPassthroughProtocolName; + private boolean isPassthroughProtocolName = false; @JsonProperty("legacy-ping-passthrough") - private boolean isLegacyPingPassthrough; + private boolean isLegacyPingPassthrough = false; @JsonProperty("ping-passthrough-interval") - private int pingPassthroughInterval; + private int pingPassthroughInterval = 3; @JsonProperty("max-players") - private int maxPlayers; + private int maxPlayers = 100; @JsonProperty("debug-mode") - private boolean debugMode; + private boolean debugMode = false; @JsonProperty("general-thread-pool") - private int generalThreadPool; + private int generalThreadPool = 32; @JsonProperty("allow-third-party-capes") - private boolean allowThirdPartyCapes; + private boolean allowThirdPartyCapes = true; @JsonProperty("show-cooldown") private boolean showCooldown = true; @JsonProperty("allow-third-party-ears") - private boolean allowThirdPartyEars; + private boolean allowThirdPartyEars = false; @JsonProperty("default-locale") - private String defaultLocale; + private String defaultLocale = null; // is null by default so system language takes priority @JsonProperty("cache-chunks") - private boolean cacheChunks; + private boolean cacheChunks = false; @JsonProperty("cache-images") private int cacheImages = 0; @JsonProperty("above-bedrock-nether-building") - private boolean aboveBedrockNetherBuilding; + private boolean aboveBedrockNetherBuilding = false; private MetricsInfo metrics; @Getter public static class BedrockConfiguration implements IBedrockConfiguration { - @AsteriskSerializer.Asterisk(sensitive = true) - private String address; + private String address = "0.0.0.0"; @Setter - private int port; + private int port = 19132; @JsonProperty("clone-remote-port") - private boolean cloneRemotePort; + private boolean cloneRemotePort = false; - private String motd1; - private String motd2; + private String motd1 = "GeyserMC"; + private String motd2 = "Geyser"; @JsonProperty("server-name") private String serverName = GeyserConnector.NAME; @@ -126,17 +125,16 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @Getter public static class RemoteConfiguration implements IRemoteConfiguration { - @Setter @AsteriskSerializer.Asterisk(sensitive = true) - private String address; + private String address = "auto"; @Setter - private int port; + private int port = 25565; @Setter @JsonProperty("auth-type") - private String authType; + private String authType = "online"; } @Getter @@ -150,13 +148,15 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @Getter public static class MetricsInfo implements IMetricsInfo { - - private boolean enabled; + private boolean enabled = true; @JsonProperty("uuid") - private String uniqueId; + private String uniqueId = "generateuuid"; } + @JsonProperty("scoreboard-packet-threshold") + private int scoreboardPacketThreshold = 10; + @JsonProperty("enable-proxy-connections") private boolean enableProxyConnections = false; @@ -164,5 +164,5 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration private int mtu = 1400; @JsonProperty("config-version") - private int configVersion; + private int configVersion = 0; } diff --git a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java index 52b273513..da75812fb 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java @@ -30,13 +30,15 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadat import com.github.steveice10.mc.protocol.data.message.TextMessage; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector3f; -import com.nukkitx.protocol.bedrock.data.AdventureSetting; import com.nukkitx.protocol.bedrock.data.AttributeData; import com.nukkitx.protocol.bedrock.data.PlayerPermission; import com.nukkitx.protocol.bedrock.data.command.CommandPermission; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityLinkData; -import com.nukkitx.protocol.bedrock.packet.*; +import com.nukkitx.protocol.bedrock.packet.AddPlayerPacket; +import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket; +import com.nukkitx.protocol.bedrock.packet.SetEntityLinkPacket; +import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket; import lombok.Getter; import lombok.Setter; import org.geysermc.connector.entity.attribute.Attribute; @@ -48,7 +50,10 @@ import org.geysermc.connector.scoreboard.Team; import org.geysermc.connector.utils.AttributeUtils; import org.geysermc.connector.utils.MessageUtils; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; import java.util.concurrent.TimeUnit; @Getter @Setter @@ -209,11 +214,6 @@ public class PlayerEntity extends LivingEntity { super.updateBedrockMetadata(entityMetadata, session); if (entityMetadata.getId() == 2) { - // System.out.println(session.getScoreboardCache().getScoreboard().getObjectives().keySet()); - for (Team team : session.getWorldCache().getScoreboard().getTeams().values()) { - // session.getConnector().getLogger().info("team name " + team.getName()); - // session.getConnector().getLogger().info("team entities " + team.getEntities()); - } String username = this.username; TextMessage name = (TextMessage) entityMetadata.getValue(); if (name != null) { @@ -221,7 +221,6 @@ public class PlayerEntity extends LivingEntity { } Team team = session.getWorldCache().getScoreboard().getTeamFor(username); if (team != null) { - // session.getConnector().getLogger().info("team name es " + team.getName() + " with prefix " + team.getPrefix() + " and suffix " + team.getSuffix()); metadata.put(EntityData.NAMETAG, team.getPrefix() + MessageUtils.toChatColor(team.getColor()) + username + team.getSuffix()); } } 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 b208aaf3c..3b27fc6ae 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 @@ -346,7 +346,7 @@ public class GeyserSession implements CommandSender { PublicKey key = null; try { key = EncryptionUtil.getKeyFromFile( - connector.getConfig().getFloodgateKeyFile(), + connector.getConfig().getFloodgateKeyPath(), PublicKey.class ); } catch (IOException | InvalidKeySpecException | NoSuchAlgorithmException e) { @@ -626,7 +626,7 @@ public class GeyserSession implements CommandSender { * @param packet the bedrock packet from the NukkitX protocol lib */ public void sendUpstreamPacket(BedrockPacket packet) { - if (upstream != null && !upstream.isClosed()) { + if (upstream != null) { upstream.sendPacket(packet); } else { connector.getLogger().debug("Tried to send upstream packet " + packet.getClass().getSimpleName() + " but the session was null"); @@ -639,7 +639,7 @@ public class GeyserSession implements CommandSender { * @param packet the bedrock packet from the NukkitX protocol lib */ public void sendUpstreamPacketImmediately(BedrockPacket packet) { - if (upstream != null && !upstream.isClosed()) { + if (upstream != null) { upstream.sendPacketImmediately(packet); } else { connector.getLogger().debug("Tried to send upstream packet " + packet.getClass().getSimpleName() + " immediately but the session was null"); diff --git a/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java b/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java index 09870eef0..393ebfa82 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/UpstreamSession.java @@ -41,17 +41,15 @@ public class UpstreamSession { private boolean initialized = false; public void sendPacket(@NonNull BedrockPacket packet) { - if (isClosed()) - return; - - session.sendPacket(packet); + if (!isClosed()) { + session.sendPacket(packet); + } } public void sendPacketImmediately(@NonNull BedrockPacket packet) { - if (isClosed()) - return; - - session.sendPacketImmediately(packet); + if (!isClosed()) { + session.sendPacketImmediately(packet); + } } public void disconnect(String reason) { diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java index 310e5f9d7..ce49d2a09 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java @@ -31,37 +31,40 @@ import lombok.Setter; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.scoreboard.Objective; import org.geysermc.connector.scoreboard.Scoreboard; - -import java.util.Collection; +import org.geysermc.connector.scoreboard.ScoreboardUpdater; @Getter public class WorldCache { - - private GeyserSession session; - + private final GeyserSession session; @Setter private Difficulty difficulty = Difficulty.EASY; - private boolean showCoordinates = true; private Scoreboard scoreboard; + private final ScoreboardUpdater scoreboardUpdater; public WorldCache(GeyserSession session) { this.session = session; this.scoreboard = new Scoreboard(session); + scoreboardUpdater = new ScoreboardUpdater(this); + scoreboardUpdater.start(); } public void removeScoreboard() { if (scoreboard != null) { - Collection objectives = scoreboard.getObjectives().values(); - scoreboard = new Scoreboard(session); - - for (Objective objective : objectives) { + for (Objective objective : scoreboard.getObjectives().values()) { scoreboard.despawnObjective(objective); } + scoreboard = new Scoreboard(session); } } + public int increaseAndGetScoreboardPacketsPerSecond() { + int pendingPps = scoreboardUpdater.incrementAndGetPacketsPerSecond(); + int pps = scoreboardUpdater.getPacketsPerSecond(); + return Math.max(pps, pendingPps); + } + /** * Tell the client to hide or show the coordinates * diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaScoreboardObjectiveTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaScoreboardObjectiveTranslator.java index ce1ba3987..141ff03f1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaScoreboardObjectiveTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaScoreboardObjectiveTranslator.java @@ -41,13 +41,11 @@ public class JavaScoreboardObjectiveTranslator extends PacketTranslator { + private static final GeyserLogger LOGGER = GeyserConnector.getInstance().getLogger(); @Override public void translate(ServerTeamPacket packet, GeyserSession session) { - GeyserConnector.getInstance().getLogger().debug("Team packet " + packet.getTeamName() + " " + packet.getAction() + " " + Arrays.toString(packet.getPlayers())); + if (LOGGER.isDebug()) { + LOGGER.debug("Team packet " + packet.getTeamName() + " " + packet.getAction() + " " + Arrays.toString(packet.getPlayers())); + } + + int pps = session.getWorldCache().increaseAndGetScoreboardPacketsPerSecond(); Scoreboard scoreboard = session.getWorldCache().getScoreboard(); Team team = scoreboard.getTeam(packet.getTeamName()); @@ -58,38 +65,53 @@ public class JavaTeamTranslator extends PacketTranslator { .setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getClientData().getLanguageCode())); break; case UPDATE: - if (team != null) { - team.setName(MessageUtils.getBedrockMessage(packet.getDisplayName())) - .setColor(packet.getColor()) - .setPrefix(MessageUtils.getTranslatedBedrockMessage(packet.getPrefix(), session.getClientData().getLanguageCode())) - .setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getClientData().getLanguageCode())) - .setUpdateType(UpdateType.UPDATE); - } else { - GeyserConnector.getInstance().getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName())); + if (team == null) { + LOGGER.debug(LanguageUtils.getLocaleStringLog( + "geyser.network.translator.team.failed_not_registered", + packet.getAction(), packet.getTeamName() + )); + return; } + + team.setName(MessageUtils.getBedrockMessage(packet.getDisplayName())) + .setColor(packet.getColor()) + .setPrefix(MessageUtils.getTranslatedBedrockMessage(packet.getPrefix(), session.getClientData().getLanguageCode())) + .setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getClientData().getLanguageCode())) + .setUpdateType(UpdateType.UPDATE); break; case ADD_PLAYER: - if (team != null) { - team.addEntities(packet.getPlayers()); - } else { - GeyserConnector.getInstance().getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName())); + if (team == null) { + LOGGER.debug(LanguageUtils.getLocaleStringLog( + "geyser.network.translator.team.failed_not_registered", + packet.getAction(), packet.getTeamName() + )); + return; } + team.addEntities(packet.getPlayers()); break; case REMOVE_PLAYER: - if (team != null) { - team.removeEntities(packet.getPlayers()); - } else { - GeyserConnector.getInstance().getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName())); + if (team == null) { + LOGGER.debug(LanguageUtils.getLocaleStringLog( + "geyser.network.translator.team.failed_not_registered", + packet.getAction(), packet.getTeamName() + )); + return; } + team.removeEntities(packet.getPlayers()); break; case REMOVE: scoreboard.removeTeam(packet.getTeamName()); break; } - scoreboard.onUpdate(); + + // ScoreboardUpdater will handle it for us if the packets per second + // (for score and team packets) is higher then the first threshold + if (pps < ScoreboardUpdater.FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD) { + scoreboard.onUpdate(); + } } private Set toPlayerSet(String[] players) { - return new ObjectOpenHashSet<>(Arrays.asList(players)); + return new ObjectOpenHashSet<>(players); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java index 8d7d59a89..35033ca52 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaUpdateScoreTranslator.java @@ -25,48 +25,58 @@ package org.geysermc.connector.network.translators.java.scoreboard; +import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardAction; +import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerUpdateScorePacket; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.GeyserLogger; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.session.cache.WorldCache; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.scoreboard.Objective; import org.geysermc.connector.scoreboard.Scoreboard; - -import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardAction; -import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerUpdateScorePacket; +import org.geysermc.connector.scoreboard.ScoreboardUpdater; import org.geysermc.connector.utils.LanguageUtils; @Translator(packet = ServerUpdateScorePacket.class) public class JavaUpdateScoreTranslator extends PacketTranslator { + private final GeyserLogger logger; + + public JavaUpdateScoreTranslator() { + logger = GeyserConnector.getInstance().getLogger(); + } @Override public void translate(ServerUpdateScorePacket packet, GeyserSession session) { - try { - Scoreboard scoreboard = session.getWorldCache().getScoreboard(); + WorldCache worldCache = session.getWorldCache(); + Scoreboard scoreboard = worldCache.getScoreboard(); + int pps = worldCache.increaseAndGetScoreboardPacketsPerSecond(); - Objective objective = scoreboard.getObjective(packet.getObjective()); - if (objective == null && packet.getAction() != ScoreboardAction.REMOVE) { - GeyserConnector.getInstance().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.translator.score.failed_objective", packet.getObjective())); - return; - } + Objective objective = scoreboard.getObjective(packet.getObjective()); + if (objective == null && packet.getAction() != ScoreboardAction.REMOVE) { + logger.info(LanguageUtils.getLocaleStringLog("geyser.network.translator.score.failed_objective", packet.getObjective())); + return; + } - switch (packet.getAction()) { - case ADD_OR_UPDATE: - objective.setScore(packet.getEntry(), packet.getValue()); - break; - case REMOVE: - if (objective != null) { - objective.resetScore(packet.getEntry()); - } else { - for (Objective objective1 : scoreboard.getObjectives().values()) { - objective1.resetScore(packet.getEntry()); - } + switch (packet.getAction()) { + case ADD_OR_UPDATE: + objective.setScore(packet.getEntry(), packet.getValue()); + break; + case REMOVE: + if (objective != null) { + objective.removeScore(packet.getEntry()); + } else { + for (Objective objective1 : scoreboard.getObjectives().values()) { + objective1.removeScore(packet.getEntry()); } - break; - } + } + break; + } + + // ScoreboardUpdater will handle it for us if the packets per second + // (for score and team packets) is higher then the first threshold + if (pps < ScoreboardUpdater.FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD) { scoreboard.onUpdate(); - } catch (Exception ex) { - ex.printStackTrace(); } } } diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java index c3e6c863c..92a1add34 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java @@ -29,23 +29,23 @@ import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardPosition import lombok.Getter; import lombok.Setter; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; @Getter public class Objective { - private Scoreboard scoreboard; - private long id; - private boolean temp; + private final Scoreboard scoreboard; + private final long id; + private boolean active = true; @Setter private UpdateType updateType = UpdateType.ADD; private String objectiveName; - private String displaySlot; + private String displaySlotName; private String displayName = "unknown"; private int type = 0; // 0 = integer, 1 = heart - private Map scores = new HashMap<>(); + private Map scores = new ConcurrentHashMap<>(); private Objective(Scoreboard scoreboard) { this.id = scoreboard.getNextId().getAndIncrement(); @@ -54,23 +54,20 @@ public class Objective { /** * /!\ This method is made for temporary objectives until the real objective is received - * @param scoreboard the scoreboard + * + * @param scoreboard the scoreboard * @param objectiveName the name of the objective */ public Objective(Scoreboard scoreboard, String objectiveName) { this(scoreboard); this.objectiveName = objectiveName; - this.temp = true; + this.active = false; } public Objective(Scoreboard scoreboard, String objectiveName, ScoreboardPosition displaySlot, String displayName, int type) { - this(scoreboard, objectiveName, displaySlot.name().toLowerCase(), displayName, type); - } - - public Objective(Scoreboard scoreboard, String objectiveName, String displaySlot, String displayName, int type) { this(scoreboard); this.objectiveName = objectiveName; - this.displaySlot = displaySlot; + this.displaySlotName = translateDisplaySlot(displaySlot); this.displayName = displayName; this.type = type; } @@ -87,21 +84,9 @@ public class Objective { public void setScore(String id, int score) { if (scores.containsKey(id)) { scores.get(id).setScore(score).setUpdateType(UpdateType.ADD); - } else { - registerScore(id, score); + return; } - } - - public void setScoreText(String oldText, String newText) { - if (!scores.containsKey(oldText) || oldText.equals(newText)) return; - Score oldScore = scores.get(oldText); - - Score newScore = new Score(this, newText) - .setScore(oldScore.getScore()) - .setTeam(scoreboard.getTeamFor(newText)); - - scores.put(newText, newScore); - oldScore.setUpdateType(UpdateType.REMOVE); + registerScore(id, score); } public int getScore(String id) { @@ -113,37 +98,61 @@ public class Objective { public Score getScore(int line) { for (Score score : scores.values()) { - if (score.getScore() == line) return score; + if (score.getScore() == line) { + return score; + } } return null; } - public void resetScore(String id) { + public void removeScore(String id) { if (scores.containsKey(id)) { scores.get(id).setUpdateType(UpdateType.REMOVE); } } - public void removeScore(String id) { + /** + * Used internally to remove a score from the score map + */ + public void removeScore0(String id) { scores.remove(id); } public Objective setDisplayName(String displayName) { this.displayName = displayName; - if (updateType == UpdateType.NOTHING) updateType = UpdateType.UPDATE; + if (updateType == UpdateType.NOTHING) { + updateType = UpdateType.UPDATE; + } return this; } public Objective setType(int type) { this.type = type; - if (updateType == UpdateType.NOTHING) updateType = UpdateType.UPDATE; + if (updateType == UpdateType.NOTHING) { + updateType = UpdateType.UPDATE; + } return this; } - public void removeTemp(ScoreboardPosition displaySlot) { - if (temp) { - temp = false; - this.displaySlot = displaySlot.name().toLowerCase(); + public void setActive(ScoreboardPosition displaySlot) { + if (!active) { + active = true; + displaySlotName = translateDisplaySlot(displaySlot); + } + } + + public void removed() { + scores = null; + } + + private static String translateDisplaySlot(ScoreboardPosition displaySlot) { + switch (displaySlot) { + case BELOW_NAME: + return "belowname"; + case PLAYER_LIST: + return "list"; + default: + return "sidebar"; } } } diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java index d5a65e8c0..635bafa3d 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java @@ -25,40 +25,63 @@ package org.geysermc.connector.scoreboard; +import com.nukkitx.protocol.bedrock.data.ScoreInfo; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; -@Getter @Setter +@Getter @Accessors(chain = true) public class Score { - private Objective objective; - private long id; + private final Objective objective; + private ScoreInfo cachedInfo; + private final long id; + @Setter private UpdateType updateType = UpdateType.ADD; - private String name; + private final String name; private Team team; private int score; + @Setter private int oldScore = Integer.MIN_VALUE; public Score(Objective objective, String name) { this.id = objective.getScoreboard().getNextId().getAndIncrement(); this.objective = objective; this.name = name; + update(); } public String getDisplayName() { - if (team != null && team.getUpdateType() != UpdateType.REMOVE) { + if (team != null) { return team.getPrefix() + name + team.getSuffix(); } return name; } public Score setScore(int score) { - if (oldScore == Integer.MIN_VALUE) { - this.oldScore = score; - } this.score = score; + updateType = UpdateType.UPDATE; return this; } + + public Score setTeam(Team team) { + if (this.team != null && team != null) { + if (!this.team.equals(team)) { + this.team = team; + updateType = UpdateType.UPDATE; + } + return this; + } + // simplified from (this.team != null && team == null) || (this.team == null && team != null) + if (this.team != null || team != null) { + this.team = team; + updateType = UpdateType.UPDATE; + } + return this; + } + + public void update() { + cachedInfo = new ScoreInfo(id, objective.getObjectiveName(), score, getDisplayName()); + } } diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java index 5fdda617f..9f89d9d2b 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java @@ -26,73 +26,72 @@ package org.geysermc.connector.scoreboard; import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardPosition; +import com.nukkitx.protocol.bedrock.BedrockPacket; import com.nukkitx.protocol.bedrock.data.ScoreInfo; import com.nukkitx.protocol.bedrock.packet.RemoveObjectivePacket; import com.nukkitx.protocol.bedrock.packet.SetDisplayObjectivePacket; import com.nukkitx.protocol.bedrock.packet.SetScorePacket; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import lombok.Getter; - +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.GeyserLogger; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.utils.LanguageUtils; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import static org.geysermc.connector.scoreboard.UpdateType.*; @Getter public class Scoreboard { - private GeyserSession session; - private AtomicLong nextId = new AtomicLong(0); + private final GeyserSession session; + private final GeyserLogger logger; + private final AtomicLong nextId = new AtomicLong(0); - private Map objectives = new HashMap<>(); - private Map teams = new HashMap<>(); + private final Map objectives = new ConcurrentHashMap<>(); + private final Map teams = new HashMap<>(); + + private int lastScoreCount = 0; public Scoreboard(GeyserSession session) { this.session = session; + this.logger = GeyserConnector.getInstance().getLogger(); } - public Objective registerNewObjective(String objectiveId, boolean temp) { - if (!temp || objectives.containsKey(objectiveId)) return objectives.get(objectiveId); + public Objective registerNewObjective(String objectiveId, boolean active) { + if (active || objectives.containsKey(objectiveId)) { + return objectives.get(objectiveId); + } Objective objective = new Objective(this, objectiveId); objectives.put(objectiveId, objective); return objective; } public Objective registerNewObjective(String objectiveId, ScoreboardPosition displaySlot) { - Objective objective = null; - if (objectives.containsKey(objectiveId)) { - objective = objectives.get(objectiveId); - if (objective.isTemp()) objective.removeTemp(displaySlot); - else { - despawnObjective(objective); - objective = null; + Objective objective = objectives.get(objectiveId); + if (objective != null) { + if (!objective.isActive()) { + objective.setActive(displaySlot); + return objective; } + despawnObjective(objective); } - if (objective == null) { - objective = new Objective(this, objectiveId, displaySlot, "unknown", 0); - objectives.put(objectiveId, objective); - } + + objective = new Objective(this, objectiveId, displaySlot, "unknown", 0); + objectives.put(objectiveId, objective); return objective; } public Team registerNewTeam(String teamName, Set players) { - if (teams.containsKey(teamName)) { - session.getConnector().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_overrides", teamName)); - return getTeam(teamName); + Team team = teams.get(teamName); + if (team != null) { + logger.info(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_overrides", teamName)); + return team; } - Team team = new Team(this, teamName).setEntities(players); + team = new Team(this, teamName).setEntities(players); teams.put(teamName, team); - - for (Objective objective : objectives.values()) { - for (Score score : objective.getScores().values()) { - if (players.contains(score.getName())) { - score.setTeam(team); - } - } - } return team; } @@ -106,102 +105,119 @@ public class Scoreboard { public void unregisterObjective(String objectiveName) { Objective objective = getObjective(objectiveName); - if (objective != null) objective.setUpdateType(REMOVE); + if (objective != null) { + objective.setUpdateType(REMOVE); + } } public void removeTeam(String teamName) { Team remove = teams.remove(teamName); - if (remove != null) remove.setUpdateType(REMOVE); + if (remove != null) { + remove.setUpdateType(REMOVE); + } } public void onUpdate() { - Set changedObjectives = new ObjectOpenHashSet<>(); - List addScores = new ArrayList<>(); - List removeScores = new ArrayList<>(); + List addScores = new ArrayList<>(getLastScoreCount()); + List removeScores = new ArrayList<>(getLastScoreCount()); - for (String objectiveId : new ArrayList<>(objectives.keySet())) { - Objective objective = objectives.get(objectiveId); - if (objective.isTemp()) { - session.getConnector().getLogger().debug("Ignoring temp Scoreboard Objective '"+ objectiveId +'\''); + for (Objective objective : objectives.values()) { + if (!objective.isActive()) { + logger.debug("Ignoring non-active Scoreboard Objective '"+ objective.getObjectiveName() +'\''); continue; } - if (objective.getUpdateType() != NOTHING) changedObjectives.add(objective); + // hearts can't hold teams, so we treat them differently + if (objective.getType() == 1) { + for (Score score : objective.getScores().values()) { + if (score.getUpdateType() == NOTHING) { + continue; + } + + boolean update = score.getUpdateType() == UPDATE; + if (update) { + score.update(); + } + + if (score.getUpdateType() == ADD || update) { + addScores.add(score.getCachedInfo()); + } + if (score.getUpdateType() == REMOVE || update) { + removeScores.add(score.getCachedInfo()); + } + } + continue; + } boolean globalUpdate = objective.getUpdateType() == UPDATE; - boolean globalAdd = objective.getUpdateType() == ADD || globalUpdate; - boolean globalRemove = objective.getUpdateType() == REMOVE || globalUpdate; + boolean globalAdd = objective.getUpdateType() == ADD; + boolean globalRemove = objective.getUpdateType() == REMOVE; - boolean hasUpdate = globalUpdate; - - List handledScores = new ArrayList<>(); - for (String identifier : new ObjectOpenHashSet<>(objective.getScores().keySet())) { - Score score = objective.getScores().get(identifier); + for (Score score : objective.getScores().values()) { Team team = score.getTeam(); - boolean inTeam = team != null && team.getEntities().contains(score.getName()); + boolean add = globalAdd || globalUpdate; + boolean remove = globalRemove; + boolean teamChanged = false; + if (team != null) { + if (team.getUpdateType() == REMOVE || !team.hasEntity(score.getName())) { + score.setTeam(null); + teamChanged = true; + } - boolean teamAdd = team != null && (team.getUpdateType() == ADD || team.getUpdateType() == UPDATE); - boolean teamRemove = team != null && (team.getUpdateType() == REMOVE || team.getUpdateType() == UPDATE); + teamChanged |= team.getUpdateType() == UPDATE; - if (team != null && (team.getUpdateType() == REMOVE || !inTeam)) score.setTeam(null); - - boolean add = (hasUpdate || globalAdd || teamAdd || teamRemove || score.getUpdateType() == ADD || score.getUpdateType() == UPDATE) && (score.getUpdateType() != REMOVE); - boolean remove = hasUpdate || globalRemove || teamAdd || teamRemove || score.getUpdateType() == REMOVE || score.getUpdateType() == UPDATE; - - boolean updated = false; - if (!hasUpdate) { - updated = hasUpdate = add; + add |= team.getUpdateType() == ADD || team.getUpdateType() == UPDATE; + remove |= team.getUpdateType() == REMOVE; } - if (updated) { - for (Score score1 : handledScores) { - ScoreInfo scoreInfo = new ScoreInfo(score1.getId(), score1.getObjective().getObjectiveName(), score1.getScore(), score1.getDisplayName()); - addScores.add(scoreInfo); - removeScores.add(scoreInfo); - } + add |= score.getUpdateType() == ADD || score.getUpdateType() == UPDATE; + remove |= score.getUpdateType() == REMOVE; + if (score.getUpdateType() == REMOVE) { + add = false; + } + + if (score.getUpdateType() == UPDATE || teamChanged) { + score.update(); } if (add) { - addScores.add(new ScoreInfo(score.getId(), score.getObjective().getObjectiveName(), score.getScore(), score.getDisplayName())); + addScores.add(score.getCachedInfo()); } if (remove) { - removeScores.add(new ScoreInfo(score.getId(), score.getObjective().getObjectiveName(), score.getOldScore(), score.getDisplayName())); + removeScores.add(score.getCachedInfo()); } + // score is pending to be updated, so we use the current score as the old score score.setOldScore(score.getScore()); + // score is pending to be removed, so we can remove it from the objective if (score.getUpdateType() == REMOVE) { - objective.removeScore(score.getName()); + objective.removeScore0(score.getName()); } - if (add || remove) { - changedObjectives.add(objective); - } else { // stays the same like before - handledScores.add(score); - } score.setUpdateType(NOTHING); } - } - for (Objective objective : changedObjectives) { - boolean update = objective.getUpdateType() == NOTHING || objective.getUpdateType() == UPDATE; - if (objective.getUpdateType() == REMOVE || update) { + if (globalRemove || globalUpdate) { RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket(); removeObjectivePacket.setObjectiveId(objective.getObjectiveName()); session.sendUpstreamPacket(removeObjectivePacket); if (objective.getUpdateType() == REMOVE) { objectives.remove(objective.getObjectiveName()); // now we can deregister + objective.removed(); } } - if (objective.getUpdateType() == ADD || update) { + + if (globalAdd || globalUpdate) { SetDisplayObjectivePacket displayObjectivePacket = new SetDisplayObjectivePacket(); displayObjectivePacket.setObjectiveId(objective.getObjectiveName()); displayObjectivePacket.setDisplayName(objective.getDisplayName()); displayObjectivePacket.setCriteria("dummy"); - displayObjectivePacket.setDisplaySlot(objective.getDisplaySlot()); + displayObjectivePacket.setDisplaySlot(objective.getDisplaySlotName()); displayObjectivePacket.setSortOrder(1); // ?? session.sendUpstreamPacket(displayObjectivePacket); } + objective.setUpdateType(NOTHING); } @@ -218,6 +234,8 @@ public class Scoreboard { setScorePacket.setInfos(addScores); session.sendUpstreamPacket(setScorePacket); } + + lastScoreCount = addScores.size(); } public void despawnObjective(Objective objective) { @@ -234,6 +252,8 @@ public class Scoreboard { 0, "" )); } + + objective.removed(); if (!toRemove.isEmpty()) { SetScorePacket setScorePacket = new SetScorePacket(); diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/ScoreboardUpdater.java b/connector/src/main/java/org/geysermc/connector/scoreboard/ScoreboardUpdater.java new file mode 100644 index 000000000..ee38cecd9 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/ScoreboardUpdater.java @@ -0,0 +1,121 @@ +/* + * 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.scoreboard; + +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.configuration.GeyserConfiguration; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.session.cache.WorldCache; +import org.geysermc.connector.utils.LanguageUtils; + +import java.util.concurrent.atomic.AtomicInteger; + +public class ScoreboardUpdater extends Thread { + public static final int FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD; + public static final int SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD = 250; + + private static final int FIRST_MILLIS_BETWEEN_UPDATES = 250; // 4 updates per second + private static final int SECOND_MILLIS_BETWEEN_UPDATES = 1000 * 3; // 1 update per 3 seconds + + private static final boolean DEBUG_ENABLED; + + private final WorldCache worldCache; + private final GeyserSession session; + + private int millisBetweenUpdates = FIRST_MILLIS_BETWEEN_UPDATES; + private long lastUpdate = System.currentTimeMillis(); + private long lastLog = -1; + + private long lastPacketsPerSecondUpdate = System.currentTimeMillis(); + private final AtomicInteger packetsPerSecond = new AtomicInteger(0); + private final AtomicInteger pendingPacketsPerSecond = new AtomicInteger(0); + + public ScoreboardUpdater(WorldCache worldCache) { + super("Scoreboard Updater"); + this.worldCache = worldCache; + session = worldCache.getSession(); + } + + @Override + public void run() { + while (!session.isClosed()) { + long currentTime = System.currentTimeMillis(); + + // reset score-packets per second every second + if (currentTime - lastPacketsPerSecondUpdate > 1000) { + lastPacketsPerSecondUpdate = currentTime; + packetsPerSecond.set(pendingPacketsPerSecond.get()); + pendingPacketsPerSecond.set(0); + } + + if (currentTime - lastUpdate > millisBetweenUpdates) { + lastUpdate = currentTime; + + int pps = packetsPerSecond.get(); + if (pps >= FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD) { + boolean reachedSecondThreshold = pps >= SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD; + if (reachedSecondThreshold) { + millisBetweenUpdates = SECOND_MILLIS_BETWEEN_UPDATES; + } else { + millisBetweenUpdates = FIRST_MILLIS_BETWEEN_UPDATES; + } + + worldCache.getScoreboard().onUpdate(); + + if (DEBUG_ENABLED && (currentTime - lastLog > 60000)) { // one minute + int threshold = reachedSecondThreshold ? + SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD : + FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD; + + GeyserConnector.getInstance().getLogger().info( + LanguageUtils.getLocaleStringLog("geyser.scoreboard.updater.threshold_reached.log", session.getName(), threshold, pps) + + LanguageUtils.getLocaleStringLog("geyser.scoreboard.updater.threshold_reached", (millisBetweenUpdates / 1000.0)) + ); + + lastLog = currentTime; + } + } + } + } + } + + public int getPacketsPerSecond() { + return packetsPerSecond.get(); + } + + /** + * Increase the Scoreboard Packets Per Second and return the updated value + */ + public int incrementAndGetPacketsPerSecond() { + return pendingPacketsPerSecond.incrementAndGet(); + } + + static { + GeyserConfiguration config = GeyserConnector.getInstance().getConfig(); + FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD = Math.min(config.getScoreboardPacketThreshold(), SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD); + DEBUG_ENABLED = config.isDebugMode(); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java index c2fcc02c8..087ea2dde 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java @@ -35,8 +35,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; -@Getter -@Setter +@Getter @Setter @Accessors(chain = true) public class Team { private final Scoreboard scoreboard; @@ -44,6 +43,7 @@ public class Team { private UpdateType updateType = UpdateType.ADD; private String name; + private String prefix; private TeamColor color; private String suffix; @@ -57,8 +57,7 @@ public class Team { public void addEntities(String... names) { List added = new ArrayList<>(); for (String name : names) { - if (!entities.contains(name)) { - entities.add(name); + if (entities.add(name)) { added.add(name); } } @@ -78,4 +77,35 @@ public class Team { } setUpdateType(UpdateType.UPDATE); } + + public boolean hasEntity(String name) { + return entities.contains(name); + } + + public Team setPrefix(String prefix) { + // replace "null" to an empty string, + // we do this here to improve the performance of Score#getDisplayName + if (prefix.length() == 4 && "null".equals(prefix)) { + this.prefix = ""; + return this; + } + this.prefix = prefix; + return this; + } + + public Team setSuffix(String suffix) { + // replace "null" to an empty string, + // we do this here to improve the performance of Score#getDisplayName + if (suffix.length() == 4 && "null".equals(suffix)) { + this.suffix = ""; + return this; + } + this.suffix = suffix; + return this; + } + + @Override + public int hashCode() { + return id.hashCode(); + } } diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index af5881858..bde95d24b 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -124,6 +124,12 @@ metrics: # ADVANCED OPTIONS - DO NOT TOUCH UNLESS YOU KNOW WHAT YOU ARE DOING! +# Geyser updates the Scoreboard after every Scoreboard packet, but when Geyser tries to handle +# a lot of scoreboard packets per second can cause serious lag. +# This option allows you to specify after how many Scoreboard packets per seconds +# the Scoreboard updates will be limited to four updates per second. +scoreboard-packet-threshold: 20 + # Allow connections from ProxyPass and Waterdog. # See https://www.spigotmc.org/wiki/firewall-guide/ for assistance - use UDP instead of TCP. enable-proxy-connections: false From 79bf56a75cdc04f58748a4c819555937a2908499 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Fri, 28 Aug 2020 19:36:24 +0100 Subject: [PATCH 063/140] Tweaks to support Android (#1206) * Downgrade reflections to 0.9.11 * Add comment explaining downgrade * Move to pre-build reflections * Update skins to use https and relative cache dir * Move to https OptiFine cape url * Add javadoc to isProduction * Add ANDROID as a platform type * Re-ordered PlatformType * Change stop command to call onDisable --- connector/pom.xml | 53 ++++++++++++++++++- .../geysermc/connector/GeyserConnector.java | 14 ++++- .../command/defaults/StopCommand.java | 6 +-- .../connector/common/PlatformType.java | 1 + .../translators/PacketTranslatorRegistry.java | 3 +- .../translators/item/ItemTranslator.java | 3 +- .../sound/SoundHandlerRegistry.java | 4 +- .../world/block/BlockTranslator.java | 2 +- .../block/entity/BlockEntityTranslator.java | 3 +- .../geysermc/connector/utils/FileUtils.java | 21 ++++++++ .../geysermc/connector/utils/LocaleUtils.java | 2 +- .../connector/utils/SkinProvider.java | 6 +-- .../geysermc/connector/utils/SkinUtils.java | 4 +- 13 files changed, 104 insertions(+), 18 deletions(-) diff --git a/connector/pom.xml b/connector/pom.xml index a3748044a..e2c01e647 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -129,7 +129,12 @@ org.reflections reflections - 0.9.12 + 0.9.11 + + + org.dom4j + dom4j + 2.1.3 net.kyori @@ -232,6 +237,52 @@ + + org.codehaus.gmavenplus + gmavenplus-plugin + 1.9.1 + + + process-classes + + execute + + + + + + + + + + + org.reflections + reflections + 0.9.11 + + + org.dom4j + dom4j + 2.1.3 + + + org.codehaus.groovy + groovy-all + 3.0.5 + runtime + pom + + + diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index ec332ed66..2593c516a 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -163,7 +163,7 @@ public class GeyserConnector { config.getRemote().setPort(remotePort = Integer.parseInt(record[2])); logger.debug("Found SRV record \"" + remoteAddress + ":" + remotePort + "\""); } - } catch (Exception ex) { + } catch (Exception | NoClassDefFoundError ex) { // Check for a NoClassDefFoundError to prevent Android crashes logger.debug("Exception while trying to find an SRV record for the remote host."); if (config.isDebugMode()) ex.printStackTrace(); // Otherwise we can get a stack trace for any domain that doesn't have an SRV record @@ -307,6 +307,18 @@ public class GeyserConnector { return bootstrap.getWorldManager(); } + /** + * Get the production status of the current runtime. + * Will return true if the version number is not 'DEV'. + * Should only happen in compiled jars. + * + * @return If we are in a production build/environment + */ + public boolean isProduction() { + //noinspection ConstantConditions + return !"DEV".equals(GeyserConnector.VERSION); + } + public static GeyserConnector getInstance() { return instance; } diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/StopCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/StopCommand.java index 636058a02..c69a8705f 100644 --- a/connector/src/main/java/org/geysermc/connector/command/defaults/StopCommand.java +++ b/connector/src/main/java/org/geysermc/connector/command/defaults/StopCommand.java @@ -49,10 +49,6 @@ public class StopCommand extends GeyserCommand { return; } - connector.shutdown(); - - if (connector.getPlatformType() == PlatformType.STANDALONE) { - System.exit(0); - } + connector.getBootstrap().onDisable(); } } diff --git a/connector/src/main/java/org/geysermc/connector/common/PlatformType.java b/connector/src/main/java/org/geysermc/connector/common/PlatformType.java index 7c245c9bf..4daa5d37d 100644 --- a/connector/src/main/java/org/geysermc/connector/common/PlatformType.java +++ b/connector/src/main/java/org/geysermc/connector/common/PlatformType.java @@ -32,6 +32,7 @@ import lombok.Getter; @AllArgsConstructor public enum PlatformType { + ANDROID("Android"), BUNGEECORD("BungeeCord"), SPIGOT("Spigot"), SPONGE("Sponge"), diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java index 92d2e9102..c43864816 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java @@ -33,6 +33,7 @@ import com.nukkitx.protocol.bedrock.BedrockPacket; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.LanguageUtils; import org.reflections.Reflections; @@ -48,7 +49,7 @@ public class PacketTranslatorRegistry { private static final ObjectArrayList> IGNORED_PACKETS = new ObjectArrayList<>(); static { - Reflections ref = new Reflections("org.geysermc.connector.network.translators"); + Reflections ref = GeyserConnector.getInstance().isProduction() ? FileUtils.getReflections("org.geysermc.connector.network.translators") : new Reflections("org.geysermc.connector.network.translators"); for (Class clazz : ref.getTypesAnnotatedWith(Translator.class)) { Class packet = clazz.getAnnotation(Translator.class).packet(); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java index dce0f4b4d..c08531205 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java @@ -41,6 +41,7 @@ import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; +import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.connector.utils.MessageUtils; import org.reflections.Reflections; @@ -62,7 +63,7 @@ public abstract class ItemTranslator { static { /* Load item translators */ - Reflections ref = new Reflections("org.geysermc.connector.network.translators.item"); + Reflections ref = GeyserConnector.getInstance().isProduction() ? FileUtils.getReflections("org.geysermc.connector.network.translators.item") : new Reflections("org.geysermc.connector.network.translators.item"); Map loadedNbtItemTranslators = new HashMap<>(); for (Class clazz : ref.getTypesAnnotatedWith(ItemRemapper.class)) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundHandlerRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundHandlerRegistry.java index 893975e5e..163c451cb 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundHandlerRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundHandlerRegistry.java @@ -25,6 +25,8 @@ package org.geysermc.connector.network.translators.sound; +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.utils.FileUtils; import org.reflections.Reflections; import java.util.HashMap; @@ -38,7 +40,7 @@ public class SoundHandlerRegistry { static final Map> INTERACTION_HANDLERS = new HashMap<>(); static { - Reflections ref = new Reflections("org.geysermc.connector.network.translators.sound"); + Reflections ref = GeyserConnector.getInstance().isProduction() ? FileUtils.getReflections("org.geysermc.connector.network.translators.sound") : new Reflections("org.geysermc.connector.network.translators.sound"); for (Class clazz : ref.getTypesAnnotatedWith(SoundHandler.class)) { try { SoundInteractionHandler interactionHandler = (SoundInteractionHandler) clazz.newInstance(); 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 e627b8454..8da09f6f3 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 @@ -106,7 +106,7 @@ public class BlockTranslator { addedStatesMap.defaultReturnValue(-1); List paletteList = new ArrayList<>(); - Reflections ref = new Reflections("org.geysermc.connector.network.translators.world.block.entity"); + Reflections ref = GeyserConnector.getInstance().isProduction() ? FileUtils.getReflections("org.geysermc.connector.network.translators.world.block.entity") : new Reflections("org.geysermc.connector.network.translators.world.block.entity"); ref.getTypesAnnotatedWith(BlockEntity.class); int waterRuntimeId = -1; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java index c4401c4c8..a538c2dcc 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java @@ -34,6 +34,7 @@ import com.nukkitx.nbt.NbtMapBuilder; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.utils.BlockEntityUtils; +import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.LanguageUtils; import org.reflections.Reflections; @@ -66,7 +67,7 @@ public abstract class BlockEntityTranslator { } static { - Reflections ref = new Reflections("org.geysermc.connector.network.translators.world.block.entity"); + Reflections ref = GeyserConnector.getInstance().isProduction() ? FileUtils.getReflections("org.geysermc.connector.network.translators.world.block.entity") : new Reflections("org.geysermc.connector.network.translators.world.block.entity"); for (Class clazz : ref.getTypesAnnotatedWith(BlockEntity.class)) { GeyserConnector.getInstance().getLogger().debug("Found annotated block entity: " + clazz.getCanonicalName()); diff --git a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java index 38369d6c8..300946b34 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java @@ -28,11 +28,15 @@ package org.geysermc.connector.utils; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import org.geysermc.connector.GeyserConnector; +import org.reflections.Reflections; +import org.reflections.serializers.XmlSerializer; +import org.reflections.util.ConfigurationBuilder; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.net.URL; import java.util.function.Function; public class FileUtils { @@ -140,4 +144,21 @@ public class FileUtils { } return stream; } + + /** + * Get the stored reflection data for a given path + * + * @param path The path to get the reflection data for + * @return The created Reflections object + */ + public static Reflections getReflections(String path) { + Reflections reflections = new Reflections(new ConfigurationBuilder()); + XmlSerializer serializer = new XmlSerializer(); + URL resource = FileUtils.class.getClassLoader().getResource("META-INF/reflections/" + path + "-reflections.xml"); + try (InputStream inputStream = resource.openConnection().getInputStream()) { + reflections.merge(serializer.read(inputStream)); + } catch (IOException e) { } + + return reflections; + } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java index 285846a97..c09af2b7d 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java @@ -150,7 +150,7 @@ public class LocaleUtils { // Get the hash and download the locale String hash = ASSET_MAP.get("minecraft/lang/" + locale + ".json").getHash(); - WebUtils.downloadFile("http://resources.download.minecraft.net/" + hash.substring(0, 2) + "/" + hash, localeFile.toString()); + WebUtils.downloadFile("https://resources.download.minecraft.net/" + hash.substring(0, 2) + "/" + hash, localeFile.toString()); } /** diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java index 5551230b9..7c30e48ab 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinProvider.java @@ -119,7 +119,7 @@ public class SkinProvider { // Schedule Daily Image Expiry if we are caching them if (GeyserConnector.getInstance().getConfig().getCacheImages() > 0) { GeyserConnector.getInstance().getGeneralThreadPool().scheduleAtFixedRate(() -> { - File cacheFolder = Paths.get("cache", "images").toFile(); + File cacheFolder = GeyserConnector.getInstance().getBootstrap().getConfigFolder().resolve("cache").resolve("images").toFile(); if (!cacheFolder.exists()) { return; } @@ -395,7 +395,7 @@ public class SkinProvider { BufferedImage image = null; // First see if we have a cached file. We also update the modification stamp so we know when the file was last used - File imageFile = Paths.get("cache", "images", UUID.nameUUIDFromBytes(imageUrl.getBytes()).toString() + ".png").toFile(); + File imageFile = GeyserConnector.getInstance().getBootstrap().getConfigFolder().resolve("cache").resolve("images").resolve(UUID.nameUUIDFromBytes(imageUrl.getBytes()).toString() + ".png").toFile(); if (imageFile.exists()) { try { GeyserConnector.getInstance().getLogger().debug("Reading cached image from file " + imageFile.getPath() + " for " + imageUrl); @@ -600,7 +600,7 @@ public class SkinProvider { @Getter public enum CapeProvider { MINECRAFT, - OPTIFINE("http://s.optifine.net/capes/%s.png", CapeUrlType.USERNAME), + OPTIFINE("https://optifine.net/capes/%s.png", CapeUrlType.USERNAME), LABYMOD("https://www.labymod.net/page/php/getCapeTexture.php?uuid=%s", CapeUrlType.UUID_DASHED), FIVEZIG("https://textures.5zigreborn.eu/profile/%s", CapeUrlType.UUID_DASHED), MINECRAFTCAPES("https://minecraftcapes.net/profile/%s/cape", CapeUrlType.UUID); diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java index f0b88c435..e3488d6e7 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java @@ -150,14 +150,14 @@ public class SkinUtils { JsonNode textures = skinObject.get("textures"); JsonNode skinTexture = textures.get("SKIN"); - String skinUrl = skinTexture.get("url").asText(); + String skinUrl = skinTexture.get("url").asText().replace("http://", "https://"); isAlex = skinTexture.has("metadata"); String capeUrl = null; if (textures.has("CAPE")) { JsonNode capeTexture = textures.get("CAPE"); - capeUrl = capeTexture.get("url").asText(); + capeUrl = capeTexture.get("url").asText().replace("http://", "https://"); } return new GameProfileData(skinUrl, capeUrl, isAlex); From 3f0080349986ac687e152f5fbb12f4f62b0d24cf Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Fri, 28 Aug 2020 15:16:35 -0400 Subject: [PATCH 064/140] connector/pom.xml: Move back to main MCProtocolLib repository (#1211) Fixes a regression where ClientPlayerAbilitiesPacket was sending the incorrect flag. --- connector/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/connector/pom.xml b/connector/pom.xml index e2c01e647..76a83c23f 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -109,9 +109,9 @@ compile - com.github.GeyserMC - MCProtocolLib - f37c98dc70 + com.github.steveice10 + mcprotocollib + b3cf3acbb3 compile From caa5098d2c9ad44c0121fec076c3899fe3e6c2be Mon Sep 17 00:00:00 2001 From: jackson-57 <49173011+jackson-57@users.noreply.github.com> Date: Fri, 28 Aug 2020 13:58:37 -0700 Subject: [PATCH 065/140] Fix minor spelling error in issue template (#1214) --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 75a76cd05..46653e624 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -11,4 +11,4 @@ assignees: '' Add a description **Alternatives?** -Any alternatives you have tryed +List any alternatives you might have tried From 37c4192c1270c1e9bbe13e7bcc76c052a456f308 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Fri, 28 Aug 2020 19:11:32 -0500 Subject: [PATCH 066/140] Show the supported Bedrock versions in the version command rather than just the default codec --- .../connector/command/defaults/VersionCommand.java | 12 +++++++++++- .../geysermc/connector/network/BedrockProtocol.java | 11 ++++++----- connector/src/main/resources/mappings | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) 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 681474a98..f7f62e597 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 @@ -26,6 +26,7 @@ package org.geysermc.connector.command.defaults; import com.github.steveice10.mc.protocol.MinecraftConstants; +import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.command.GeyserCommand; @@ -38,6 +39,7 @@ import org.geysermc.connector.utils.WebUtils; import java.io.IOException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.Properties; public class VersionCommand extends GeyserCommand { @@ -51,7 +53,15 @@ public class VersionCommand extends GeyserCommand { @Override public void execute(CommandSender sender, String[] args) { - sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.version.version", GeyserConnector.NAME, GeyserConnector.VERSION, MinecraftConstants.GAME_VERSION, BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion())); + String bedrockVersions; + List supportedCodecs = BedrockProtocol.SUPPORTED_BEDROCK_CODECS; + if (supportedCodecs.size() > 1) { + bedrockVersions = supportedCodecs.get(0).getMinecraftVersion() + " - " + supportedCodecs.get(supportedCodecs.size() - 1).getMinecraftVersion(); + } else { + bedrockVersions = BedrockProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion(); + } + + sender.sendMessage(LanguageUtils.getLocaleStringLog("geyser.commands.version.version", GeyserConnector.NAME, GeyserConnector.VERSION, MinecraftConstants.GAME_VERSION, bedrockVersions)); // Disable update checking in dev mode //noinspection ConstantConditions - changes in production diff --git a/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java b/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java index 904f70a0a..f040fc1fe 100644 --- a/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java +++ b/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java @@ -30,25 +30,26 @@ import com.nukkitx.protocol.bedrock.v407.Bedrock_v407; import com.nukkitx.protocol.bedrock.v408.Bedrock_v408; import com.nukkitx.protocol.bedrock.v409.Bedrock_v409; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; +import java.util.ArrayList; +import java.util.List; /** * Contains information about the supported Bedrock protocols in Geyser. */ public class BedrockProtocol { /** - * Default Bedrock codec that should act as a fallback and as the version shown in /geyser version + * Default Bedrock codec that should act as a fallback. Should represent the latest available + * release of the game that Geyser supports. */ public static final BedrockPacketCodec DEFAULT_BEDROCK_CODEC = Bedrock_v408.V408_CODEC; /** * A list of all supported Bedrock versions that can join Geyser */ - public static final Set SUPPORTED_BEDROCK_CODECS = ConcurrentHashMap.newKeySet(); + public static final List SUPPORTED_BEDROCK_CODECS = new ArrayList<>(); static { - SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); SUPPORTED_BEDROCK_CODECS.add(Bedrock_v407.V407_CODEC); + SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); SUPPORTED_BEDROCK_CODECS.add(Bedrock_v409.V409_CODEC); } diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index ec8b68297..15958f16c 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit ec8b68297c4d62a9e4d640a8c7e77977062628ee +Subproject commit 15958f16ca36c9536490f8af67f556eba5665dcb From 2f9ff0c6220376f276ca46f1e038411a834da356 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Sun, 30 Aug 2020 00:18:23 -0400 Subject: [PATCH 067/140] ShulkerBoxItemTranslator: Ensure Items ListTag is present (#1221) * ShulkerBoxItemTranslator: Ensure Items ListTag is present * Compile --- .../connector/network/translators/item/ItemTranslator.java | 4 ++-- .../item/translators/nbt/ShulkerBoxItemTranslator.java | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java index c08531205..13725522b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java @@ -35,7 +35,7 @@ import com.nukkitx.nbt.NbtType; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.geysermc.connector.GeyserConnector; @@ -358,7 +358,7 @@ public abstract class ItemTranslator { // If its not a message convert it if (!MessageUtils.isMessage(name)) { - TextComponent component = LegacyComponentSerializer.legacySection().deserialize(name); + Component component = LegacyComponentSerializer.legacySection().deserialize(name); name = GsonComponentSerializer.gson().serialize(component); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java index 1780537c5..0645c9807 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java @@ -42,6 +42,7 @@ public class ShulkerBoxItemTranslator extends NbtItemStackTranslator { if (!itemTag.contains("BlockEntityTag")) return; // Empty shulker box CompoundTag blockEntityTag = itemTag.get("BlockEntityTag"); + if (blockEntityTag.get("Items") == null) return; ListTag itemsList = new ListTag("Items"); for (Tag item : (ListTag) blockEntityTag.get("Items")) { CompoundTag itemData = (CompoundTag) item; // Information about the item From b5f6ada4aea1d5235ec89af508336e092f5ce002 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Tue, 1 Sep 2020 13:30:40 -0400 Subject: [PATCH 068/140] ScoreboardUpdater: Quick fix to lessen CPU usage (#1238) This prevents one/multiple CPU cores from taking up 100% usage. A better, permanent fix will replace this in the coming days. --- .../org/geysermc/connector/scoreboard/ScoreboardUpdater.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/ScoreboardUpdater.java b/connector/src/main/java/org/geysermc/connector/scoreboard/ScoreboardUpdater.java index ee38cecd9..3812fb141 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/ScoreboardUpdater.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/ScoreboardUpdater.java @@ -31,6 +31,7 @@ import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.cache.WorldCache; import org.geysermc.connector.utils.LanguageUtils; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class ScoreboardUpdater extends Thread { @@ -61,7 +62,7 @@ public class ScoreboardUpdater extends Thread { @Override public void run() { - while (!session.isClosed()) { + if (!session.isClosed()) { long currentTime = System.currentTimeMillis(); // reset score-packets per second every second @@ -99,6 +100,8 @@ public class ScoreboardUpdater extends Thread { } } } + + session.getConnector().getGeneralThreadPool().schedule(this, 50, TimeUnit.MILLISECONDS); } } From 7c4868cada6d133ad3e25ca2191bea0efd6ba49d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Tue, 1 Sep 2020 18:29:53 -0400 Subject: [PATCH 069/140] LocaleUtils: don't NPE if no default locale mapping exists (#1239) --- .../main/java/org/geysermc/connector/utils/LocaleUtils.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java index c09af2b7d..07c33eb1a 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java @@ -246,6 +246,11 @@ public class LocaleUtils { Map localeStrings = LocaleUtils.LOCALE_MAPPINGS.get(locale.toLowerCase()); if (localeStrings == null) localeStrings = LocaleUtils.LOCALE_MAPPINGS.get(LanguageUtils.getDefaultLocale()); + if (localeStrings == null) { + // Don't cause a NPE if the locale is STILL missing + GeyserConnector.getInstance().getLogger().debug("MISSING DEFAULT LOCALE: " + LanguageUtils.getDefaultLocale()); + return messageText; + } return localeStrings.getOrDefault(messageText, messageText); } From d717085c6b1689ad5b305c27d79ab3d9ac7290d3 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Wed, 2 Sep 2020 00:36:53 -0400 Subject: [PATCH 070/140] JaveNotifyClientTranslator: Translate invalid bed message (#1212) This isn't sent as its own message but as a specific event. --- .../translators/java/world/JavaNotifyClientTranslator.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java index 33e602eb3..a7bc7b61f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java @@ -43,6 +43,7 @@ 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.inventory.PlayerInventoryTranslator; +import org.geysermc.connector.utils.LocaleUtils; @Translator(packet = ServerNotifyClientPacket.class) public class JavaNotifyClientTranslator extends PacketTranslator { @@ -141,6 +142,11 @@ public class JavaNotifyClientTranslator extends PacketTranslator Date: Wed, 2 Sep 2020 00:37:24 -0400 Subject: [PATCH 071/140] GeyserSession: Always set Keep Inventory to true (#1213) * GeyserSession: Always set Keep Inventory to true This prevents the client from removing items on death in creative mode if Keep Inventory is true, but doesn't break existing behavior. Essentially, this assures full server-side behavior of the inventory during death. * Small comment update * OK, it was fine before the last commit, but make it better --- .../geysermc/connector/network/session/GeyserSession.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 3b27fc6ae..865ef0f4d 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 @@ -301,10 +301,13 @@ public class GeyserSession implements CommandSender { attributesPacket.setAttributes(attributes); upstream.sendPacket(attributesPacket); + GameRulesChangedPacket gamerulePacket = new GameRulesChangedPacket(); // Only allow the server to send health information // Setting this to false allows natural regeneration to work false but doesn't break it being true - GameRulesChangedPacket gamerulePacket = new GameRulesChangedPacket(); gamerulePacket.getGameRules().add(new GameRuleData<>("naturalregeneration", false)); + // Don't let the client modify the inventory on death + // Setting this to true allows keep inventory to work if enabled but doesn't break functionality being false + gamerulePacket.getGameRules().add(new GameRuleData<>("keepinventory", true)); upstream.sendPacket(gamerulePacket); } From b97bf56d99fdeae9e59332f759353aed9645b47c Mon Sep 17 00:00:00 2001 From: Comstepr <32700815+Comstepr@users.noreply.github.com> Date: Wed, 2 Sep 2020 12:37:49 +0800 Subject: [PATCH 072/140] Minor changes to config.yml (#1224) --- connector/src/main/resources/config.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index bde95d24b..7b0bd8513 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -13,14 +13,14 @@ bedrock: address: 0.0.0.0 # The port that will listen for connections port: 19132 - # Some hosting services change your Java port everytime you open the server, and require the same port to be used for Bedrock. + # Some hosting services change your Java port everytime you start the server and require the same port to be used for Bedrock. # This option makes the Bedrock port the same as the Java port every time you start the server. # This option is for the plugin version only. clone-remote-port: false - # The MOTD that will be broadcasted to Minecraft: Bedrock Edition clients. Irrelevant if "passthrough-motd" is set to true + # The MOTD that will be broadcasted to Minecraft: Bedrock Edition clients. This is irrelevant if "passthrough-motd" is set to true motd1: "GeyserMC" motd2: "Another GeyserMC forced host." - # The Server Name that will be sent to Minecraft: Bedrock Edition clients. Visible in both the pause menu and the settings menu. + # The Server Name that will be sent to Minecraft: Bedrock Edition clients. This is visible in both the pause menu and the settings menu. server-name: "Geyser" remote: # The IP address of the remote (Java Edition) server @@ -55,7 +55,7 @@ floodgate-key-file: public-key.pem # Disabling this will prevent command suggestions from being sent and solve freezing for Bedrock clients. command-suggestions: true -# The following three options enable "ping passthrough" -the MOTD, player count and/or protocol name gets retrieved from the Java server. +# The following three options enable "ping passthrough" - the MOTD, player count and/or protocol name gets retrieved from the Java server. # Relay the MOTD from the remote server to Bedrock players. passthrough-motd: false # Relay the protocol name (e.g. BungeeCord [X.X], Paper 1.X) - only really useful when using a custom protocol name! From 81a48bf96d0f336c4e2e96e67f77b3162e64ea77 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Wed, 2 Sep 2020 00:38:10 -0400 Subject: [PATCH 073/140] Relocate the rest of our dependencies (#1227) - Relocate all of our dependencies. This does not include MCProtocolLib and Nukkit dependencies at this time as there are no other known plugins that use these dependencies. - Switch to a static commit for Adventure dependencies. Tested working on all versions. --- bootstrap/bungeecord/pom.xml | 16 ++++++++++++++++ bootstrap/spigot/pom.xml | 12 ++++++++++++ bootstrap/sponge/pom.xml | 12 ++++++++++++ bootstrap/velocity/pom.xml | 16 ++++++++++++++++ connector/pom.xml | 12 ++++++------ 5 files changed, 62 insertions(+), 6 deletions(-) diff --git a/bootstrap/bungeecord/pom.xml b/bootstrap/bungeecord/pom.xml index 682690a21..3fb7090f8 100644 --- a/bootstrap/bungeecord/pom.xml +++ b/bootstrap/bungeecord/pom.xml @@ -61,6 +61,10 @@ net.md_5.bungee.jni org.geysermc.platform.bungeecord.shaded.jni + + com.fasterxml.jackson + org.geysermc.platform.bungeecord.shaded.jackson + io.netty org.geysermc.platform.bungeecord.shaded.netty @@ -69,6 +73,18 @@ org.reflections org.geysermc.platform.bungeecord.shaded.reflections + + com.google.guava + org.geysermc.platform.bungeecord.shaded.guava + + + org.dom4j + org.geysermc.platform.bungeecord.shaded.dom4j + + + net.kyori.adventure + org.geysermc.platform.bungeecord.shaded.adventure + diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml index 8b3ad7428..79bdfea01 100644 --- a/bootstrap/spigot/pom.xml +++ b/bootstrap/spigot/pom.xml @@ -79,6 +79,18 @@ org.reflections org.geysermc.platform.spigot.shaded.reflections + + com.google.guava + org.geysermc.platform.spigot.shaded.guava + + + org.dom4j + org.geysermc.platform.spigot.shaded.dom4j + + + net.kyori.adventure + org.geysermc.platform.spigot.shaded.adventure + diff --git a/bootstrap/sponge/pom.xml b/bootstrap/sponge/pom.xml index c21a3a539..3c20239f1 100644 --- a/bootstrap/sponge/pom.xml +++ b/bootstrap/sponge/pom.xml @@ -73,6 +73,18 @@ org.reflections org.geysermc.platform.sponge.shaded.reflections + + com.google.guava + org.geysermc.platform.sponge.shaded.guava + + + org.dom4j + org.geysermc.platform.sponge.shaded.dom4j + + + net.kyori.adventure + org.geysermc.platform.sponge.shaded.adventure + diff --git a/bootstrap/velocity/pom.xml b/bootstrap/velocity/pom.xml index 0ebb70cab..efe6c80a3 100644 --- a/bootstrap/velocity/pom.xml +++ b/bootstrap/velocity/pom.xml @@ -57,6 +57,10 @@ + + com.fasterxml.jackson + org.geysermc.platform.velocity.shaded.jackson + it.unimi.dsi.fastutil org.geysermc.platform.velocity.shaded.fastutil @@ -65,6 +69,18 @@ org.reflections org.geysermc.platform.velocity.shaded.reflections + + com.google.guava + org.geysermc.platform.velocity.shaded.guava + + + org.dom4j + org.geysermc.platform.velocity.shaded.dom4j + + + net.kyori.adventure + org.geysermc.platform.velocity.shaded.adventure + diff --git a/connector/pom.xml b/connector/pom.xml index 76a83c23f..fe35b6782 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -137,21 +137,21 @@ 2.1.3 - net.kyori + com.github.kyoripowered.adventure adventure-api - 4.0.0-SNAPSHOT + 557865caef compile - net.kyori + com.github.kyoripowered.adventure adventure-text-serializer-gson - 4.0.0-SNAPSHOT + 557865caef compile - net.kyori + com.github.kyoripowered.adventure adventure-text-serializer-legacy - 4.0.0-SNAPSHOT + 557865caef compile From dcf1731d8adc2f0f6ea176239a71cd7520da3038 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Wed, 2 Sep 2020 00:38:36 -0400 Subject: [PATCH 074/140] Implement correct sign wrapping (#1228) * Implement correct sign wrapping This commit ensures that the auto-wrapping nature of Bedrock with signs is corrected. If a Bedrock player sends a sign that is auto-wrapped, it will now be interpreted by Geyser to fit on multiple lines. Additionally, Geyser will crop incoming sign text to prevent auto-wrapping. * Don't wrap if it's the last line --- .../network/session/GeyserSession.java | 9 ++ .../BedrockBlockEntityDataTranslator.java | 59 +++++++---- .../entity/SignBlockEntityTranslator.java | 17 +++- .../geysermc/connector/utils/SignUtils.java | 99 +++++++++++++++++++ 4 files changed, 163 insertions(+), 21 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/utils/SignUtils.java 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 865ef0f4d..274230564 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 @@ -240,6 +240,15 @@ public class GeyserSession implements CommandSender { @Setter private boolean thunder = false; + /** + * Stores the last text inputted into a sign. + * + * Bedrock sends packets every time you update the sign, Java only wants the final packet. + * Until we determine that the user has finished editing, we save the sign's current status. + */ + @Setter + private String lastSignMessage; + private MinecraftProtocol protocol; public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServerSession) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockEntityDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockEntityDataTranslator.java index 38b940399..147b8a3b1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockEntityDataTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockEntityDataTranslator.java @@ -32,17 +32,11 @@ import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; - -import java.util.HashMap; -import java.util.Map; +import org.geysermc.connector.utils.SignUtils; @Translator(packet = BlockEntityDataPacket.class) public class BedrockBlockEntityDataTranslator extends PacketTranslator { - // In case two people are editing signs at the same time this array holds the temporary messages to be sent - // Position -> Message being held - protected static Map lastMessages = new HashMap<>(); - @Override public void translate(BlockEntityDataPacket packet, GeyserSession session) { NbtMap tag = packet.getData(); @@ -50,9 +44,8 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator SignUtils.JAVA_CHARACTER_WIDTH_MAX) { + // We need to apply some more logic if we went over the character width max + boolean wentOverMax = widthCount > SignUtils.JAVA_CHARACTER_WIDTH_MAX && character != '\n'; + widthCount = 0; + // Saves if we're moving a word to the next line + String word = null; + if (wentOverMax && iterator < lines.length - 1) { + // If we went over the max, we want to try to wrap properly like Bedrock does. + // So we look for a space in the Bedrock user's text to imply a word. + int index = newMessage.lastIndexOf(" "); + if (index != -1) { + // There is indeed a space in this line; let's get it + word = newMessage.substring(index + 1); + // 'Delete' that word from the string builder + newMessage.delete(index, newMessage.length()); + } + } lines[iterator] = newMessage.toString(); iterator++; - // Bedrock, for whatever reason, can hold a message out of bounds + // Bedrock, for whatever reason, can hold a message out of the bounds of the four lines // We don't care about that so we discard that if (iterator > lines.length - 1) { break; } newMessage = new StringBuilder(); + if (wentOverMax) { + // Apply the wrapped word to the new line + if (word != null) { + newMessage.append(word); + // And apply the width count + for (char wordCharacter : word.toCharArray()) { + widthCount += SignUtils.getCharacterWidth(wordCharacter); + } + } + // If we went over the max, we want to append the character to the new line. + newMessage.append(character); + widthCount += SignUtils.getCharacterWidth(character); + } } else newMessage.append(character); } // Put the final line on since it isn't done in the for loop if (iterator < lines.length) lines[iterator] = newMessage.toString(); + Position pos = new Position(tag.getInt("x"), tag.getInt("y"), tag.getInt("z")); ClientUpdateSignPacket clientUpdateSignPacket = new ClientUpdateSignPacket(pos, lines); session.sendDownstreamPacket(clientUpdateSignPacket); - //TODO (potentially): originally I was going to update the sign blocks so Bedrock and Java users would match visually - // However Java can still store a lot per-line and visuals are still messed up so that doesn't work - // We remove the sign position from map to indicate there is no work-in-progress sign - lastMessages.remove(pos); + // We set the sign text cached in the session to null to indicate there is no work-in-progress sign + session.setLastSignMessage(null); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java index ab9ba3065..acec16c5c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java @@ -29,6 +29,7 @@ import com.github.steveice10.mc.protocol.data.message.MessageSerializer; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.nbt.NbtMap; import org.geysermc.connector.utils.MessageUtils; +import org.geysermc.connector.utils.SignUtils; import java.util.HashMap; import java.util.Map; @@ -46,9 +47,17 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator { String signLine = getOrDefault(tag.getValue().get("Text" + currentLine), ""); signLine = MessageUtils.getBedrockMessage(MessageSerializer.fromString(signLine)); - //Java allows up to 16+ characters on certain symbols. - if(signLine.length() >= 15 && (signLine.contains("-") || signLine.contains("="))) { - signLine = signLine.substring(0, 14); + // Check the character width on the sign to ensure there is no overflow that is usually hidden + // to Java Edition clients but will appear to Bedrock clients + int signWidth = 0; + StringBuilder finalSignLine = new StringBuilder(); + for (char c : signLine.toCharArray()) { + signWidth += SignUtils.getCharacterWidth(c); + if (signWidth <= SignUtils.BEDROCK_CHARACTER_WIDTH_MAX) { + finalSignLine.append(c); + } else { + break; + } } // Java Edition 1.14 added the ability to change the text color of the whole sign using dye @@ -56,7 +65,7 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator { signText.append(getBedrockSignColor(tag.get("Color").getValue().toString())); } - signText.append(signLine); + signText.append(finalSignLine.toString()); signText.append("\n"); } diff --git a/connector/src/main/java/org/geysermc/connector/utils/SignUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SignUtils.java new file mode 100644 index 000000000..06406b55e --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/utils/SignUtils.java @@ -0,0 +1,99 @@ +/* + * 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.utils; + +/** + * Provides utilities for interacting with signs. Mainly, it deals with the widths of each character. + * Since Bedrock auto-wraps signs and Java does not, we have to take this into account when translating signs. + */ +public class SignUtils { + + // TODO: If we send the Java font via resource pack, does width change? + /** + * The maximum character width that a sign can hold in Bedrock + */ + public static final int BEDROCK_CHARACTER_WIDTH_MAX = 88; + + /** + * The maximum character width that a sign can hold in Java + */ + public static final int JAVA_CHARACTER_WIDTH_MAX = 90; + + /** + * Gets the Minecraft width of a character + * @param c character to determine + * @return width of the character + */ + public static int getCharacterWidth(char c) { + switch (c) { + case '!': + case ',': + case '.': + case ':': + case ';': + case 'i': + case '|': + case '¡': + return 2; + + case '\'': + case 'l': + case 'ì': + case 'í': + return 3; + + case ' ': + case 'I': + case '[': + case ']': + case 't': + case '×': + case 'ï': + return 4; + + case '"': + case '(': + case ')': + case '*': + case '<': + case '>': + case 'f': + case 'k': + case '{': + case '}': + return 5; + + case '@': + case '~': + case '®': + return 7; + + default: + return 6; + } + } + +} From b21f4773669a304694bc64ed5ed199263f5ab473 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Wed, 2 Sep 2020 00:39:06 -0400 Subject: [PATCH 075/140] Parrot mounting fixes (#1236) * Parrot mounting fixes - Fix duplicate parrots when a parrot leaves the player - Fix rotation of parrots * Remove critical debug information --- .../connector/entity/PlayerEntity.java | 31 ++++++++++++++++--- .../player/BedrockMovePlayerTranslator.java | 7 +++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java index da75812fb..58b32e622 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java @@ -43,6 +43,7 @@ import lombok.Getter; import lombok.Setter; import org.geysermc.connector.entity.attribute.Attribute; import org.geysermc.connector.entity.attribute.AttributeType; +import org.geysermc.connector.entity.living.animal.tameable.ParrotEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.cache.EntityEffectCache; @@ -65,8 +66,14 @@ public class PlayerEntity extends LivingEntity { private boolean playerList = true; // Player is in the player list private final EntityEffectCache effectCache; - private Entity leftParrot; - private Entity rightParrot; + /** + * Saves the parrot currently on the player's left shoulder; otherwise null + */ + private ParrotEntity leftParrot; + /** + * Saves the parrot currently on the player's right shoulder; otherwise null + */ + private ParrotEntity rightParrot; public PlayerEntity(GameProfile gameProfile, long entityId, long geyserId, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, EntityType.PLAYER, position, motion, rotation); @@ -189,6 +196,12 @@ public class PlayerEntity extends LivingEntity { @Override public void updatePositionAndRotation(GeyserSession session, double moveX, double moveY, double moveZ, float yaw, float pitch, boolean isOnGround) { moveRelative(session, moveX, moveY, moveZ, yaw, pitch, isOnGround); + if (leftParrot != null) { + leftParrot.moveRelative(session, moveX, moveY, moveZ, yaw, pitch, isOnGround); + } + if (rightParrot != null) { + rightParrot.moveRelative(session, moveX, moveY, moveZ, yaw, pitch, isOnGround); + } } @Override @@ -202,6 +215,12 @@ public class PlayerEntity extends LivingEntity { movePlayerPacket.setOnGround(isOnGround); movePlayerPacket.setMode(MovePlayerPacket.Mode.HEAD_ROTATION); session.sendUpstreamPacket(movePlayerPacket); + if (leftParrot != null) { + leftParrot.updateRotation(session, yaw, pitch, isOnGround); + } + if (rightParrot != null) { + rightParrot.updateRotation(session, yaw, pitch, isOnGround); + } } @Override @@ -237,11 +256,15 @@ public class PlayerEntity extends LivingEntity { } // Parrot occupying shoulder - if ((entityMetadata.getId() == 18 && leftParrot == null) || (entityMetadata.getId() == 19 && rightParrot == null)) { // null check since this code just creates the parrot + if (entityMetadata.getId() == 18 || entityMetadata.getId() == 19) { CompoundTag tag = (CompoundTag) entityMetadata.getValue(); if (tag != null && !tag.isEmpty()) { + if ((entityMetadata.getId() == 18 && leftParrot != null) || (entityMetadata.getId() == 19 && rightParrot != null)) { + // No need to update a parrot's data when it already exists + return; + } // The parrot is a separate entity in Bedrock, but part of the player entity in Java - Entity parrot = new Entity(0, session.getEntityCache().getNextEntityId().incrementAndGet(), + ParrotEntity parrot = new ParrotEntity(0, session.getEntityCache().getNextEntityId().incrementAndGet(), EntityType.PARROT, position, motion, rotation); parrot.spawnEntity(session); parrot.getMetadata().put(EntityData.VARIANT, tag.get("Variant").getValue()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java index be918ba74..8809941b9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockMovePlayerTranslator.java @@ -86,6 +86,13 @@ public class BedrockMovePlayerTranslator extends PacketTranslator Date: Wed, 2 Sep 2020 00:39:14 -0400 Subject: [PATCH 076/140] Translate scoreboard nametag visibility (#1240) This commit adds support for name tag visibility in teams. If a player is set to hide their nametag, it will be hidden from the Bedrock client. Notably, this fixes most NPC nametag hiding, including Citizens. This does not fix some NPC nametag hiding - there are several NPCs in Hypixel that still have a nametag show up, and they are not a part of any team. --- .../geysermc/connector/entity/PlayerEntity.java | 14 +++++++++++++- .../java/scoreboard/JavaTeamTranslator.java | 2 ++ .../org/geysermc/connector/scoreboard/Team.java | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java index 58b32e622..0c269b10d 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java @@ -27,6 +27,7 @@ package org.geysermc.connector.entity; 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.scoreboard.NameTagVisibility; import com.github.steveice10.mc.protocol.data.message.TextMessage; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector3f; @@ -240,7 +241,18 @@ public class PlayerEntity extends LivingEntity { } Team team = session.getWorldCache().getScoreboard().getTeamFor(username); if (team != null) { - metadata.put(EntityData.NAMETAG, team.getPrefix() + MessageUtils.toChatColor(team.getColor()) + username + team.getSuffix()); + // Cover different visibility settings + if (team.getNameTagVisibility() == NameTagVisibility.NEVER) { + metadata.put(EntityData.NAMETAG, ""); + } else if (team.getNameTagVisibility() == NameTagVisibility.HIDE_FOR_OTHER_TEAMS && + !team.getEntities().contains(session.getPlayerEntity().getUsername())) { + metadata.put(EntityData.NAMETAG, ""); + } else if (team.getNameTagVisibility() == NameTagVisibility.HIDE_FOR_OWN_TEAM && + team.getEntities().contains(session.getPlayerEntity().getUsername())) { + metadata.put(EntityData.NAMETAG, ""); + } else { + metadata.put(EntityData.NAMETAG, team.getPrefix() + MessageUtils.toChatColor(team.getColor()) + username + team.getSuffix()); + } } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaTeamTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaTeamTranslator.java index 684245d47..998e5effe 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaTeamTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/scoreboard/JavaTeamTranslator.java @@ -61,6 +61,7 @@ public class JavaTeamTranslator extends PacketTranslator { scoreboard.registerNewTeam(packet.getTeamName(), toPlayerSet(packet.getPlayers())) .setName(MessageUtils.getBedrockMessage(packet.getDisplayName())) .setColor(packet.getColor()) + .setNameTagVisibility(packet.getNameTagVisibility()) .setPrefix(MessageUtils.getTranslatedBedrockMessage(packet.getPrefix(), session.getClientData().getLanguageCode())) .setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getClientData().getLanguageCode())); break; @@ -75,6 +76,7 @@ public class JavaTeamTranslator extends PacketTranslator { team.setName(MessageUtils.getBedrockMessage(packet.getDisplayName())) .setColor(packet.getColor()) + .setNameTagVisibility(packet.getNameTagVisibility()) .setPrefix(MessageUtils.getTranslatedBedrockMessage(packet.getPrefix(), session.getClientData().getLanguageCode())) .setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getClientData().getLanguageCode())) .setUpdateType(UpdateType.UPDATE); diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java index 087ea2dde..a073e2e99 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Team.java @@ -25,6 +25,7 @@ package org.geysermc.connector.scoreboard; +import com.github.steveice10.mc.protocol.data.game.scoreboard.NameTagVisibility; import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import lombok.Getter; @@ -44,6 +45,7 @@ public class Team { private UpdateType updateType = UpdateType.ADD; private String name; + private NameTagVisibility nameTagVisibility; private String prefix; private TeamColor color; private String suffix; From 4c58568eb411cc4a83bc772148f7f5956de68d9b Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Wed, 2 Sep 2020 23:42:53 -0400 Subject: [PATCH 077/140] Relocate Google Common (#1242) Fixes NoSuchMethodErrors from occuring on certain Spigot servers. --- bootstrap/bungeecord/pom.xml | 6 +++++- bootstrap/spigot/pom.xml | 6 +++++- bootstrap/sponge/pom.xml | 6 +++++- bootstrap/velocity/pom.xml | 6 +++++- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/bootstrap/bungeecord/pom.xml b/bootstrap/bungeecord/pom.xml index 3fb7090f8..44b28e931 100644 --- a/bootstrap/bungeecord/pom.xml +++ b/bootstrap/bungeecord/pom.xml @@ -73,9 +73,13 @@ org.reflections org.geysermc.platform.bungeecord.shaded.reflections + + com.google.common + org.geysermc.platform.bungeecord.shaded.google.common + com.google.guava - org.geysermc.platform.bungeecord.shaded.guava + org.geysermc.platform.bungeecord.shaded.google.guava org.dom4j diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml index 79bdfea01..194ec6e5a 100644 --- a/bootstrap/spigot/pom.xml +++ b/bootstrap/spigot/pom.xml @@ -79,9 +79,13 @@ org.reflections org.geysermc.platform.spigot.shaded.reflections + + com.google.common + org.geysermc.platform.spigot.shaded.google.common + com.google.guava - org.geysermc.platform.spigot.shaded.guava + org.geysermc.platform.spigot.shaded.google.guava org.dom4j diff --git a/bootstrap/sponge/pom.xml b/bootstrap/sponge/pom.xml index 3c20239f1..132f38173 100644 --- a/bootstrap/sponge/pom.xml +++ b/bootstrap/sponge/pom.xml @@ -73,9 +73,13 @@ org.reflections org.geysermc.platform.sponge.shaded.reflections + + com.google.common + org.geysermc.platform.sponge.shaded.google.common + com.google.guava - org.geysermc.platform.sponge.shaded.guava + org.geysermc.platform.sponge.shaded.google.guava org.dom4j diff --git a/bootstrap/velocity/pom.xml b/bootstrap/velocity/pom.xml index efe6c80a3..ee445b6e7 100644 --- a/bootstrap/velocity/pom.xml +++ b/bootstrap/velocity/pom.xml @@ -69,9 +69,13 @@ org.reflections org.geysermc.platform.velocity.shaded.reflections + + com.google.common + org.geysermc.platform.velocity.shaded.google.common + com.google.guava - org.geysermc.platform.velocity.shaded.guava + org.geysermc.platform.velocity.shaded.google.guava org.dom4j From 5b76a858954ebc2e41058f1ebcf4c4faa28b8589 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Thu, 3 Sep 2020 19:00:36 -0400 Subject: [PATCH 078/140] Non-full-chunk support (#574) This commit adds non-full chunk support if chunk caching is enabled. --- .gitignore | 2 +- bootstrap/spigot/pom.xml | 2 +- .../platform/spigot/GeyserSpigotPlugin.java | 12 +- .../world/GeyserSpigotWorldManager.java | 93 ++++++++++- .../spigot/src/main/resources/biomes.json | 155 ++++++++++++++++++ .../network/session/cache/ChunkCache.java | 11 +- .../java/world/JavaChunkDataTranslator.java | 26 ++- .../translators/world/GeyserWorldManager.java | 8 + .../translators/world/WorldManager.java | 10 ++ .../geysermc/connector/utils/ChunkUtils.java | 18 +- 10 files changed, 320 insertions(+), 17 deletions(-) create mode 100644 bootstrap/spigot/src/main/resources/biomes.json diff --git a/.gitignore b/.gitignore index c4c878af4..69c07e500 100644 --- a/.gitignore +++ b/.gitignore @@ -241,4 +241,4 @@ config.yml logs/ public-key.pem locales/ -cache/ \ No newline at end of file +/cache/ \ No newline at end of file diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml index 194ec6e5a..714c0852f 100644 --- a/bootstrap/spigot/pom.xml +++ b/bootstrap/spigot/pom.xml @@ -20,7 +20,7 @@ org.spigotmc spigot-api - 1.14-R0.1-SNAPSHOT + 1.15.2-R0.1-SNAPSHOT provided diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java index d51cf21a5..9cc0bc064 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/GeyserSpigotPlugin.java @@ -56,7 +56,6 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { private GeyserSpigotConfiguration geyserConfig; private GeyserSpigotLogger geyserLogger; private IGeyserPingPassthrough geyserSpigotPingPassthrough; - private GeyserSpigotBlockPlaceListener blockPlaceListener; private GeyserSpigotWorldManager geyserWorldManager; private GeyserConnector connector; @@ -124,10 +123,15 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { // Used to determine if Block.getBlockData() is present. boolean isLegacy = !isCompatible(Bukkit.getServer().getVersion(), "1.13.0"); if (isLegacy) - geyserLogger.debug("Legacy version of Minecraft (1.12.2 or older) detected."); + geyserLogger.debug("Legacy version of Minecraft (1.12.2 or older) detected; falling back to ViaVersion for block state retrieval."); - this.geyserWorldManager = new GeyserSpigotWorldManager(isLegacy, isViaVersion); - this.blockPlaceListener = new GeyserSpigotBlockPlaceListener(connector, isLegacy, isViaVersion); + boolean use3dBiomes = isCompatible(Bukkit.getServer().getVersion(), "1.16.0"); + if (!use3dBiomes) { + geyserLogger.debug("Legacy version of Minecraft (1.15.2 or older) detected; not using 3D biomes."); + } + + this.geyserWorldManager = new GeyserSpigotWorldManager(isLegacy, use3dBiomes, isViaVersion); + GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(connector, isLegacy, isViaVersion); Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java index 4873a1750..c6443bd05 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java @@ -25,23 +25,71 @@ package org.geysermc.platform.spigot.world; -import lombok.AllArgsConstructor; +import com.fasterxml.jackson.databind.JsonNode; +import it.unimi.dsi.fastutil.ints.Int2IntMap; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.block.Biome; import org.bukkit.block.Block; +import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.GeyserWorldManager; import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.GameRule; +import org.geysermc.connector.utils.LanguageUtils; import us.myles.ViaVersion.protocols.protocol1_13_1to1_13.Protocol1_13_1To1_13; import us.myles.ViaVersion.protocols.protocol1_16_2to1_16_1.data.MappingData; -@AllArgsConstructor +import java.io.InputStream; + public class GeyserSpigotWorldManager extends GeyserWorldManager { private final boolean isLegacy; - // You need ViaVersion to connect to an older server with Geyser. - // However, we still check for ViaVersion in case there's some other way that gets Geyser on a pre-1.13 Bukkit server + private final boolean use3dBiomes; + /** + * You need ViaVersion to connect to an older server with Geyser. + * However, we still check for ViaVersion in case there's some other way that gets Geyser on a pre-1.13 Bukkit server + */ private final boolean isViaVersion; + /** + * Stores a list of {@link Biome} ordinal numbers to Minecraft biome numeric IDs. + * + * Working with the Biome enum in Spigot poses two problems: + * 1: The Biome enum values change in both order and names over the years. + * 2: There is no way to get the Minecraft biome ID from the name itself with Spigot. + * To solve both of these problems, we store a JSON file of every Biome enum that has existed, + * along with its 1.16 biome number. + * + * The key is the Spigot Biome ordinal; the value is the Minecraft Java biome numerical ID + */ + private final Int2IntMap biomeToIdMap = new Int2IntOpenHashMap(Biome.values().length); + + public GeyserSpigotWorldManager(boolean isLegacy, boolean use3dBiomes, boolean isViaVersion) { + this.isLegacy = isLegacy; + this.use3dBiomes = use3dBiomes; + this.isViaVersion = isViaVersion; + + // Load the values into the biome-to-ID map + InputStream biomeStream = FileUtils.getResource("biomes.json"); + JsonNode biomes; + try { + biomes = GeyserConnector.JSON_MAPPER.readTree(biomeStream); + } catch (Exception e) { + throw new AssertionError(LanguageUtils.getLocaleStringLog("geyser.toolbox.fail.runtime_java"), e); + } + // Only load in the biomes that are present in this version of Minecraft + for (Biome enumBiome : Biome.values()) { + if (biomes.has(enumBiome.toString())) { + biomeToIdMap.put(enumBiome.ordinal(), biomes.get(enumBiome.toString()).intValue()); + } else { + GeyserConnector.getInstance().getLogger().debug("No biome mapping found for " + enumBiome.toString() + + ", defaulting to 0"); + biomeToIdMap.put(enumBiome.ordinal(), 0); + } + } + } @Override public int getBlockAt(GeyserSession session, int x, int y, int z) { @@ -77,6 +125,43 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { } @Override + @SuppressWarnings("deprecation") + public int[] getBiomeDataAt(GeyserSession session, int x, int z) { + if (session.getPlayerEntity() == null) { + return new int[1024]; + } + int[] biomeData = new int[1024]; + World world = Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld(); + int chunkX = x << 4; + int chunkZ = z << 4; + int chunkXmax = chunkX + 16; + int chunkZmax = chunkZ + 16; + // 3D biomes didn't exist until 1.15 + if (use3dBiomes) { + for (int localX = chunkX; localX < chunkXmax; localX += 4) { + for (int localY = 0; localY < 255; localY += + 4) { + for (int localZ = chunkZ; localZ < chunkZmax; localZ += 4) { + // Index is based on wiki.vg's index requirements + final int i = ((localY >> 2) & 63) << 4 | ((localZ >> 2) & 3) << 2 | ((localX >> 2) & 3); + biomeData[i] = biomeToIdMap.getOrDefault(world.getBiome(localX, localY, localZ).ordinal(), 0); + } + } + } + } else { + // Looks like the same code, but we're not checking the Y coordinate here + for (int localX = chunkX; localX < chunkXmax; localX += 4) { + for (int localY = 0; localY < 255; localY += + 4) { + for (int localZ = chunkZ; localZ < chunkZmax; localZ += 4) { + // Index is based on wiki.vg's index requirements + final int i = ((localY >> 2) & 63) << 4 | ((localZ >> 2) & 3) << 2 | ((localX >> 2) & 3); + biomeData[i] = biomeToIdMap.getOrDefault(world.getBiome(localX, localZ).ordinal(), 0); + } + } + } + } + return biomeData; + } + public Boolean getGameRuleBool(GeyserSession session, GameRule gameRule) { return Boolean.parseBoolean(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getGameRuleValue(gameRule.getJavaID())); } diff --git a/bootstrap/spigot/src/main/resources/biomes.json b/bootstrap/spigot/src/main/resources/biomes.json new file mode 100644 index 000000000..56520e914 --- /dev/null +++ b/bootstrap/spigot/src/main/resources/biomes.json @@ -0,0 +1,155 @@ +{ + "MUTATED_ICE_FLATS" : 140, + "MUTATED_TAIGA" : 133, + "SAVANNA_PLATEAU_MOUNTAINS" : 164, + "DEEP_WARM_OCEAN" : 47, + "REDWOOD_TAIGA_HILLS" : 33, + "THE_VOID" : 127, + "COLD_TAIGA_MOUNTAINS" : 158, + "BAMBOO_JUNGLE_HILLS" : 169, + "MOUNTAINS" : 3, + "MESA_PLATEAU" : 39, + "SNOWY_TAIGA_HILLS" : 31, + "DEEP_FROZEN_OCEAN" : 50, + "EXTREME_HILLS" : 3, + "BIRCH_FOREST_MOUNTAINS" : 155, + "FOREST" : 4, + "BIRCH_FOREST" : 27, + "SNOWY_TUNDRA" : 12, + "ICE_SPIKES" : 140, + "FROZEN_OCEAN" : 10, + "WARPED_FOREST" : 172, + "WOODED_BADLANDS_PLATEAU" : 38, + "BADLANDS_PLATEAU" : 39, + "ICE_PLAINS_SPIKES" : 140, + "MEGA_TAIGA" : 32, + "MUTATED_SAVANNA_ROCK" : 164, + "SAVANNA_PLATEAU" : 36, + "DARK_FOREST_HILLS" : 157, + "END_MIDLANDS" : 41, + "SHATTERED_SAVANNA_PLATEAU" : 164, + "SAVANNA" : 35, + "MUSHROOM_ISLAND_SHORE" : 15, + "SWAMP" : 6, + "ICE_MOUNTAINS" : 13, + "BEACH" : 16, + "MUTATED_MESA_CLEAR_ROCK" : 167, + "END_HIGHLANDS" : 42, + "COLD_BEACH" : 26, + "JUNGLE" : 21, + "MUTATED_TAIGA_COLD" : 158, + "TALL_BIRCH_HILLS" : 156, + "DARK_FOREST" : 29, + "WOODED_HILLS" : 18, + "HELL" : 8, + "MUTATED_REDWOOD_TAIGA" : 160, + "MESA_PLATEAU_FOREST" : 38, + "MUSHROOM_ISLAND" : 14, + "BADLANDS" : 37, + "END_BARRENS" : 43, + "MUTATED_EXTREME_HILLS_WITH_TREES" : 162, + "MUTATED_JUNGLE_EDGE" : 151, + "MODIFIED_BADLANDS_PLATEAU" : 167, + "ROOFED_FOREST_MOUNTAINS" : 157, + "SOUL_SAND_VALLEY" : 170, + "DESERT" : 2, + "MUTATED_PLAINS" : 129, + "MUTATED_BIRCH_FOREST" : 155, + "WOODED_MOUNTAINS" : 34, + "TAIGA_HILLS" : 19, + "BAMBOO_JUNGLE" : 168, + "SWAMPLAND_MOUNTAINS" : 134, + "DESERT_MOUNTAINS" : 130, + "REDWOOD_TAIGA" : 32, + "MUSHROOM_FIELDS" : 14, + "GIANT_TREE_TAIGA_HILLS" : 33, + "PLAINS" : 1, + "JUNGLE_EDGE" : 23, + "SAVANNA_MOUNTAINS" : 163, + "DEEP_COLD_OCEAN" : 49, + "DESERT_LAKES" : 130, + "MOUNTAIN_EDGE" : 20, + "SNOWY_MOUNTAINS" : 13, + "MESA_PLATEAU_MOUNTAINS" : 167, + "JUNGLE_MOUNTAINS" : 149, + "SMALLER_EXTREME_HILLS" : 20, + "MESA_PLATEAU_FOREST_MOUNTAINS" : 166, + "NETHER_WASTES" : 8, + "BIRCH_FOREST_HILLS_MOUNTAINS" : 156, + "MUTATED_JUNGLE" : 149, + "WARM_OCEAN" : 44, + "DEEP_OCEAN" : 24, + "STONE_BEACH" : 25, + "MODIFIED_JUNGLE" : 149, + "MUTATED_SAVANNA" : 163, + "TAIGA_COLD_HILLS" : 31, + "OCEAN" : 0, + "SMALL_END_ISLANDS" : 40, + "MUSHROOM_FIELD_SHORE" : 15, + "GRAVELLY_MOUNTAINS" : 131, + "FROZEN_RIVER" : 11, + "TAIGA_COLD" : 30, + "BASALT_DELTAS" : 173, + "EXTREME_HILLS_WITH_TREES" : 34, + "MEGA_TAIGA_HILLS" : 33, + "MUTATED_FOREST" : 132, + "MUTATED_BIRCH_FOREST_HILLS" : 156, + "SKY" : 9, + "LUKEWARM_OCEAN" : 45, + "EXTREME_HILLS_MOUNTAINS" : 131, + "COLD_TAIGA_HILLS" : 31, + "THE_END" : 9, + "SUNFLOWER_PLAINS" : 129, + "SAVANNA_ROCK" : 36, + "ERODED_BADLANDS" : 165, + "STONE_SHORE" : 25, + "EXTREME_HILLS_PLUS_MOUNTAINS" : 162, + "CRIMSON_FOREST" : 171, + "VOID" : 127, + "SNOWY_TAIGA" : 30, + "SNOWY_TAIGA_MOUNTAINS" : 158, + "FLOWER_FOREST" : 132, + "COLD_OCEAN" : 46, + "BEACHES" : 16, + "MESA" : 37, + "MUSHROOM_SHORE" : 15, + "MESA_CLEAR_ROCK" : 39, + "NETHER" : 8, + "ICE_PLAINS" : 12, + "SHATTERED_SAVANNA" : 163, + "ROOFED_FOREST" : 29, + "GIANT_SPRUCE_TAIGA_HILLS" : 161, + "SNOWY_BEACH" : 26, + "MESA_BRYCE" : 165, + "JUNGLE_EDGE_MOUNTAINS" : 151, + "MUTATED_DESERT" : 130, + "MODIFIED_GRAVELLY_MOUNTAINS" : 158, + "MEGA_SPRUCE_TAIGA" : 160, + "TAIGA_MOUNTAINS" : 133, + "SMALL_MOUNTAINS" : 20, + "EXTREME_HILLS_PLUS" : 34, + "GIANT_SPRUCE_TAIGA" : 160, + "FOREST_HILLS" : 18, + "DESERT_HILLS" : 17, + "MUTATED_REDWOOD_TAIGA_HILLS" : 161, + "MEGA_SPRUCE_TAIGA_HILLS" : 161, + "RIVER" : 7, + "GIANT_TREE_TAIGA" : 32, + "SWAMPLAND" : 6, + "JUNGLE_HILLS" : 22, + "TALL_BIRCH_FOREST" : 155, + "DEEP_LUKEWARM_OCEAN" : 48, + "MESA_ROCK" : 38, + "SWAMP_HILLS" : 134, + "MODIFIED_WOODED_BADLANDS_PLATEAU" : 166, + "MODIFIED_JUNGLE_EDGE" : 151, + "BIRCH_FOREST_HILLS" : 28, + "COLD_TAIGA" : 30, + "TAIGA" : 5, + "MUTATED_MESA_ROCK" : 166, + "MUTATED_SWAMPLAND" : 134, + "ICE_FLATS" : 12, + "MUTATED_ROOFED_FOREST" : 157, + "MUTATED_MESA" : 165, + "MUTATED_EXTREME_HILLS" : 131 +} diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java index 9601a2981..2cc9ea134 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java @@ -42,7 +42,7 @@ public class ChunkCache { private final boolean cache; @Getter - private Map chunks = new HashMap<>(); + private final Map chunks = new HashMap<>(); public ChunkCache(GeyserSession session) { if (session.getConnector().getWorldManager().getClass() == GeyserBootstrap.DEFAULT_CHUNK_MANAGER.getClass()) { @@ -57,6 +57,15 @@ public class ChunkCache { return; } ChunkPosition position = new ChunkPosition(chunk.getX(), chunk.getZ()); + if (chunk.getBiomeData() == null && chunks.containsKey(position)) { + Column newColumn = chunk; + chunk = chunks.get(position); + for (int i = 0; i < newColumn.getChunks().length; i++) { + if (newColumn.getChunks()[i] != null) { + chunk.getChunks()[i] = newColumn.getChunks()[i]; + } + } + } chunks.put(position, chunk); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java index 5170929a9..6dae9b4d6 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java @@ -45,18 +45,33 @@ import org.geysermc.connector.utils.ChunkUtils; @Translator(packet = ServerChunkDataPacket.class) public class JavaChunkDataTranslator extends PacketTranslator { + /** + * Determines if we should process non-full chunks + */ + private final boolean isCacheChunks; + + public JavaChunkDataTranslator() { + isCacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks(); + } + @Override public void translate(ServerChunkDataPacket packet, GeyserSession session) { if (session.isSpawned()) { ChunkUtils.updateChunkPosition(session, session.getPlayerEntity().getPosition().toInt()); } - if (packet.getColumn().getBiomeData() == null) //Non-full chunk + if (packet.getColumn().getBiomeData() == null && !isCacheChunks) { + // Non-full chunk without chunk caching + session.getConnector().getLogger().debug("Not sending non-full chunk because chunk caching is off."); return; + } + + // Non-full chunks don't have all the chunk data, and Bedrock won't accept that + final boolean isNonFullChunk = (packet.getColumn().getBiomeData() == null); GeyserConnector.getInstance().getGeneralThreadPool().execute(() -> { try { - ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(packet.getColumn()); + ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(session, packet.getColumn(), isNonFullChunk); ByteBuf byteBuf = Unpooled.buffer(32); ChunkSection[] sections = chunkData.sections; @@ -71,7 +86,12 @@ public class JavaChunkDataTranslator extends PacketTranslator Date: Fri, 4 Sep 2020 14:08:04 -0400 Subject: [PATCH 079/140] Update mappings to fix 1.16 wood slabs (#1249) --- connector/src/main/resources/mappings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index 15958f16c..28a22d2ba 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 15958f16ca36c9536490f8af67f556eba5665dcb +Subproject commit 28a22d2baad680f511bffc36d90d06bf626f0527 From d47360d6fb7b1625b89968deb6e9d829099d099a Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Sat, 5 Sep 2020 16:22:31 -0400 Subject: [PATCH 080/140] GeyserSession: send command permission level OPERATOR if qualified (#1254) Mobile clients have a GUI for commands that shows if CommandPermission.OPERATOR or higher is sent. The commands present all require OP permission 2 or higher; therefore we set that command permission if the server tells us we have a OP permission level of 2 or higher. --- .../geysermc/connector/network/session/GeyserSession.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 274230564..7a61eb220 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 @@ -710,7 +710,10 @@ public class GeyserSession implements CommandSender { public void sendAdventureSettings() { AdventureSettingsPacket adventureSettingsPacket = new AdventureSettingsPacket(); adventureSettingsPacket.setUniqueEntityId(playerEntity.getGeyserId()); - adventureSettingsPacket.setCommandPermission(CommandPermission.NORMAL); + // Set command permission if OP permission level is high enough + // This allows mobile players access to a GUI for doing commands. The commands there do not change above OPERATOR + // and all commands there are accessible with OP permission level 2 + adventureSettingsPacket.setCommandPermission(opPermissionLevel >= 2 ? CommandPermission.OPERATOR : CommandPermission.NORMAL); adventureSettingsPacket.setPlayerPermission(PlayerPermission.MEMBER); Set flags = new HashSet<>(); From 8c8630814d5f2e238844ade6d3d65c053b47b0c7 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Thu, 10 Sep 2020 11:30:56 -0400 Subject: [PATCH 081/140] Update to 1.16.3 (#1272) * Update for 1.16.3-rc1 * Update to 1.16.3 * Update README * Update MCProtocolLib --- README.md | 2 +- bootstrap/spigot/pom.xml | 2 +- connector/pom.xml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ead5b3b70..156745df3 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have now joined us here! -### Currently supporting Minecraft Bedrock v1.16.x and Minecraft Java v1.16.2. +### Currently supporting Minecraft Bedrock v1.16.x and Minecraft Java v1.16.3. ## Setting Up Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set up Geyser. diff --git a/bootstrap/spigot/pom.xml b/bootstrap/spigot/pom.xml index 714c0852f..d4dc33260 100644 --- a/bootstrap/spigot/pom.xml +++ b/bootstrap/spigot/pom.xml @@ -26,7 +26,7 @@ us.myles viaversion - 3.1.0 + 3.1.1 provided diff --git a/connector/pom.xml b/connector/pom.xml index fe35b6782..5a19a5015 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -109,9 +109,9 @@ compile - com.github.steveice10 + com.github.GeyserMC mcprotocollib - b3cf3acbb3 + e4a3aa636a compile From 46c34842d8de76a51cf5ef4fdf1b53369d07a2eb Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Sat, 12 Sep 2020 09:47:43 -0400 Subject: [PATCH 082/140] BedrockInventoryTransactionTranslator: check to make sure bucket usage is on purpose (#1280) Otherwise buckets can be activated when opening block inventories. --- .../bedrock/BedrockInventoryTransactionTranslator.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java index f8e4eb6d0..cc1da85e8 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java @@ -98,7 +98,9 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator Date: Mon, 14 Sep 2020 20:40:41 -0400 Subject: [PATCH 083/140] Check if Fireworks tag is null (#1255) Thank you Mineplex, very cool. --- .../java/org/geysermc/connector/entity/FireworkEntity.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java b/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java index b940b87b9..c100c6f37 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/FireworkEntity.java @@ -72,6 +72,10 @@ public class FireworkEntity extends Entity { } CompoundTag fireworks = tag.get("Fireworks"); + if (fireworks == null) { + // Thank you Mineplex very cool + return; + } NbtMapBuilder fireworksBuilder = NbtMap.builder(); if (fireworks.get("Flight") != null) { From 26802e6dabb796121cb21a95a3e17175b68e959a Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 14 Sep 2020 20:50:07 -0400 Subject: [PATCH 084/140] Translate CanPlaceOn/CanDestroy NBT (#1253) * Translate CanPlaceOn/CanDestroy NBT This commit adds support for the translation of the CanPlaceOn/CanDestroy NBT for Bedrock clients. * Remove debug line --- .../connector/entity/ItemFrameEntity.java | 9 +---- .../translators/item/ItemRegistry.java | 17 +++++++++ .../translators/item/ItemTranslator.java | 35 +++++++++++++++++++ .../nbt/ShulkerBoxItemTranslator.java | 9 +---- .../world/block/BlockTranslator.java | 34 +++++++++++++----- 5 files changed, 79 insertions(+), 25 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java index 392cec24c..f9d2ace46 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/ItemFrameEntity.java @@ -34,7 +34,6 @@ import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; -import com.nukkitx.protocol.bedrock.packet.StartGamePacket; import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; @@ -102,13 +101,7 @@ public class ItemFrameEntity extends Entity { ItemEntry itemEntry = ItemRegistry.getItem((ItemStack) entityMetadata.getValue()); NbtMapBuilder builder = NbtMap.builder(); - String blockName = ""; - for (StartGamePacket.ItemEntry startGamePacketItemEntry : ItemRegistry.ITEMS) { - if (startGamePacketItemEntry.getId() == (short) itemEntry.getBedrockId()) { - blockName = startGamePacketItemEntry.getIdentifier(); - break; - } - } + String blockName = ItemRegistry.getBedrockIdentifer(itemEntry); builder.putByte("Count", (byte) itemData.getCount()); if (itemData.getTag() != null) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java index 56ed2d6e0..1f3af019f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java @@ -221,6 +221,23 @@ public class ItemRegistry { .stream().filter(itemEntry -> itemEntry.getJavaIdentifier().equals(key)).findFirst().orElse(null)); } + /** + * Finds the Bedrock string identifier of an ItemEntry + * + * @param entry the ItemEntry to search for + * @return the Bedrock identifier + */ + public static String getBedrockIdentifer(ItemEntry entry) { + String blockName = ""; + for (StartGamePacket.ItemEntry startGamePacketItemEntry : ItemRegistry.ITEMS) { + if (startGamePacketItemEntry.getId() == (short) entry.getBedrockId()) { + blockName = startGamePacketItemEntry.getIdentifier(); // Find the Bedrock string name + break; + } + } + return blockName; + } + /** * Gets a Bedrock {@link ItemData} from a {@link JsonNode} * @param itemNode the JSON node that contains ProxyPass-compatible Bedrock item data diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java index 13725522b..864e9fe03 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java @@ -41,6 +41,7 @@ import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; +import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.connector.utils.MessageUtils; @@ -154,9 +155,43 @@ public abstract class ItemTranslator { itemData = DEFAULT_TRANSLATOR.translateToBedrock(itemStack, bedrockItem); } + if (nbt != null) { + // Translate the canDestroy and canPlaceOn Java NBT + ListTag canDestroy = nbt.get("CanDestroy"); + String[] canBreak = new String[0]; + ListTag canPlaceOn = nbt.get("CanPlaceOn"); + String[] canPlace = new String[0]; + canBreak = getCanModify(canDestroy, canBreak); + canPlace = getCanModify(canPlaceOn, canPlace); + itemData = ItemData.of(itemData.getId(), itemData.getDamage(), itemData.getCount(), itemData.getTag(), canPlace, canBreak); + } + return itemData; } + /** + * Translates the Java NBT of canDestroy and canPlaceOn to its Bedrock counterparts. + * In Java, this is treated as normal NBT, but in Bedrock, these arguments are extra parts of the item data itself. + * @param canModifyJava the list of items in Java + * @param canModifyBedrock the empty list of items in Bedrock + * @return the new list of items in Bedrock + */ + private static String[] getCanModify(ListTag canModifyJava, String[] canModifyBedrock) { + if (canModifyJava != null && canModifyJava.size() > 0) { + canModifyBedrock = new String[canModifyJava.size()]; + for (int i = 0; i < canModifyBedrock.length; i++) { + // Get the Java identifier of the block that can be placed + String block = ((StringTag) canModifyJava.get(i)).getValue(); + // Sometimes this is done but it's still valid + if (!block.startsWith("minecraft:")) block = "minecraft:" + block; + // Get the Bedrock identifier of the item and replace it. + // This will unfortunately be limited - for example, beds and banners will be translated weirdly + canModifyBedrock[i] = BlockTranslator.getBedrockBlockIdentifier(block).replace("minecraft:", ""); + } + } + return canModifyBedrock; + } + private static final ItemTranslator DEFAULT_TRANSLATOR = new ItemTranslator() { @Override public List getAppliedItems() { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java index 0645c9807..a9930f698 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java @@ -26,7 +26,6 @@ package org.geysermc.connector.network.translators.item.translators.nbt; import com.github.steveice10.opennbt.tag.builtin.*; -import com.nukkitx.protocol.bedrock.packet.StartGamePacket; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.item.ItemEntry; @@ -51,13 +50,7 @@ public class ShulkerBoxItemTranslator extends NbtItemStackTranslator { boxItemTag.put(new ByteTag("WasPickedUp", (byte) 0)); // ??? ItemEntry boxItemEntry = ItemRegistry.getItemEntry(((StringTag) itemData.get("id")).getValue()); - String blockName = ""; - for (StartGamePacket.ItemEntry startGamePacketItemEntry : ItemRegistry.ITEMS) { - if (startGamePacketItemEntry.getId() == (short) boxItemEntry.getBedrockId()) { - blockName = startGamePacketItemEntry.getIdentifier(); // Find the Bedrock string name - break; - } - } + String blockName = ItemRegistry.getBedrockIdentifer(boxItemEntry); boxItemTag.put(new StringTag("Name", blockName)); boxItemTag.put(new ShortTag("Damage", (short) boxItemEntry.getBedrockData())); 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 8da09f6f3..81c86c307 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 @@ -28,15 +28,12 @@ package org.geysermc.connector.network.translators.world.block; import com.fasterxml.jackson.databind.JsonNode; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; -import com.nukkitx.nbt.NBTInputStream; -import com.nukkitx.nbt.NbtList; -import com.nukkitx.nbt.NbtMap; -import com.nukkitx.nbt.NbtMapBuilder; -import com.nukkitx.nbt.NbtType; -import com.nukkitx.nbt.NbtUtils; +import com.nukkitx.nbt.*; import it.unimi.dsi.fastutil.ints.*; 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.network.translators.world.block.entity.BlockEntity; import org.geysermc.connector.utils.FileUtils; @@ -52,6 +49,11 @@ public class BlockTranslator { private static final Int2IntMap JAVA_TO_BEDROCK_BLOCK_MAP = new Int2IntOpenHashMap(); private static final Int2IntMap BEDROCK_TO_JAVA_BLOCK_MAP = new Int2IntOpenHashMap(); + /** + * Stores a list of differences in block identifiers. + * Items will not be added to this list if the key and value is the same. + */ + private static final Object2ObjectMap JAVA_TO_BEDROCK_IDENTIFIERS = new Object2ObjectOpenHashMap<>(); private static final BiMap JAVA_ID_BLOCK_MAP = HashBiMap.create(); private static final IntSet WATERLOGGED = new IntOpenHashSet(); private static final Object2IntMap ITEM_FRAMES = new Object2IntOpenHashMap<>(); @@ -152,11 +154,11 @@ public class BlockTranslator { // Used for adding all "special" Java block states to block state map String identifier; - String bedrock_identifer = entry.getValue().get("bedrock_identifier").asText(); + String bedrockIdentifier = entry.getValue().get("bedrock_identifier").asText(); for (Class clazz : ref.getTypesAnnotatedWith(BlockEntity.class)) { identifier = clazz.getAnnotation(BlockEntity.class).regex(); // Endswith, or else the block bedrock gets picked up for bed - if (bedrock_identifer.endsWith(identifier) && !identifier.equals("")) { + if (bedrockIdentifier.endsWith(identifier) && !identifier.equals("")) { JAVA_ID_TO_BLOCK_ENTITY_MAP.put(javaRuntimeId, clazz.getAnnotation(BlockEntity.class).name()); break; } @@ -164,9 +166,15 @@ public class BlockTranslator { BlockStateValues.storeBlockStateValues(entry, javaRuntimeId); + String cleanJavaIdentifier = entry.getKey().split("\\[")[0]; + + if (!cleanJavaIdentifier.equals(bedrockIdentifier)) { + JAVA_TO_BEDROCK_IDENTIFIERS.put(cleanJavaIdentifier, bedrockIdentifier); + } + // Get the tag needed for non-empty flower pots if (entry.getValue().get("pottable") != null) { - BlockStateValues.getFlowerPotBlocks().put(entry.getKey().split("\\[")[0], buildBedrockState(entry.getValue())); + BlockStateValues.getFlowerPotBlocks().put(cleanJavaIdentifier, buildBedrockState(entry.getValue())); } if ("minecraft:water[level=0]".equals(javaId)) { @@ -297,6 +305,14 @@ public class BlockTranslator { return BEDROCK_TO_JAVA_BLOCK_MAP.get(bedrockId); } + /** + * @param javaIdentifier the Java identifier of the block to search for + * @return the Bedrock identifier if different, or else the Java identifier + */ + public static String getBedrockBlockIdentifier(String javaIdentifier) { + return JAVA_TO_BEDROCK_IDENTIFIERS.getOrDefault(javaIdentifier, javaIdentifier); + } + public static int getItemFrame(NbtMap tag) { return ITEM_FRAMES.getOrDefault(tag, -1); } From b13f5e900f448f7a40f5df27373a5d2d96e22794 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 14 Sep 2020 20:50:21 -0400 Subject: [PATCH 085/140] PlayerEntity: despawn even if still on the player list (#1263) Fixes LibsDisguises not working, as it uses the same entity ID for the disguised entity and player. The player still appears on the player list. --- .../java/org/geysermc/connector/entity/PlayerEntity.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java index 0c269b10d..cc00403a0 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java @@ -86,12 +86,6 @@ public class PlayerEntity extends LivingEntity { if (geyserId == 1) valid = true; } - @Override - public boolean despawnEntity(GeyserSession session) { - super.despawnEntity(session); - return !playerList; // don't remove from cache when still on playerlist - } - @Override public void spawnEntity(GeyserSession session) { if (geyserId == 1) return; From 1e1402a23ffd26205a78739210c5d0f122674249 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 14 Sep 2020 20:51:07 -0400 Subject: [PATCH 086/140] DumpInfo: Mark internal IP as sensitive (#1264) Sometimes the internal IP is the external IP of the server. --- .../org/geysermc/connector/dump/DumpInfo.java | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java b/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java index 9d91cde6b..8193953a7 100644 --- a/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java +++ b/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java @@ -31,6 +31,7 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.Getter; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.common.serializer.AsteriskSerializer; import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.network.BedrockProtocol; import org.geysermc.connector.network.session.GeyserSession; @@ -112,16 +113,21 @@ public class DumpInfo { private final boolean dockerCheck; NetworkInfo() { - try { - // This is the most reliable for getting the main local IP - Socket socket = new Socket(); - socket.connect(new InetSocketAddress("geysermc.org", 80)); - this.internalIP = socket.getLocalAddress().getHostAddress(); - } catch (IOException e1) { + if (AsteriskSerializer.showSensitive) { try { - // Fallback to the normal way of getting the local IP - this.internalIP = InetAddress.getLocalHost().getHostAddress(); - } catch (UnknownHostException ignored) { } + // This is the most reliable for getting the main local IP + Socket socket = new Socket(); + socket.connect(new InetSocketAddress("geysermc.org", 80)); + this.internalIP = socket.getLocalAddress().getHostAddress(); + } catch (IOException e1) { + try { + // Fallback to the normal way of getting the local IP + this.internalIP = InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException ignored) { } + } + } else { + // Sometimes the internal IP is the external IP... + this.internalIP = "***"; } this.dockerCheck = DockerCheck.checkBasic(); From 3b274ef9d15d925cb8f2511e06ceb670b89a6484 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 14 Sep 2020 20:52:16 -0400 Subject: [PATCH 087/140] Pick block improvements (#1265) * Pick block improvements - Creative block picking is now implemented. If the survival-styled block picking fails, then the item is created, following Java-style mechanics. - Entity 'picking' is also implemented. The item is crafted using the same mechanics, and the same rules apply as normal block-picking (except it only works in creative mode, following Java. * Switch some logic around --- .../connector/inventory/PlayerInventory.java | 4 + ...drockBlockPickRequestPacketTranslator.java | 99 --------------- .../BedrockBlockPickRequestTranslator.java | 52 ++++++++ .../BedrockEntityPickRequestTranslator.java | 115 ++++++++++++++++++ .../connector/utils/InventoryUtils.java | 96 ++++++++++++++- 5 files changed, 266 insertions(+), 100 deletions(-) delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestPacketTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEntityPickRequestTranslator.java diff --git a/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java b/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java index 432ca8270..225335a97 100644 --- a/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java +++ b/connector/src/main/java/org/geysermc/connector/inventory/PlayerInventory.java @@ -31,6 +31,10 @@ import lombok.Setter; public class PlayerInventory extends Inventory { + /** + * Stores the held item slot, starting at index 0. + * Add 36 in order to get the network item slot. + */ @Getter @Setter private int heldItemSlot; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestPacketTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestPacketTranslator.java deleted file mode 100644 index 39cfceb10..000000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestPacketTranslator.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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.github.steveice10.mc.protocol.packet.ingame.client.window.ClientMoveItemToHotbarPacket; -import com.nukkitx.math.vector.Vector3i; -import com.nukkitx.protocol.bedrock.packet.BlockPickRequestPacket; -import com.nukkitx.protocol.bedrock.packet.PlayerHotbarPacket; -import org.geysermc.connector.inventory.Inventory; -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.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ItemRegistry; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; - -@Translator(packet = BlockPickRequestPacket.class) -public class BedrockBlockPickRequestPacketTranslator extends PacketTranslator { - - @Override - public void translate(BlockPickRequestPacket packet, GeyserSession session) { - Vector3i vector = packet.getBlockPosition(); - int blockToPick = session.getConnector().getWorldManager().getBlockAt(session, vector.getX(), vector.getY(), vector.getZ()); - - // Block is air - chunk caching is probably off - if (blockToPick == 0) { - return; - } - - // Get the inventory to choose a slot to pick - Inventory inventory = session.getInventoryCache().getOpenInventory(); - if (inventory == null) { - inventory = session.getInventory(); - } - - String targetIdentifier = BlockTranslator.getJavaIdBlockMap().inverse().get(blockToPick).split("\\[")[0]; - - // Check hotbar for item - for (int i = 36; i < 45; i++) { - if (inventory.getItem(i) == null) { - continue; - } - ItemEntry item = ItemRegistry.getItem(inventory.getItem(i)); - // If this isn't the item we're looking for - if (!item.getJavaIdentifier().equals(targetIdentifier)) { - continue; - } - - PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket(); - hotbarPacket.setContainerId(0); - // Java inventory slot to hotbar slot ID - hotbarPacket.setSelectedHotbarSlot(i - 36); - hotbarPacket.setSelectHotbarSlot(true); - session.sendUpstreamPacket(hotbarPacket); - session.getInventory().setHeldItemSlot(i - 36); - // Don't check inventory if item was in hotbar - return; - } - - // Check inventory for item - for (int i = 9; i < 36; i++) { - if (inventory.getItem(i) == null) { - continue; - } - ItemEntry item = ItemRegistry.getItem(inventory.getItem(i)); - // If this isn't the item we're looking for - if (!item.getJavaIdentifier().equals(targetIdentifier)) { - continue; - } - - ClientMoveItemToHotbarPacket packetToSend = new ClientMoveItemToHotbarPacket(i); // https://wiki.vg/Protocol#Pick_Item - session.sendDownstreamPacket(packetToSend); - return; - } - } -} 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 new file mode 100644 index 000000000..023a83afe --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockPickRequestTranslator.java @@ -0,0 +1,52 @@ +/* + * 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.math.vector.Vector3i; +import com.nukkitx.protocol.bedrock.packet.BlockPickRequestPacket; +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.utils.InventoryUtils; + +@Translator(packet = BlockPickRequestPacket.class) +public class BedrockBlockPickRequestTranslator extends PacketTranslator { + + @Override + public void translate(BlockPickRequestPacket packet, GeyserSession session) { + Vector3i vector = packet.getBlockPosition(); + int blockToPick = session.getConnector().getWorldManager().getBlockAt(session, vector.getX(), vector.getY(), vector.getZ()); + + // Block is air - chunk caching is probably off + if (blockToPick == 0) { + return; + } + + String targetIdentifier = BlockTranslator.getJavaIdBlockMap().inverse().get(blockToPick).split("\\[")[0]; + InventoryUtils.findOrCreatePickedBlock(session, targetIdentifier); + } +} 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 new file mode 100644 index 000000000..4aa044ee4 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEntityPickRequestTranslator.java @@ -0,0 +1,115 @@ +/* + * 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.github.steveice10.mc.protocol.data.game.entity.player.GameMode; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.packet.EntityPickRequestPacket; +import org.geysermc.connector.entity.Entity; +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.item.ItemEntry; +import org.geysermc.connector.network.translators.item.ItemRegistry; +import org.geysermc.connector.utils.InventoryUtils; + +/** + * Called when the Bedrock user uses the pick block button on an entity + */ +@Translator(packet = EntityPickRequestPacket.class) +public class BedrockEntityPickRequestTranslator extends PacketTranslator { + + @Override + public void translate(EntityPickRequestPacket packet, GeyserSession session) { + if (session.getGameMode() != GameMode.CREATIVE) return; // Apparently Java behavior + Entity entity = session.getEntityCache().getEntityByGeyserId(packet.getRuntimeEntityId()); + if (entity == null) return; + + // Get the corresponding item + String itemName; + switch (entity.getEntityType()) { + case BOAT: + // Include type of boat in the name + int variant = entity.getMetadata().getInt(EntityData.VARIANT); + String typeOfBoat; + switch (variant) { + case 1: + typeOfBoat = "spruce"; + break; + case 2: + typeOfBoat = "birch"; + break; + case 3: + typeOfBoat = "jungle"; + break; + case 4: + typeOfBoat = "acacia"; + break; + case 5: + typeOfBoat = "dark_oak"; + break; + default: + typeOfBoat = "oak"; + break; + } + itemName = typeOfBoat + "_boat"; + break; + case LEASH_KNOT: + itemName = "lead"; + break; + case MINECART_CHEST: + case MINECART_COMMAND_BLOCK: + case MINECART_FURNACE: + case MINECART_HOPPER: + case MINECART_TNT: + // Move MINECART to the end of the name + itemName = entity.getEntityType().toString().toLowerCase().replace("minecart_", "") + "_minecart"; + break; + case MINECART_SPAWNER: + // Turns into a normal minecart + itemName = "minecart"; + break; + case ARMOR_STAND: + case END_CRYSTAL: + case ITEM_FRAME: + case MINECART: + case PAINTING: + // No spawn egg, just an item + itemName = entity.getEntityType().toString().toLowerCase(); + break; + default: + itemName = entity.getEntityType().toString().toLowerCase() + "_spawn_egg"; + break; + } + + String fullItemName = "minecraft:" + itemName; + ItemEntry entry = ItemRegistry.getItemEntry(fullItemName); + // Verify it is, indeed, an item + if (entry == null) return; + + InventoryUtils.findOrCreatePickedBlock(session, fullItemName); + } +} 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 cb51e2f3b..b3cc14b97 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java @@ -26,6 +26,10 @@ package org.geysermc.connector.utils; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; +import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerChangeHeldItemPacket; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCreativeInventoryActionPacket; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientMoveItemToHotbarPacket; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; @@ -33,12 +37,14 @@ import com.nukkitx.nbt.NbtType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; -import org.geysermc.connector.common.ChatColor; +import com.nukkitx.protocol.bedrock.packet.PlayerHotbarPacket; import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.common.ChatColor; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.DoubleChestInventoryTranslator; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; +import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; @@ -151,4 +157,92 @@ public class InventoryUtils { root.put("display", display.build()); return ItemData.of(ItemRegistry.ITEM_ENTRIES.get(ItemRegistry.BARRIER_INDEX).getBedrockId(), (short) 0, 1, root.build()); } + + /** + * Attempt to find the specified item name in the session's inventory. + * If it is found and in the hotbar, set the user's held item to that slot. + * If it is found in another part of the inventory, move it. + * If it is not found and the user is in creative mode, create the item, + * overriding the current item slot if no other hotbar slots are empty, or otherwise selecting the empty slot. + * + * This attempts to mimic Java Edition behavior as best as it can. + * @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) { + // Get the inventory to choose a slot to pick + Inventory inventory = session.getInventoryCache().getOpenInventory(); + if (inventory == null) { + inventory = session.getInventory(); + } + + // Check hotbar for item + for (int i = 36; i < 45; i++) { + if (inventory.getItem(i) == null) { + continue; + } + ItemEntry item = ItemRegistry.getItem(inventory.getItem(i)); + // If this isn't the item we're looking for + if (!item.getJavaIdentifier().equals(itemName)) { + continue; + } + + setHotbarItem(session, i); + // Don't check inventory if item was in hotbar + return; + } + + // Check inventory for item + for (int i = 9; i < 36; i++) { + if (inventory.getItem(i) == null) { + continue; + } + ItemEntry item = ItemRegistry.getItem(inventory.getItem(i)); + // If this isn't the item we're looking for + if (!item.getJavaIdentifier().equals(itemName)) { + continue; + } + + ClientMoveItemToHotbarPacket packetToSend = new ClientMoveItemToHotbarPacket(i); // https://wiki.vg/Protocol#Pick_Item + session.sendDownstreamPacket(packetToSend); + return; + } + + // If we still have not found the item, and we're in creative, ask for the item from the server. + if (session.getGameMode() == GameMode.CREATIVE) { + int slot = session.getInventory().getHeldItemSlot() + 36; + if (session.getInventory().getItemInHand() != null) { // Otherwise we should just use the current slot + for (int i = 36; i < 45; i++) { + if (inventory.getItem(i) == null) { + slot = i; + break; + } + } + } + + ClientCreativeInventoryActionPacket actionPacket = new ClientCreativeInventoryActionPacket(slot, + new ItemStack(ItemRegistry.getItemEntry(itemName).getJavaId())); + if ((slot - 36) != session.getInventory().getHeldItemSlot()) { + setHotbarItem(session, slot); + } + session.sendDownstreamPacket(actionPacket); + } + } + + /** + * Changes the held item slot to the specified slot + * @param session GeyserSession + * @param slot inventory slot to be selected + */ + private static void setHotbarItem(GeyserSession session, int slot) { + PlayerHotbarPacket hotbarPacket = new PlayerHotbarPacket(); + hotbarPacket.setContainerId(0); + // Java inventory slot to hotbar slot ID + hotbarPacket.setSelectedHotbarSlot(slot - 36); + hotbarPacket.setSelectHotbarSlot(true); + session.sendUpstreamPacket(hotbarPacket); + ClientPlayerChangeHeldItemPacket heldItemPacket = new ClientPlayerChangeHeldItemPacket(slot); + session.sendDownstreamPacket(heldItemPacket); + session.getInventory().setHeldItemSlot(slot - 36); + } } From 9c8eb00cd59d9272314d2b127bab3c6b5ed02bc8 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 14 Sep 2020 20:52:50 -0400 Subject: [PATCH 088/140] JavaCollectItemTranslator: check null for entities (#1267) --- .../translators/java/world/JavaCollectItemTranslator.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaCollectItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaCollectItemTranslator.java index 31379bd2d..a90c7016b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaCollectItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaCollectItemTranslator.java @@ -41,6 +41,7 @@ public class JavaCollectItemTranslator extends PacketTranslator Date: Mon, 14 Sep 2020 20:53:47 -0400 Subject: [PATCH 089/140] Disconnect player if Java sends disconnection (#1274) --- .../java/JavaDisconnectPacket.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDisconnectPacket.java diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDisconnectPacket.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDisconnectPacket.java new file mode 100644 index 000000000..432111119 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDisconnectPacket.java @@ -0,0 +1,41 @@ +/* + * 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.java; + +import com.github.steveice10.mc.protocol.packet.ingame.server.ServerDisconnectPacket; +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.utils.MessageUtils; + +@Translator(packet = ServerDisconnectPacket.class) +public class JavaDisconnectPacket extends PacketTranslator { + + @Override + public void translate(ServerDisconnectPacket packet, GeyserSession session) { + session.disconnect(MessageUtils.getTranslatedBedrockMessage(packet.getReason(), session.getClientData().getLanguageCode(), true)); + } +} From 6638c53029eca3dc742ec6db08f92d011771cb8a Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 14 Sep 2020 20:54:19 -0400 Subject: [PATCH 090/140] Implement command block and jigsaw support (#1291) * Implement command block and jigsaw support - Command block UI is now fully implemented to match Java Edition. - Command block minecarts are now supported. - Command blocks now show the correct type of command block. - Jigsaw blocks are translated. Structure blocks can be implemented, but these will be trickier as there are significant GUI differences between Java and Bedrock. * Add more detail about command block minecart color * Set PlayerPermission.OPERATOR to allow command blocks to be destroyed --- README.md | 1 - .../entity/CommandBlockMinecartEntity.java | 67 +++++++++++++++++ .../connector/entity/type/EntityType.java | 2 +- .../network/session/GeyserSession.java | 3 +- .../BedrockBlockEntityDataTranslator.java | 13 ++++ .../BedrockCommandBlockUpdateTranslator.java | 71 +++++++++++++++++++ ...BedrockInventoryTransactionTranslator.java | 33 +++++++++ .../world/JavaUpdateTileEntityTranslator.java | 16 ++++- .../world/block/BlockStateValues.java | 16 +++++ .../world/block/BlockTranslator.java | 33 ++++++--- .../entity/BannerBlockEntityTranslator.java | 16 ----- .../entity/BedBlockEntityTranslator.java | 12 ---- .../block/entity/BlockEntityTranslator.java | 9 +-- .../entity/CampfireBlockEntityTranslator.java | 12 ---- .../CommandBlockBlockEntityTranslator.java | 67 +++++++++++++++++ .../DoubleChestBlockEntityTranslator.java | 10 --- .../entity/EmptyBlockEntityTranslator.java | 10 --- .../EndGatewayBlockEntityTranslator.java | 20 +----- .../JigsawBlockBlockEntityTranslator.java | 47 ++++++++++++ .../ShulkerBoxBlockEntityTranslator.java | 12 ---- .../entity/SignBlockEntityTranslator.java | 18 ----- .../entity/SkullBlockEntityTranslator.java | 13 ---- .../entity/SpawnerBlockEntityTranslator.java | 13 ---- 23 files changed, 359 insertions(+), 155 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockCommandBlockUpdateTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CommandBlockBlockEntityTranslator.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/JigsawBlockBlockEntityTranslator.java diff --git a/README.md b/README.md index 156745df3..ba3e6fc2d 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,6 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set - [ ] Beacon - [ ] Cartography Table - [ ] Stonecutter - - [ ] Command Block - [ ] Structure Block - [ ] Horse Inventory - [ ] Loom diff --git a/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java b/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java new file mode 100644 index 000000000..8cabba645 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/CommandBlockMinecartEntity.java @@ -0,0 +1,67 @@ +/* + * 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.message.Message; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.utils.MessageUtils; + +public class CommandBlockMinecartEntity extends DefaultBlockMinecartEntity { + + public CommandBlockMinecartEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + // Required, or else the GUI will not open + metadata.put(EntityData.CONTAINER_TYPE, (byte) 16); + metadata.put(EntityData.CONTAINER_BASE_SIZE, 1); + // Required, or else the client does not bother to send a packet back with the new information + metadata.put(EntityData.COMMAND_BLOCK_ENABLED, (byte) 1); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 13) { + metadata.put(EntityData.COMMAND_BLOCK_COMMAND, entityMetadata.getValue()); + } + if (entityMetadata.getId() == 14) { + metadata.put(EntityData.COMMAND_BLOCK_LAST_OUTPUT, MessageUtils.getBedrockMessage((Message) entityMetadata.getValue())); + } + super.updateBedrockMetadata(entityMetadata, session); + } + + /** + * By default, the command block shown is purple on Bedrock, which does not match Java Edition's orange. + */ + @Override + public void updateDefaultBlockMetadata() { + metadata.put(EntityData.DISPLAY_ITEM, BlockTranslator.BEDROCK_RUNTIME_COMMAND_BLOCK_ID); + metadata.put(EntityData.DISPLAY_OFFSET, 6); + } +} 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 87f4c8b50..500e135ed 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 @@ -135,7 +135,7 @@ public enum EntityType { MINECART_CHEST(MinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:chest_minecart"), MINECART_FURNACE(FurnaceMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"), MINECART_SPAWNER(SpawnerMinecartEntity.class, 98, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:minecart"), - MINECART_COMMAND_BLOCK(MinecartEntity.class, 100, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:command_block_minecart"), + MINECART_COMMAND_BLOCK(CommandBlockMinecartEntity.class, 100, 0.7f, 0.98f, 0.98f, 0.35f, "minecraft:command_block_minecart"), LINGERING_POTION(ThrowableEntity.class, 101, 0f), LLAMA_SPIT(Entity.class, 102, 0.25f), EVOKER_FANGS(Entity.class, 103, 0.8f, 0.5f, 0.5f, 0f, "minecraft:evocation_fang"), 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 7a61eb220..efdb1aa61 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 @@ -714,7 +714,8 @@ public class GeyserSession implements CommandSender { // This allows mobile players access to a GUI for doing commands. The commands there do not change above OPERATOR // and all commands there are accessible with OP permission level 2 adventureSettingsPacket.setCommandPermission(opPermissionLevel >= 2 ? CommandPermission.OPERATOR : CommandPermission.NORMAL); - adventureSettingsPacket.setPlayerPermission(PlayerPermission.MEMBER); + // Required to make command blocks destroyable + adventureSettingsPacket.setPlayerPermission(opPermissionLevel >= 2 ? PlayerPermission.OPERATOR : PlayerPermission.MEMBER); Set flags = new HashSet<>(); if (canFly) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockEntityDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockEntityDataTranslator.java index 147b8a3b1..3522b4d56 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockEntityDataTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockBlockEntityDataTranslator.java @@ -26,6 +26,7 @@ package org.geysermc.connector.network.translators.bedrock; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; +import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientUpdateJigsawBlockPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientUpdateSignPacket; import com.nukkitx.nbt.NbtMap; import com.nukkitx.protocol.bedrock.packet.BlockEntityDataPacket; @@ -109,6 +110,18 @@ public class BedrockBlockEntityDataTranslator extends PacketTranslator { + + @Override + public void translate(CommandBlockUpdatePacket packet, GeyserSession session) { + String command = packet.getCommand(); + boolean outputTracked = packet.isOutputTracked(); + if (packet.isBlock()) { + CommandBlockMode mode; + switch (packet.getMode()) { + case CHAIN: // The green one + mode = CommandBlockMode.SEQUENCE; + break; + case REPEATING: // The purple one + mode = CommandBlockMode.AUTO; + break; + default: // NORMAL, the orange one + mode = CommandBlockMode.REDSTONE; + break; + } + boolean isConditional = packet.isConditional(); + boolean automatic = !packet.isRedstoneMode(); // Automatic = Always Active option in Java + ClientUpdateCommandBlockPacket commandBlockPacket = new ClientUpdateCommandBlockPacket( + new Position(packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()), + command, mode, outputTracked, isConditional, automatic); + session.sendDownstreamPacket(commandBlockPacket); + } else { + ClientUpdateCommandBlockMinecartPacket commandMinecartPacket = new ClientUpdateCommandBlockMinecartPacket( + (int) session.getEntityCache().getEntityByGeyserId(packet.getMinecartRuntimeEntityId()).getEntityId(), + command, outputTracked + ); + session.sendDownstreamPacket(commandMinecartPacket); + } + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java index cc1da85e8..95e669572 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java @@ -39,8 +39,11 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlaye import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.LevelEventType; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; +import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket; import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; +import org.geysermc.connector.entity.CommandBlockMinecartEntity; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.ItemFrameEntity; import org.geysermc.connector.entity.living.merchant.AbstractMerchantEntity; @@ -105,6 +108,24 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator= 2 && session.getGameMode() == GameMode.CREATIVE) { + // Otherwise insufficient permissions + int blockState = BlockTranslator.getJavaBlockState(packet.getBlockRuntimeId()); + String blockName = BlockTranslator.getJavaIdBlockMap().inverse().getOrDefault(blockState, ""); + // In the future this can be used for structure blocks too, however not all elements + // are available in each GUI + if (blockName.contains("jigsaw")) { + ContainerOpenPacket openPacket = new ContainerOpenPacket(); + openPacket.setBlockPosition(packet.getBlockPosition()); + openPacket.setId((byte) 1); + openPacket.setType(ContainerType.JIGSAW_EDITOR); + openPacket.setUniqueEntityId(-1); + session.sendUpstreamPacket(openPacket); + } + } + } + Vector3i blockPos = packet.getBlockPosition(); // TODO: Find a better way to do this? switch (packet.getBlockFace()) { @@ -197,6 +218,18 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator= 2 && + session.getGameMode() == GameMode.CREATIVE && packet.getNbt().size() > 5) { + ContainerOpenPacket openPacket = new ContainerOpenPacket(); + openPacket.setBlockPosition(Vector3i.from(packet.getPosition().getX(), packet.getPosition().getY(), packet.getPosition().getZ())); + openPacket.setId((byte) 1); + openPacket.setType(ContainerType.COMMAND_BLOCK); + openPacket.setUniqueEntityId(-1); + session.sendUpstreamPacket(openPacket); + } } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java index 53607317a..305118e6f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/BlockStateValues.java @@ -39,6 +39,7 @@ public class BlockStateValues { private static final Int2IntMap BANNER_COLORS = new Int2IntOpenHashMap(); private static final Int2ByteMap BED_COLORS = new Int2ByteOpenHashMap(); + private static final Int2ByteMap COMMAND_BLOCK_VALUES = new Int2ByteOpenHashMap(); private static final Int2ObjectMap DOUBLE_CHEST_VALUES = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap FLOWER_POT_VALUES = new Int2ObjectOpenHashMap<>(); private static final Map FLOWER_POT_BLOCKS = new HashMap<>(); @@ -67,6 +68,11 @@ public class BlockStateValues { return; } + if (entry.getKey().contains("command_block")) { + COMMAND_BLOCK_VALUES.put(javaBlockState, entry.getKey().contains("conditional=true") ? (byte) 1 : (byte) 0); + return; + } + if (entry.getValue().get("double_chest_position") != null) { boolean isX = (entry.getValue().get("x") != null); boolean isDirectionPositive = ((entry.getValue().get("x") != null && entry.getValue().get("x").asBoolean()) || @@ -138,6 +144,16 @@ public class BlockStateValues { return -1; } + /** + * The block state in Java and Bedrock both contain the conditional bit, however command block block entity tags + * in Bedrock need the conditional information. + * + * @return the list of all command blocks and if they are conditional (1 or 0) + */ + public static Int2ByteMap getCommandBlockValues() { + return COMMAND_BLOCK_VALUES; + } + /** * All double chest values are part of the block state in Java and part of the block entity tag in Bedrock. * This gives the DoubleChestValue that can be calculated into the final tag. 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 81c86c307..fc7c852cb 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 @@ -67,6 +67,11 @@ public class BlockTranslator { 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<>(); + /** + * Runtime command block ID, used for fixing command block minecart appearances + */ + public static final int BEDROCK_RUNTIME_COMMAND_BLOCK_ID; + // For block breaking animation math public static final IntSet JAVA_RUNTIME_WOOL_IDS = new IntOpenHashSet(); public static final int JAVA_RUNTIME_COBWEB_ID; @@ -115,6 +120,7 @@ public class BlockTranslator { int javaRuntimeId = -1; int bedrockRuntimeId = 0; int cobwebRuntimeId = -1; + int commandBlockRuntimeId = -1; int furnaceRuntimeId = -1; int furnaceLitRuntimeId = -1; int spawnerRuntimeId = -1; @@ -142,14 +148,6 @@ public class BlockTranslator { JAVA_RUNTIME_ID_TO_TOOL_TYPE.put(javaRuntimeId, toolTypeNode.textValue()); } - if (javaId.contains("wool")) { - JAVA_RUNTIME_WOOL_IDS.add(javaRuntimeId); - } - - if (javaId.contains("cobweb")) { - cobwebRuntimeId = javaRuntimeId; - } - JAVA_ID_BLOCK_MAP.put(javaId, javaRuntimeId); // Used for adding all "special" Java block states to block state map @@ -205,15 +203,23 @@ public class BlockTranslator { } JAVA_TO_BEDROCK_BLOCK_MAP.put(javaRuntimeId, bedrockRuntimeId); - if (javaId.startsWith("minecraft:furnace[facing=north")) { + if (javaId.contains("wool")) { + JAVA_RUNTIME_WOOL_IDS.add(javaRuntimeId); + + } else if (javaId.contains("cobweb")) { + cobwebRuntimeId = javaRuntimeId; + + } else if (javaId.equals("minecraft:command_block[conditional=false,facing=north]")) { + commandBlockRuntimeId = bedrockRuntimeId; + + } else if (javaId.startsWith("minecraft:furnace[facing=north")) { if (javaId.contains("lit=true")) { furnaceLitRuntimeId = javaRuntimeId; } else { furnaceRuntimeId = javaRuntimeId; } - } - if (javaId.startsWith("minecraft:spawner")) { + } else if (javaId.startsWith("minecraft:spawner")) { spawnerRuntimeId = javaRuntimeId; } @@ -225,6 +231,11 @@ public class BlockTranslator { } JAVA_RUNTIME_COBWEB_ID = cobwebRuntimeId; + if (commandBlockRuntimeId == -1) { + throw new AssertionError("Unable to find command block in palette"); + } + BEDROCK_RUNTIME_COMMAND_BLOCK_ID = commandBlockRuntimeId; + if (furnaceRuntimeId == -1) { throw new AssertionError("Unable to find furnace in palette"); } 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 9e86cb4cf..57393a6c5 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 @@ -27,12 +27,9 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.ListTag; -import com.nukkitx.nbt.NbtMap; -import com.nukkitx.nbt.NbtType; import org.geysermc.connector.network.translators.item.translators.BannerTranslator; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -65,17 +62,4 @@ public class BannerBlockEntityTranslator extends BlockEntityTranslator implement return tags; } - @Override - public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) { - CompoundTag tag = getConstantJavaTag(javaId, x, y, z); - tag.put(new ListTag("Patterns")); - return tag; - } - - @Override - public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) { - return getConstantBedrockTag(bedrockId, x, y, z).toBuilder() - .putList("Patterns", NbtType.COMPOUND, new ArrayList<>()) - .build(); - } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java index b84aad984..080bdc3b2 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BedBlockEntityTranslator.java @@ -26,7 +26,6 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import com.nukkitx.nbt.NbtMap; import org.geysermc.connector.network.translators.world.block.BlockStateValues; import java.util.HashMap; @@ -50,15 +49,4 @@ public class BedBlockEntityTranslator extends BlockEntityTranslator implements R return tags; } - @Override - public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) { - return null; - } - - @Override - public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) { - return getConstantBedrockTag(bedrockId, x, y, z).toBuilder() - .putByte("color", (byte) 0) - .build(); - } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java index a538c2dcc..54f593a61 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java @@ -28,9 +28,9 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.IntTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; - import it.unimi.dsi.fastutil.objects.ObjectArrayList; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.utils.BlockEntityUtils; @@ -53,6 +53,7 @@ public abstract class BlockEntityTranslator { { // Bedrock/Java differences put("minecraft:enchanting_table", "EnchantTable"); + put("minecraft:jigsaw", "JigsawBlock"); put("minecraft:piston_head", "PistonArm"); put("minecraft:trapped_chest", "Chest"); // There are some legacy IDs sent but as far as I can tell they are not needed for things to work properly @@ -90,10 +91,6 @@ public abstract class BlockEntityTranslator { public abstract Map translateTag(CompoundTag tag, int blockState); - public abstract CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z); - - public abstract NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z); - public NbtMap getBlockEntityTag(String id, CompoundTag tag, int blockState) { int x = Integer.parseInt(String.valueOf(tag.getValue().get("x").getValue())); int y = Integer.parseInt(String.valueOf(tag.getValue().get("y").getValue())); @@ -124,7 +121,7 @@ public abstract class BlockEntityTranslator { } @SuppressWarnings("unchecked") - protected T getOrDefault(com.github.steveice10.opennbt.tag.builtin.Tag tag, T defaultValue) { + protected T getOrDefault(Tag tag, T defaultValue) { return (tag != null && tag.getValue() != null) ? (T) tag.getValue() : defaultValue; } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java index e3d2c9f5e..d6ac0281b 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CampfireBlockEntityTranslator.java @@ -50,18 +50,6 @@ public class CampfireBlockEntityTranslator extends BlockEntityTranslator { return tags; } - @Override - public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) { - CompoundTag tag = getConstantJavaTag(javaId, x, y, z); - tag.put(new ListTag("Items")); - return tag; - } - - @Override - public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) { - return getConstantBedrockTag(bedrockId, x, y, z); - } - protected NbtMap getItem(CompoundTag tag) { ItemEntry entry = ItemRegistry.getItemEntry((String) tag.get("id").getValue()); NbtMapBuilder tagBuilder = NbtMap.builder() diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CommandBlockBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CommandBlockBlockEntityTranslator.java new file mode 100644 index 000000000..6bc940adb --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/CommandBlockBlockEntityTranslator.java @@ -0,0 +1,67 @@ +/* + * 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.world.block.entity; + +import com.github.steveice10.opennbt.tag.builtin.*; +import org.geysermc.connector.network.translators.world.block.BlockStateValues; +import org.geysermc.connector.utils.MessageUtils; + +import java.util.HashMap; +import java.util.Map; + +@BlockEntity(name = "CommandBlock", regex = "command_block") +public class CommandBlockBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { + + @Override + public Map translateTag(CompoundTag tag, int blockState) { + Map map = new HashMap<>(); + if (tag.size() < 5) { + return map; // These values aren't here + } + // Java infers from the block state, but Bedrock needs it in the tag + map.put("conditionalMode", BlockStateValues.getCommandBlockValues().getOrDefault(blockState, (byte) 0)); + // Java and Bedrock values + map.put("conditionMet", ((ByteTag) tag.get("conditionMet")).getValue()); + map.put("auto", ((ByteTag) tag.get("auto")).getValue()); + map.put("CustomName", MessageUtils.getBedrockMessage(((StringTag) tag.get("CustomName")).getValue())); + map.put("powered", ((ByteTag) tag.get("powered")).getValue()); + map.put("Command", ((StringTag) tag.get("Command")).getValue()); + map.put("SuccessCount", ((IntTag) tag.get("SuccessCount")).getValue()); + map.put("TrackOutput", ((ByteTag) tag.get("TrackOutput")).getValue()); + map.put("UpdateLastExecution", ((ByteTag) tag.get("UpdateLastExecution")).getValue()); + if (tag.get("LastExecution") != null) { + map.put("LastExecution", ((LongTag) tag.get("LastExecution")).getValue()); + } else { + map.put("LastExecution", (long) 0); + } + return map; + } + + @Override + public boolean isBlock(int blockState) { + return BlockStateValues.getCommandBlockValues().containsKey(blockState); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java index fa8bab3b0..5b59420e0 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/DoubleChestBlockEntityTranslator.java @@ -27,7 +27,6 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.nukkitx.math.vector.Vector3i; -import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtMapBuilder; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockStateValues; @@ -92,13 +91,4 @@ public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator impl return tags; } - @Override - public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) { - return null; - } - - @Override - public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) { - return null; - } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java index 6de136119..e9715bd32 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EmptyBlockEntityTranslator.java @@ -26,7 +26,6 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import com.nukkitx.nbt.NbtMap; import java.util.HashMap; import java.util.Map; @@ -39,13 +38,4 @@ public class EmptyBlockEntityTranslator extends BlockEntityTranslator { return new HashMap<>(); } - @Override - public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) { - return getConstantJavaTag(javaId, x, y, z); - } - - @Override - public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) { - return getConstantBedrockTag(bedrockId, x, y, z); - } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java index 784afed5b..af94c560d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/EndGatewayBlockEntityTranslator.java @@ -26,14 +26,12 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import com.github.steveice10.opennbt.tag.builtin.LongTag; +import com.github.steveice10.opennbt.tag.builtin.IntTag; import com.nukkitx.nbt.NbtList; -import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtType; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; -import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @@ -56,25 +54,11 @@ public class EndGatewayBlockEntityTranslator extends BlockEntityTranslator { return tags; } - @Override - public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) { - CompoundTag tag = getConstantJavaTag(javaId, x, y, z); - tag.put(new LongTag("Age")); - return tag; - } - - @Override - public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) { - return getConstantBedrockTag(bedrockId, x, y, z).toBuilder() - .putList("ExitPortal", NbtType.INT, Arrays.asList(0, 0, 0)) - .build(); - } - private int getExitPortalCoordinate(CompoundTag tag, String axis) { // Return 0 if it doesn't exist, otherwise give proper value if (tag.get("ExitPortal") != null) { LinkedHashMap compoundTag = (LinkedHashMap) tag.get("ExitPortal").getValue(); - com.github.steveice10.opennbt.tag.builtin.IntTag intTag = (com.github.steveice10.opennbt.tag.builtin.IntTag) compoundTag.get(axis); + IntTag intTag = (IntTag) compoundTag.get(axis); return intTag.getValue(); } return 0; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/JigsawBlockBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/JigsawBlockBlockEntityTranslator.java new file mode 100644 index 000000000..43ac1a96f --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/JigsawBlockBlockEntityTranslator.java @@ -0,0 +1,47 @@ +/* + * 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.world.block.entity; + +import com.github.steveice10.opennbt.tag.builtin.CompoundTag; +import com.github.steveice10.opennbt.tag.builtin.StringTag; + +import java.util.HashMap; +import java.util.Map; + +@BlockEntity(name = "JigsawBlock", regex = "jigsaw") +public class JigsawBlockBlockEntityTranslator extends BlockEntityTranslator { + + @Override + public Map translateTag(CompoundTag tag, int blockState) { + Map map = new HashMap<>(); + map.put("joint", ((StringTag) tag.get("joint")).getValue()); + map.put("name", ((StringTag) tag.get("name")).getValue()); + map.put("target_pool", ((StringTag) tag.get("pool")).getValue()); + map.put("final_state", ((StringTag) tag.get("final_state")).getValue()); + map.put("target", ((StringTag) tag.get("target")).getValue()); + return map; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java index 08a7ae187..08e3abaab 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/ShulkerBoxBlockEntityTranslator.java @@ -26,7 +26,6 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import com.nukkitx.nbt.NbtMap; import org.geysermc.connector.network.translators.world.block.BlockStateValues; import java.util.HashMap; @@ -46,15 +45,4 @@ public class ShulkerBoxBlockEntityTranslator extends BlockEntityTranslator { return tags; } - @Override - public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) { - return null; - } - - @Override - public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) { - return getConstantBedrockTag(bedrockId, x, y, z).toBuilder() - .putByte("facing", (byte) 1) - .build(); - } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java index acec16c5c..b40ed42c9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SignBlockEntityTranslator.java @@ -27,7 +27,6 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.mc.protocol.data.message.MessageSerializer; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import com.nukkitx.nbt.NbtMap; import org.geysermc.connector.utils.MessageUtils; import org.geysermc.connector.utils.SignUtils; @@ -73,23 +72,6 @@ public class SignBlockEntityTranslator extends BlockEntityTranslator { return tags; } - @Override - public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) { - CompoundTag tag = getConstantJavaTag(javaId, x, y, z); - tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text1", "{\"text\":\"\"}")); - tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text2", "{\"text\":\"\"}")); - tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text3", "{\"text\":\"\"}")); - tag.put(new com.github.steveice10.opennbt.tag.builtin.StringTag("Text4", "{\"text\":\"\"}")); - return tag; - } - - @Override - public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) { - return getConstantBedrockTag(bedrockId, x, y, z).toBuilder() - .putString("Text", "") - .build(); - } - /** * Maps a color stored in a sign's Color tag to a Bedrock Edition formatting code. *
diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java index 9547ba2ff..6d350c0cc 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SkullBlockEntityTranslator.java @@ -25,7 +25,6 @@ package org.geysermc.connector.network.translators.world.block.entity; -import com.nukkitx.nbt.NbtMap; import org.geysermc.connector.network.translators.world.block.BlockStateValues; import java.util.HashMap; @@ -51,16 +50,4 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements return tags; } - @Override - public com.github.steveice10.opennbt.tag.builtin.CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) { - return null; - } - - @Override - public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) { - return getConstantBedrockTag(bedrockId, x, y, z).toBuilder() - .putFloat("Rotation", 0f) - .putByte("SkullType", (byte) 0) - .build(); - } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java index 3c443eeeb..2601e3de9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/SpawnerBlockEntityTranslator.java @@ -26,7 +26,6 @@ package org.geysermc.connector.network.translators.world.block.entity; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import com.nukkitx.nbt.NbtMap; import org.geysermc.connector.entity.type.EntityType; import java.util.HashMap; @@ -86,16 +85,4 @@ public class SpawnerBlockEntityTranslator extends BlockEntityTranslator { return tags; } - @Override - public CompoundTag getDefaultJavaTag(String javaId, int x, int y, int z) { - return null; - } - - @Override - public NbtMap getDefaultBedrockTag(String bedrockId, int x, int y, int z) { - return getConstantBedrockTag(bedrockId, x, y, z).toBuilder() - .putByte("isMovable", (byte) 1) - .putString("id", "MobSpawner") - .build(); - } } From f9c1d3f21837f9426db9bf9108faca38e0d6a333 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Wed, 16 Sep 2020 00:11:56 -0400 Subject: [PATCH 091/140] Remove Protocol v409 support (#1300) * Remove Protocol v409 support Protocol has dropped support for this version. * Fix movement * Use a static commit for Protocol --- connector/pom.xml | 6 +++--- .../org/geysermc/connector/network/BedrockProtocol.java | 2 -- .../geysermc/connector/network/session/GeyserSession.java | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/connector/pom.xml b/connector/pom.xml index 5a19a5015..f52713cea 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -31,9 +31,9 @@ compile
- com.nukkitx.protocol - bedrock-v409 - 2.6.0-SNAPSHOT + com.github.CloudburstMC.Protocol + bedrock-v408 + 250beb2a94 compile diff --git a/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java b/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java index f040fc1fe..5d4462b45 100644 --- a/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java +++ b/connector/src/main/java/org/geysermc/connector/network/BedrockProtocol.java @@ -28,7 +28,6 @@ package org.geysermc.connector.network; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import com.nukkitx.protocol.bedrock.v407.Bedrock_v407; import com.nukkitx.protocol.bedrock.v408.Bedrock_v408; -import com.nukkitx.protocol.bedrock.v409.Bedrock_v409; import java.util.ArrayList; import java.util.List; @@ -50,7 +49,6 @@ public class BedrockProtocol { static { SUPPORTED_BEDROCK_CODECS.add(Bedrock_v407.V407_CODEC); SUPPORTED_BEDROCK_CODECS.add(DEFAULT_BEDROCK_CODEC); - SUPPORTED_BEDROCK_CODECS.add(Bedrock_v409.V409_CODEC); } /** 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 efdb1aa61..81b922826 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 @@ -613,7 +613,7 @@ public class GeyserSession implements CommandSender { startGamePacket.setBlockPalette(BlockTranslator.BLOCKS); startGamePacket.setItemEntries(ItemRegistry.ITEMS); startGamePacket.setVanillaVersion("*"); - // startGamePacket.setMovementServerAuthoritative(true); + startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.CLIENT); upstream.sendPacket(startGamePacket); } From 3c1d4aae937f445aab50ea62f8e5f02150bba526 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Wed, 16 Sep 2020 00:18:18 -0400 Subject: [PATCH 092/140] Fix inconsistencies with players and the player list (#1298) * Fix inconsistencies with players and the player list This commit makes the player list entry packet control the player cache, fixing inconsistencies that appeared when removing the override on despawning the player. * Update comments --- .../geysermc/connector/entity/PlayerEntity.java | 2 +- .../network/session/cache/EntityCache.java | 3 --- .../player/JavaPlayerListEntryTranslator.java | 15 +++++---------- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java index cc00403a0..390110d18 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/PlayerEntity.java @@ -118,7 +118,7 @@ public class PlayerEntity extends LivingEntity { } public void sendPlayer(GeyserSession session) { - if(session.getEntityCache().getPlayerEntity(uuid) == null) + if (session.getEntityCache().getPlayerEntity(uuid) == null) return; if (session.getUpstream().isInitialized() && session.getEntityCache().getEntityByGeyserId(geyserId) == null) { diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java index 0bc51ac7a..4b54c9434 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java @@ -76,9 +76,6 @@ public class EntityCache { if (entity != null && entity.isValid() && (force || entity.despawnEntity(session))) { long geyserId = entityIdTranslations.remove(entity.getEntityId()); entities.remove(geyserId); - if (entity.is(PlayerEntity.class)) { - playerEntities.remove(entity.as(PlayerEntity.class).getUuid()); - } return true; } return false; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerListEntryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerListEntryTranslator.java index 10b2ba9ad..6a9ef4dc6 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerListEntryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerListEntryTranslator.java @@ -88,18 +88,13 @@ public class JavaPlayerListEntryTranslator extends PacketTranslator Date: Wed, 16 Sep 2020 16:33:59 +0100 Subject: [PATCH 093/140] [Android] Remove usage of MCProtocolLib Base64 in SkinUtils + more (#1237) * Remove usage of MCProtocolLib Base64 in SkinUtils * Fix path resolution for downloading locales --- .../src/main/java/org/geysermc/connector/utils/LocaleUtils.java | 2 +- .../src/main/java/org/geysermc/connector/utils/SkinUtils.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java index 07c33eb1a..d1d59490f 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/LocaleUtils.java @@ -133,7 +133,7 @@ public class LocaleUtils { * @param locale Locale to download */ private static void downloadLocale(String locale) { - File localeFile = Paths.get(GeyserConnector.getInstance().getBootstrap().getConfigFolder().toString(),"locales",locale + ".json").toFile(); + File localeFile = GeyserConnector.getInstance().getBootstrap().getConfigFolder().resolve("locales/" + locale + ".json").toFile(); // Check if we have already downloaded the locale file if (localeFile.exists()) { diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java index e3488d6e7..fe2a8aa96 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java @@ -270,7 +270,7 @@ public class SkinUtils { GeyserConnector.getInstance().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.skin.bedrock.register", playerEntity.getUsername(), playerEntity.getUuid())); try { - byte[] skinBytes = com.github.steveice10.mc.auth.util.Base64.decode(clientData.getSkinData().getBytes("UTF-8")); + byte[] skinBytes = Base64.getDecoder().decode(clientData.getSkinData().getBytes("UTF-8")); byte[] capeBytes = clientData.getCapeData(); byte[] geometryNameBytes = Base64.getDecoder().decode(clientData.getGeometryName().getBytes("UTF-8")); From 99e72f35b308542cf0dbfb5b58816503c3d6a129 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Thu, 17 Sep 2020 01:08:26 +0100 Subject: [PATCH 094/140] Add support for manually supplying Bedrock resource packs (#1076) * send resource packs A lot of this code is nukkit-credits in the classes * send resource packs A lot of this code is nukkit-credits in the classes * Remove unnecessary code/debugs * use separately generated hashes * Updated mappings and added .mcpack support * "packs" directory auto-create (#484) * "packs" directory auto-create * cleaned indentation in ResourcePack.java * Cleaned ResourcePack.java * Another cleanup I hate editor on github. * Yet another * Another indentation cleanup * Fix resource pack loading (cherry picked from commit f93b07491ea0d4cd96254af47a16f10ce9b92128) * Move back to internal sha256 hashing (cherry picked from commit 812a3d82b2a4d34e67bbccc193fbd87ac9fb174b) * Add resource pack loading back after merge * Add comments, config option and removed unused files * Fix packs folder location and cleanup code * Move to better options for the client * Fix typos in comments * Fix pack loading * Try to make it compile * Final touches? * Add Javadoc for MathUtils#constrain Co-authored-by: EOT3000 <43685885+EOT3000@users.noreply.github.com> Co-authored-by: Vesek <61123478+Vesek@users.noreply.github.com> Co-authored-by: Heath123 Co-authored-by: Camotoy <20743703+DoctorMacc@users.noreply.github.com> --- .gitignore | 3 +- .../geysermc/connector/GeyserConnector.java | 6 +- .../configuration/GeyserConfiguration.java | 2 + .../GeyserJacksonConfiguration.java | 3 + .../network/UpstreamPacketHandler.java | 83 +++++++++++-- .../geysermc/connector/utils/FileUtils.java | 30 +++++ .../geysermc/connector/utils/MathUtils.java | 20 +++ .../connector/utils/ResourcePack.java | 114 +++++++++++++++++ .../connector/utils/ResourcePackManifest.java | 117 ++++++++++++++++++ connector/src/main/resources/config.yml | 5 + 10 files changed, 373 insertions(+), 10 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/utils/ResourcePack.java create mode 100644 connector/src/main/java/org/geysermc/connector/utils/ResourcePackManifest.java diff --git a/.gitignore b/.gitignore index 69c07e500..e3c3b0a56 100644 --- a/.gitignore +++ b/.gitignore @@ -241,4 +241,5 @@ config.yml logs/ public-key.pem locales/ -/cache/ \ No newline at end of file +/cache/ +/packs/ \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 2593c516a..3c814393d 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -25,6 +25,7 @@ package org.geysermc.connector; +import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.nukkitx.network.raknet.RakNetConstants; @@ -56,6 +57,7 @@ import org.geysermc.connector.network.translators.world.block.entity.BlockEntity import org.geysermc.connector.utils.DimensionUtils; import org.geysermc.connector.utils.LanguageUtils; import org.geysermc.connector.utils.LocaleUtils; +import org.geysermc.connector.utils.ResourcePack; import javax.naming.directory.Attribute; import javax.naming.directory.InitialDirContext; @@ -73,7 +75,7 @@ import java.util.concurrent.TimeUnit; @Getter public class GeyserConnector { - public static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES); + public static final ObjectMapper JSON_MAPPER = new ObjectMapper().enable(JsonParser.Feature.IGNORE_UNDEFINED).enable(JsonParser.Feature.ALLOW_COMMENTS).disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); public static final String NAME = "Geyser"; public static final String VERSION = "DEV"; // A fallback for running in IDEs @@ -136,6 +138,8 @@ public class GeyserConnector { SoundRegistry.init(); SoundHandlerRegistry.init(); + ResourcePack.loadPacks(); + if (platformType != PlatformType.STANDALONE && config.getRemote().getAddress().equals("auto")) { // Set the remote address to localhost since that is where we are always connecting try { 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 2d76e5740..c1cc4d036 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java @@ -79,6 +79,8 @@ public interface GeyserConfiguration { boolean isCacheChunks(); + boolean isForceResourcePacks(); + int getCacheImages(); IMetricsInfo getMetrics(); 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 6748ef7e7..99a3a7a5a 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java @@ -103,6 +103,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonProperty("above-bedrock-nether-building") private boolean aboveBedrockNetherBuilding = false; + @JsonProperty("force-resource-packs") + private boolean forceResourcePacks = true; + private MetricsInfo metrics; @Getter diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index f65b3ef1a..7e97d4298 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -26,17 +26,24 @@ package org.geysermc.connector.network; import com.nukkitx.protocol.bedrock.BedrockPacket; +import com.nukkitx.protocol.bedrock.data.ResourcePackType; import com.nukkitx.protocol.bedrock.BedrockPacketCodec; import com.nukkitx.protocol.bedrock.packet.*; +import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.common.AuthType; import org.geysermc.connector.configuration.GeyserConfiguration; -import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslatorRegistry; -import org.geysermc.connector.utils.LoginEncryptionUtils; import org.geysermc.connector.utils.LanguageUtils; +import org.geysermc.connector.utils.LoginEncryptionUtils; +import org.geysermc.connector.utils.MathUtils; +import org.geysermc.connector.utils.ResourcePack; +import org.geysermc.connector.utils.ResourcePackManifest; import org.geysermc.connector.utils.SettingsUtils; +import java.io.FileInputStream; +import java.io.InputStream; + public class UpstreamPacketHandler extends LoggingPacketHandler { public UpstreamPacketHandler(GeyserConnector connector, GeyserSession session) { @@ -70,6 +77,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { session.sendUpstreamPacket(playStatus); ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket(); + for(ResourcePack resourcePack : ResourcePack.PACKS.values()) { + ResourcePackManifest.Header header = resourcePack.getManifest().getHeader(); + resourcePacksInfo.getResourcePackInfos().add(new ResourcePacksInfoPacket.Entry(header.getUuid().toString(), header.getVersionString(), resourcePack.getFile().length(), "", "", "", false)); + } + resourcePacksInfo.setForcedToAccept(GeyserConnector.getInstance().getConfig().isForceResourcePacks()); session.sendUpstreamPacket(resourcePacksInfo); return true; } @@ -81,13 +93,42 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { session.connect(connector.getRemoteServer()); connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.connect", session.getAuthData().getName())); break; - case HAVE_ALL_PACKS: - ResourcePackStackPacket stack = new ResourcePackStackPacket(); - stack.setExperimental(false); - stack.setForcedToAccept(false); - stack.setGameVersion("*"); - session.sendUpstreamPacket(stack); + + case SEND_PACKS: + for(String id : packet.getPackIds()) { + ResourcePackDataInfoPacket data = new ResourcePackDataInfoPacket(); + String[] packID = id.split("_"); + ResourcePack pack = ResourcePack.PACKS.get(packID[0]); + ResourcePackManifest.Header header = pack.getManifest().getHeader(); + + data.setPackId(header.getUuid()); + int chunkCount = (int) Math.ceil((int) pack.getFile().length() / (double) ResourcePack.CHUNK_SIZE); + data.setChunkCount(chunkCount); + data.setCompressedPackSize(pack.getFile().length()); + data.setMaxChunkSize(ResourcePack.CHUNK_SIZE); + data.setHash(pack.getSha256()); + data.setPackVersion(packID[1]); + data.setPremium(false); + data.setType(ResourcePackType.RESOURCE); + + session.sendUpstreamPacket(data); + } break; + + case HAVE_ALL_PACKS: + ResourcePackStackPacket stackPacket = new ResourcePackStackPacket(); + stackPacket.setExperimental(false); + stackPacket.setForcedToAccept(false); // Leaving this as false allows the player to choose to download or not + stackPacket.setGameVersion(session.getClientData().getGameVersion()); + + for (ResourcePack pack : ResourcePack.PACKS.values()) { + ResourcePackManifest.Header header = pack.getManifest().getHeader(); + stackPacket.getResourcePacks().add(new ResourcePackStackPacket.Entry(header.getUuid().toString(), header.getVersionString(), "")); + } + + session.sendUpstreamPacket(stackPacket); + break; + default: session.disconnect("disconnectionScreen.resourcePack"); break; @@ -149,4 +190,30 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { boolean defaultHandler(BedrockPacket packet) { return translateAndDefault(packet); } + + @Override + public boolean handle(ResourcePackChunkRequestPacket packet) { + ResourcePackChunkDataPacket data = new ResourcePackChunkDataPacket(); + ResourcePack pack = ResourcePack.PACKS.get(packet.getPackId().toString()); + + data.setChunkIndex(packet.getChunkIndex()); + data.setProgress(packet.getChunkIndex() * ResourcePack.CHUNK_SIZE); + data.setPackVersion(packet.getPackVersion()); + data.setPackId(packet.getPackId()); + + int offset = packet.getChunkIndex() * ResourcePack.CHUNK_SIZE; + byte[] packData = new byte[(int) MathUtils.constrain(pack.getFile().length() - offset, 0, ResourcePack.CHUNK_SIZE)]; + + try (InputStream inputStream = new FileInputStream(pack.getFile())) { + inputStream.skip(offset); + inputStream.read(packData, 0, packData.length); + } catch (Exception e) { + e.printStackTrace(); + } + + data.setData(packData); + + session.sendUpstreamPacket(data); + return true; + } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java index 300946b34..63255cfa0 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/FileUtils.java @@ -25,6 +25,8 @@ package org.geysermc.connector.utils; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import org.geysermc.connector.GeyserConnector; @@ -37,6 +39,8 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; +import java.nio.file.Files; +import java.security.MessageDigest; import java.util.function.Function; public class FileUtils { @@ -55,6 +59,15 @@ public class FileUtils { return objectMapper.readValue(src, valueType); } + public static T loadYaml(InputStream src, Class valueType) throws IOException { + ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory()).enable(JsonParser.Feature.IGNORE_UNDEFINED).disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + return objectMapper.readValue(src, valueType); + } + + public static T loadJson(InputStream src, Class valueType) throws IOException { + return GeyserConnector.JSON_MAPPER.readValue(src, valueType); + } + /** * Open the specified file or copy if from resources * @@ -145,6 +158,23 @@ public class FileUtils { return stream; } + /** + * Calculate the SHA256 hash of the resource pack file + * @param file File to calculate the hash for + * @return A byte[] representation of the hash + */ + public static byte[] calculateSHA256(File file) { + byte[] sha256; + + try { + sha256 = MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(file.toPath())); + } catch (Exception e) { + throw new RuntimeException("Could not calculate pack hash", e); + } + + return sha256; + } + /** * Get the stored reflection data for a given path * diff --git a/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java b/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java index 487024922..29dd2cc23 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java @@ -40,6 +40,26 @@ public class MathUtils { return floatNumber > truncated ? truncated + 1 : truncated; } + /** + * If number is greater than the max, set it to max, and if number is lower than low, set it to low. + * @param num number to calculate + * @param min the lowest value the number can be + * @param max the greatest value the number can be + * @return - min if num is lower than min
+ * - max if num is greater than max
+ * - num otherwise + */ + public static double constrain(double num, double min, double max) { + if (num > max) { + num = max; + } + if (num < min) { + num = min; + } + + return num; + } + /** * Converts the given object from an int or byte to byte. * This is used for NBT data that might be either an int diff --git a/connector/src/main/java/org/geysermc/connector/utils/ResourcePack.java b/connector/src/main/java/org/geysermc/connector/utils/ResourcePack.java new file mode 100644 index 000000000..3e9848dbe --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/utils/ResourcePack.java @@ -0,0 +1,114 @@ +/* + * 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.utils; + +import org.geysermc.connector.GeyserConnector; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.ZipFile; + +/** + * This represents a resource pack and all the data relevant to it + */ +public class ResourcePack { + /** + * The list of loaded resource packs + */ + public static final Map PACKS = new HashMap<>(); + + /** + * The size of each chunk to use when sending the resource packs to clients in bytes + */ + public static final int CHUNK_SIZE = 102400; + + private byte[] sha256; + private File file; + private ResourcePackManifest manifest; + private ResourcePackManifest.Version version; + + /** + * Loop through the packs directory and locate valid resource pack files + */ + public static void loadPacks() { + File directory = GeyserConnector.getInstance().getBootstrap().getConfigFolder().resolve("packs").toFile(); + + if (!directory.exists()) { + directory.mkdir(); + + // As we just created the directory it will be empty + return; + } + + for (File file : directory.listFiles()) { + if (file.getName().endsWith(".zip") || file.getName().endsWith(".mcpack")) { + ResourcePack pack = new ResourcePack(); + + pack.sha256 = FileUtils.calculateSHA256(file); + + try { + ZipFile zip = new ZipFile(file); + + zip.stream().forEach((x) -> { + if (x.getName().contains("manifest.json")) { + try { + ResourcePackManifest manifest = FileUtils.loadJson(zip.getInputStream(x), ResourcePackManifest.class); + + pack.file = file; + pack.manifest = manifest; + pack.version = ResourcePackManifest.Version.fromArray(manifest.getHeader().getVersion()); + + PACKS.put(pack.getManifest().getHeader().getUuid().toString(), pack); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } catch (Exception e) { + GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.resource_pack.broken", file.getName())); + e.printStackTrace(); + } + } + } + } + + public byte[] getSha256() { + return sha256; + } + + public File getFile() { + return file; + } + + public ResourcePackManifest getManifest() { + return manifest; + } + + public ResourcePackManifest.Version getVersion() { + return version; + } +} diff --git a/connector/src/main/java/org/geysermc/connector/utils/ResourcePackManifest.java b/connector/src/main/java/org/geysermc/connector/utils/ResourcePackManifest.java new file mode 100644 index 000000000..6a08c4dbc --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/utils/ResourcePackManifest.java @@ -0,0 +1,117 @@ +/* + * 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.utils; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; +import lombok.Value; + +import java.util.Collection; +import java.util.Collections; +import java.util.UUID; + +/** + * author: NukkitX + * Nukkit Project + */ +@Getter +@EqualsAndHashCode +public class ResourcePackManifest { + @JsonProperty("format_version") + private Integer formatVersion; + private Header header; + private Collection modules; + protected Collection dependencies; + + public Collection getModules() { + return Collections.unmodifiableCollection(modules); + } + + @Getter + @ToString + public static class Header { + private String description; + private String name; + private UUID uuid; + private int[] version; + @JsonProperty("min_engine_version") + private int[] minimumSupportedMinecraftVersion; + + public String getVersionString() { + return version[0] + "." + version[1] + "." + version[2]; + } + } + + @Getter + @ToString + public static class Module { + private String description; + private String name; + private UUID uuid; + private int[] version; + } + + @Getter + @ToString + public static class Dependency { + private UUID uuid; + private int[] version; + } + + @Value + public static class Version { + private final int major; + private final int minor; + private final int patch; + + public static Version fromString(String ver) { + String[] split = ver.replace(']', ' ') + .replace('[', ' ') + .replaceAll(" ", "").split(","); + + return new Version(Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2])); + } + + public static Version fromArray(int[] ver) { + return new Version(ver[0], ver[1], ver[2]); + } + + private Version(int major, int minor, int patch) { + this.major = major; + this.minor = minor; + this.patch = patch; + } + + + @Override + public String toString() { + return major + "." + minor + "." + patch; + } + } +} + diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index 7b0bd8513..0602bb546 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -112,6 +112,11 @@ cache-images: 0 # the end sky in the nether, but ultimately it's the only way for this feature to work. above-bedrock-nether-building: false +# Force clients to load all resource packs if there are any. +# If set to false it allows the user to disconnect from the server if they don't +# want to download the resource packs +force-resource-packs: true + # bStats is a stat tracker that is entirely anonymous and tracks only basic information # about Geyser, such as how many people are online, how many servers are using Geyser, # what OS is being used, etc. You can learn more about bStats here: https://bstats.org/. From 2f2164f38775442fd909f32267774dcc96d5fc6c Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Thu, 17 Sep 2020 23:07:20 -0400 Subject: [PATCH 095/140] InventoryUtils: Don't send Java packet on hotbar item selection (#1301) The Bedrock client sends a confirmation packet we translate regardless. --- .../java/org/geysermc/connector/utils/InventoryUtils.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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 b3cc14b97..c1224e6e2 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/InventoryUtils.java @@ -27,7 +27,6 @@ package org.geysermc.connector.utils; import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; -import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerChangeHeldItemPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCreativeInventoryActionPacket; import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientMoveItemToHotbarPacket; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; @@ -241,8 +240,6 @@ public class InventoryUtils { hotbarPacket.setSelectedHotbarSlot(slot - 36); hotbarPacket.setSelectHotbarSlot(true); session.sendUpstreamPacket(hotbarPacket); - ClientPlayerChangeHeldItemPacket heldItemPacket = new ClientPlayerChangeHeldItemPacket(slot); - session.sendDownstreamPacket(heldItemPacket); - session.getInventory().setHeldItemSlot(slot - 36); + // No need to send a Java packet as Bedrock sends a confirmation packet back that we translate } } From 5fafa0759ef847382785e686c6e7c4a92214845e Mon Sep 17 00:00:00 2001 From: Comstepr <32700815+Comstepr@users.noreply.github.com> Date: Sat, 19 Sep 2020 09:21:44 +0800 Subject: [PATCH 096/140] Update pullrequest.yml (#1305) --- .github/workflows/pullrequest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 9cb0726ca..78a3ce299 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -22,7 +22,7 @@ jobs: - name: submodules-init uses: snickerbockers/submodules-init@v4 - name: Build with Maven - run: mvn -B package + run: mvn -B package -T 2C - name: Archive artifacts (Geyser Standalone) uses: actions/upload-artifact@v2 if: success() From 2db1d16f5c2b923bcc4bf3145e9e6f64a021829a Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 21 Sep 2020 16:06:25 -0400 Subject: [PATCH 097/140] Only send the client brand on game join (#1299) * Only send the client brand on game join * Apply suggested changes --- .../java/JavaJoinGameTranslator.java | 4 +++ .../PluginMessageUtils.java} | 33 +++++++------------ 2 files changed, 16 insertions(+), 21 deletions(-) rename connector/src/main/java/org/geysermc/connector/{network/translators/java/JavaPluginMessageTranslator.java => utils/PluginMessageUtils.java} (66%) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java index a8fc122bd..c479b23fe 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java @@ -28,6 +28,7 @@ package org.geysermc.connector.network.translators.java; import com.github.steveice10.mc.protocol.data.game.entity.player.HandPreference; import com.github.steveice10.mc.protocol.data.game.setting.ChatVisibility; import com.github.steveice10.mc.protocol.data.game.setting.SkinPart; +import com.github.steveice10.mc.protocol.packet.ingame.client.ClientPluginMessagePacket; import com.github.steveice10.mc.protocol.packet.ingame.client.ClientSettingsPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.ServerJoinGamePacket; import com.nukkitx.protocol.bedrock.data.GameRuleData; @@ -38,6 +39,7 @@ 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.utils.DimensionUtils; +import org.geysermc.connector.utils.PluginMessageUtils; import java.util.Arrays; import java.util.List; @@ -92,6 +94,8 @@ public class JavaJoinGameTranslator extends PacketTranslator { +public class PluginMessageUtils { - private static byte[] brandData; + private static final byte[] BRAND_DATA; static { byte[] data = GeyserConnector.NAME.getBytes(StandardCharsets.UTF_8); byte[] varInt = writeVarInt(data.length); - brandData = new byte[varInt.length + data.length]; - System.arraycopy(varInt, 0, brandData, 0, varInt.length); - System.arraycopy(data, 0, brandData, varInt.length, data.length); + BRAND_DATA = new byte[varInt.length + data.length]; + System.arraycopy(varInt, 0, BRAND_DATA, 0, varInt.length); + System.arraycopy(data, 0, BRAND_DATA, varInt.length, data.length); } - - @Override - public void translate(ServerPluginMessagePacket packet, GeyserSession session) { - if (packet.getChannel().equals("minecraft:brand")) { - session.sendDownstreamPacket( - new ClientPluginMessagePacket(packet.getChannel(), brandData) - ); - } + /** + * Get the prebuilt brand as a byte array + * @return the brand information of the Geyser client + */ + public static byte[] getGeyserBrandData() { + return BRAND_DATA; } private static byte[] writeVarInt(int value) { From 02aeddbadd7c1366c3719b74d22ba52a6920f82c Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 21 Sep 2020 23:55:13 -0400 Subject: [PATCH 098/140] Scoreboard: Fix various issues (#1286) * Scoreboard: update score on UpdateType.ADD * Actually fix * Readd the Objective when a score changes It looks like Objectives only update when you Remove the Objective and add it back using the SetDisplayObjective. This is hopefully a hotfix, but I think that there is no better way. * Explain score tracking Co-authored-by: Tim203 --- .../geysermc/connector/scoreboard/Objective.java | 2 +- .../geysermc/connector/scoreboard/Scoreboard.java | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java index 92a1add34..b2f648612 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java @@ -83,7 +83,7 @@ public class Objective { public void setScore(String id, int score) { if (scores.containsKey(id)) { - scores.get(id).setScore(score).setUpdateType(UpdateType.ADD); + scores.get(id).setScore(score); return; } registerScore(id, score); diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java index 9f89d9d2b..ae1b82757 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java @@ -26,7 +26,6 @@ package org.geysermc.connector.scoreboard; import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardPosition; -import com.nukkitx.protocol.bedrock.BedrockPacket; import com.nukkitx.protocol.bedrock.data.ScoreInfo; import com.nukkitx.protocol.bedrock.packet.RemoveObjectivePacket; import com.nukkitx.protocol.bedrock.packet.SetDisplayObjectivePacket; @@ -153,6 +152,10 @@ public class Scoreboard { boolean globalAdd = objective.getUpdateType() == ADD; boolean globalRemove = objective.getUpdateType() == REMOVE; + // Track if any scores changed + // Used to delete and resend scoreboard objectives; otherwise they won't update on Bedrock + boolean scoreChanged = false; + for (Score score : objective.getScores().values()) { Team team = score.getTeam(); @@ -187,6 +190,10 @@ public class Scoreboard { if (remove) { removeScores.add(score.getCachedInfo()); } + + if (add || remove) { + scoreChanged = true; + } // score is pending to be updated, so we use the current score as the old score score.setOldScore(score.getScore()); @@ -198,7 +205,7 @@ public class Scoreboard { score.setUpdateType(NOTHING); } - if (globalRemove || globalUpdate) { + if (globalRemove || globalUpdate || scoreChanged) { RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket(); removeObjectivePacket.setObjectiveId(objective.getObjectiveName()); session.sendUpstreamPacket(removeObjectivePacket); @@ -208,7 +215,7 @@ public class Scoreboard { } } - if (globalAdd || globalUpdate) { + if (globalAdd || globalUpdate || scoreChanged) { SetDisplayObjectivePacket displayObjectivePacket = new SetDisplayObjectivePacket(); displayObjectivePacket.setObjectiveId(objective.getObjectiveName()); displayObjectivePacket.setDisplayName(objective.getDisplayName()); From 2dc7dc10ff11b9435920d8a2188a57298dca9f2c Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Tue, 22 Sep 2020 14:15:59 -0400 Subject: [PATCH 099/140] Remove warning about slot 50 (#1325) We know that it occurs with console crafting. --- .../translators/inventory/CraftingInventoryTranslator.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java index f5ebe62df..b260565b8 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/CraftingInventoryTranslator.java @@ -30,12 +30,9 @@ import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.data.inventory.InventoryActionData; import com.nukkitx.protocol.bedrock.data.inventory.InventorySource; -import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; -import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.inventory.updater.CursorInventoryUpdater; -import org.geysermc.connector.network.translators.inventory.updater.InventoryUpdater; import org.geysermc.connector.utils.InventoryUtils; import java.util.List; @@ -48,7 +45,7 @@ public class CraftingInventoryTranslator extends BlockInventoryTranslator { @Override public int bedrockSlotToJava(InventoryActionData action) { if (action.getSlot() == 50) { - GeyserConnector.getInstance().getLogger().warning("Slot 50 found, please report: " + action); + // Slot 50 is used for crafting with a controller. return 0; } From b4c7682130d4e8931da2f5dfa10d984eb2e17f9d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Tue, 22 Sep 2020 14:16:57 -0400 Subject: [PATCH 100/140] Implement experience sounds (#1320) Bedrock sends a level event for the experience sound around the same time as a Java entity collect item packet is sent. --- .../JavaEntityCollectItemTranslator.java} | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) rename connector/src/main/java/org/geysermc/connector/network/translators/java/{world/JavaCollectItemTranslator.java => entity/JavaEntityCollectItemTranslator.java} (61%) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaCollectItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityCollectItemTranslator.java similarity index 61% rename from connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaCollectItemTranslator.java rename to connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityCollectItemTranslator.java index a90c7016b..270c33a7a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaCollectItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityCollectItemTranslator.java @@ -23,26 +23,32 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.translators.java.world; +package org.geysermc.connector.network.translators.java.entity; import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityCollectItemPacket; +import com.nukkitx.protocol.bedrock.data.LevelEventType; +import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; import com.nukkitx.protocol.bedrock.packet.TakeItemEntityPacket; import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.ExpOrbEntity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; +/** + * This packet is called whenever a player picks up an item. + * In Java, this is called for item entities, experience orbs and arrows + * Bedrock uses it for arrows and item entities, but not experience orbs. + */ @Translator(packet = ServerEntityCollectItemPacket.class) -public class JavaCollectItemTranslator extends PacketTranslator { +public class JavaEntityCollectItemTranslator extends PacketTranslator { @Override public void translate(ServerEntityCollectItemPacket packet, GeyserSession session) { - // This is the definition of translating - both packets take the same values - TakeItemEntityPacket takeItemEntityPacket = new TakeItemEntityPacket(); - // Collected entity is the item + // Collected entity is the other entity Entity collectedEntity = session.getEntityCache().getEntityByJavaId(packet.getCollectedEntityId()); if (collectedEntity == null) return; - // Collector is the entity picking up the item + // Collector is the entity 'picking up' the item Entity collectorEntity; if (packet.getCollectorEntityId() == session.getPlayerEntity().getEntityId()) { collectorEntity = session.getPlayerEntity(); @@ -50,8 +56,19 @@ public class JavaCollectItemTranslator extends PacketTranslator Date: Tue, 22 Sep 2020 20:10:38 -0500 Subject: [PATCH 101/140] Use SERVER_READY in BedrockRespawnTranslator to fix respawn bug --- .../network/translators/bedrock/BedrockRespawnTranslator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java index 4e4b4a307..71d52e134 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java @@ -43,7 +43,7 @@ public class BedrockRespawnTranslator extends PacketTranslator { RespawnPacket respawnPacket = new RespawnPacket(); respawnPacket.setRuntimeEntityId(0); respawnPacket.setPosition(Vector3f.ZERO); - respawnPacket.setState(RespawnPacket.State.SERVER_SEARCHING); + respawnPacket.setState(RespawnPacket.State.SERVER_READY); session.sendUpstreamPacket(respawnPacket); } From 1ec768d95d313525e03af4b500927c29a2eb5762 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Thu, 24 Sep 2020 12:54:18 -0400 Subject: [PATCH 102/140] Fix interaction spam bug (#1324) * Fix interaction spam bug This references the Nukkit 1.0 fix for the client bug of spamming to interact. Holding down still works. * Remove interaction position set at action type 1 * Remove debug line --- .../connector/network/session/GeyserSession.java | 14 +++++++++++++- .../BedrockInventoryTransactionTranslator.java | 14 +++++++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) 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 81b922826..0a28b11f1 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 @@ -156,8 +156,13 @@ public class GeyserSession implements CommandSender { @Setter private boolean interacting; + /** + * Stores the last position of the block the player interacted with. This can either be a block that the client + * placed or an existing block the player interacted with (for example, a chest).
+ * Initialized as (0, 0, 0) so it is always not-null. + */ @Setter - private Vector3i lastInteractionPosition; + private Vector3i lastInteractionPosition = Vector3i.ZERO; private boolean manyDimPackets = false; private ServerRespawnPacket lastDimPacket = null; @@ -193,6 +198,13 @@ public class GeyserSession implements CommandSender { @Setter private long lastHitTime; + /** + * Store the last time the player interacted. Used to fix a right-click spam bug. + * See https://github.com/GeyserMC/Geyser/issues/503 for context. + */ + @Setter + private long lastInteractionTime; + private boolean reducedDebugInfo = false; @Setter diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java index 95e669572..131fdcc97 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java @@ -78,6 +78,17 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator Date: Thu, 24 Sep 2020 15:11:42 -0400 Subject: [PATCH 103/140] Fix picking up liquids with buckets (#1311) * Fix picking up liquids with buckets The last fix to prevent bucket placement upon interacting with an inventory had an oversight with empty buckets, making them unusable. This commit fixes that while keeping the previous fix. * Remove debug line * Fix milk drinking and visual bucket item apperance * Comment elaboration * Make indentiation better --- ...BedrockInventoryTransactionTranslator.java | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java index 131fdcc97..8e891d925 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java @@ -39,8 +39,10 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlaye import com.nukkitx.math.vector.Vector3f; import com.nukkitx.math.vector.Vector3i; import com.nukkitx.protocol.bedrock.data.LevelEventType; +import com.nukkitx.protocol.bedrock.data.inventory.ContainerId; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; +import com.nukkitx.protocol.bedrock.packet.InventorySlotPacket; import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket; import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; import org.geysermc.connector.entity.CommandBlockMinecartEntity; @@ -112,11 +114,23 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator Date: Fri, 25 Sep 2020 15:09:02 -0400 Subject: [PATCH 104/140] Prevent Bedrock from changing gamemode client-side (#1337) In vanilla Bedrock, if you have operator status, the client sends a packet to change gamemode without confirmation from the server. Since we have a custom server option to request the gamemode, we just reset the gamemode and ignore this packet. --- .../BedrockSetPlayerGameTypeTranslator.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockSetPlayerGameTypeTranslator.java diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockSetPlayerGameTypeTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockSetPlayerGameTypeTranslator.java new file mode 100644 index 000000000..d61b37863 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockSetPlayerGameTypeTranslator.java @@ -0,0 +1,47 @@ +/* + * 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.entity.player; + +import com.nukkitx.protocol.bedrock.packet.SetPlayerGameTypePacket; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.Translator; + +/** + * In vanilla Bedrock, if you have operator status, this sets the player's gamemode without confirmation from the server. + * Since we have a custom server option to request the gamemode, we just reset the gamemode and ignore this. + */ +@Translator(packet = SetPlayerGameTypePacket.class) +public class BedrockSetPlayerGameTypeTranslator extends PacketTranslator { + + @Override + public void translate(SetPlayerGameTypePacket packet, GeyserSession session) { + // no + SetPlayerGameTypePacket playerGameTypePacket = new SetPlayerGameTypePacket(); + playerGameTypePacket.setGamemode(session.getGameMode().ordinal()); + session.sendUpstreamPacket(playerGameTypePacket); + } +} From d9b05f5b72beed41d0a57d0594de6c88b47b649d Mon Sep 17 00:00:00 2001 From: Luke <32024335+lukeeey@users.noreply.github.com> Date: Fri, 25 Sep 2020 20:52:44 +0100 Subject: [PATCH 105/140] Update the CI link in the README (#1339) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ba3e6fc2d..e0404a934 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/Geyser/job/master/) +[![Build Status](https://ci.nukkitx.com/job/Geyser/job/master/badge/icon)](https://ci.nukkitx.com/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/) From 7c49391b9d38373bbe0164032c8d01b41929a8c7 Mon Sep 17 00:00:00 2001 From: rtm516 Date: Mon, 28 Sep 2020 22:43:50 +0100 Subject: [PATCH 106/140] Fix gamemodes not fully applying on server switch (#1348) * Fix gamemodes not fully applying on server switch * Revert previous commit and move session flag updating to the adventure settings method --- .../org/geysermc/connector/network/session/GeyserSession.java | 4 ++++ .../translators/java/world/JavaNotifyClientTranslator.java | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) 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 0a28b11f1..300877f81 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 @@ -729,6 +729,10 @@ public class GeyserSession implements CommandSender { // Required to make command blocks destroyable adventureSettingsPacket.setPlayerPermission(opPermissionLevel >= 2 ? PlayerPermission.OPERATOR : PlayerPermission.MEMBER); + // Update the noClip and worldImmutable values based on the current gamemode + noClip = gameMode == GameMode.SPECTATOR; + worldImmutable = gameMode == GameMode.ADVENTURE || gameMode == GameMode.SPECTATOR; + Set flags = new HashSet<>(); if (canFly) { flags.add(AdventureSetting.MAY_FLY); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java index a7bc7b61f..493a7ca1e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java @@ -103,8 +103,6 @@ public class JavaNotifyClientTranslator extends PacketTranslator Date: Tue, 29 Sep 2020 01:09:57 +0300 Subject: [PATCH 107/140] Update README.md (#1349) Make the server address look cool (for build 405, sorry) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e0404a934..f7201bb08 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set - Download: http://ci.geysermc.org - Discord: http://discord.geysermc.org/ - ~~Donate: https://patreon.com/GeyserMC~~ Currently disabled. -- Test Server: test.geysermc.org port 25565 for Java and 19132 for Bedrock +- Test Server: `test.geysermc.org` port `25565` for Java and `19132` for Bedrock ## What's Left to be Added/Fixed - The Following Inventories From 650c02ef669c3a525c66810fbd834ad191991c47 Mon Sep 17 00:00:00 2001 From: bundabrg Date: Tue, 29 Sep 2020 11:49:46 +0800 Subject: [PATCH 108/140] Remove 'geyser' from parameters when executing a command under Spigot, Bungeecord, Sponge, Velocity (#1266) * Remove 'geyser' from parameters when executing a command under Spigot, Bungeecode, Sponge, Velocity Fixes https://github.com/bundabrg/GeyserReversion/issues/8 * Fix case when there are no sub commands Co-authored-by: bundabrg --- .../bungeecord/command/GeyserBungeeCommandExecutor.java | 4 ++-- .../spigot/command/GeyserSpigotCommandExecutor.java | 4 ++-- .../sponge/command/GeyserSpongeCommandExecutor.java | 4 ++-- .../velocity/command/GeyserVelocityCommandExecutor.java | 6 ++++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/GeyserBungeeCommandExecutor.java b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/GeyserBungeeCommandExecutor.java index 3b051c5c3..f673a3f51 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/GeyserBungeeCommandExecutor.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/platform/bungeecord/command/GeyserBungeeCommandExecutor.java @@ -64,10 +64,10 @@ public class GeyserBungeeCommandExecutor extends Command implements TabExecutor sender.sendMessage(TextComponent.fromLegacyText(ChatColor.RED + message)); return; } - getCommand(args[0]).execute(new BungeeCommandSender(sender), args); + getCommand(args[0]).execute(new BungeeCommandSender(sender), args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]); } } else { - getCommand("help").execute(new BungeeCommandSender(sender), args); + getCommand("help").execute(new BungeeCommandSender(sender), new String[0]); } } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/command/GeyserSpigotCommandExecutor.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/command/GeyserSpigotCommandExecutor.java index 381872752..2dba29015 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/command/GeyserSpigotCommandExecutor.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/command/GeyserSpigotCommandExecutor.java @@ -59,11 +59,11 @@ public class GeyserSpigotCommandExecutor implements TabExecutor { sender.sendMessage(ChatColor.RED + message); return true; } - getCommand(args[0]).execute(new SpigotCommandSender(sender), args); + getCommand(args[0]).execute(new SpigotCommandSender(sender), args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]); return true; } } else { - getCommand("help").execute(new SpigotCommandSender(sender), args); + getCommand("help").execute(new SpigotCommandSender(sender), new String[0]); return true; } return true; diff --git a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/command/GeyserSpongeCommandExecutor.java b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/command/GeyserSpongeCommandExecutor.java index d37321ffe..c77e82718 100644 --- a/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/command/GeyserSpongeCommandExecutor.java +++ b/bootstrap/sponge/src/main/java/org/geysermc/platform/sponge/command/GeyserSpongeCommandExecutor.java @@ -59,10 +59,10 @@ public class GeyserSpongeCommandExecutor implements CommandCallable { source.sendMessage(Text.of(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.bootstrap.command.permission_fail"))); return CommandResult.success(); } - getCommand(args[0]).execute(new SpongeCommandSender(source), args); + getCommand(args[0]).execute(new SpongeCommandSender(source), args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]); } } else { - getCommand("help").execute(new SpongeCommandSender(source), args); + getCommand("help").execute(new SpongeCommandSender(source), new String[0]); } return CommandResult.success(); } diff --git a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/GeyserVelocityCommandExecutor.java b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/GeyserVelocityCommandExecutor.java index fa3aaa3c3..afd6c3bfd 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/GeyserVelocityCommandExecutor.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/platform/velocity/command/GeyserVelocityCommandExecutor.java @@ -37,6 +37,8 @@ import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.GeyserCommand; import org.geysermc.connector.utils.LanguageUtils; +import java.util.Arrays; + @AllArgsConstructor public class GeyserVelocityCommandExecutor implements Command { @@ -51,10 +53,10 @@ public class GeyserVelocityCommandExecutor implements Command { source.sendMessage(TextComponent.of(ChatColor.RED + LanguageUtils.getLocaleStringLog("geyser.bootstrap.command.permission_fail"))); return; } - getCommand(args[0]).execute(new VelocityCommandSender(source), args); + getCommand(args[0]).execute(new VelocityCommandSender(source), args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : new String[0]); } } else { - getCommand("help").execute(new VelocityCommandSender(source), args); + getCommand("help").execute(new VelocityCommandSender(source), new String[0]); } } From a5b00e09a1111d9f0999989ff773dc1d405e2786 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Tue, 29 Sep 2020 13:19:37 -0400 Subject: [PATCH 109/140] Villager trade fixes (#1350) This commit mainly focuses on fixing the crashing of villagers that occurred pre-1.14. Co-authored-by: AJ Ferguson --- .../holder/BlockInventoryHolder.java | 5 ++--- .../java/window/JavaOpenWindowTranslator.java | 4 +++- .../java/world/JavaTradeListTranslator.java | 20 +++++++++++++------ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java index 6dfde5d1c..6afdb25dd 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/inventory/holder/BlockInventoryHolder.java @@ -35,9 +35,8 @@ import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; import lombok.AllArgsConstructor; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; -import org.geysermc.connector.utils.LocaleUtils; +import org.geysermc.connector.network.translators.world.block.BlockTranslator; @AllArgsConstructor public class BlockInventoryHolder extends InventoryHolder { @@ -60,7 +59,7 @@ public class BlockInventoryHolder extends InventoryHolder { .putInt("x", position.getX()) .putInt("y", position.getY()) .putInt("z", position.getZ()) - .putString("CustomName", LocaleUtils.getLocaleString(inventory.getTitle(), session.getClientData().getLanguageCode())).build(); + .putString("CustomName", inventory.getTitle()).build(); BlockEntityDataPacket dataPacket = new BlockEntityDataPacket(); dataPacket.setData(tag); dataPacket.setBlockPosition(position); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java index dde45cf89..820639b3d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java @@ -29,7 +29,6 @@ import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientClose import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket; import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import com.nukkitx.protocol.bedrock.packet.ContainerClosePacket; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; @@ -37,6 +36,7 @@ import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.utils.InventoryUtils; +import org.geysermc.connector.utils.LocaleUtils; @Translator(packet = ServerOpenWindowPacket.class) public class JavaOpenWindowTranslator extends PacketTranslator { @@ -71,6 +71,8 @@ public class JavaOpenWindowTranslator extends PacketTranslator 0 ? packet.getVillagerLevel() - 1 : 0); // -1 crashes client recipe.put("buyA", getItemTag(session, trade.getFirstInput(), trade.getSpecialPrice())); if (trade.getSecondInput() != null) { recipe.put("buyB", getItemTag(session, trade.getSecondInput(), 0)); From 9bb52afc8a8ca4ebb89e7e7fc39a8a678bb56244 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Tue, 29 Sep 2020 13:21:25 -0400 Subject: [PATCH 110/140] BedrockRespawnTranslator: prevent some respawn bugs (#1346) --- .../bedrock/BedrockRespawnTranslator.java | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java index 71d52e134..dc98c3630 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockRespawnTranslator.java @@ -28,7 +28,10 @@ package org.geysermc.connector.network.translators.bedrock; import com.github.steveice10.mc.protocol.data.game.ClientRequest; import com.github.steveice10.mc.protocol.packet.ingame.client.ClientRequestPacket; import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket; import com.nukkitx.protocol.bedrock.packet.RespawnPacket; +import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket; +import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; @@ -39,12 +42,30 @@ public class BedrockRespawnTranslator extends PacketTranslator { @Override public void translate(RespawnPacket packet, GeyserSession session) { if (packet.getState() == RespawnPacket.State.CLIENT_READY) { - if (!session.isSpawned()) { // Otherwise when immediate respawn is on the client never loads - RespawnPacket respawnPacket = new RespawnPacket(); - respawnPacket.setRuntimeEntityId(0); - respawnPacket.setPosition(Vector3f.ZERO); - respawnPacket.setState(RespawnPacket.State.SERVER_READY); - session.sendUpstreamPacket(respawnPacket); + // Previously we only sent the respawn packet before the server finished loading + // The message included was 'Otherwise when immediate respawn is on the client never loads' + // But I assume the new if statement below fixes that problem + RespawnPacket respawnPacket = new RespawnPacket(); + respawnPacket.setRuntimeEntityId(0); + respawnPacket.setPosition(Vector3f.ZERO); + respawnPacket.setState(RespawnPacket.State.SERVER_READY); + session.sendUpstreamPacket(respawnPacket); + + if (session.isSpawned()) { + // Client might be stuck; resend spawn information + PlayerEntity entity = session.getPlayerEntity(); + if (entity == null) return; + SetEntityDataPacket entityDataPacket = new SetEntityDataPacket(); + entityDataPacket.setRuntimeEntityId(entity.getGeyserId()); + entityDataPacket.getMetadata().putAll(entity.getMetadata()); + session.sendUpstreamPacket(entityDataPacket); + + MovePlayerPacket movePlayerPacket = new MovePlayerPacket(); + movePlayerPacket.setRuntimeEntityId(entity.getGeyserId()); + movePlayerPacket.setPosition(entity.getPosition()); + movePlayerPacket.setRotation(entity.getBedrockRotation()); + movePlayerPacket.setMode(MovePlayerPacket.Mode.RESPAWN); + session.sendUpstreamPacket(movePlayerPacket); } ClientRequestPacket javaRespawnPacket = new ClientRequestPacket(ClientRequest.RESPAWN); From aee9ccc7d2ca8ecc932ae92dcab38161e1653bc4 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Tue, 29 Sep 2020 13:21:43 -0400 Subject: [PATCH 111/140] DoorSoundInteractionHandler: ignore iron [trap]doors (#1343) --- .../translators/sound/block/DoorSoundInteractionHandler.java | 1 + 1 file changed, 1 insertion(+) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/DoorSoundInteractionHandler.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/DoorSoundInteractionHandler.java index 10d4bb895..a1df72d08 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/DoorSoundInteractionHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/block/DoorSoundInteractionHandler.java @@ -37,6 +37,7 @@ public class DoorSoundInteractionHandler implements BlockSoundInteractionHandler @Override public void handleInteraction(GeyserSession session, Vector3f position, String identifier) { + if (identifier.contains("iron")) return; LevelEventPacket levelEventPacket = new LevelEventPacket(); levelEventPacket.setType(LevelEventType.SOUND_DOOR_OPEN); levelEventPacket.setPosition(position); From 3c4cde96779f490f302aa420513e03bcad1b9dd6 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Tue, 29 Sep 2020 13:30:37 -0400 Subject: [PATCH 112/140] Tipped arrow translation (#1331) * Tipped arrow translation - Tipped arrow items are now properly translated both ways - Tipped arrow particle effects are also translated, by having a list of all colors Java could send us and their Bedrock ID * Remove a whitespace --- .../connector/entity/TippedArrowEntity.java | 27 ++++ .../translators/item/ItemRegistry.java | 4 +- .../network/translators/item/Potion.java | 2 +- .../translators/item/TippedArrowPotion.java | 151 ++++++++++++++++++ .../item/translators/BannerTranslator.java | 2 +- .../item/translators/CompassTranslator.java | 2 +- .../item/translators/PotionTranslator.java | 4 +- .../translators/TippedArrowTranslator.java | 87 ++++++++++ 8 files changed, 273 insertions(+), 6 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/item/TippedArrowPotion.java create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/item/translators/TippedArrowTranslator.java diff --git a/connector/src/main/java/org/geysermc/connector/entity/TippedArrowEntity.java b/connector/src/main/java/org/geysermc/connector/entity/TippedArrowEntity.java index def5715c7..949764b92 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/TippedArrowEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/TippedArrowEntity.java @@ -25,12 +25,39 @@ package org.geysermc.connector.entity; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.item.TippedArrowPotion; +/** + * Internally this is known as TippedArrowEntity but is used with tipped arrows and normal arrows + */ public class TippedArrowEntity extends AbstractArrowEntity { public TippedArrowEntity(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) { + // Arrow potion effect color + if (entityMetadata.getId() == 9) { + int potionColor = (int) entityMetadata.getValue(); + // -1 means no color + if (potionColor == -1) { + metadata.remove(EntityData.CUSTOM_DISPLAY); + } else { + TippedArrowPotion potion = TippedArrowPotion.getByJavaColor(potionColor); + if (potion != null && potion.getJavaColor() != -1) { + metadata.put(EntityData.CUSTOM_DISPLAY, (byte) potion.getBedrockId()); + } else { + metadata.remove(EntityData.CUSTOM_DISPLAY); + } + } + } + super.updateBedrockMetadata(entityMetadata, session); + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java index 1f3af019f..850e4e059 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java @@ -197,7 +197,9 @@ public class ItemRegistry { */ public static ItemEntry getItem(ItemData data) { for (ItemEntry itemEntry : ITEM_ENTRIES.values()) { - if (itemEntry.getBedrockId() == data.getId() && (itemEntry.getBedrockData() == data.getDamage() || itemEntry.getJavaIdentifier().endsWith("potion"))) { + if (itemEntry.getBedrockId() == data.getId() && (itemEntry.getBedrockData() == data.getDamage() || + // Make exceptions for potions and tipped arrows, whose damage values can vary + (itemEntry.getJavaIdentifier().endsWith("potion") || itemEntry.getJavaIdentifier().equals("minecraft:arrow")))) { return itemEntry; } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java index 51ae36e49..b9a213d84 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java @@ -48,7 +48,7 @@ public enum Potion { STRONG_SWIFTNESS(16), LONG_SWIFTNESS(15), SLOWNESS(17), - STRONG_SLOWNESS(18), //does not exist + STRONG_SLOWNESS(42), LONG_SLOWNESS(18), WATER_BREATHING(19), LONG_WATER_BREATHING(20), diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/TippedArrowPotion.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/TippedArrowPotion.java new file mode 100644 index 000000000..7a5b576be --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/TippedArrowPotion.java @@ -0,0 +1,151 @@ +/* + * 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.item; + +import lombok.Getter; + +import java.util.Locale; + +/** + * Potion identifiers and their respective Bedrock IDs used with arrows. + * https://minecraft.gamepedia.com/Arrow#Item_Data + */ +@Getter +public enum TippedArrowPotion { + MUNDANE(2, ArrowParticleColors.NONE), // 3 is extended? + THICK(4, ArrowParticleColors.NONE), + AWKWARD(5, ArrowParticleColors.NONE), + NIGHT_VISION(6, ArrowParticleColors.NIGHT_VISION), + LONG_NIGHT_VISION(7, ArrowParticleColors.NIGHT_VISION), + INVISIBILITY(8, ArrowParticleColors.INVISIBILITY), + LONG_INVISIBILITY(9, ArrowParticleColors.INVISIBILITY), + LEAPING(10, ArrowParticleColors.LEAPING), + LONG_LEAPING(11, ArrowParticleColors.LEAPING), + STRONG_LEAPING(12, ArrowParticleColors.LEAPING), + FIRE_RESISTANCE(13, ArrowParticleColors.FIRE_RESISTANCE), + LONG_FIRE_RESISTANCE(14, ArrowParticleColors.FIRE_RESISTANCE), + SWIFTNESS(15, ArrowParticleColors.SWIFTNESS), + LONG_SWIFTNESS(16, ArrowParticleColors.SWIFTNESS), + STRONG_SWIFTNESS(17, ArrowParticleColors.SWIFTNESS), + SLOWNESS(18, ArrowParticleColors.SLOWNESS), + LONG_SLOWNESS(19, ArrowParticleColors.SLOWNESS), + STRONG_SLOWNESS(43, ArrowParticleColors.SLOWNESS), + WATER_BREATHING(20, ArrowParticleColors.WATER_BREATHING), + LONG_WATER_BREATHING(21, ArrowParticleColors.WATER_BREATHING), + HEALING(22, ArrowParticleColors.HEALING), + STRONG_HEALING(23, ArrowParticleColors.HEALING), + HARMING(24, ArrowParticleColors.HARMING), + STRONG_HARMING(25, ArrowParticleColors.HARMING), + POISON(26, ArrowParticleColors.POISON), + LONG_POISON(27, ArrowParticleColors.POISON), + STRONG_POISON(28, ArrowParticleColors.POISON), + REGENERATION(29, ArrowParticleColors.REGENERATION), + LONG_REGENERATION(30, ArrowParticleColors.REGENERATION), + STRONG_REGENERATION(31, ArrowParticleColors.REGENERATION), + STRENGTH(32, ArrowParticleColors.STRENGTH), + LONG_STRENGTH(33, ArrowParticleColors.STRENGTH), + STRONG_STRENGTH(34, ArrowParticleColors.STRENGTH), + WEAKNESS(35, ArrowParticleColors.WEAKNESS), + LONG_WEAKNESS(36, ArrowParticleColors.WEAKNESS), + LUCK(2, ArrowParticleColors.NONE), // does not exist in Bedrock + TURTLE_MASTER(38, ArrowParticleColors.TURTLE_MASTER), + LONG_TURTLE_MASTER(39, ArrowParticleColors.TURTLE_MASTER), + STRONG_TURTLE_MASTER(40, ArrowParticleColors.TURTLE_MASTER), + SLOW_FALLING(41, ArrowParticleColors.SLOW_FALLING), + LONG_SLOW_FALLING(42, ArrowParticleColors.SLOW_FALLING); + + private final String javaIdentifier; + private final short bedrockId; + /** + * The Java color associated with this ID. + * Used for looking up Java arrow color entity metadata as Bedrock potion IDs, which is what is used for entities in Bedrock + */ + private final int javaColor; + + TippedArrowPotion(int bedrockId, ArrowParticleColors arrowParticleColor) { + this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ENGLISH); + this.bedrockId = (short) bedrockId; + this.javaColor = arrowParticleColor.getColor(); + } + + public static TippedArrowPotion getByJavaIdentifier(String javaIdentifier) { + for (TippedArrowPotion potion : TippedArrowPotion.values()) { + if (potion.javaIdentifier.equals(javaIdentifier)) { + return potion; + } + } + return null; + } + + public static TippedArrowPotion getByBedrockId(short bedrockId) { + for (TippedArrowPotion potion : TippedArrowPotion.values()) { + if (potion.bedrockId == bedrockId) { + return potion; + } + } + return null; + } + + /** + * @param color the potion color to look up + * @return the tipped arrow potion that most closely resembles that color. + */ + public static TippedArrowPotion getByJavaColor(int color) { + for (TippedArrowPotion potion : TippedArrowPotion.values()) { + if (potion.javaColor == color) { + return potion; + } + } + return null; + } + + private enum ArrowParticleColors { + NONE(-1), + NIGHT_VISION(2039713), + INVISIBILITY(8356754), + LEAPING(2293580), + FIRE_RESISTANCE(14981690), + SWIFTNESS(8171462), + SLOWNESS(5926017), + TURTLE_MASTER(7691106), + WATER_BREATHING(3035801), + HEALING(16262179), + HARMING(4393481), + POISON(5149489), + REGENERATION(13458603), + STRENGTH(9643043), + WEAKNESS(4738376), + LUCK(3381504), + SLOW_FALLING(16773073); + + @Getter + private final int color; + + ArrowParticleColors(int color) { + this.color = color; + } + } +} 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 304ea3fb2..f4bfdfb64 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 @@ -50,7 +50,7 @@ import java.util.stream.Collectors; @ItemRemapper public class BannerTranslator extends ItemTranslator { - private List appliedItems; + private final List appliedItems; public BannerTranslator() { appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("banner")).collect(Collectors.toList()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/CompassTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/CompassTranslator.java index 675d42555..159b9ab49 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/CompassTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/CompassTranslator.java @@ -40,7 +40,7 @@ import java.util.stream.Collectors; @ItemRemapper public class CompassTranslator extends ItemTranslator { - private List appliedItems; + private final List appliedItems; public CompassTranslator() { appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("compass")).collect(Collectors.toList()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/PotionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/PotionTranslator.java index 7cb88d70c..24130a7f5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/PotionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/PotionTranslator.java @@ -42,7 +42,7 @@ import java.util.stream.Collectors; @ItemRemapper public class PotionTranslator extends ItemTranslator { - private List appliedItems; + private final List appliedItems; public PotionTranslator() { appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> entry.getJavaIdentifier().endsWith("potion")).collect(Collectors.toList()); @@ -57,7 +57,7 @@ public class PotionTranslator extends ItemTranslator { if (potion != null) { return ItemData.of(itemEntry.getBedrockId(), potion.getBedrockId(), itemStack.getAmount(), translateNbtToBedrock(itemStack.getNbt())); } - GeyserConnector.getInstance().getLogger().debug("Unknown java potion: " + potionTag.getValue()); + GeyserConnector.getInstance().getLogger().debug("Unknown Java potion: " + potionTag.getValue()); } return super.translateToBedrock(itemStack, itemEntry); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/TippedArrowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/TippedArrowTranslator.java new file mode 100644 index 000000000..0b69d6a2e --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/TippedArrowTranslator.java @@ -0,0 +1,87 @@ +/* + * 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.item.translators; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.github.steveice10.opennbt.tag.builtin.StringTag; +import com.github.steveice10.opennbt.tag.builtin.Tag; +import com.nukkitx.protocol.bedrock.data.inventory.ItemData; +import org.geysermc.connector.GeyserConnector; +import org.geysermc.connector.network.translators.ItemRemapper; +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.item.TippedArrowPotion; + +import java.util.List; +import java.util.stream.Collectors; + +@ItemRemapper +public class TippedArrowTranslator extends ItemTranslator { + + private final List appliedItems; + + private static final int TIPPED_ARROW_JAVA_ID = ItemRegistry.getItemEntry("minecraft:tipped_arrow").getJavaId(); + + public TippedArrowTranslator() { + appliedItems = ItemRegistry.ITEM_ENTRIES.values().stream().filter(entry -> + entry.getJavaIdentifier().contains("arrow") && !entry.getJavaIdentifier().contains("spectral")).collect(Collectors.toList()); + } + + @Override + public ItemData translateToBedrock(ItemStack itemStack, ItemEntry itemEntry) { + if (!itemEntry.getJavaIdentifier().equals("minecraft:tipped_arrow") || itemStack.getNbt() == null) { + // We're only concerned about minecraft:arrow when translating Bedrock -> Java + return super.translateToBedrock(itemStack, itemEntry); + } + Tag potionTag = itemStack.getNbt().get("Potion"); + if (potionTag instanceof StringTag) { + TippedArrowPotion tippedArrowPotion = TippedArrowPotion.getByJavaIdentifier(((StringTag) potionTag).getValue()); + if (tippedArrowPotion != null) { + return ItemData.of(itemEntry.getBedrockId(), tippedArrowPotion.getBedrockId(), itemStack.getAmount(), translateNbtToBedrock(itemStack.getNbt())); + } + GeyserConnector.getInstance().getLogger().debug("Unknown Java potion (tipped arrow): " + potionTag.getValue()); + } + return super.translateToBedrock(itemStack, itemEntry); + } + + @Override + public ItemStack translateToJava(ItemData itemData, ItemEntry itemEntry) { + TippedArrowPotion tippedArrowPotion = TippedArrowPotion.getByBedrockId(itemData.getDamage()); + ItemStack itemStack = super.translateToJava(itemData, itemEntry); + if (tippedArrowPotion != null) { + itemStack = new ItemStack(TIPPED_ARROW_JAVA_ID, itemStack.getAmount(), itemStack.getNbt()); + StringTag potionTag = new StringTag("Potion", tippedArrowPotion.getJavaIdentifier()); + itemStack.getNbt().put(potionTag); + } + return itemStack; + } + + @Override + public List getAppliedItems() { + return appliedItems; + } +} From 772cb246f02bc6349d9a95f14fa62bcc9e2be0d2 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Tue, 29 Sep 2020 14:15:11 -0400 Subject: [PATCH 113/140] Forward keep alive packets to the client (#1344) * Forward keep alive packets to the client Previously, MCProtocolLib (our Java protocol library) handled keep alive packets for us. This commit disables that option and 'forwards' the keep alive packets to the client, and sending the keep alive packet back once Bedrock sends us a ping response. * Delete DataCache * Update to latest MCProtocolLib * Swap values around as a sanity check --- connector/pom.xml | 4 +- .../network/session/GeyserSession.java | 13 +++-- .../translators/PacketTranslatorRegistry.java | 2 - ...BedrockNetworkStackLatencyTranslator.java} | 25 ++++++---- .../java/JavaKeepAliveTranslator.java | 48 +++++++++++++++++++ 5 files changed, 76 insertions(+), 16 deletions(-) rename connector/src/main/java/org/geysermc/connector/network/{session/cache/DataCache.java => translators/bedrock/BedrockNetworkStackLatencyTranslator.java} (54%) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/java/JavaKeepAliveTranslator.java diff --git a/connector/pom.xml b/connector/pom.xml index f52713cea..26ec0c4e9 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -109,9 +109,9 @@ compile
- com.github.GeyserMC + com.github.steveice10 mcprotocollib - e4a3aa636a + 976c2d0f89 compile 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 300877f81..5225632c3 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 @@ -28,6 +28,7 @@ package org.geysermc.connector.network.session; import com.github.steveice10.mc.auth.data.GameProfile; import com.github.steveice10.mc.auth.exception.request.InvalidCredentialsException; import com.github.steveice10.mc.auth.exception.request.RequestException; +import com.github.steveice10.mc.protocol.MinecraftConstants; import com.github.steveice10.mc.protocol.MinecraftProtocol; import com.github.steveice10.mc.protocol.data.SubProtocol; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; @@ -117,8 +118,6 @@ public class GeyserSession implements CommandSender { */ private final Object2LongMap itemFrameCache = new Object2LongOpenHashMap<>(); - private DataCache javaPacketCache; - @Setter private Vector2i lastChunkPosition = null; private int renderDistance; @@ -176,6 +175,12 @@ public class GeyserSession implements CommandSender { @Setter private long lastWindowCloseTime = 0; + /** + * Saves the timestamp of the last keep alive packet + */ + @Setter + private long lastKeepAliveTimestamp = 0; + @Setter private VillagerTrade[] villagerTrades; @Setter @@ -276,8 +281,6 @@ public class GeyserSession implements CommandSender { this.playerEntity = new PlayerEntity(new GameProfile(UUID.randomUUID(), "unknown"), 1, 1, Vector3f.ZERO, Vector3f.ZERO, Vector3f.ZERO); this.inventory = new PlayerInventory(); - this.javaPacketCache = new DataCache<>(); - this.spawned = false; this.loggedIn = false; @@ -384,6 +387,8 @@ public class GeyserSession implements CommandSender { } downstream = new Client(remoteServer.getAddress(), remoteServer.getPort(), protocol, new TcpSessionFactory()); + // Let Geyser handle sending the keep alive + downstream.getSession().setFlag(MinecraftConstants.AUTOMATIC_KEEP_ALIVE_MANAGEMENT, false); downstream.getSession().addListener(new SessionAdapter() { @Override public void packetSending(PacketSendingEvent event) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java index c43864816..6517f498f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java @@ -25,7 +25,6 @@ package org.geysermc.connector.network.translators; -import com.github.steveice10.mc.protocol.packet.ingame.server.ServerKeepAlivePacket; import com.github.steveice10.mc.protocol.packet.ingame.server.ServerPlayerListDataPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdateLightPacket; import com.github.steveice10.packetlib.packet.Packet; @@ -75,7 +74,6 @@ public class PacketTranslatorRegistry { } } - IGNORED_PACKETS.add(ServerKeepAlivePacket.class); // Handled by MCProtocolLib IGNORED_PACKETS.add(ServerUpdateLightPacket.class); // Light is handled on Bedrock for us IGNORED_PACKETS.add(ServerPlayerListDataPacket.class); // Cant be implemented in bedrock } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/DataCache.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockNetworkStackLatencyTranslator.java similarity index 54% rename from connector/src/main/java/org/geysermc/connector/network/session/cache/DataCache.java rename to connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockNetworkStackLatencyTranslator.java index 4b2af9630..d480b526f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/DataCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockNetworkStackLatencyTranslator.java @@ -23,15 +23,24 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.connector.network.session.cache; +package org.geysermc.connector.network.translators.bedrock; -import lombok.Getter; +import com.github.steveice10.mc.protocol.packet.ingame.client.ClientKeepAlivePacket; +import com.nukkitx.protocol.bedrock.packet.NetworkStackLatencyPacket; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.Translator; -import java.util.HashMap; -import java.util.Map; +/** + * Used to send the keep alive packet back to the server + */ +@Translator(packet = NetworkStackLatencyPacket.class) +public class BedrockNetworkStackLatencyTranslator extends PacketTranslator { -public class DataCache { - - @Getter - private Map cachedValues = new HashMap(); + @Override + public void translate(NetworkStackLatencyPacket packet, GeyserSession session) { + // The client sends a timestamp back but it's rounded and therefore unreliable when we need the exact number + ClientKeepAlivePacket keepAlivePacket = new ClientKeepAlivePacket(session.getLastKeepAliveTimestamp()); + session.sendDownstreamPacket(keepAlivePacket); + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaKeepAliveTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaKeepAliveTranslator.java new file mode 100644 index 000000000..1dd156e4f --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaKeepAliveTranslator.java @@ -0,0 +1,48 @@ +/* + * 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.java; + +import com.github.steveice10.mc.protocol.packet.ingame.server.ServerKeepAlivePacket; +import com.nukkitx.protocol.bedrock.packet.NetworkStackLatencyPacket; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.translators.PacketTranslator; +import org.geysermc.connector.network.translators.Translator; + +/** + * Used to forward the keep alive packet to the client in order to get back a reliable ping. + */ +@Translator(packet = ServerKeepAlivePacket.class) +public class JavaKeepAliveTranslator extends PacketTranslator { + + @Override + public void translate(ServerKeepAlivePacket packet, GeyserSession session) { + session.setLastKeepAliveTimestamp(packet.getPingId()); + NetworkStackLatencyPacket latencyPacket = new NetworkStackLatencyPacket(); + latencyPacket.setFromServer(true); + latencyPacket.setTimestamp(packet.getPingId()); + session.sendUpstreamPacket(latencyPacket); + } +} From ba6f174058dcfbc0e37aaa9af7e731bb18e17842 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Fri, 2 Oct 2020 15:44:46 -0400 Subject: [PATCH 114/140] Add support for fishing rods pulling Bedrock players (#1355) Fishing rods pulling players is a clientside feature on Java. On Bedrock, a SetEntityMotionPacket is sent to the client. Therefore this PR implements the Java fishing rod pulling mechanics and sends it off to Bedrock, which sends MovePlayerPackets that are sent to the server. --- .../entity/JavaEntityStatusTranslator.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) 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 ac2a80f7a..c3fbd2e92 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 @@ -30,8 +30,8 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket; +import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket; import org.geysermc.connector.entity.Entity; -import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; @@ -96,8 +96,20 @@ public class JavaEntityStatusTranslator extends PacketTranslator Date: Wed, 7 Oct 2020 18:50:39 -0400 Subject: [PATCH 115/140] Jenkins improvements (#1368) - Show changes in the Discord webhook - Delete after 20 builds Co-authored-by: rtm516 --- Jenkinsfile | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index ffa4f1bdd..1a93391db 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,7 +5,7 @@ pipeline { jdk 'Java 8' } options { - buildDiscarder(logRotator(artifactNumToKeepStr: '5')) + buildDiscarder(logRotator(artifactNumToKeepStr: '20')) } stages { stage ('Build') { @@ -32,9 +32,40 @@ pipeline { post { always { + script { + def changeLogSets = currentBuild.changeSets + def message = "**Changes:**" + + if (changeLogSets.size() == 0) { + message += "\n*No changes.*" + } else { + def repositoryUrl = scm.userRemoteConfigs[0].url.replace(".git", "") + def count = 0; + def extra = 0; + for (int i = 0; i < changeLogSets.size(); i++) { + def entries = changeLogSets[i].items + for (int j = 0; j < entries.length; j++) { + if (count <= 10) { + def entry = entries[j] + def commitId = entry.commitId.substring(0, 6) + message += "\n - [`${commitId}`](${repositoryUrl}/commit/${entry.commitId}) ${entry.msg}" + count++ + } else { + extra++; + } + } + } + + if (extra != 0) { + message += "\n - ${extra} more commits" + } + } + + env.changes = message + } 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\n[**Artifacts on Jenkins**](https://ci.nukkitx.com/job/Geyser)", footer: 'NukkitX 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.nukkitx.com/job/Geyser)", footer: 'Cloudburst Jenkins', link: env.BUILD_URL, successful: currentBuild.resultIsBetterOrEqualTo('SUCCESS'), title: "${env.JOB_NAME} #${currentBuild.id}", webhookURL: DISCORD_WEBHOOK } } } From 172a5a6db863624f65913d5672b9de60db1b70da Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Wed, 7 Oct 2020 18:51:36 -0400 Subject: [PATCH 116/140] Add Fabric as a platform type (#1376) * PlatformType: Add Fabric as a platform * Don't use XML reflections on Fabric --- .../org/geysermc/connector/GeyserConnector.java | 13 +++++++------ .../org/geysermc/connector/common/PlatformType.java | 3 ++- .../translators/PacketTranslatorRegistry.java | 2 +- .../network/translators/item/ItemTranslator.java | 2 +- .../translators/sound/SoundHandlerRegistry.java | 2 +- .../translators/world/block/BlockTranslator.java | 2 +- .../world/block/entity/BlockEntityTranslator.java | 2 +- 7 files changed, 14 insertions(+), 12 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 3c814393d..924118976 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -312,15 +312,16 @@ public class GeyserConnector { } /** - * Get the production status of the current runtime. - * Will return true if the version number is not 'DEV'. - * Should only happen in compiled jars. + * Whether to use XML reflections in the jar or manually find the reflections. + * Will return true if the version number is not 'DEV' and the platform is not Fabric. + * On Fabric - it complains about being unable to create a default XMLReader. + * On other platforms this should only be true in compiled jars. * - * @return If we are in a production build/environment + * @return whether to use XML reflections */ - public boolean isProduction() { + public boolean useXmlReflections() { //noinspection ConstantConditions - return !"DEV".equals(GeyserConnector.VERSION); + return !this.getPlatformType().equals(PlatformType.FABRIC) && !"DEV".equals(GeyserConnector.VERSION); } public static GeyserConnector getInstance() { diff --git a/connector/src/main/java/org/geysermc/connector/common/PlatformType.java b/connector/src/main/java/org/geysermc/connector/common/PlatformType.java index 4daa5d37d..3e945d3ad 100644 --- a/connector/src/main/java/org/geysermc/connector/common/PlatformType.java +++ b/connector/src/main/java/org/geysermc/connector/common/PlatformType.java @@ -34,10 +34,11 @@ public enum PlatformType { ANDROID("Android"), BUNGEECORD("BungeeCord"), + FABRIC("Fabric"), SPIGOT("Spigot"), SPONGE("Sponge"), STANDALONE("Standalone"), VELOCITY("Velocity"); - private String platformName; + private final String platformName; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java index 6517f498f..b6c6f3aec 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/PacketTranslatorRegistry.java @@ -48,7 +48,7 @@ public class PacketTranslatorRegistry { private static final ObjectArrayList> IGNORED_PACKETS = new ObjectArrayList<>(); static { - Reflections ref = GeyserConnector.getInstance().isProduction() ? FileUtils.getReflections("org.geysermc.connector.network.translators") : new Reflections("org.geysermc.connector.network.translators"); + Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections("org.geysermc.connector.network.translators") : new Reflections("org.geysermc.connector.network.translators"); for (Class clazz : ref.getTypesAnnotatedWith(Translator.class)) { Class packet = clazz.getAnnotation(Translator.class).packet(); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java index 864e9fe03..ef6294944 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java @@ -64,7 +64,7 @@ public abstract class ItemTranslator { static { /* Load item translators */ - Reflections ref = GeyserConnector.getInstance().isProduction() ? FileUtils.getReflections("org.geysermc.connector.network.translators.item") : new Reflections("org.geysermc.connector.network.translators.item"); + Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections("org.geysermc.connector.network.translators.item") : new Reflections("org.geysermc.connector.network.translators.item"); Map loadedNbtItemTranslators = new HashMap<>(); for (Class clazz : ref.getTypesAnnotatedWith(ItemRemapper.class)) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundHandlerRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundHandlerRegistry.java index 163c451cb..03b346e35 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundHandlerRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/SoundHandlerRegistry.java @@ -40,7 +40,7 @@ public class SoundHandlerRegistry { static final Map> INTERACTION_HANDLERS = new HashMap<>(); static { - Reflections ref = GeyserConnector.getInstance().isProduction() ? FileUtils.getReflections("org.geysermc.connector.network.translators.sound") : new Reflections("org.geysermc.connector.network.translators.sound"); + Reflections ref = GeyserConnector.getInstance().useXmlReflections() ? FileUtils.getReflections("org.geysermc.connector.network.translators.sound") : new Reflections("org.geysermc.connector.network.translators.sound"); for (Class clazz : ref.getTypesAnnotatedWith(SoundHandler.class)) { try { SoundInteractionHandler interactionHandler = (SoundInteractionHandler) clazz.newInstance(); 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 fc7c852cb..e5f8d6aa0 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 @@ -113,7 +113,7 @@ public class BlockTranslator { addedStatesMap.defaultReturnValue(-1); List paletteList = new ArrayList<>(); - Reflections ref = GeyserConnector.getInstance().isProduction() ? FileUtils.getReflections("org.geysermc.connector.network.translators.world.block.entity") : new Reflections("org.geysermc.connector.network.translators.world.block.entity"); + 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"); ref.getTypesAnnotatedWith(BlockEntity.class); int waterRuntimeId = -1; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java index 54f593a61..4df4fd95e 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/block/entity/BlockEntityTranslator.java @@ -68,7 +68,7 @@ public abstract class BlockEntityTranslator { } static { - Reflections ref = GeyserConnector.getInstance().isProduction() ? FileUtils.getReflections("org.geysermc.connector.network.translators.world.block.entity") : new Reflections("org.geysermc.connector.network.translators.world.block.entity"); + 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"); for (Class clazz : ref.getTypesAnnotatedWith(BlockEntity.class)) { GeyserConnector.getInstance().getLogger().debug("Found annotated block entity: " + clazz.getCanonicalName()); From 16eb2a491a0e7d9b12061fb7dd91b516bb15ae20 Mon Sep 17 00:00:00 2001 From: Arktisfox <65837019+Arktisfox@users.noreply.github.com> Date: Thu, 8 Oct 2020 18:33:36 -0400 Subject: [PATCH 117/140] Area cloud fixes (#684) * Fix NotNull error with particles, replace incorrect string meta with int meta. * Add back newline * Remove debug line * Update Protocol and prepare for merge Co-authored-by: DoctorMacc --- connector/pom.xml | 2 +- .../entity/AreaEffectCloudEntity.java | 7 +- .../network/UpstreamPacketHandler.java | 2 +- .../translators/effect/EffectRegistry.java | 72 +++++++++++++------ connector/src/main/resources/languages | 2 +- connector/src/main/resources/mappings | 2 +- 6 files changed, 59 insertions(+), 28 deletions(-) diff --git a/connector/pom.xml b/connector/pom.xml index 26ec0c4e9..741acee51 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -33,7 +33,7 @@ com.github.CloudburstMC.Protocol bedrock-v408 - 250beb2a94 + 02f46a8700 compile 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 218615899..567a08ed3 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java @@ -50,11 +50,12 @@ public class AreaEffectCloudEntity extends Entity { if (entityMetadata.getId() == 7) { metadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS, entityMetadata.getValue()); metadata.put(EntityData.BOUNDING_BOX_WIDTH, 2.0f * (float) entityMetadata.getValue()); - } else if (entityMetadata.getId() == 10) { - Particle particle = (Particle) entityMetadata.getValue(); - metadata.put(EntityData.AREA_EFFECT_CLOUD_PARTICLE_ID, EffectRegistry.getParticleString(particle.getType())); } else if (entityMetadata.getId() == 8) { metadata.put(EntityData.POTION_AUX_VALUE, entityMetadata.getValue()); + } else if (entityMetadata.getId() == 10) { + Particle particle = (Particle) entityMetadata.getValue(); + int particleId = EffectRegistry.getParticleId(particle.getType()); + metadata.put(EntityData.AREA_EFFECT_CLOUD_PARTICLE_ID, particleId); } super.updateBedrockMetadata(entityMetadata, session); } diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index 7e97d4298..f76d64edd 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -117,7 +117,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { case HAVE_ALL_PACKS: ResourcePackStackPacket stackPacket = new ResourcePackStackPacket(); - stackPacket.setExperimental(false); + stackPacket.setExperimentsPreviouslyToggled(false); stackPacket.setForcedToAccept(false); // Leaving this as false allows the player to choose to download or not stackPacket.setGameVersion(session.getClientData().getGameVersion()); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/effect/EffectRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/effect/EffectRegistry.java index 39c586db7..75cab1521 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/effect/EffectRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/effect/EffectRegistry.java @@ -32,10 +32,11 @@ import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.SoundEvent; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.NonNull; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.utils.FileUtils; -import org.geysermc.connector.utils.LanguageUtils; import java.io.InputStream; import java.util.HashMap; @@ -50,8 +51,19 @@ public class EffectRegistry { public static final Map SOUND_EFFECTS = new HashMap<>(); public static final Int2ObjectMap RECORDS = new Int2ObjectOpenHashMap<>(); - private static Map particleTypeMap = new HashMap<>(); - private static Map particleStringMap = new HashMap<>(); + /** + * Java particle type to Bedrock particle ID + * Used for area effect clouds. + */ + private static final Object2IntMap PARTICLE_TO_ID = new Object2IntOpenHashMap<>(); + /** + * Java particle type to Bedrock level event + */ + private static final Map PARTICLE_TO_LEVEL_EVENT = new HashMap<>(); + /** + * Java particle type to Bedrock namespaced string ID + */ + private static final Map PARTICLE_TO_STRING = new HashMap<>(); public static void init() { // no-op @@ -68,22 +80,24 @@ public class EffectRegistry { } Iterator> particlesIterator = particleEntries.fields(); - while (particlesIterator.hasNext()) { - Map.Entry entry = particlesIterator.next(); - try { - particleTypeMap.put(ParticleType.valueOf(entry.getKey().toUpperCase()), LevelEventType.valueOf(entry.getValue().asText().toUpperCase())); - } catch (IllegalArgumentException e1) { - try { - particleStringMap.put(ParticleType.valueOf(entry.getKey().toUpperCase()), entry.getValue().asText()); - GeyserConnector.getInstance().getLogger().debug("Force to map particle " - + entry.getKey() - + "=>" - + entry.getValue().asText() - + ", it will take effect."); - } catch (IllegalArgumentException e2){ - GeyserConnector.getInstance().getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.particle.failed_map", entry.getKey(), entry.getValue().asText())); + try { + while (particlesIterator.hasNext()) { + Map.Entry entry = particlesIterator.next(); + JsonNode bedrockId = entry.getValue().get("bedrockId"); + JsonNode bedrockIdNumeric = entry.getValue().get("bedrockNumericId"); + JsonNode eventType = entry.getValue().get("eventType"); + if (bedrockIdNumeric != null) { + PARTICLE_TO_ID.put(ParticleType.valueOf(entry.getKey().toUpperCase()), bedrockIdNumeric.asInt()); + } + if (bedrockId != null) { + PARTICLE_TO_STRING.put(ParticleType.valueOf(entry.getKey().toUpperCase()), bedrockId.asText()); + } + if (eventType != null) { + PARTICLE_TO_LEVEL_EVENT.put(ParticleType.valueOf(entry.getKey().toUpperCase()), LevelEventType.valueOf(eventType.asText().toUpperCase())); } } + } catch (Exception e) { + e.printStackTrace(); } /* Load effects */ @@ -149,11 +163,27 @@ public class EffectRegistry { } } - public static LevelEventType getParticleLevelEventType(@NonNull ParticleType type) { - return particleTypeMap.getOrDefault(type, null); + /** + * @param type the Java particle to search for + * @return the Bedrock integer ID of the particle, or -1 if it does not exist + */ + public static int getParticleId(@NonNull ParticleType type) { + return PARTICLE_TO_ID.getOrDefault(type, -1); } - public static String getParticleString(@NonNull ParticleType type){ - return particleStringMap.getOrDefault(type, null); + /** + * @param type the Java particle to search for + * @return the level event equivalent Bedrock particle + */ + public static LevelEventType getParticleLevelEventType(@NonNull ParticleType type) { + return PARTICLE_TO_LEVEL_EVENT.getOrDefault(type, null); + } + + /** + * @param type the Java particle to search for + * @return the namespaced ID equivalent for Bedrock + */ + public static String getParticleString(@NonNull ParticleType type) { + return PARTICLE_TO_STRING.getOrDefault(type, null); } } diff --git a/connector/src/main/resources/languages b/connector/src/main/resources/languages index 93b2caed3..5f2179226 160000 --- a/connector/src/main/resources/languages +++ b/connector/src/main/resources/languages @@ -1 +1 @@ -Subproject commit 93b2caed3c4ecd94b3c77a87f1b2304a7bf4f062 +Subproject commit 5f21792264a364e32425014e0be79db93593da1e diff --git a/connector/src/main/resources/mappings b/connector/src/main/resources/mappings index 28a22d2ba..0fae8d3f0 160000 --- a/connector/src/main/resources/mappings +++ b/connector/src/main/resources/mappings @@ -1 +1 @@ -Subproject commit 28a22d2baad680f511bffc36d90d06bf626f0527 +Subproject commit 0fae8d3f0de6210a10435a36128db14cb7650ae6 From 4514167835e44838980c7202204a769cd1784653 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Thu, 8 Oct 2020 18:44:15 -0400 Subject: [PATCH 118/140] Fix fire being punched in all directions (#1370) Apply the fix we have for fire but for all block faces. --- ...BedrockInventoryTransactionTranslator.java | 24 ++---------------- .../player/BedrockActionTranslator.java | 19 +++++++------- .../geysermc/connector/utils/BlockUtils.java | 25 +++++++++++++++++++ 3 files changed, 37 insertions(+), 31 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java index 8e891d925..b92a84eb6 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java @@ -58,6 +58,7 @@ import org.geysermc.connector.network.translators.item.ItemEntry; import org.geysermc.connector.network.translators.item.ItemRegistry; import org.geysermc.connector.network.translators.sound.EntitySoundInteractionHandler; import org.geysermc.connector.network.translators.world.block.BlockTranslator; +import org.geysermc.connector.utils.BlockUtils; import org.geysermc.connector.utils.InventoryUtils; @Translator(packet = InventoryTransactionPacket.class) @@ -151,28 +152,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator Date: Fri, 9 Oct 2020 01:30:05 +0200 Subject: [PATCH 119/140] Various Scoreboard fixes (#1381) * Various Scoreboard fixes Fixes #1328 and a few other potential Scoreboard problems * Consistent whitespacing Co-authored-by: DoctorMacc --- .../connector/scoreboard/Objective.java | 12 ++----- .../geysermc/connector/scoreboard/Score.java | 1 - .../connector/scoreboard/Scoreboard.java | 31 ++++++++----------- 3 files changed, 15 insertions(+), 29 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java index b2f648612..3accbc120 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Objective.java @@ -76,7 +76,8 @@ public class Objective { if (!scores.containsKey(id)) { Score score1 = new Score(this, id) .setScore(score) - .setTeam(scoreboard.getTeamFor(id)); + .setTeam(scoreboard.getTeamFor(id)) + .setUpdateType(UpdateType.ADD); scores.put(id, score1); } } @@ -96,15 +97,6 @@ public class Objective { return 0; } - public Score getScore(int line) { - for (Score score : scores.values()) { - if (score.getScore() == line) { - return score; - } - } - return null; - } - public void removeScore(String id) { if (scores.containsKey(id)) { scores.get(id).setUpdateType(UpdateType.REMOVE); diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java index 635bafa3d..3dfd6ed38 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Score.java @@ -49,7 +49,6 @@ public class Score { this.id = objective.getScoreboard().getNextId().getAndIncrement(); this.objective = objective; this.name = name; - update(); } public String getDisplayName() { diff --git a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java index ae1b82757..732a056eb 100644 --- a/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java +++ b/connector/src/main/java/org/geysermc/connector/scoreboard/Scoreboard.java @@ -122,7 +122,7 @@ public class Scoreboard { for (Objective objective : objectives.values()) { if (!objective.isActive()) { - logger.debug("Ignoring non-active Scoreboard Objective '"+ objective.getObjectiveName() +'\''); + logger.debug("Ignoring non-active Scoreboard Objective '" + objective.getObjectiveName() + '\''); continue; } @@ -152,10 +152,6 @@ public class Scoreboard { boolean globalAdd = objective.getUpdateType() == ADD; boolean globalRemove = objective.getUpdateType() == REMOVE; - // Track if any scores changed - // Used to delete and resend scoreboard objectives; otherwise they won't update on Bedrock - boolean scoreChanged = false; - for (Score score : objective.getScores().values()) { Team team = score.getTeam(); @@ -171,16 +167,21 @@ public class Scoreboard { teamChanged |= team.getUpdateType() == UPDATE; add |= team.getUpdateType() == ADD || team.getUpdateType() == UPDATE; - remove |= team.getUpdateType() == REMOVE; + remove |= team.getUpdateType() != NOTHING; } add |= score.getUpdateType() == ADD || score.getUpdateType() == UPDATE; - remove |= score.getUpdateType() == REMOVE; - if (score.getUpdateType() == REMOVE) { + remove |= score.getUpdateType() == REMOVE || score.getUpdateType() == UPDATE; + + if (score.getUpdateType() == REMOVE || globalRemove) { add = false; } - if (score.getUpdateType() == UPDATE || teamChanged) { + if (score.getUpdateType() == ADD) { + remove = false; + } + + if (score.getUpdateType() == ADD || score.getUpdateType() == UPDATE || teamChanged) { score.update(); } @@ -191,12 +192,6 @@ public class Scoreboard { removeScores.add(score.getCachedInfo()); } - if (add || remove) { - scoreChanged = true; - } - // score is pending to be updated, so we use the current score as the old score - score.setOldScore(score.getScore()); - // score is pending to be removed, so we can remove it from the objective if (score.getUpdateType() == REMOVE) { objective.removeScore0(score.getName()); @@ -205,17 +200,17 @@ public class Scoreboard { score.setUpdateType(NOTHING); } - if (globalRemove || globalUpdate || scoreChanged) { + if (globalRemove || globalUpdate) { RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket(); removeObjectivePacket.setObjectiveId(objective.getObjectiveName()); session.sendUpstreamPacket(removeObjectivePacket); - if (objective.getUpdateType() == REMOVE) { + if (globalRemove) { objectives.remove(objective.getObjectiveName()); // now we can deregister objective.removed(); } } - if (globalAdd || globalUpdate || scoreChanged) { + if ((globalAdd || globalUpdate) && !globalRemove) { SetDisplayObjectivePacket displayObjectivePacket = new SetDisplayObjectivePacket(); displayObjectivePacket.setObjectiveId(objective.getObjectiveName()); displayObjectivePacket.setDisplayName(objective.getDisplayName()); From 59f72d0e65f3c421b92179b68d83c1b2ef1d7a4d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Thu, 8 Oct 2020 20:07:50 -0400 Subject: [PATCH 120/140] BedrockMobEquipmentTranslator: Don't change item slot if already on that slot (#1353) * BedrockMobEquipmentTranslator: Don't change item slot if already on that slot * Update comment --- .../translators/bedrock/BedrockMobEquipmentTranslator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMobEquipmentTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMobEquipmentTranslator.java index 02835151a..a220e389f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMobEquipmentTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockMobEquipmentTranslator.java @@ -40,7 +40,8 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator 8 || - packet.getContainerId() != ContainerId.INVENTORY) { + packet.getContainerId() != ContainerId.INVENTORY || session.getInventory().getHeldItemSlot() == packet.getHotbarSlot()) { + // 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; } From ec609fa86817666348af0cc96ceb452212250c32 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Thu, 8 Oct 2020 20:40:50 -0400 Subject: [PATCH 121/140] Make crossbows prettier (#1359) - Fix crossbow NBT translation - now crossbows will show as loaded - Pillagers now more closely resemble Java Edition pose behavior --- .../living/monster/raid/PillagerEntity.java | 49 +++++++++++++++++++ .../connector/entity/type/EntityType.java | 3 +- .../translators/nbt/CrossbowTranslator.java | 26 ++++++---- 3 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/PillagerEntity.java diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/PillagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/PillagerEntity.java new file mode 100644 index 000000000..c7a8f24d0 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/PillagerEntity.java @@ -0,0 +1,49 @@ +/* + * 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.living.monster.raid; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +public class PillagerEntity extends AbstractIllagerEntity { + + public PillagerEntity(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() == 16) { + // Java Edition always has the Pillager entity as positioning the crossbow + metadata.getFlags().setFlag(EntityFlag.USING_ITEM, true); + metadata.getFlags().setFlag(EntityFlag.CHARGED, true); + } + 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 500e135ed..24c15018c 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 @@ -34,6 +34,7 @@ import org.geysermc.connector.entity.living.animal.tameable.*; import org.geysermc.connector.entity.living.merchant.*; import org.geysermc.connector.entity.living.monster.*; import org.geysermc.connector.entity.living.monster.raid.AbstractIllagerEntity; +import org.geysermc.connector.entity.living.monster.raid.PillagerEntity; import org.geysermc.connector.entity.living.monster.raid.RaidParticipantEntity; import org.geysermc.connector.entity.living.monster.raid.SpellcasterIllagerEntity; @@ -90,7 +91,7 @@ public enum EntityType { ENDERMITE(MonsterEntity.class, 55, 0.3f, 0.4f), AGENT(Entity.class, 56, 0f), VINDICATOR(AbstractIllagerEntity.class, 57, 1.8f, 0.6f, 0.6f, 1.62f), - PILLAGER(AbstractIllagerEntity.class, 114, 1.8f, 0.6f, 0.6f, 1.62f), + PILLAGER(PillagerEntity.class, 114, 1.8f, 0.6f, 0.6f, 1.62f), WANDERING_TRADER(AbstractMerchantEntity.class, 118, 1.8f, 0.6f, 0.6f, 1.62f), PHANTOM(FlyingEntity.class, 58, 0.5f, 0.9f, 0.9f, 0.6f), RAVAGER(RaidParticipantEntity.class, 59, 1.9f, 1.2f), diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java index 979c5a205..67f137ff9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/CrossbowTranslator.java @@ -25,14 +25,15 @@ package org.geysermc.connector.network.translators.item.translators.nbt; -import com.github.steveice10.opennbt.tag.builtin.ByteTag; -import com.github.steveice10.opennbt.tag.builtin.CompoundTag; -import com.github.steveice10.opennbt.tag.builtin.ListTag; -import com.github.steveice10.opennbt.tag.builtin.StringTag; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; +import com.github.steveice10.opennbt.tag.builtin.*; +import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; -import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; 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.item.NbtItemStackTranslator; @ItemRemapper public class CrossbowTranslator extends NbtItemStackTranslator { @@ -44,12 +45,17 @@ public class CrossbowTranslator extends NbtItemStackTranslator { if (!chargedProjectiles.getValue().isEmpty()) { CompoundTag projectile = (CompoundTag) chargedProjectiles.getValue().get(0); - CompoundTag newProjectile = new CompoundTag("chargedItem"); - newProjectile.put(new ByteTag("Count", (byte) projectile.get("Count").getValue())); - newProjectile.put(new StringTag("Name", (String) projectile.get("id").getValue())); + ItemEntry entry = ItemRegistry.getItemEntry((String) projectile.get("id").getValue()); + if (entry == null) return; + CompoundTag tag = projectile.get("tag"); + ItemStack itemStack = new ItemStack(itemEntry.getJavaId(), (byte) projectile.get("Count").getValue(), tag); + ItemData itemData = ItemTranslator.translateToBedrock(session, itemStack); - // Not sure what this is for - newProjectile.put(new ByteTag("Damage", (byte) 0)); + CompoundTag newProjectile = new CompoundTag("chargedItem"); + newProjectile.put(new ByteTag("Count", (byte) itemData.getCount())); + newProjectile.put(new StringTag("Name", ItemRegistry.getBedrockIdentifer(entry))); + + newProjectile.put(new ShortTag("Damage", itemData.getDamage())); itemTag.put(newProjectile); } From ffcff96bea6e43ec34725c648eb5e9c76cad40ea Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Sat, 10 Oct 2020 18:08:21 -0400 Subject: [PATCH 122/140] Set default values for classes as well (#1387) Geyser can now start even if the config file is empty. Tested on Spigot and doesn't affect custom values. --- .../configuration/GeyserJacksonConfiguration.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 99a3a7a5a..45676fbd8 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java @@ -34,6 +34,7 @@ import org.geysermc.connector.common.serializer.AsteriskSerializer; import java.nio.file.Path; import java.util.Map; +import java.util.UUID; @Getter @JsonIgnoreProperties(ignoreUnknown = true) @@ -45,8 +46,8 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @Setter private boolean autoconfiguredRemote = false; - private BedrockConfiguration bedrock; - private RemoteConfiguration remote; + private BedrockConfiguration bedrock = new BedrockConfiguration(); + private RemoteConfiguration remote = new RemoteConfiguration(); @JsonProperty("floodgate-key-file") private String floodgateKeyFile = "public-key.pem"; @@ -106,7 +107,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonProperty("force-resource-packs") private boolean forceResourcePacks = true; - private MetricsInfo metrics; + private MetricsInfo metrics = new MetricsInfo(); @Getter public static class BedrockConfiguration implements IBedrockConfiguration { @@ -154,7 +155,7 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration private boolean enabled = true; @JsonProperty("uuid") - private String uniqueId = "generateuuid"; + private String uniqueId = UUID.randomUUID().toString(); } @JsonProperty("scoreboard-packet-threshold") From 96db37c14c924f9bce1c827d25416817281e2a6f Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 12 Oct 2020 20:02:41 -0400 Subject: [PATCH 123/140] Fix bucket interactions on creative mode (#1369) * Fix bucket interactions on creative mode Bedrock uses the BLOCK_INTERACT enum of BedrockActionTranslator to truly indicate if a bucket should be used or not. In order to hook into this, we need to delay the bucket placing by about 5 milliseconds - this gives us time to cancel the interaction if needed. Bucket sounds will now not play in this case as well. --- .../network/session/GeyserSession.java | 8 ++++++++ ...BedrockInventoryTransactionTranslator.java | 20 ++++++++++--------- .../player/BedrockActionTranslator.java | 7 ++++++- .../block/BucketSoundInteractionHandler.java | 2 ++ 4 files changed, 27 insertions(+), 10 deletions(-) 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 5225632c3..1a0bbfb29 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 @@ -84,6 +84,7 @@ import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.util.*; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicInteger; @Getter @@ -210,6 +211,13 @@ public class GeyserSession implements CommandSender { @Setter private long lastInteractionTime; + /** + * Stores a future interaction to place a bucket. Will be cancelled if the client instead intended to + * interact with a block. + */ + @Setter + private ScheduledFuture bucketScheduledFuture; + private boolean reducedDebugInfo = false; @Setter diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java index b92a84eb6..b81025beb 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java @@ -61,6 +61,8 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.utils.BlockUtils; import org.geysermc.connector.utils.InventoryUtils; +import java.util.concurrent.TimeUnit; + @Translator(packet = InventoryTransactionPacket.class) public class BedrockInventoryTransactionTranslator extends PacketTranslator { @@ -120,18 +122,19 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator { + ClientPlayerUseItemPacket itemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND); + session.sendDownstreamPacket(itemPacket); + }, 5, TimeUnit.MILLISECONDS)); } if (packet.getActions().isEmpty()) { @@ -167,10 +170,9 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator Date: Tue, 13 Oct 2020 02:17:15 +0100 Subject: [PATCH 124/140] Add player device OS to metrics (#1391) * Add player device os to metrics * Add player version, Geyser version, and default locale Co-authored-by: DoctorMacc --- .../geysermc/connector/GeyserConnector.java | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 924118976..109a5a600 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -66,7 +66,9 @@ 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.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -199,8 +201,39 @@ public class GeyserConnector { metrics = new Metrics(this, "GeyserMC", config.getMetrics().getUniqueId(), false, java.util.logging.Logger.getLogger("")); metrics.addCustomChart(new Metrics.SingleLineChart("servers", () -> 1)); metrics.addCustomChart(new Metrics.SingleLineChart("players", players::size)); - metrics.addCustomChart(new Metrics.SimplePie("authMode", authType.name()::toLowerCase)); + // Prevent unwanted words best we can + metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> AuthType.getByName(config.getRemote().getAuthType()).toString())); metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::getPlatformName)); + metrics.addCustomChart(new Metrics.SimplePie("defaultLocale", LanguageUtils::getDefaultLocale)); + metrics.addCustomChart(new Metrics.SimplePie("version", () -> GeyserConnector.VERSION)); + metrics.addCustomChart(new Metrics.AdvancedPie("playerPlatform", () -> { + Map valueMap = new HashMap<>(); + for (GeyserSession session : players) { + if (session == null) continue; + if (session.getClientData() == null) continue; + String os = session.getClientData().getDeviceOS().toString(); + if (!valueMap.containsKey(os)) { + valueMap.put(os, 1); + } else { + valueMap.put(os, valueMap.get(os) + 1); + } + } + return valueMap; + })); + metrics.addCustomChart(new Metrics.AdvancedPie("playerVersion", () -> { + Map valueMap = new HashMap<>(); + for (GeyserSession session : players) { + if (session == null) continue; + if (session.getClientData() == null) continue; + String version = session.getClientData().getGameVersion(); + if (!valueMap.containsKey(version)) { + valueMap.put(version, 1); + } else { + valueMap.put(version, valueMap.get(version) + 1); + } + } + return valueMap; + })); } boolean isGui = false; From 1b00eaca4aa722ff15647879c04e9708dc704458 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 12 Oct 2020 21:28:54 -0400 Subject: [PATCH 125/140] Set AuthType in Metrics to lowercase (#1395) --- .../src/main/java/org/geysermc/connector/GeyserConnector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 109a5a600..afed4dfdf 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -202,7 +202,7 @@ public class GeyserConnector { metrics.addCustomChart(new Metrics.SingleLineChart("servers", () -> 1)); metrics.addCustomChart(new Metrics.SingleLineChart("players", players::size)); // Prevent unwanted words best we can - metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> AuthType.getByName(config.getRemote().getAuthType()).toString())); + metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> AuthType.getByName(config.getRemote().getAuthType()).toString().toLowerCase())); metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::getPlatformName)); metrics.addCustomChart(new Metrics.SimplePie("defaultLocale", LanguageUtils::getDefaultLocale)); metrics.addCustomChart(new Metrics.SimplePie("version", () -> GeyserConnector.VERSION)); From 9b3cd8f725ff26e6a0abfd87481f021d959a9be6 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Mon, 12 Oct 2020 20:35:31 -0500 Subject: [PATCH 126/140] Fix area effect clouds --- .../geysermc/connector/entity/AreaEffectCloudEntity.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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 567a08ed3..308d2121a 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/AreaEffectCloudEntity.java @@ -43,6 +43,8 @@ public class AreaEffectCloudEntity extends Entity { // This disabled client side shrink of the cloud 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); } @Override @@ -51,11 +53,13 @@ public class AreaEffectCloudEntity extends Entity { metadata.put(EntityData.AREA_EFFECT_CLOUD_RADIUS, entityMetadata.getValue()); metadata.put(EntityData.BOUNDING_BOX_WIDTH, 2.0f * (float) entityMetadata.getValue()); } else if (entityMetadata.getId() == 8) { - metadata.put(EntityData.POTION_AUX_VALUE, entityMetadata.getValue()); + metadata.put(EntityData.EFFECT_COLOR, entityMetadata.getValue()); } else if (entityMetadata.getId() == 10) { Particle particle = (Particle) entityMetadata.getValue(); int particleId = EffectRegistry.getParticleId(particle.getType()); - metadata.put(EntityData.AREA_EFFECT_CLOUD_PARTICLE_ID, particleId); + if (particleId != -1) { + metadata.put(EntityData.AREA_EFFECT_CLOUD_PARTICLE_ID, particleId); + } } super.updateBedrockMetadata(entityMetadata, session); } From 191777773c5da59e4752e86dfeed202517af4a19 Mon Sep 17 00:00:00 2001 From: DaPorkchop_ Date: Tue, 13 Oct 2020 16:44:47 +0200 Subject: [PATCH 127/140] Don't use wrapper objects for positions in ChunkCache (#1398) * make ChunkPosition use a hashCode implementation with far better hash distribution this should improve the performance when used as a hash table key * ChunkCache no longer uses position wrapper objects this yields a roughly 15-20% increase in performance when converting chunk data * fix code style issues --- .../network/session/cache/ChunkCache.java | 87 ++++++++++--------- .../java/world/JavaUnloadChunkTranslator.java | 3 +- .../translators/world/GeyserWorldManager.java | 6 +- .../world/chunk/ChunkPosition.java | 30 ++++++- .../geysermc/connector/utils/ChunkUtils.java | 5 +- 5 files changed, 79 insertions(+), 52 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java index 2cc9ea134..14825b71a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java @@ -27,22 +27,18 @@ package org.geysermc.connector.network.session.cache; import com.github.steveice10.mc.protocol.data.game.chunk.Chunk; import com.github.steveice10.mc.protocol.data.game.chunk.Column; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; -import lombok.Getter; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockTranslator; import org.geysermc.connector.network.translators.world.chunk.ChunkPosition; -import java.util.HashMap; -import java.util.Map; - public class ChunkCache { private final boolean cache; - @Getter - private final Map chunks = new HashMap<>(); + private final Long2ObjectMap chunks = new Long2ObjectOpenHashMap<>(); public ChunkCache(GeyserSession session) { if (session.getConnector().getWorldManager().getClass() == GeyserBootstrap.DEFAULT_CHUNK_MANAGER.getClass()) { @@ -56,57 +52,66 @@ public class ChunkCache { if (!cache) { return; } - ChunkPosition position = new ChunkPosition(chunk.getX(), chunk.getZ()); - if (chunk.getBiomeData() == null && chunks.containsKey(position)) { - Column newColumn = chunk; - chunk = chunks.get(position); - for (int i = 0; i < newColumn.getChunks().length; i++) { - if (newColumn.getChunks()[i] != null) { - chunk.getChunks()[i] = newColumn.getChunks()[i]; + + long chunkPosition = ChunkPosition.toLong(chunk.getX(), chunk.getZ()); + Column existingChunk; + if (chunk.getBiomeData() != null // Only consider merging columns if the new chunk isn't a full chunk + && (existingChunk = chunks.getOrDefault(chunkPosition, null)) != null) { // Column is already present in cache, we can merge with existing + for (int i = 0; i < chunk.getChunks().length; i++) { // The chunks member is final, so chunk.getChunks() will probably be inlined and then completely optimized away + if (chunk.getChunks()[i] != null) { + existingChunk.getChunks()[i] = chunk.getChunks()[i]; } } - } - chunks.put(position, chunk); - } - - public void updateBlock(Position position, int block) { - if (!cache) { - return; - } - ChunkPosition chunkPosition = new ChunkPosition(position.getX() >> 4, position.getZ() >> 4); - if (!chunks.containsKey(chunkPosition)) - return; - - Column column = chunks.get(chunkPosition); - Chunk chunk = column.getChunks()[position.getY() >> 4]; - Position blockPosition = chunkPosition.getChunkBlock(position.getX(), position.getY(), position.getZ()); - if (chunk != null) { - chunk.set(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), block); + } else { + chunks.put(chunkPosition, chunk); } } - public int getBlockAt(Position position) { + public Column getChunk(int chunkX, int chunkZ) { + long chunkPosition = ChunkPosition.toLong(chunkX, chunkZ); + return chunks.getOrDefault(chunkPosition, null); + } + + public void updateBlock(int x, int y, int z, int block) { + if (!cache) { + return; + } + + Column column = this.getChunk(x >> 4, z >> 4); + if (column == null) { + return; + } + + Chunk chunk = column.getChunks()[y >> 4]; + if (chunk != null) { + chunk.set(x & 0xF, y & 0xF, z & 0xF, block); + } + } + + public int getBlockAt(int x, int y, int z) { if (!cache) { return BlockTranslator.AIR; } - ChunkPosition chunkPosition = new ChunkPosition(position.getX() >> 4, position.getZ() >> 4); - if (!chunks.containsKey(chunkPosition)) - return BlockTranslator.AIR; - Column column = chunks.get(chunkPosition); - Chunk chunk = column.getChunks()[position.getY() >> 4]; - Position blockPosition = chunkPosition.getChunkBlock(position.getX(), position.getY(), position.getZ()); + Column column = this.getChunk(x >> 4, z >> 4); + if (column == null) { + return BlockTranslator.AIR; + } + + Chunk chunk = column.getChunks()[y >> 4]; if (chunk != null) { - return chunk.get(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ()); + return chunk.get(x & 0xF, y & 0xF, z & 0xF); } return BlockTranslator.AIR; } - public void removeChunk(ChunkPosition position) { + public void removeChunk(int chunkX, int chunkZ) { if (!cache) { return; } - chunks.remove(position); + + long chunkPosition = ChunkPosition.toLong(chunkX, chunkZ); + chunks.remove(chunkPosition); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUnloadChunkTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUnloadChunkTranslator.java index d54d8b6a8..652e12947 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUnloadChunkTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaUnloadChunkTranslator.java @@ -28,7 +28,6 @@ package org.geysermc.connector.network.translators.java.world; 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.chunk.ChunkPosition; import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUnloadChunkPacket; @@ -37,6 +36,6 @@ public class JavaUnloadChunkTranslator extends PacketTranslator Date: Tue, 13 Oct 2020 17:11:52 +0200 Subject: [PATCH 128/140] fix some NPEs caused by race conditions in chunk conversion (#1396) * fix some NPEs caused by race conditions in chunk conversion tbh the whole session should be read-write locked for every operation * fix code style issues --- .../java/world/JavaChunkDataTranslator.java | 6 +++--- .../translators/world/GeyserWorldManager.java | 21 +++++++++++++++---- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java index 6dae9b4d6..ddd5e004d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java @@ -66,11 +66,11 @@ public class JavaChunkDataTranslator extends PacketTranslator { try { + // Non-full chunks don't have all the chunk data, and Bedrock won't accept that + final boolean isNonFullChunk = (packet.getColumn().getBiomeData() == null); + ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(session, packet.getColumn(), isNonFullChunk); ByteBuf byteBuf = Unpooled.buffer(32); ChunkSection[] sections = chunkData.sections; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java index 983e42b73..6972d77be 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java @@ -25,12 +25,14 @@ package org.geysermc.connector.network.translators.world; +import com.github.steveice10.mc.protocol.data.game.chunk.Column; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; import com.github.steveice10.mc.protocol.packet.ingame.client.ClientChatPacket; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.network.session.cache.ChunkCache; import org.geysermc.connector.utils.GameRule; public class GeyserWorldManager extends WorldManager { @@ -39,14 +41,25 @@ public class GeyserWorldManager extends WorldManager { @Override public int getBlockAt(GeyserSession session, int x, int y, int z) { - return session.getChunkCache().getBlockAt(x, y, z); + ChunkCache chunkCache = session.getChunkCache(); + if (chunkCache != null) { // Chunk cache can be null if the session is closed asynchronously + return chunkCache.getBlockAt(x, y, z); + } + return 0; } @Override public int[] getBiomeDataAt(GeyserSession session, int x, int z) { - if (!session.getConnector().getConfig().isCacheChunks()) - return new int[1024]; - return session.getChunkCache().getChunk(x, z).getBiomeData(); + if (session.getConnector().getConfig().isCacheChunks()) { + ChunkCache chunkCache = session.getChunkCache(); + if (chunkCache != null) { // Chunk cache can be null if the session is closed asynchronously + Column column = chunkCache.getChunk(x, z); + if (column != null) { // Column can be null if the server sent a partial chunk update before the first ground-up-continuous one + return column.getBiomeData(); + } + } + } + return new int[1024]; } @Override From 2ca2436cdc47a94dbf50e469eeeb33f99f6bb79c Mon Sep 17 00:00:00 2001 From: Tim203 Date: Thu, 15 Oct 2020 05:34:24 +0200 Subject: [PATCH 129/140] Don't use the general thread pool to run an async method (#1397) * Don't use the general thread pool for an async method * Align nested class at the bottom --- .../player/JavaPlayerListEntryTranslator.java | 5 +- .../geysermc/connector/utils/SkinUtils.java | 268 ++++++++---------- 2 files changed, 128 insertions(+), 145 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerListEntryTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerListEntryTranslator.java index 6a9ef4dc6..da402f66c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerListEntryTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/player/JavaPlayerListEntryTranslator.java @@ -57,9 +57,8 @@ public class JavaPlayerListEntryTranslator extends PacketTranslator { - GeyserConnector.getInstance().getLogger().debug("Loading Local Bedrock Java Skin Data"); - }); + SkinUtils.requestAndHandleSkinAndCape(playerEntity, session, skinAndCape -> + GeyserConnector.getInstance().getLogger().debug("Loaded Local Bedrock Java Skin Data")); } else { playerEntity = session.getEntityCache().getPlayerEntity(entry.getProfile().getId()); } diff --git a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java index fe2a8aa96..5505acdf6 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SkinUtils.java @@ -33,9 +33,8 @@ import com.nukkitx.protocol.bedrock.data.skin.SerializedSkin; import com.nukkitx.protocol.bedrock.packet.PlayerListPacket; import lombok.AllArgsConstructor; import lombok.Getter; -import org.geysermc.connector.common.AuthType; import org.geysermc.connector.GeyserConnector; -import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.common.AuthType; import org.geysermc.connector.entity.PlayerEntity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.auth.BedrockClientData; @@ -73,21 +72,6 @@ public class SkinUtils { ); } - public static PlayerListPacket.Entry buildDefaultEntry(GeyserSession session, GameProfile profile, long geyserId) { - return buildEntryManually( - session, - profile.getId(), - profile.getName(), - geyserId, - "default", - SkinProvider.STEVE_SKIN, - SkinProvider.EMPTY_CAPE.getCapeId(), - SkinProvider.EMPTY_CAPE.getCapeData(), - SkinProvider.EMPTY_GEOMETRY.getGeometryName(), - SkinProvider.EMPTY_GEOMETRY.getGeometryData() - ); - } - public static PlayerListPacket.Entry buildEntryManually(GeyserSession session, UUID uuid, String username, long geyserId, String skinId, byte[] skinData, String capeId, byte[] capeData, @@ -115,7 +99,7 @@ public class SkinUtils { } else { entry = new PlayerListPacket.Entry(uuid); } - + entry.setName(username); entry.setEntityId(geyserId); entry.setSkin(serializedSkin); @@ -126,12 +110,133 @@ public class SkinUtils { return entry; } + public static void requestAndHandleSkinAndCape(PlayerEntity entity, GeyserSession session, + Consumer skinAndCapeConsumer) { + GameProfileData data = GameProfileData.from(entity.getProfile()); + + SkinProvider.requestSkinAndCape(entity.getUuid(), data.getSkinUrl(), data.getCapeUrl()) + .whenCompleteAsync((skinAndCape, throwable) -> { + try { + SkinProvider.Skin skin = skinAndCape.getSkin(); + SkinProvider.Cape cape = skinAndCape.getCape(); + + if (cape.isFailed()) { + cape = SkinProvider.getOrDefault(SkinProvider.requestBedrockCape( + entity.getUuid(), false + ), SkinProvider.EMPTY_CAPE, 3); + } + + if (cape.isFailed() && SkinProvider.ALLOW_THIRD_PARTY_CAPES) { + cape = SkinProvider.getOrDefault(SkinProvider.requestUnofficialCape( + cape, entity.getUuid(), + entity.getUsername(), false + ), SkinProvider.EMPTY_CAPE, SkinProvider.CapeProvider.VALUES.length * 3); + } + + SkinProvider.SkinGeometry geometry = SkinProvider.SkinGeometry.getLegacy(data.isAlex()); + geometry = SkinProvider.getOrDefault(SkinProvider.requestBedrockGeometry( + geometry, entity.getUuid(), false + ), geometry, 3); + + // Not a bedrock player check for ears + if (geometry.isFailed() && SkinProvider.ALLOW_THIRD_PARTY_EARS) { + boolean isEars; + + // Its deadmau5, gotta support his skin :) + if (entity.getUuid().toString().equals("1e18d5ff-643d-45c8-b509-43b8461d8614")) { + isEars = true; + } else { + // Get the ears texture for the player + skin = SkinProvider.getOrDefault(SkinProvider.requestUnofficialEars( + skin, entity.getUuid(), entity.getUsername(), false + ), skin, 3); + + isEars = skin.isEars(); + } + + // Does the skin have an ears texture + if (isEars) { + // Get the new geometry + geometry = SkinProvider.SkinGeometry.getEars(data.isAlex()); + + // Store the skin and geometry for the ears + SkinProvider.storeEarSkin(entity.getUuid(), skin); + SkinProvider.storeEarGeometry(entity.getUuid(), data.isAlex()); + } + } + + entity.setLastSkinUpdate(skin.getRequestedOn()); + + if (session.getUpstream().isInitialized()) { + PlayerListPacket.Entry updatedEntry = buildEntryManually( + session, + entity.getUuid(), + entity.getUsername(), + entity.getGeyserId(), + skin.getTextureUrl(), + skin.getSkinData(), + cape.getCapeId(), + cape.getCapeData(), + geometry.getGeometryName(), + geometry.getGeometryData() + ); + + + PlayerListPacket playerAddPacket = new PlayerListPacket(); + playerAddPacket.setAction(PlayerListPacket.Action.ADD); + playerAddPacket.getEntries().add(updatedEntry); + session.sendUpstreamPacket(playerAddPacket); + + if (!entity.isPlayerList()) { + PlayerListPacket playerRemovePacket = new PlayerListPacket(); + playerRemovePacket.setAction(PlayerListPacket.Action.REMOVE); + playerRemovePacket.getEntries().add(updatedEntry); + session.sendUpstreamPacket(playerRemovePacket); + + } + } + } catch (Exception e) { + GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.skin.fail", entity.getUuid()), e); + } + + if (skinAndCapeConsumer != null) { + skinAndCapeConsumer.accept(skinAndCape); + } + }); + } + + public static void handleBedrockSkin(PlayerEntity playerEntity, BedrockClientData clientData) { + GeyserConnector.getInstance().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.skin.bedrock.register", playerEntity.getUsername(), playerEntity.getUuid())); + + try { + byte[] skinBytes = Base64.getDecoder().decode(clientData.getSkinData().getBytes(StandardCharsets.UTF_8)); + byte[] capeBytes = clientData.getCapeData(); + + byte[] geometryNameBytes = Base64.getDecoder().decode(clientData.getGeometryName().getBytes(StandardCharsets.UTF_8)); + byte[] geometryBytes = Base64.getDecoder().decode(clientData.getGeometryData().getBytes(StandardCharsets.UTF_8)); + + if (skinBytes.length <= (128 * 128 * 4) && !clientData.isPersonaSkin()) { + SkinProvider.storeBedrockSkin(playerEntity.getUuid(), clientData.getSkinId(), skinBytes); + SkinProvider.storeBedrockGeometry(playerEntity.getUuid(), geometryNameBytes, geometryBytes); + } else { + GeyserConnector.getInstance().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.skin.bedrock.fail", playerEntity.getUsername())); + GeyserConnector.getInstance().getLogger().debug("The size of '" + playerEntity.getUsername() + "' skin is: " + clientData.getSkinImageWidth() + "x" + clientData.getSkinImageHeight()); + } + + if (!clientData.getCapeId().equals("")) { + SkinProvider.storeBedrockCape(playerEntity.getUuid(), capeBytes); + } + } catch (Exception e) { + throw new AssertionError("Failed to cache skin for bedrock user (" + playerEntity.getUsername() + "): ", e); + } + } + @AllArgsConstructor @Getter public static class GameProfileData { - private String skinUrl; - private String capeUrl; - private boolean alex; + private final String skinUrl; + private final String capeUrl; + private final boolean alex; /** * Generate the GameProfileData from the given GameProfile @@ -170,125 +275,4 @@ public class SkinUtils { } } } - - public static void requestAndHandleSkinAndCape(PlayerEntity entity, GeyserSession session, - Consumer skinAndCapeConsumer) { - GeyserConnector.getInstance().getGeneralThreadPool().execute(() -> { - GameProfileData data = GameProfileData.from(entity.getProfile()); - - SkinProvider.requestSkinAndCape(entity.getUuid(), data.getSkinUrl(), data.getCapeUrl()) - .whenCompleteAsync((skinAndCape, throwable) -> { - try { - SkinProvider.Skin skin = skinAndCape.getSkin(); - SkinProvider.Cape cape = skinAndCape.getCape(); - - if (cape.isFailed()) { - cape = SkinProvider.getOrDefault(SkinProvider.requestBedrockCape( - entity.getUuid(), false - ), SkinProvider.EMPTY_CAPE, 3); - } - - if (cape.isFailed() && SkinProvider.ALLOW_THIRD_PARTY_CAPES) { - cape = SkinProvider.getOrDefault(SkinProvider.requestUnofficialCape( - cape, entity.getUuid(), - entity.getUsername(), false - ), SkinProvider.EMPTY_CAPE, SkinProvider.CapeProvider.VALUES.length * 3); - } - - SkinProvider.SkinGeometry geometry = SkinProvider.SkinGeometry.getLegacy(data.isAlex()); - geometry = SkinProvider.getOrDefault(SkinProvider.requestBedrockGeometry( - geometry, entity.getUuid(), false - ), geometry, 3); - - // Not a bedrock player check for ears - if (geometry.isFailed() && SkinProvider.ALLOW_THIRD_PARTY_EARS) { - boolean isEars = false; - - // Its deadmau5, gotta support his skin :) - if (entity.getUuid().toString().equals("1e18d5ff-643d-45c8-b509-43b8461d8614")) { - isEars = true; - } else { - // Get the ears texture for the player - skin = SkinProvider.getOrDefault(SkinProvider.requestUnofficialEars( - skin, entity.getUuid(), entity.getUsername(), false - ), skin, 3); - - isEars = skin.isEars(); - } - - // Does the skin have an ears texture - if (isEars) { - // Get the new geometry - geometry = SkinProvider.SkinGeometry.getEars(data.isAlex()); - - // Store the skin and geometry for the ears - SkinProvider.storeEarSkin(entity.getUuid(), skin); - SkinProvider.storeEarGeometry(entity.getUuid(), data.isAlex()); - } - } - - entity.setLastSkinUpdate(skin.getRequestedOn()); - - if (session.getUpstream().isInitialized()) { - PlayerListPacket.Entry updatedEntry = buildEntryManually( - session, - entity.getUuid(), - entity.getUsername(), - entity.getGeyserId(), - skin.getTextureUrl(), - skin.getSkinData(), - cape.getCapeId(), - cape.getCapeData(), - geometry.getGeometryName(), - geometry.getGeometryData() - ); - - - PlayerListPacket playerAddPacket = new PlayerListPacket(); - playerAddPacket.setAction(PlayerListPacket.Action.ADD); - playerAddPacket.getEntries().add(updatedEntry); - session.sendUpstreamPacket(playerAddPacket); - - if (!entity.isPlayerList()) { - PlayerListPacket playerRemovePacket = new PlayerListPacket(); - playerRemovePacket.setAction(PlayerListPacket.Action.REMOVE); - playerRemovePacket.getEntries().add(updatedEntry); - session.sendUpstreamPacket(playerRemovePacket); - - } - } - } catch (Exception e) { - GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.skin.fail", entity.getUuid()), e); - } - - if (skinAndCapeConsumer != null) skinAndCapeConsumer.accept(skinAndCape); - }); - }); - } - - public static void handleBedrockSkin(PlayerEntity playerEntity, BedrockClientData clientData) { - GeyserConnector.getInstance().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.skin.bedrock.register", playerEntity.getUsername(), playerEntity.getUuid())); - - try { - byte[] skinBytes = Base64.getDecoder().decode(clientData.getSkinData().getBytes("UTF-8")); - byte[] capeBytes = clientData.getCapeData(); - - byte[] geometryNameBytes = Base64.getDecoder().decode(clientData.getGeometryName().getBytes("UTF-8")); - byte[] geometryBytes = Base64.getDecoder().decode(clientData.getGeometryData().getBytes("UTF-8")); - - if (skinBytes.length <= (128 * 128 * 4) && !clientData.isPersonaSkin()) { - SkinProvider.storeBedrockSkin(playerEntity.getUuid(), clientData.getSkinId(), skinBytes); - SkinProvider.storeBedrockGeometry(playerEntity.getUuid(), geometryNameBytes, geometryBytes); - } else { - GeyserConnector.getInstance().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.skin.bedrock.fail", playerEntity.getUsername())); - GeyserConnector.getInstance().getLogger().debug("The size of '" + playerEntity.getUsername() + "' skin is: " + clientData.getSkinImageWidth() + "x" + clientData.getSkinImageHeight()); - } - - if (!clientData.getCapeId().equals("")) { - SkinProvider.storeBedrockCape(playerEntity.getUuid(), capeBytes); - } - } catch (Exception e) { - throw new AssertionError("Failed to cache skin for bedrock user (" + playerEntity.getUsername() + "): ", e); - } - } } From 40de801eb061f3ea6dbef2c8acf56f321b46174f Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Thu, 15 Oct 2020 01:21:12 -0500 Subject: [PATCH 130/140] Add sound when an arrow hits a player --- .../java/world/JavaNotifyClientTranslator.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java index 493a7ca1e..d7961dd98 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaNotifyClientTranslator.java @@ -145,6 +145,14 @@ public class JavaNotifyClientTranslator extends PacketTranslator Date: Thu, 15 Oct 2020 08:30:25 +0200 Subject: [PATCH 131/140] Faster chunk conversion (#1400) * BlockStorage is never used concurrently, no need to synchronize * initial, semi-functional, faster chunk conversion * faster chunk conversion works well for every situation except spigot * delete unused ChunkPosition class * preallocate and pool chunk encoding buffers * make it work correctly on spigot * make field naming more consistent * attempt to upgrade to latest MCProtocolLib * remove debug code * compile against my MCProtocolLib fork while i wait for my upstream PR to be accepted * return to Steveice10 MCProtocolLib --- .../world/GeyserSpigotWorldManager.java | 63 +++++- connector/pom.xml | 2 +- .../network/session/cache/ChunkCache.java | 18 +- .../java/world/JavaChunkDataTranslator.java | 81 ++++--- .../translators/world/GeyserWorldManager.java | 26 +++ .../translators/world/WorldManager.java | 22 ++ .../translators/world/chunk/BlockStorage.java | 26 ++- .../world/chunk/ChunkPosition.java | 79 ------- .../translators/world/chunk/ChunkSection.java | 65 +----- .../world/chunk/bitarray/BitArrayVersion.java | 18 +- .../geysermc/connector/utils/ChunkUtils.java | 213 +++++++++++++----- .../geysermc/connector/utils/MathUtils.java | 11 + 12 files changed, 371 insertions(+), 253 deletions(-) delete mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkPosition.java diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java index c6443bd05..8a92526f1 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotWorldManager.java @@ -26,12 +26,14 @@ package org.geysermc.platform.spigot.world; import com.fasterxml.jackson.databind.JsonNode; +import com.github.steveice10.mc.protocol.data.game.chunk.Chunk; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.block.Block; +import org.bukkit.entity.Player; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.GeyserWorldManager; @@ -93,23 +95,32 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { @Override public int getBlockAt(GeyserSession session, int x, int y, int z) { - if (session.getPlayerEntity() == null) { - return BlockTranslator.AIR; - } - if (Bukkit.getPlayer(session.getPlayerEntity().getUsername()) == null) { + Player bukkitPlayer; + if ((this.isLegacy && !this.isViaVersion) + || session.getPlayerEntity() == null + || (bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) { return BlockTranslator.AIR; } + World world = bukkitPlayer.getWorld(); if (isLegacy) { - return getLegacyBlock(session, x, y, z, isViaVersion); + return getLegacyBlock(session, x, y, z, true); } //TODO possibly: detect server version for all versions and use ViaVersion for block state mappings like below - return BlockTranslator.getJavaIdBlockMap().getOrDefault(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getBlockAt(x, y, z).getBlockData().getAsString(), 0); + return BlockTranslator.getJavaIdBlockMap().getOrDefault(world.getBlockAt(x, y, z).getBlockData().getAsString(), 0); + } + + public static int getLegacyBlock(GeyserSession session, int x, int y, int z, boolean isViaVersion) { + if (isViaVersion) { + return getLegacyBlock(Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld(), x, y, z, true); + } else { + return BlockTranslator.AIR; + } } @SuppressWarnings("deprecation") - public static int getLegacyBlock(GeyserSession session, int x, int y, int z, boolean isViaVersion) { + public static int getLegacyBlock(World world, int x, int y, int z, boolean isViaVersion) { if (isViaVersion) { - Block block = Bukkit.getPlayer(session.getPlayerEntity().getUsername()).getWorld().getBlockAt(x, y, z); + Block block = world.getBlockAt(x, y, z); // Black magic that gets the old block state ID int oldBlockId = (block.getType().getId() << 4) | (block.getData() & 0xF); // Convert block state from old version -> 1.13 -> 1.13.1 -> 1.14 -> 1.15 -> 1.16 -> 1.16.2 @@ -124,6 +135,42 @@ public class GeyserSpigotWorldManager extends GeyserWorldManager { } } + @Override + public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) { + Player bukkitPlayer; + if ((this.isLegacy && !this.isViaVersion) + || session.getPlayerEntity() == null + || (bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) { + return; + } + World world = bukkitPlayer.getWorld(); + if (this.isLegacy) { + 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++) { + chunk.set(blockX, blockY, blockZ, getLegacyBlock(world, (x << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ, true)); + } + } + } + } else { + //TODO: see above TODO in getBlockAt + 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 << 4) + blockX, (y << 4) + blockY, (z << 4) + blockZ); + int id = BlockTranslator.getJavaIdBlockMap().getOrDefault(block.getBlockData().getAsString(), 0); + chunk.set(blockX, blockY, blockZ, id); + } + } + } + } + } + + @Override + public boolean hasMoreBlockDataThanChunkCache() { + return true; + } + @Override @SuppressWarnings("deprecation") public int[] getBiomeDataAt(GeyserSession session, int x, int z) { diff --git a/connector/pom.xml b/connector/pom.xml index 741acee51..5df525567 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -111,7 +111,7 @@ com.github.steveice10 mcprotocollib - 976c2d0f89 + 3a69a0614c compile diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java index 14825b71a..7bf84b8db 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/ChunkCache.java @@ -32,7 +32,7 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import org.geysermc.connector.bootstrap.GeyserBootstrap; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockTranslator; -import org.geysermc.connector.network.translators.world.chunk.ChunkPosition; +import org.geysermc.connector.utils.MathUtils; public class ChunkCache { @@ -48,27 +48,31 @@ public class ChunkCache { } } - public void addToCache(Column chunk) { + public Column addToCache(Column chunk) { if (!cache) { - return; + return chunk; } - long chunkPosition = ChunkPosition.toLong(chunk.getX(), chunk.getZ()); + long chunkPosition = MathUtils.chunkPositionToLong(chunk.getX(), chunk.getZ()); Column existingChunk; - if (chunk.getBiomeData() != null // Only consider merging columns if the new chunk isn't a full chunk + if (chunk.getBiomeData() == null // Only consider merging columns if the new chunk isn't a full chunk && (existingChunk = chunks.getOrDefault(chunkPosition, null)) != null) { // Column is already present in cache, we can merge with existing + boolean changed = false; for (int i = 0; i < chunk.getChunks().length; i++) { // The chunks member is final, so chunk.getChunks() will probably be inlined and then completely optimized away if (chunk.getChunks()[i] != null) { existingChunk.getChunks()[i] = chunk.getChunks()[i]; + changed = true; } } + return changed ? existingChunk : null; } else { chunks.put(chunkPosition, chunk); + return chunk; } } public Column getChunk(int chunkX, int chunkZ) { - long chunkPosition = ChunkPosition.toLong(chunkX, chunkZ); + long chunkPosition = MathUtils.chunkPositionToLong(chunkX, chunkZ); return chunks.getOrDefault(chunkPosition, null); } @@ -111,7 +115,7 @@ public class ChunkCache { return; } - long chunkPosition = ChunkPosition.toLong(chunkX, chunkZ); + long chunkPosition = MathUtils.chunkPositionToLong(chunkX, chunkZ); chunks.remove(chunkPosition); } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java index ddd5e004d..cd1a321c2 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/world/JavaChunkDataTranslator.java @@ -25,6 +25,7 @@ package org.geysermc.connector.network.translators.java.world; +import com.github.steveice10.mc.protocol.data.game.chunk.Column; import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerChunkDataPacket; import com.nukkitx.nbt.NBTOutputStream; import com.nukkitx.nbt.NbtMap; @@ -32,8 +33,8 @@ import com.nukkitx.nbt.NbtUtils; import com.nukkitx.network.VarInts; import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufOutputStream; -import io.netty.buffer.Unpooled; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.BiomeTranslator; @@ -66,57 +67,69 @@ public class JavaChunkDataTranslator extends PacketTranslator { try { - // Non-full chunks don't have all the chunk data, and Bedrock won't accept that - final boolean isNonFullChunk = (packet.getColumn().getBiomeData() == null); - - ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(session, packet.getColumn(), isNonFullChunk); - ByteBuf byteBuf = Unpooled.buffer(32); - ChunkSection[] sections = chunkData.sections; + ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(session, mergedColumn, isNonFullChunk); + ChunkSection[] sections = chunkData.getSections(); + // Find highest section int sectionCount = sections.length - 1; - while (sectionCount >= 0 && sections[sectionCount].isEmpty()) { + while (sectionCount >= 0 && sections[sectionCount] == null) { sectionCount--; } sectionCount++; + // Estimate chunk size + int size = 0; for (int i = 0; i < sectionCount; i++) { - ChunkSection section = chunkData.sections[i]; - section.writeToNetwork(byteBuf); + ChunkSection section = sections[i]; + size += (section != null ? section : ChunkUtils.EMPTY_SECTION).estimateNetworkSize(); } + size += 256; // Biomes + size += 1; // Border blocks + size += 1; // Extra data length (always 0) + size += chunkData.getBlockEntities().length * 64; // Conservative estimate of 64 bytes per tile entity - byte[] bedrockBiome; - if (packet.getColumn().getBiomeData() == null) { - bedrockBiome = BiomeTranslator.toBedrockBiome(session.getConnector().getWorldManager().getBiomeDataAt(session, packet.getColumn().getX(), packet.getColumn().getZ())); - } else { - bedrockBiome = BiomeTranslator.toBedrockBiome(packet.getColumn().getBiomeData()); + // Allocate output buffer + ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(size); + byte[] payload; + try { + for (int i = 0; i < sectionCount; i++) { + ChunkSection section = sections[i]; + (section != null ? section : ChunkUtils.EMPTY_SECTION).writeToNetwork(byteBuf); + } + + byteBuf.writeBytes(BiomeTranslator.toBedrockBiome(mergedColumn.getBiomeData())); // Biomes - 256 bytes + byteBuf.writeByte(0); // Border blocks - Edu edition only + VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now + + // Encode tile entities into buffer + NBTOutputStream nbtStream = NbtUtils.createNetworkWriter(new ByteBufOutputStream(byteBuf)); + for (NbtMap blockEntity : chunkData.getBlockEntities()) { + nbtStream.writeTag(blockEntity); + } + + // Copy data into byte[], because the protocol lib really likes things that are s l o w + byteBuf.readBytes(payload = new byte[byteBuf.readableBytes()]); + } finally { + byteBuf.release(); // Release buffer to allow buffer pooling to be useful } - byteBuf.writeBytes(bedrockBiome); // Biomes - 256 bytes - byteBuf.writeByte(0); // Border blocks - Edu edition only - VarInts.writeUnsignedInt(byteBuf, 0); // extra data length, 0 for now - - ByteBufOutputStream stream = new ByteBufOutputStream(Unpooled.buffer()); - NBTOutputStream nbtStream = NbtUtils.createNetworkWriter(stream); - for (NbtMap blockEntity : chunkData.getBlockEntities()) { - nbtStream.writeTag(blockEntity); - } - - byteBuf.writeBytes(stream.buffer()); - - byte[] payload = new byte[byteBuf.writerIndex()]; - byteBuf.readBytes(payload); - LevelChunkPacket levelChunkPacket = new LevelChunkPacket(); levelChunkPacket.setSubChunksLength(sectionCount); levelChunkPacket.setCachingEnabled(false); - levelChunkPacket.setChunkX(packet.getColumn().getX()); - levelChunkPacket.setChunkZ(packet.getColumn().getZ()); + levelChunkPacket.setChunkX(mergedColumn.getX()); + levelChunkPacket.setChunkZ(mergedColumn.getZ()); levelChunkPacket.setData(payload); session.sendUpstreamPacket(levelChunkPacket); - - session.getChunkCache().addToCache(packet.getColumn()); } catch (Exception ex) { ex.printStackTrace(); } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java index 6972d77be..2ab3c0108 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/GeyserWorldManager.java @@ -25,6 +25,7 @@ package org.geysermc.connector.network.translators.world; +import com.github.steveice10.mc.protocol.data.game.chunk.Chunk; import com.github.steveice10.mc.protocol.data.game.chunk.Column; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; @@ -48,6 +49,31 @@ public class GeyserWorldManager extends WorldManager { return 0; } + @Override + public void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk chunk) { + ChunkCache chunkCache = session.getChunkCache(); + Column cachedColumn; + Chunk cachedChunk; + if (chunkCache == null || (cachedColumn = chunkCache.getChunk(x, z)) == null || (cachedChunk = cachedColumn.getChunks()[y]) == null) { + return; + } + + // Copy state IDs from cached chunk to output 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++) { + chunk.set(blockX, blockY, blockZ, cachedChunk.get(blockX, blockY, blockZ)); + } + } + } + } + + @Override + public boolean hasMoreBlockDataThanChunkCache() { + // This implementation can only fetch data from the session chunk cache + return false; + } + @Override public int[] getBiomeDataAt(GeyserSession session, int x, int z) { if (session.getConnector().getConfig().isCacheChunks()) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java index ba78b8f6f..fec3bb33a 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/WorldManager.java @@ -25,6 +25,7 @@ package org.geysermc.connector.network.translators.world; +import com.github.steveice10.mc.protocol.data.game.chunk.Chunk; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode; import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; @@ -74,6 +75,27 @@ public abstract class WorldManager { */ public abstract int getBlockAt(GeyserSession session, int x, int y, int z); + /** + * Gets all block states in the specified chunk section. + * + * @param session the session + * @param x the chunk's X coordinate + * @param y the chunk's Y coordinate + * @param z the chunk's Z coordinate + * @param section the chunk section to store the block data in + */ + public abstract void getBlocksInSection(GeyserSession session, int x, int y, int z, Chunk section); + + /** + * Checks whether or not this world manager has access to more block data than the chunk cache. + *

+ * Some world managers (e.g. Spigot) can provide access to block data outside of the chunk cache, and even with chunk caching disabled. This + * method provides a means to check if this manager has this capability. + * + * @return whether or not this world manager has access to more block data than the chunk cache + */ + public abstract boolean hasMoreBlockDataThanChunkCache(); + /** * Gets the biome data for the specified chunk. * diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java index 30edf1781..d8cd75206 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java @@ -29,14 +29,16 @@ import com.nukkitx.network.VarInts; import io.netty.buffer.ByteBuf; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; +import lombok.Getter; import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArray; import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArrayVersion; import java.util.function.IntConsumer; +@Getter public class BlockStorage { - private static final int SIZE = 4096; + public static final int SIZE = 4096; private final IntList palette; private BitArray bitArray; @@ -46,12 +48,12 @@ public class BlockStorage { } public BlockStorage(BitArrayVersion version) { - this.bitArray = version.createPalette(SIZE); + this.bitArray = version.createArray(SIZE); this.palette = new IntArrayList(16); this.palette.add(0); // Air is at the start of every palette. } - private BlockStorage(BitArray bitArray, IntArrayList palette) { + public BlockStorage(BitArray bitArray, IntList palette) { this.palette = palette; this.bitArray = bitArray; } @@ -64,16 +66,16 @@ public class BlockStorage { return BitArrayVersion.get(header >> 1, true); } - public synchronized int getFullBlock(int index) { + public int getFullBlock(int index) { return this.palette.getInt(this.bitArray.get(index)); } - public synchronized void setFullBlock(int index, int runtimeId) { + public void setFullBlock(int index, int runtimeId) { int idx = this.idFor(runtimeId); this.bitArray.set(index, idx); } - public synchronized void writeToNetwork(ByteBuf buffer) { + public void writeToNetwork(ByteBuf buffer) { buffer.writeByte(getPaletteHeader(bitArray.getVersion(), true)); for (int word : bitArray.getWords()) { @@ -84,8 +86,18 @@ public class BlockStorage { palette.forEach((IntConsumer) id -> VarInts.writeInt(buffer, id)); } + public int estimateNetworkSize() { + int size = 1; // Palette header + size += this.bitArray.getWords().length * 4; + + // We assume that none of the VarInts will be larger than 3 bytes + size += 3; // Palette size + size += this.palette.size() * 3; + return size; + } + private void onResize(BitArrayVersion version) { - BitArray newBitArray = version.createPalette(SIZE); + BitArray newBitArray = version.createArray(SIZE); for (int i = 0; i < SIZE; i++) { newBitArray.set(i, this.bitArray.get(i)); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkPosition.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkPosition.java deleted file mode 100644 index 9e721aa9f..000000000 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkPosition.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.world.chunk; - -import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -@AllArgsConstructor -public class ChunkPosition { - - /** - * Packs a chunk's X and Z coordinates into a single {@code long}. - * - * @param x the X coordinate - * @param z the Z coordinate - * @return the packed coordinates - */ - public static long toLong(int x, int z) { - return ((x & 0xFFFFFFFFL) << 32L) | (z & 0xFFFFFFFFL); - } - - private int x; - private int z; - - public Position getBlock(int x, int y, int z) { - return new Position((this.x << 4) + x, y, (this.z << 4) + z); - } - - public Position getChunkBlock(int x, int y, int z) { - int chunkX = x & 15; - int chunkY = y & 15; - int chunkZ = z & 15; - return new Position(chunkX, chunkY, chunkZ); - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } else if (obj instanceof ChunkPosition) { - ChunkPosition chunkPosition = (ChunkPosition) obj; - return this.x == chunkPosition.x && this.z == chunkPosition.z; - } else { - return false; - } - } - - @Override - public int hashCode() { - return this.x * 2061811133 + this.z * 1424368303; - } -} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkSection.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkSection.java index 48ec88064..979b79c93 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkSection.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/ChunkSection.java @@ -27,42 +27,19 @@ package org.geysermc.connector.network.translators.world.chunk; import com.nukkitx.network.util.Preconditions; import io.netty.buffer.ByteBuf; -import lombok.Synchronized; public class ChunkSection { private static final int CHUNK_SECTION_VERSION = 8; - public static final int SIZE = 4096; private final BlockStorage[] storage; - private final NibbleArray blockLight; - private final NibbleArray skyLight; public ChunkSection() { - this(new BlockStorage[]{new BlockStorage(), new BlockStorage()}, new NibbleArray(SIZE), - new NibbleArray(SIZE)); + this(new BlockStorage[]{new BlockStorage(), new BlockStorage()}); } - public ChunkSection(BlockStorage[] blockStorage) { - this(blockStorage, new NibbleArray(SIZE), new NibbleArray(SIZE)); - } - - public ChunkSection(BlockStorage[] storage, byte[] blockLight, byte[] skyLight) { - Preconditions.checkNotNull(storage, "storage"); - Preconditions.checkArgument(storage.length > 1, "Block storage length must be at least 2"); - for (BlockStorage blockStorage : storage) { - Preconditions.checkNotNull(blockStorage, "storage"); - } - + public ChunkSection(BlockStorage[] storage) { this.storage = storage; - this.blockLight = new NibbleArray(blockLight); - this.skyLight = new NibbleArray(skyLight); - } - - private ChunkSection(BlockStorage[] storage, NibbleArray blockLight, NibbleArray skyLight) { - this.storage = storage; - this.blockLight = blockLight; - this.skyLight = skyLight; } public int getFullBlock(int x, int y, int z, int layer) { @@ -77,30 +54,6 @@ public class ChunkSection { this.storage[layer].setFullBlock(blockPosition(x, y, z), fullBlock); } - @Synchronized("skyLight") - public byte getSkyLight(int x, int y, int z) { - checkBounds(x, y, z); - return this.skyLight.get(blockPosition(x, y, z)); - } - - @Synchronized("skyLight") - public void setSkyLight(int x, int y, int z, byte val) { - checkBounds(x, y, z); - this.skyLight.set(blockPosition(x, y, z), val); - } - - @Synchronized("blockLight") - public byte getBlockLight(int x, int y, int z) { - checkBounds(x, y, z); - return this.blockLight.get(blockPosition(x, y, z)); - } - - @Synchronized("blockLight") - public void setBlockLight(int x, int y, int z, byte val) { - checkBounds(x, y, z); - this.blockLight.set(blockPosition(x, y, z), val); - } - public void writeToNetwork(ByteBuf buffer) { buffer.writeByte(CHUNK_SECTION_VERSION); buffer.writeByte(this.storage.length); @@ -109,12 +62,12 @@ public class ChunkSection { } } - public NibbleArray getSkyLightArray() { - return skyLight; - } - - public NibbleArray getBlockLightArray() { - return blockLight; + public int estimateNetworkSize() { + int size = 2; // Version + storage count + for (BlockStorage blockStorage : this.storage) { + size += blockStorage.estimateNetworkSize(); + } + return size; } public BlockStorage[] getBlockStorageArray() { @@ -135,7 +88,7 @@ public class ChunkSection { for (int i = 0; i < storage.length; i++) { storage[i] = this.storage[i].copy(); } - return new ChunkSection(storage, skyLight.copy(), blockLight.copy()); + return new ChunkSection(storage); } public static int blockPosition(int x, int y, int z) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/bitarray/BitArrayVersion.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/bitarray/BitArrayVersion.java index 20fa849c2..47a73f7c1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/bitarray/BitArrayVersion.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/bitarray/BitArrayVersion.java @@ -37,6 +37,8 @@ public enum BitArrayVersion { V2(2, 16, V3), V1(1, 32, V2); + private static final BitArrayVersion[] VALUES = values(); + final byte bits; final byte entriesPerWord; final int maxEntryValue; @@ -58,8 +60,14 @@ public enum BitArrayVersion { throw new IllegalArgumentException("Invalid palette version: " + version); } - public BitArray createPalette(int size) { - return this.createPalette(size, new int[MathUtils.ceil((float) size / entriesPerWord)]); + public static BitArrayVersion forBitsCeil(int bits) { + for (int i = VALUES.length - 1; i >= 0; i--) { + BitArrayVersion version = VALUES[i]; + if (version.bits >= bits) { + return version; + } + } + return null; } public byte getId() { @@ -78,7 +86,11 @@ public enum BitArrayVersion { return next; } - public BitArray createPalette(int size, int[] words) { + public BitArray createArray(int size) { + return this.createArray(size, new int[MathUtils.ceil((float) size / entriesPerWord)]); + } + + public BitArray createArray(int size, int[] words) { if (this == V3 || this == V5 || this == V6) { // Padded palettes aren't able to use bitwise operations due to their padding. return new PaddedBitArray(this, size, words); diff --git a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java index d47584e9d..a63eeb424 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/ChunkUtils.java @@ -25,8 +25,10 @@ package org.geysermc.connector.utils; +import com.github.steveice10.mc.protocol.data.game.chunk.BitStorage; import com.github.steveice10.mc.protocol.data.game.chunk.Chunk; import com.github.steveice10.mc.protocol.data.game.chunk.Column; +import com.github.steveice10.mc.protocol.data.game.chunk.palette.Palette; import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.opennbt.tag.builtin.CompoundTag; import com.github.steveice10.opennbt.tag.builtin.StringTag; @@ -36,27 +38,39 @@ import com.nukkitx.math.vector.Vector3i; import com.nukkitx.nbt.NBTOutputStream; import com.nukkitx.nbt.NbtMap; import com.nukkitx.nbt.NbtUtils; -import com.nukkitx.protocol.bedrock.packet.*; +import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket; +import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket; +import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import lombok.Getter; +import lombok.Data; +import lombok.experimental.UtilityClass; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.ItemFrameEntity; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.world.block.BlockStateValues; -import org.geysermc.connector.network.translators.world.block.entity.*; import org.geysermc.connector.network.translators.world.block.BlockTranslator; -import org.geysermc.connector.network.translators.world.chunk.ChunkPosition; +import org.geysermc.connector.network.translators.world.block.entity.BedrockOnlyBlockEntity; +import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator; +import org.geysermc.connector.network.translators.world.block.entity.RequiresBlockState; +import org.geysermc.connector.network.translators.world.chunk.BlockStorage; import org.geysermc.connector.network.translators.world.chunk.ChunkSection; +import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArray; +import org.geysermc.connector.network.translators.world.chunk.bitarray.BitArrayVersion; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collections; +import java.util.List; -import static org.geysermc.connector.network.translators.world.block.BlockTranslator.AIR; -import static org.geysermc.connector.network.translators.world.block.BlockTranslator.BEDROCK_WATER_ID; +import static org.geysermc.connector.network.translators.world.block.BlockTranslator.*; +@UtilityClass public class ChunkUtils { /** @@ -67,6 +81,9 @@ public class ChunkUtils { private static final NbtMap EMPTY_TAG = NbtMap.builder().build(); public static final byte[] EMPTY_LEVEL_CHUNK_DATA; + public static final BlockStorage EMPTY_STORAGE = new BlockStorage(); + public static final ChunkSection EMPTY_SECTION = new ChunkSection(new BlockStorage[]{ EMPTY_STORAGE }); + static { try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { outputStream.write(new byte[258]); // Biomes + Border Size + Extra Data Size @@ -76,72 +93,144 @@ public class ChunkUtils { } EMPTY_LEVEL_CHUNK_DATA = outputStream.toByteArray(); - }catch (IOException e) { + } catch (IOException e) { throw new AssertionError("Unable to generate empty level chunk data"); } } - public static ChunkData translateToBedrock(GeyserSession session, Column column, boolean isNonFullChunk) { - ChunkData chunkData = new ChunkData(); - Chunk[] chunks = column.getChunks(); - chunkData.sections = new ChunkSection[chunks.length]; + private static int indexYZXtoXZY(int yzx) { + return (yzx >> 8) | (yzx & 0x0F0) | ((yzx & 0x00F) << 8); + } - CompoundTag[] blockEntities = column.getTileEntities(); - // Temporarily stores positions of BlockState values per chunk load - Object2IntMap blockEntityPositions = new Object2IntOpenHashMap<>(); + public static ChunkData translateToBedrock(GeyserSession session, Column column, boolean isNonFullChunk) { + Chunk[] javaSections = column.getChunks(); + ChunkSection[] sections = new ChunkSection[javaSections.length]; // Temporarily stores compound tags of Bedrock-only block entities - ObjectArrayList bedrockOnlyBlockEntities = new ObjectArrayList<>(); + List bedrockOnlyBlockEntities = Collections.emptyList(); - for (int chunkY = 0; chunkY < chunks.length; chunkY++) { - chunkData.sections[chunkY] = new ChunkSection(); - Chunk chunk = chunks[chunkY]; + BitSet waterloggedPaletteIds = new BitSet(); + BitSet pistonOrFlowerPaletteIds = new BitSet(); - // Chunk is null and caching chunks is off or this isn't a non-full chunk - if (chunk == null && (!session.getConnector().getConfig().isCacheChunks() || !isNonFullChunk)) + boolean worldManagerHasMoreBlockDataThanCache = session.getConnector().getWorldManager().hasMoreBlockDataThanChunkCache(); + + // If the received packet was a full chunk update, null sections in the chunk are guaranteed to also be null in the world manager + boolean shouldCheckWorldManagerOnMissingSections = isNonFullChunk && worldManagerHasMoreBlockDataThanCache; + Chunk temporarySection = null; + + for (int sectionY = 0; sectionY < javaSections.length; sectionY++) { + Chunk javaSection = javaSections[sectionY]; + + // Section is null, the cache will not contain anything of use + if (javaSection == null) { + // The column parameter contains all data currently available from the cache. If the chunk is null and the world manager + // reports the ability to access more data than the cache, attempt to fetch from the world manager instead. + if (shouldCheckWorldManagerOnMissingSections) { + // Ensure that temporary chunk is set + if (temporarySection == null) { + temporarySection = new Chunk(); + } + + // Read block data in section + session.getConnector().getWorldManager().getBlocksInSection(session, column.getX(), sectionY, column.getZ(), temporarySection); + + if (temporarySection.isEmpty()) { + // The world manager only contains air for the given section + // We can leave temporarySection as-is to allow it to potentially be re-used for later sections + continue; + } else { + javaSection = temporarySection; + + // Section contents have been modified, we can't re-use it + temporarySection = null; + } + } else { + continue; + } + } + + // No need to encode an empty section... + if (javaSection.isEmpty()) { continue; + } - // If chunk is empty then no need to process - if (chunk != null && chunk.isEmpty()) - continue; + Palette javaPalette = javaSection.getPalette(); + IntList bedrockPalette = new IntArrayList(javaPalette.size()); + waterloggedPaletteIds.clear(); + pistonOrFlowerPaletteIds.clear(); - ChunkSection section = chunkData.sections[chunkY]; - for (int x = 0; x < 16; x++) { - for (int y = 0; y < 16; y++) { - for (int z = 0; z < 16; z++) { - int blockState; - // If a non-full chunk, then grab the block that should be here to create a 'full' chunk - if (chunk == null) { - blockState = session.getConnector().getWorldManager().getBlockAt(session, (column.getX() << 4) + x, (chunkY << 4) + y, (column.getZ() << 4) + z); - } else { - blockState = chunk.get(x, y, z); - } - int id = BlockTranslator.getBedrockBlockId(blockState); + // Iterate through palette and convert state IDs to Bedrock, doing some additional checks as we go + for (int i = 0; i < javaPalette.size(); i++) { + int javaId = javaPalette.idToState(i); + bedrockPalette.add(BlockTranslator.getBedrockBlockId(javaId)); - // Check to see if the name is in BlockTranslator.getBlockEntityString, and therefore must be handled differently - if (BlockTranslator.getBlockEntityString(blockState) != null) { - Position pos = new ChunkPosition(column.getX(), column.getZ()).getBlock(x, (chunkY << 4) + y, z); - blockEntityPositions.put(pos, blockState); - } + if (BlockTranslator.isWaterlogged(javaId)) { + waterloggedPaletteIds.set(i); + } - section.getBlockStorageArray()[0].setFullBlock(ChunkSection.blockPosition(x, y, z), id); + // Check if block is piston or flower to see if we'll need to create additional block entities, as they're only block entities in Bedrock + if (BlockStateValues.getFlowerPotValues().containsKey(javaId) || BlockStateValues.getPistonValues().containsKey(javaId)) { + pistonOrFlowerPaletteIds.set(i); + } + } - // Check if block is piston or flower - only block entities in Bedrock - if (BlockStateValues.getFlowerPotValues().containsKey(blockState) || - BlockStateValues.getPistonValues().containsKey(blockState)) { - Position pos = new ChunkPosition(column.getX(), column.getZ()).getBlock(x, (chunkY << 4) + y, z); - bedrockOnlyBlockEntities.add(BedrockOnlyBlockEntity.getTag(Vector3i.from(pos.getX(), pos.getY(), pos.getZ()), blockState)); - } + BitStorage javaData = javaSection.getStorage(); - if (BlockTranslator.isWaterlogged(blockState)) { - section.getBlockStorageArray()[1].setFullBlock(ChunkSection.blockPosition(x, y, z), BEDROCK_WATER_ID); - } + // Add Bedrock-exclusive block entities + // We only if the palette contained any blocks that are Bedrock-exclusive block entities to avoid iterating through the whole block data + // for no reason, as most sections will not contain any pistons or flower pots + if (!pistonOrFlowerPaletteIds.isEmpty()) { + bedrockOnlyBlockEntities = new ArrayList<>(); + for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) { + int paletteId = javaData.get(yzx); + if (pistonOrFlowerPaletteIds.get(paletteId)) { + bedrockOnlyBlockEntities.add(BedrockOnlyBlockEntity.getTag( + Vector3i.from((column.getX() << 4) + (yzx & 0xF), (sectionY << 4) + ((yzx >> 8) & 0xF), (column.getZ() << 4) + ((yzx >> 4) & 0xF)), + javaPalette.idToState(paletteId) + )); } } } + BitArray bedrockData = BitArrayVersion.forBitsCeil(javaData.getBitsPerEntry()).createArray(BlockStorage.SIZE); + BlockStorage layer0 = new BlockStorage(bedrockData, bedrockPalette); + BlockStorage[] layers; + + // Convert data array from YZX to XZY coordinate order + if (waterloggedPaletteIds.isEmpty()) { + // No blocks are waterlogged, simply convert coordinate order + // This could probably be optimized further... + for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) { + bedrockData.set(indexYZXtoXZY(yzx), javaData.get(yzx)); + } + + layers = new BlockStorage[]{ layer0 }; + } else { + // The section contains waterlogged blocks, we need to convert coordinate order AND generate a V1 block storage for + // layer 1 with palette ID 1 indicating water + int[] layer1Data = new int[BlockStorage.SIZE >> 5]; + for (int yzx = 0; yzx < BlockStorage.SIZE; yzx++) { + int paletteId = javaData.get(yzx); + int xzy = indexYZXtoXZY(yzx); + bedrockData.set(xzy, paletteId); + + if (waterloggedPaletteIds.get(paletteId)) { + layer1Data[xzy >> 5] |= 1 << (xzy & 0x1F); + } + } + + // V1 palette + IntList layer1Palette = new IntArrayList(2); + layer1Palette.add(0); // Air + layer1Palette.add(BEDROCK_WATER_ID); + + layers = new BlockStorage[]{ layer0, new BlockStorage(BitArrayVersion.V1.createArray(BlockStorage.SIZE, layer1Data), layer1Palette) }; + } + + sections[sectionY] = new ChunkSection(layers); } + CompoundTag[] blockEntities = column.getTileEntities(); NbtMap[] bedrockBlockEntities = new NbtMap[blockEntities.length + bedrockOnlyBlockEntities.size()]; int i = 0; while (i < blockEntities.length) { @@ -155,7 +244,7 @@ public class ChunkUtils { for (Tag subTag : tag) { if (subTag instanceof StringTag) { StringTag stringTag = (StringTag) subTag; - if (stringTag.getValue().equals("")) { + if (stringTag.getValue().isEmpty()) { tagName = stringTag.getName(); break; } @@ -169,17 +258,25 @@ public class ChunkUtils { String id = BlockEntityUtils.getBedrockBlockEntityId(tagName); BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(id); Position pos = new Position((int) tag.get("x").getValue(), (int) tag.get("y").getValue(), (int) tag.get("z").getValue()); - int blockState = blockEntityPositions.getOrDefault(pos, 0); + + // Get Java blockstate ID from block entity position + int blockState = 0; + Chunk section = column.getChunks()[pos.getY() >> 4]; + if (section != null) { + blockState = section.get(pos.getX() & 0xF, pos.getY() & 0xF, pos.getZ() & 0xF); + } + bedrockBlockEntities[i] = blockEntityTranslator.getBlockEntityTag(tagName, tag, blockState); i++; } + + // Append Bedrock-exclusive block entities to output array for (NbtMap tag : bedrockOnlyBlockEntities) { bedrockBlockEntities[i] = tag; i++; } - chunkData.blockEntities = bedrockBlockEntities; - return chunkData; + return new ChunkData(sections, bedrockBlockEntities); } public static void updateChunkPosition(GeyserSession session, Vector3i position) { @@ -277,10 +374,10 @@ public class ChunkUtils { } } + @Data public static final class ChunkData { - public ChunkSection[] sections; + private final ChunkSection[] sections; - @Getter - private NbtMap[] blockEntities = new NbtMap[0]; + private final NbtMap[] blockEntities; } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java b/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java index 29dd2cc23..3ce4fea86 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/MathUtils.java @@ -74,4 +74,15 @@ public class MathUtils { } return (Byte) value; } + + /** + * Packs a chunk's X and Z coordinates into a single {@code long}. + * + * @param x the X coordinate + * @param z the Z coordinate + * @return the packed coordinates + */ + public static long chunkPositionToLong(int x, int z) { + return ((x & 0xFFFFFFFFL) << 32L) | (z & 0xFFFFFFFFL); + } } From 64f223358183723c82eb30e3aaf076fbcdef530d Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Thu, 15 Oct 2020 01:54:05 -0500 Subject: [PATCH 132/140] Fix wolf collar color when it's no longer angry (Closes #1404) --- .../connector/entity/living/animal/tameable/WolfEntity.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java index ab631ebe5..6fe9e5927 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java @@ -34,6 +34,8 @@ import org.geysermc.connector.network.session.GeyserSession; public class WolfEntity extends TameableEntity { + private byte collarColor; + public WolfEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); } @@ -57,12 +59,13 @@ public class WolfEntity extends TameableEntity { // Wolf collar color // Relies on EntityData.OWNER_EID being set in TameableEntity.java if (entityMetadata.getId() == 19 && !metadata.getFlags().getFlag(EntityFlag.ANGRY)) { - metadata.put(EntityData.COLOR, (byte) (int) entityMetadata.getValue()); + metadata.put(EntityData.COLOR, collarColor = (byte) (int) entityMetadata.getValue()); } // Wolf anger (1.16+) if (entityMetadata.getId() == 20) { metadata.getFlags().setFlag(EntityFlag.ANGRY, (int) entityMetadata.getValue() != 0); + metadata.put(EntityData.COLOR, (int) entityMetadata.getValue() != 0 ? (byte) 0 : collarColor); } super.updateBedrockMetadata(entityMetadata, session); From ae70dbeece0094814b80ed2edd9e39add53b033d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Sat, 17 Oct 2020 23:13:04 -0400 Subject: [PATCH 133/140] JavaOpenWindowTranslator: Use MessageUtils for inventory name (#1416) * JavaOpenWindowTranslator: Use MessageUtils for inventory name * Remove important messaging --- .../java/window/JavaOpenWindowTranslator.java | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java index 820639b3d..099de3170 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/window/JavaOpenWindowTranslator.java @@ -25,11 +25,9 @@ package org.geysermc.connector.network.translators.java.window; +import com.github.steveice10.mc.protocol.data.message.MessageSerializer; import com.github.steveice10.mc.protocol.packet.ingame.client.window.ClientCloseWindowPacket; import com.github.steveice10.mc.protocol.packet.ingame.server.window.ServerOpenWindowPacket; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.inventory.Inventory; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; @@ -37,6 +35,7 @@ import org.geysermc.connector.network.translators.Translator; import org.geysermc.connector.network.translators.inventory.InventoryTranslator; import org.geysermc.connector.utils.InventoryUtils; import org.geysermc.connector.utils.LocaleUtils; +import org.geysermc.connector.utils.MessageUtils; @Translator(packet = ServerOpenWindowPacket.class) public class JavaOpenWindowTranslator extends PacketTranslator { @@ -58,18 +57,8 @@ public class JavaOpenWindowTranslator extends PacketTranslator Date: Sat, 17 Oct 2020 23:50:41 -0400 Subject: [PATCH 134/140] Fix mob mount positions (#1392) * Fix mob mount positions Uses offsets from Java Edition. * Fix Boat mount pos for multiple passengers and Fix Ravager Remove unnecessary horse metadata * Fix Minecart & Boat Mount Pos, Fix Player Height Offset * Use offset of EntityType.PLAYER * Add back metadata --- .../JavaEntitySetPassengersTranslator.java | 170 ++++++++++++++---- 1 file changed, 133 insertions(+), 37 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntitySetPassengersTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntitySetPassengersTranslator.java index 094d64df7..64f0e3e98 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntitySetPassengersTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntitySetPassengersTranslator.java @@ -82,7 +82,6 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator 1)); rider = false; } @@ -90,6 +89,9 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator 1)); + this.updateOffset(passenger, entity, session, false, false, (packet.getPassengerIds().length > 1)); + } else { + this.updateOffset(passenger, entity, session, (packet.getPassengerIds()[0] == passengerId), true, (packet.getPassengerIds().length > 1)); } // Force an update to the passenger metadata passenger.updateBedrockMetadata(session); } - if (entity.getEntityType() == EntityType.HORSE) { - entity.getMetadata().put(EntityData.RIDER_SEAT_POSITION, Vector3f.from(0.0f, 2.3200102f, -0.2f)); - entity.getMetadata().put(EntityData.RIDER_MAX_ROTATION, 181.0f); - - entity.updateBedrockMetadata(session); + switch (entity.getEntityType()) { + case HORSE: + case SKELETON_HORSE: + case DONKEY: + case MULE: + case RAVAGER: + entity.getMetadata().put(EntityData.RIDER_MAX_ROTATION, 181.0f); + entity.updateBedrockMetadata(session); + break; } } - private void updateOffset(Entity passenger, EntityType mountType, GeyserSession session, boolean rider, boolean riding, boolean moreThanOneEntity) { - // Without the Y offset, Bedrock players will find themselves in the floor when mounting - float yOffset = 0; + private float getMountedHeightOffset(Entity mount) { + final EntityType mountType = mount.getEntityType(); + float mountedHeightOffset = mountType.getHeight() * 0.75f; switch (mountType) { - case BOAT: - yOffset = passenger.getEntityType() == EntityType.PLAYER ? 1.02001f : -0.2f; - break; - case MINECART: - yOffset = passenger.getEntityType() == EntityType.PLAYER ? 1.02001f : 0f; + case CHICKEN: + case SPIDER: + mountedHeightOffset = mountType.getHeight() * 0.5f; break; case DONKEY: - yOffset = 2.1f; - break; - case HORSE: - case SKELETON_HORSE: - case ZOMBIE_HORSE: case MULE: - yOffset = 2.3f; + mountedHeightOffset -= 0.25f; break; case LLAMA: - case TRADER_LLAMA: - yOffset = 2.5f; + mountedHeightOffset = mountType.getHeight() * 0.67f; break; - case PIG: - yOffset = 1.85001f; + case MINECART: + case MINECART_HOPPER: + case MINECART_TNT: + case MINECART_CHEST: + case MINECART_FURNACE: + case MINECART_SPAWNER: + case MINECART_COMMAND_BLOCK: + mountedHeightOffset = 0; break; - case ARMOR_STAND: - yOffset = 1.3f; + case BOAT: + mountedHeightOffset = -0.1f; + break; + case HOGLIN: + case ZOGLIN: + boolean isBaby = mount.getMetadata().getFlags().getFlag(EntityFlag.BABY); + mountedHeightOffset = mountType.getHeight() - (isBaby ? 0.2f : 0.15f); + break; + case PIGLIN: + mountedHeightOffset = mountType.getHeight() * 0.92f; + break; + case RAVAGER: + mountedHeightOffset = 2.1f; + break; + case SKELETON_HORSE: + mountedHeightOffset -= 0.1875f; break; case STRIDER: - yOffset = passenger.getEntityType() == EntityType.PLAYER ? 2.8200102f : 1.6f; + mountedHeightOffset = mountType.getHeight() - 0.19f; break; } - Vector3f offset = Vector3f.from(0f, yOffset, 0f); - if (mountType == EntityType.STRIDER) { - offset = offset.add(0f, 0f, -0.2f); - } - // Without the X offset, more than one entity on a boat is stacked on top of each other - if (rider && moreThanOneEntity) { - offset = offset.add(Vector3f.from(0.2, 0, 0)); - } else if (moreThanOneEntity) { - offset = offset.add(Vector3f.from(-0.6, 0, 0)); + return mountedHeightOffset; + } + + private float getHeightOffset(Entity passenger) { + boolean isBaby; + switch (passenger.getEntityType()) { + case SKELETON: + case STRAY: + case WITHER_SKELETON: + return -0.6f; + case ARMOR_STAND: + // Armor stand isn't a marker + if (passenger.getMetadata().getFloat(EntityData.BOUNDING_BOX_HEIGHT) != 0.0f) { + return 0.1f; + } else { + return 0.0f; + } + case ENDERMITE: + case SILVERFISH: + return 0.1f; + case PIGLIN: + case PIGLIN_BRUTE: + case ZOMBIFIED_PIGLIN: + isBaby = passenger.getMetadata().getFlags().getFlag(EntityFlag.BABY); + return isBaby ? -0.05f : -0.45f; + case ZOMBIE: + isBaby = passenger.getMetadata().getFlags().getFlag(EntityFlag.BABY); + return isBaby ? 0.0f : -0.45f; + case EVOKER: + case ILLUSIONER: + case PILLAGER: + case RAVAGER: + case VINDICATOR: + case WITCH: + return -0.45f; + case PLAYER: + return -0.35f; } + return 0f; + } + + private void updateOffset(Entity passenger, Entity mount, GeyserSession session, boolean rider, boolean riding, boolean moreThanOneEntity) { passenger.getMetadata().getFlags().setFlag(EntityFlag.RIDING, riding); if (riding) { + // Without the Y offset, Bedrock players will find themselves in the floor when mounting + float mountedHeightOffset = getMountedHeightOffset(mount); + float heightOffset = getHeightOffset(passenger); + + float xOffset = 0; + float yOffset = mountedHeightOffset + heightOffset; + float zOffset = 0; + switch (mount.getEntityType()) { + case BOAT: + // Without the X offset, more than one entity on a boat is stacked on top of each other + if (rider && moreThanOneEntity) { + xOffset = 0.2f; + } else if (moreThanOneEntity) { + xOffset = -0.6f; + } + break; + case CHICKEN: + zOffset = -0.1f; + break; + case LLAMA: + zOffset = -0.3f; + break; + } + /* + * Bedrock Differences + * Zoglin & Hoglin seem to be taller in Bedrock edition + * Horses are tinier + * Players, Minecarts, and Boats have different origins + */ + if (passenger.getEntityType() == EntityType.PLAYER) { + yOffset += EntityType.PLAYER.getOffset(); + } + switch (mount.getEntityType()) { + case MINECART: + case MINECART_HOPPER: + case MINECART_TNT: + case MINECART_CHEST: + case MINECART_FURNACE: + case MINECART_SPAWNER: + case MINECART_COMMAND_BLOCK: + case BOAT: + yOffset -= mount.getEntityType().getHeight() * 0.5f; + } + Vector3f offset = Vector3f.from(xOffset, yOffset, zOffset); passenger.getMetadata().put(EntityData.RIDER_SEAT_POSITION, offset); } passenger.updateBedrockMetadata(session); From 0635605a24448ccec5b0498275a03038ba073025 Mon Sep 17 00:00:00 2001 From: DaPorkchop_ Date: Sun, 18 Oct 2020 16:59:37 +0200 Subject: [PATCH 135/140] fix chunk section decoding (#1418) * fix chunk section decoding * switch back to official MCProtocolLib --- connector/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/pom.xml b/connector/pom.xml index 5df525567..cf2b89948 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -111,7 +111,7 @@ com.github.steveice10 mcprotocollib - 3a69a0614c + 8270ec65e3 compile From 45429a9357657024da2c914cb57220fe14ee650f Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Sun, 18 Oct 2020 23:29:11 -0400 Subject: [PATCH 136/140] SettingsUtils: fix 'show coordinates' setting persistence (#1429) The boolean that toggled this was accidentally in the wrong spot. --- .../main/java/org/geysermc/connector/utils/SettingsUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java index 89e9fe67b..13db4682c 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java @@ -58,7 +58,7 @@ public class SettingsUtils { builder.setIcon(new FormImage(FormImage.FormImageType.PATH, "textures/ui/settings_glyph_color_2x.png")); builder.addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.title.client", language))); - builder.addComponent(new ToggleComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.option.coordinates", language, session.getWorldCache().isShowCoordinates()))); + builder.addComponent(new ToggleComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.option.coordinates", language), session.getWorldCache().isShowCoordinates())); if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) { From 18e2a52d98dd833a362730ceb966494a4a2e5298 Mon Sep 17 00:00:00 2001 From: DaPorkchop_ Date: Mon, 19 Oct 2020 10:43:51 +0200 Subject: [PATCH 137/140] fix decoding sections with duplicate palette entries (#1430) --- connector/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/connector/pom.xml b/connector/pom.xml index cf2b89948..8615ad801 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -111,7 +111,7 @@ com.github.steveice10 mcprotocollib - 8270ec65e3 + 1b01b1ffef compile From b02bc33393788c59d30a8ab30f229bee07aaac12 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 19 Oct 2020 19:03:31 -0400 Subject: [PATCH 138/140] GeyserSession: Set a default value for attackSpeed (#1419) Fixes cooldowns not showing on a fresh world. --- .../org/geysermc/connector/network/session/GeyserSession.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 1a0bbfb29..79949a5fa 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 @@ -194,9 +194,10 @@ public class GeyserSession implements CommandSender { /** * The current attack speed of the player. Used for sending proper cooldown timings. + * Setting a default fixes cooldowns not showing up on a fresh world. */ @Setter - private double attackSpeed; + private double attackSpeed = 4.0d; /** * The time of the last hit. Used to gauge how long the cooldown is taking. * This is a session variable in order to prevent more scheduled threads than necessary. From 7f5fac38c6466b1ad33e85def64dc9c245160135 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+DoctorMacc@users.noreply.github.com> Date: Mon, 19 Oct 2020 19:09:16 -0400 Subject: [PATCH 139/140] Update to Adventure 4.1.1 (#1410) * Update to Adventure 4.0.0 * Update to 4.0.1 * Update again, I guess. --- connector/pom.xml | 12 ++++++------ .../item/translators/nbt/BasicItemTranslator.java | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/connector/pom.xml b/connector/pom.xml index 8615ad801..197319875 100644 --- a/connector/pom.xml +++ b/connector/pom.xml @@ -137,21 +137,21 @@ 2.1.3 - com.github.kyoripowered.adventure + net.kyori adventure-api - 557865caef + 4.1.1 compile - com.github.kyoripowered.adventure + net.kyori adventure-text-serializer-gson - 557865caef + 4.1.1 compile - com.github.kyoripowered.adventure + net.kyori adventure-text-serializer-legacy - 557865caef + 4.1.1 compile diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java index e45566264..1d21bbfb7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/BasicItemTranslator.java @@ -101,7 +101,7 @@ public class BasicItemTranslator extends NbtItemStackTranslator { if (message.startsWith("§r")) { message = message.replaceFirst("§r", ""); } - Component component = TextComponent.of(message); + Component component = Component.text(message); return GsonComponentSerializer.gson().serialize(component); } From 62d984da611b6cada4a6813a0f2655001e2a3fc8 Mon Sep 17 00:00:00 2001 From: Redned Date: Mon, 19 Oct 2020 20:56:01 -0500 Subject: [PATCH 140/140] Make userAuths information more clear --- connector/src/main/resources/config.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index 0602bb546..43e3e8edc 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -38,18 +38,18 @@ remote: # You can ignore this when not using Floodgate. floodgate-key-file: public-key.pem -## the Xbox/MCPE username is the key for the Java server auth-info -## this allows automatic configuration/login to the remote Java server -## if you are brave/stupid enough to put your Mojang account info into -## a config file +# The Xbox/Minecraft Bedrock username is the key for the Java server auth-info. +# This allows automatic configuration/login to the remote Java server. +# If you are brave enough to put your Mojang account info into a config file. +# Uncomment the lines below to enable this feature. #userAuths: -# bluerkelp2: # MCPE/Xbox username -# email: not_really_my_email_address_mr_minecrafter53267@gmail.com # Mojang account email address -# password: "this isn't really my password" +# BedrockAccountUsername: # Your Minecraft: Bedrock Edition username +# email: javaccountemail@example.com # Your Minecraft: Java Edition email +# password: javaccountpassword123 # Your Minecraft: Java Edition password # -# herpderp40300499303040503030300500293858393589: -# email: herpderp@derpherp.com -# password: dooooo +# bluerkelp2: +# email: not_really_my_email_address_mr_minecrafter53267@gmail.com +# password: "this isn't really my password" # Bedrock clients can freeze when opening up the command prompt for the first time if given a lot of commands. # Disabling this will prevent command suggestions from being sent and solve freezing for Bedrock clients.