From efe8077093f27b76172df733c8eba6ed006512e4 Mon Sep 17 00:00:00 2001 From: chris Date: Sun, 25 Aug 2024 21:31:03 +0200 Subject: [PATCH 01/18] Feature: Modrinth version names (#4989) * Feature: Version names on modrinth published builds * Also change the fabric/neoforge jar file names --- build-logic/src/main/kotlin/extensions.kt | 9 +++++++++ .../src/main/kotlin/geyser.modded-conventions.gradle.kts | 2 +- .../geyser.modrinth-uploading-conventions.gradle.kts | 3 ++- core/build.gradle.kts | 5 +---- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/build-logic/src/main/kotlin/extensions.kt b/build-logic/src/main/kotlin/extensions.kt index 41e11344b..1b81f6601 100644 --- a/build-logic/src/main/kotlin/extensions.kt +++ b/build-logic/src/main/kotlin/extensions.kt @@ -118,3 +118,12 @@ open class DownloadFilesTask : DefaultTask() { private fun calcExclusion(section: String, bit: Int, excludedOn: Int): String = if (excludedOn and bit > 0) section else "" +fun projectVersion(project: Project): String = + project.version.toString().replace("SNAPSHOT", "b" + buildNumber()) + +fun versionName(project: Project): String = + "Geyser-" + project.name.replaceFirstChar { it.uppercase() } + "-" + projectVersion(project) + +fun buildNumber(): Int = + (System.getenv("BUILD_NUMBER"))?.let { Integer.parseInt(it) } ?: -1 + diff --git a/build-logic/src/main/kotlin/geyser.modded-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.modded-conventions.gradle.kts index 8a6602778..8584c13d4 100644 --- a/build-logic/src/main/kotlin/geyser.modded-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.modded-conventions.gradle.kts @@ -87,7 +87,7 @@ tasks { register("remapModrinthJar", RemapJarTask::class) { dependsOn(shadowJar) inputFile.set(shadowJar.get().archiveFile) - archiveVersion.set(project.version.toString() + "+build." + System.getenv("BUILD_NUMBER")) + archiveVersion.set(versionName(project)) archiveClassifier.set("") } } diff --git a/build-logic/src/main/kotlin/geyser.modrinth-uploading-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.modrinth-uploading-conventions.gradle.kts index fe2284137..d2e207fa4 100644 --- a/build-logic/src/main/kotlin/geyser.modrinth-uploading-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.modrinth-uploading-conventions.gradle.kts @@ -8,7 +8,8 @@ tasks.modrinth.get().dependsOn(tasks.modrinthSyncBody) modrinth { token.set(System.getenv("MODRINTH_TOKEN") ?: "") // Even though this is the default value, apparently this prevents GitHub Actions caching the token? projectId.set("geyser") - versionNumber.set(project.version as String + "-" + System.getenv("BUILD_NUMBER")) + versionName.set(versionName(project)) + versionNumber.set(projectVersion(project)) versionType.set("beta") changelog.set(System.getenv("CHANGELOG") ?: "") gameVersions.addAll("1.21", libs.minecraft.get().version as String) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index acd6c5147..8d022271b 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -103,9 +103,6 @@ sourceSets { } } -fun buildNumber(): Int = - (System.getenv("BUILD_NUMBER"))?.let { Integer.parseInt(it) } ?: -1 - fun isDevBuild(branch: String, repository: String): Boolean { return branch != "master" || repository.equals("https://github.com/GeyserMC/Geyser", ignoreCase = true).not() } @@ -139,7 +136,7 @@ inner class GitInfo { buildNumber = buildNumber() isDev = isDevBuild(branch, repository) - val projectVersion = if (isDev) project.version else project.version.toString().replace("SNAPSHOT", "b${buildNumber}") + val projectVersion = if (isDev) project.version else projectVersion(project) version = "$projectVersion ($gitVersion)" } } From 3be9b8a183aa2fe798b6b8d19e95eb74ba096f0a Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Mon, 26 Aug 2024 00:48:42 -0400 Subject: [PATCH 02/18] Fix moving items from output slot over multiple actions (#4993) * Fix moving items from output slot over multiple actions * Refactor and use temp slot * Ensure source slot is not cursor --- .../geyser/inventory/click/ClickPlan.java | 21 +++ .../inventory/InventoryTranslator.java | 146 ++++++++++++++---- 2 files changed, 137 insertions(+), 30 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java index 53b02ef88..9d6f4d3e3 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/click/ClickPlan.java @@ -162,6 +162,27 @@ public final class ClickPlan { finished = true; } + public Inventory getInventory() { + return inventory; + } + + /** + * Test if the item stacks with another item in the specified slot. + * This will check the simulated inventory without copying. + */ + public boolean canStack(int slot, GeyserItemStack item) { + GeyserItemStack slotItem = simulatedItems.getOrDefault(slot, inventory.getItem(slot)); + return InventoryUtils.canStack(slotItem, item); + } + + /** + * Test if the specified slot is empty. + * This will check the simulated inventory without copying. + */ + public boolean isEmpty(int slot) { + return simulatedItems.getOrDefault(slot, inventory.getItem(slot)).isEmpty(); + } + public GeyserItemStack getItem(int slot) { return simulatedItems.computeIfAbsent(slot, k -> inventory.getItem(slot).copy()); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java index 546ebda19..afa11c982 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/InventoryTranslator.java @@ -201,6 +201,9 @@ public abstract class InventoryTranslator { public ItemStackResponse translateRequest(GeyserSession session, Inventory inventory, ItemStackRequest request) { ClickPlan plan = new ClickPlan(session, this, inventory); IntSet affectedSlots = new IntOpenHashSet(); + int pendingOutput = 0; + int savedTempSlot = -1; + for (ItemStackRequestAction action : request.getActions()) { GeyserItemStack cursor = session.getPlayerInventory().getCursor(); switch (action.getType()) { @@ -241,6 +244,65 @@ public abstract class InventoryTranslator { return rejectRequest(request, false); } + // Handle partial transfer of output slot + if (pendingOutput == 0 && !isSourceCursor && getSlotType(sourceSlot) == SlotType.OUTPUT + && transferAction.getCount() < plan.getItem(sourceSlot).getAmount()) { + // Cursor as dest should always be full transfer. + if (isDestCursor) { + return rejectRequest(request); + } + + if (!plan.getCursor().isEmpty()) { + savedTempSlot = findTempSlot(plan, plan.getCursor(), true); + if (savedTempSlot == -1) { + return rejectRequest(request); + } + plan.add(Click.LEFT, savedTempSlot); + } + + // Pickup entire stack from output + pendingOutput = plan.getItem(sourceSlot).getAmount(); + plan.add(Click.LEFT, sourceSlot); + } + + // Continue transferring items from output that is currently stored in the cursor + if (pendingOutput > 0) { + if (isSourceCursor || getSlotType(sourceSlot) != SlotType.OUTPUT + || transferAction.getCount() > pendingOutput + || destSlot == savedTempSlot + || isDestCursor) { + return rejectRequest(request); + } + + // Make sure item can be placed here + GeyserItemStack destItem = plan.getItem(destSlot); + if (!destItem.isEmpty() && !InventoryUtils.canStack(destItem, plan.getCursor())) { + return rejectRequest(request); + } + + // TODO: Optimize using max stack size + if (pendingOutput == transferAction.getCount()) { + plan.add(Click.LEFT, destSlot); + } else { + for (int i = 0; i < transferAction.getCount(); i++) { + plan.add(Click.RIGHT, destSlot); + } + } + + pendingOutput -= transferAction.getCount(); + if (pendingOutput != plan.getCursor().getAmount()) { + return rejectRequest(request); + } + + if (pendingOutput == 0 && savedTempSlot != -1) { + plan.add(Click.LEFT, savedTempSlot); + savedTempSlot = -1; + } + + // Skip to next action + continue; + } + if (isSourceCursor && isDestCursor) { //??? return rejectRequest(request); } else if (isSourceCursor) { //releasing cursor @@ -271,7 +333,7 @@ public abstract class InventoryTranslator { return rejectRequest(request); } if (transferAction.getCount() != sourceAmount) { - int tempSlot = findTempSlot(inventory, cursor, false, sourceSlot); + int tempSlot = findTempSlot(plan, cursor, false, sourceSlot); if (tempSlot == -1) { return rejectRequest(request); } @@ -292,7 +354,7 @@ public abstract class InventoryTranslator { } else { //transfer from one slot to another int tempSlot = -1; if (!plan.getCursor().isEmpty()) { - tempSlot = findTempSlot(inventory, cursor, false, sourceSlot, destSlot); + tempSlot = findTempSlot(plan, cursor, getSlotType(sourceSlot) != SlotType.NORMAL, sourceSlot, destSlot); if (tempSlot == -1) { return rejectRequest(request); } @@ -440,6 +502,11 @@ public abstract class InventoryTranslator { return rejectRequest(request); } } + + if (pendingOutput != 0) { + return rejectRequest(request); + } + plan.execute(false); affectedSlots.addAll(plan.getAffectedSlots()); return acceptRequest(request, makeContainerEntries(session, inventory, affectedSlots)); @@ -536,7 +603,7 @@ public abstract class InventoryTranslator { } } else { GeyserItemStack cursor = session.getPlayerInventory().getCursor(); - int tempSlot = findTempSlot(inventory, cursor, true, sourceSlot, destSlot); + int tempSlot = findTempSlot(plan, cursor, true, sourceSlot, destSlot); if (tempSlot == -1) { return rejectRequest(request); } @@ -699,7 +766,7 @@ public abstract class InventoryTranslator { int javaSlot = bedrockSlotToJava(transferAction.getDestination()); if (isCursor(transferAction.getDestination())) { //TODO if (timesCrafted > 1) { - tempSlot = findTempSlot(inventory, GeyserItemStack.from(output), true); + tempSlot = findTempSlot(plan, GeyserItemStack.from(output), true); if (tempSlot == -1) { return rejectRequest(request); } @@ -836,49 +903,68 @@ public abstract class InventoryTranslator { } /** - * Try to find a slot that can temporarily store the given item. + * Try to find a slot that is preferably empty, or does not stack with a given item. * Only looks in the main inventory and hotbar (excluding offhand). - * Only slots that are empty or contain a different type of item are valid. + *

+ * Slots are searched in the reverse order that the bedrock client uses for quick moving. * - * @return java id for the temporary slot, or -1 if no viable slot was found + * @param plan used to check the simulated inventory + * @param item the item to temporarily store + * @param emptyOnly if only empty slots should be considered + * @param slotBlacklist list of slots to exclude; the items contained in these slots will also be checked for stacking + * @return the temp slot, or -1 if no suitable slot was found */ - //TODO: compatibility for simulated inventory (ClickPlan) - private static int findTempSlot(Inventory inventory, GeyserItemStack item, boolean emptyOnly, int... slotBlacklist) { - int offset = inventory.getJavaId() == 0 ? 1 : 0; //offhand is not a viable temp slot - HashSet itemBlacklist = new HashSet<>(slotBlacklist.length + 1); - itemBlacklist.add(item); + private static int findTempSlot(ClickPlan plan, GeyserItemStack item, boolean emptyOnly, int... slotBlacklist) { + IntSortedSet potentialSlots = new IntLinkedOpenHashSet(PLAYER_INVENTORY_SIZE); + int hotbarOffset = plan.getInventory().getOffsetForHotbar(0); - IntSet potentialSlots = new IntOpenHashSet(36); - for (int i = inventory.getSize() - (36 + offset); i < inventory.getSize() - offset; i++) { + // Add main inventory slots in reverse + for (int i = hotbarOffset - 1; i >= hotbarOffset - 27; i--) { potentialSlots.add(i); } + + // Add hotbar slots in reverse + for (int i = hotbarOffset + 8; i >= hotbarOffset; i--) { + potentialSlots.add(i); + } + for (int i : slotBlacklist) { potentialSlots.remove(i); - GeyserItemStack blacklistedItem = inventory.getItem(i); - if (!blacklistedItem.isEmpty()) { - itemBlacklist.add(blacklistedItem); + } + + // Prefer empty slots + IntIterator it = potentialSlots.iterator(); + while (it.hasNext()) { + int slot = it.nextInt(); + if (plan.isEmpty(slot)) { + return slot; } } - for (int i : potentialSlots) { - GeyserItemStack testItem = inventory.getItem(i); - if ((emptyOnly && !testItem.isEmpty())) { + if (emptyOnly) { + return -1; + } + + // No empty slots. Look for a slot that does not stack + it = potentialSlots.iterator(); + + outer: + while (it.hasNext()) { + int slot = it.nextInt(); + if (plan.canStack(slot, item)) { continue; } - boolean viable = true; - for (GeyserItemStack blacklistedItem : itemBlacklist) { - if (InventoryUtils.canStack(testItem, blacklistedItem)) { - viable = false; - break; + for (int blacklistedSlot : slotBlacklist) { + GeyserItemStack blacklistedItem = plan.getItem(blacklistedSlot); + if (plan.canStack(slot, blacklistedItem)) { + continue outer; } } - if (!viable) { - continue; - } - return i; + + return slot; } - //could not find a viable temp slot + return -1; } From 46b2c032154502378c2dcd20054703cc69deeae2 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Tue, 27 Aug 2024 05:37:01 -0400 Subject: [PATCH 03/18] Fix rare netid desync when crafting (#4997) --- .../geyser/translator/inventory/PlayerInventoryTranslator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java index bc6ff2adf..21f45a5ca 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/inventory/PlayerInventoryTranslator.java @@ -123,7 +123,7 @@ public class PlayerInventoryTranslator extends InventoryTranslator { if (session.getGameMode() == GameMode.CREATIVE) { slotPacket.setItem(UNUSUABLE_CRAFTING_SPACE_BLOCK.apply(session.getUpstream().getProtocolVersion())); } else { - slotPacket.setItem(ItemTranslator.translateToBedrock(session, inventory.getItem(i).getItemStack())); + slotPacket.setItem(inventory.getItem(i).getItemData(session)); } session.sendUpstreamPacket(slotPacket); From 8356b63f5d52e8f8e61a7d0af71c924843503083 Mon Sep 17 00:00:00 2001 From: chris Date: Tue, 27 Aug 2024 18:40:22 +0200 Subject: [PATCH 04/18] Fix: Structure blocks/voids in recipes (#4999) * Fix: Structure blocks/voids in recipes * add gh issue link --- .../geyser/registry/populator/ItemRegistryPopulator.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java index f11b58bfe..55d44c3d9 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java @@ -171,6 +171,11 @@ public class ItemRegistryPopulator { List creativeItems = new ArrayList<>(); Set noBlockDefinitions = new ObjectOpenHashSet<>(); + // Fix: Usage of structure blocks/voids in recipes + // https://github.com/GeyserMC/Geyser/issues/2890 + noBlockDefinitions.add("minecraft:structure_block"); + noBlockDefinitions.add("minecraft:structure_void"); + AtomicInteger creativeNetId = new AtomicInteger(); CreativeItemRegistryPopulator.populate(palette, definitions, itemBuilder -> { ItemData item = itemBuilder.netId(creativeNetId.incrementAndGet()).build(); From 1b17c6bd8e03c8ebc9c8949b6ea7a5888e6dea43 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Thu, 29 Aug 2024 14:50:26 -0400 Subject: [PATCH 05/18] Fix loom usage, and disconnect messages for all outdated clients (#5006) --- .../geysermc/geyser/network/UpstreamPacketHandler.java | 9 +++++---- gradle/libs.versions.toml | 6 +++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java index e9c979b0c..adcaa2505 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -120,10 +120,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { session.disconnect(disconnectMessage); return false; } else if (protocolVersion < GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) { - if (protocolVersion < Bedrock_v622.CODEC.getProtocolVersion()) { - // https://github.com/GeyserMC/Geyser/issues/4378 - session.getUpstream().getSession().setCodec(BedrockCompat.CODEC_LEGACY); - } + // A note on the following line: various older client versions have different forms of DisconnectPacket. + // Using only the latest BedrockCompat for such clients leads to inaccurate disconnect messages: https://github.com/GeyserMC/Geyser/issues/4378 + // This updates the BedrockCompat protocol if necessary: + session.getUpstream().getSession().setCodec(BedrockCompat.disconnectCompat(protocolVersion)); + session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.outdated.client", supportedVersions)); return false; } else { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f49de8d20..c564720a3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,9 +10,9 @@ netty-io-uring = "0.0.25.Final-SNAPSHOT" guava = "29.0-jre" gson = "2.3.1" # Provided by Spigot 1.8.8 websocket = "1.5.1" -protocol-connection = "3.0.0.Beta3-20240819.124045-12" -protocol-common = "3.0.0.Beta3-20240819.124045-10" -protocol-codec = "3.0.0.Beta3-20240819.124045-13" +protocol-connection = "3.0.0.Beta4-20240828.162251-1" +protocol-common = "3.0.0.Beta4-20240828.162251-1" +protocol-codec = "3.0.0.Beta4-20240828.162251-1" raknet = "1.0.0.CR3-20240416.144209-1" minecraftauth = "4.1.1-20240806.235051-7" mcprotocollib = "1.21-20240725.013034-16" From 63e60bc93cfc50bdaafafde6f39f6ebbe3f3d93c Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Thu, 29 Aug 2024 21:09:36 -0400 Subject: [PATCH 06/18] Allow Bedrock players to sleep on Fabric Fixes #5001 --- .../entity/player/BedrockMovePlayerTranslator.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java index 3faa3242b..6b403a99b 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java @@ -58,6 +58,14 @@ public class BedrockMovePlayerTranslator extends PacketTranslator Date: Fri, 30 Aug 2024 22:16:45 -0400 Subject: [PATCH 07/18] Ensure players aren't floating in their beds; improve Bedrock sleeping notifications --- .../entity/type/player/PlayerEntity.java | 14 +++++- .../type/player/SessionPlayerEntity.java | 2 +- .../geyser/network/UpstreamPacketHandler.java | 1 - .../player/BedrockMovePlayerTranslator.java | 2 +- .../java/JavaSystemChatTranslator.java | 43 ++++++++++++++++--- 5 files changed, 50 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java index 4c67b882f..b326f2e04 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java @@ -42,7 +42,11 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; -import org.cloudburstmc.protocol.bedrock.packet.*; +import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket; +import org.cloudburstmc.protocol.bedrock.packet.MovePlayerPacket; +import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket; +import org.cloudburstmc.protocol.bedrock.packet.SetEntityLinkPacket; +import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket; import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.attribute.GeyserAttributeType; @@ -278,7 +282,13 @@ public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity { @Override public void setPosition(Vector3f position) { - super.setPosition(position.add(0, definition.offset(), 0)); + if (this.bedPosition != null) { + // As of Bedrock 1.21.22 and Fabric 1.21.1 + // Messes with Bedrock if we send this to the client itself, though. + super.setPosition(position.up(0.2f)); + } else { + super.setPosition(position.add(0, definition.offset(), 0)); + } } @Override diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java index ccf2d25e6..f427b001a 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/SessionPlayerEntity.java @@ -140,7 +140,7 @@ public class SessionPlayerEntity extends PlayerEntity { if (valid) { // Don't update during session init session.getCollisionManager().updatePlayerBoundingBox(position); } - super.setPosition(position); + this.position = position.add(0, definition.offset(), 0); } /** diff --git a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java index adcaa2505..5c48df1f9 100644 --- a/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/network/UpstreamPacketHandler.java @@ -29,7 +29,6 @@ import io.netty.buffer.Unpooled; import org.cloudburstmc.protocol.bedrock.BedrockDisconnectReasons; import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; import org.cloudburstmc.protocol.bedrock.codec.compat.BedrockCompat; -import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622; import org.cloudburstmc.protocol.bedrock.data.ExperimentData; import org.cloudburstmc.protocol.bedrock.data.PacketCompressionAlgorithm; import org.cloudburstmc.protocol.bedrock.data.ResourcePackType; diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java index 6b403a99b..ee80cac16 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockMovePlayerTranslator.java @@ -61,7 +61,7 @@ public class BedrockMovePlayerTranslator extends PacketTranslator { @Override public void translate(GeyserSession session, ClientboundSystemChatPacket packet) { - if (packet.getContent() instanceof TranslatableComponent component && component.key().equals("chat.disabled.missingProfileKey")) { - // We likely got this message as a response to a player trying to chat - // As there SHOULD be no false flags for this, print every time it shows up in chat. - if (Boolean.parseBoolean(System.getProperty("Geyser.PrintSecureChatInformation", "true"))) { - session.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.chat.secure_info_1", session.locale())); - session.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.chat.secure_info_2", session.locale(), "https://geysermc.link/secure-chat")); + if (packet.getContent() instanceof TranslatableComponent component) { + if (component.key().equals("chat.disabled.missingProfileKey")) { + // We likely got this message as a response to a player trying to chat + // As there SHOULD be no false flags for this, print every time it shows up in chat. + if (Boolean.parseBoolean(System.getProperty("Geyser.PrintSecureChatInformation", "true"))) { + session.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.chat.secure_info_1", session.locale())); + session.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.chat.secure_info_2", session.locale(), "https://geysermc.link/secure-chat")); + } + } else if (component.key().equals("sleep.players_sleeping")) { + if (component.arguments().size() == 2) { + // Hack FYI, but it allows Bedrock players to easily understand this information + // without it being covered up or saying the night is being slept through. + int numPlayersSleeping = ((Number) component.arguments().get(0).value()).intValue(); + int totalPlayersNeeded = ((Number) component.arguments().get(1).value()).intValue(); + LevelEventGenericPacket sleepInfoPacket = new LevelEventGenericPacket(); + sleepInfoPacket.setType(LevelEvent.SLEEPING_PLAYERS); + sleepInfoPacket.setTag(NbtMap.builder() + .putInt("ableToSleep", totalPlayersNeeded) + .putInt("overworldPlayerCount", totalPlayersNeeded) + .putInt("sleepingPlayerCount", numPlayersSleeping) + .build()); + session.sendUpstreamPacket(sleepInfoPacket); + } + } else if (component.key().equals("sleep.skipping_night")) { + LevelEventGenericPacket sleepInfoPacket = new LevelEventGenericPacket(); + sleepInfoPacket.setType(LevelEvent.SLEEPING_PLAYERS); + sleepInfoPacket.setTag(NbtMap.builder() + .putInt("ableToSleep", 1) + .putInt("overworldPlayerCount", 1) + .putInt("sleepingPlayerCount", 1) + .build()); + session.sendUpstreamPacket(sleepInfoPacket); } } From 1ab3f1f2e06de06720154f205a2f5bcf8402bf31 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Sun, 1 Sep 2024 15:34:53 -0400 Subject: [PATCH 08/18] Improve command registration timing on Velocity/BungeeCord (#5013) --- .../bungeecord/GeyserBungeePlugin.java | 28 +++++++++-------- .../neoforge/GeyserNeoForgeBootstrap.java | 1 + .../platform/spigot/GeyserSpigotPlugin.java | 2 +- .../velocity/GeyserVelocityPlugin.java | 30 ++++++++++--------- 4 files changed, 33 insertions(+), 28 deletions(-) diff --git a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java index e2735c80e..7adfd488f 100644 --- a/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java +++ b/bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java @@ -111,6 +111,21 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { if (geyser == null) { return; // Config did not load properly! } + + // After Geyser initialize for parity with other platforms. + var sourceConverter = new CommandSourceConverter<>( + CommandSender.class, + id -> getProxy().getPlayer(id), + () -> getProxy().getConsole(), + BungeeCommandSource::new + ); + CommandManager cloud = new BungeeCommandManager<>( + this, + ExecutionCoordinator.simpleCoordinator(), + sourceConverter + ); + this.commandRegistry = new CommandRegistry(geyser, cloud, false); // applying root permission would be a breaking change because we can't register permission defaults + // Big hack - Bungee does not provide us an event to listen to, so schedule a repeating // task that waits for a field to be filled which is set after the plugin enable // process is complete @@ -150,19 +165,6 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap { } this.geyserLogger.setDebug(geyserConfig.isDebugMode()); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); - } else { - var sourceConverter = new CommandSourceConverter<>( - CommandSender.class, - id -> getProxy().getPlayer(id), - () -> getProxy().getConsole(), - BungeeCommandSource::new - ); - CommandManager cloud = new BungeeCommandManager<>( - this, - ExecutionCoordinator.simpleCoordinator(), - sourceConverter - ); - this.commandRegistry = new CommandRegistry(geyser, cloud, false); // applying root permission would be a breaking change because we can't register permission defaults } // Force-disable query if enabled, or else Geyser won't enable diff --git a/bootstrap/mod/neoforge/src/main/java/org/geysermc/geyser/platform/neoforge/GeyserNeoForgeBootstrap.java b/bootstrap/mod/neoforge/src/main/java/org/geysermc/geyser/platform/neoforge/GeyserNeoForgeBootstrap.java index 7d3b9dc5f..ad56eda39 100644 --- a/bootstrap/mod/neoforge/src/main/java/org/geysermc/geyser/platform/neoforge/GeyserNeoForgeBootstrap.java +++ b/bootstrap/mod/neoforge/src/main/java/org/geysermc/geyser/platform/neoforge/GeyserNeoForgeBootstrap.java @@ -82,6 +82,7 @@ public class GeyserNeoForgeBootstrap extends GeyserModBootstrap { ); GeyserNeoForgeCommandRegistry registry = new GeyserNeoForgeCommandRegistry(getGeyser(), cloud); this.setCommandRegistry(registry); + // An auxiliary listener for registering undefined permissions belonging to commands. See javadocs for more info. NeoForge.EVENT_BUS.addListener(EventPriority.LOWEST, registry::onPermissionGatherForUndefined); } diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index c52927a83..a2d5c992b 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -181,7 +181,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { return; } - // Create command manager early so we can add Geyser extension commands + // Register commands after Geyser initialization, but before the server starts. var sourceConverter = new CommandSourceConverter<>( CommandSender.class, Bukkit::getPlayer, diff --git a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java index 413355813..8fa47f569 100644 --- a/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java +++ b/bootstrap/velocity/src/main/java/org/geysermc/geyser/platform/velocity/GeyserVelocityPlugin.java @@ -109,6 +109,22 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { this.geyser = GeyserImpl.load(PlatformType.VELOCITY, this); this.geyserInjector = new GeyserVelocityInjector(proxyServer); + + // We need to register commands here, rather than in onGeyserEnable which is invoked during the appropriate ListenerBoundEvent. + // Reason: players can connect after a listener is bound, and a player join locks registration to the cloud CommandManager. + var sourceConverter = new CommandSourceConverter<>( + CommandSource.class, + id -> proxyServer.getPlayer(id).orElse(null), + proxyServer::getConsoleCommandSource, + VelocityCommandSource::new + ); + CommandManager cloud = new VelocityCommandManager<>( + container, + proxyServer, + ExecutionCoordinator.simpleCoordinator(), + sourceConverter + ); + this.commandRegistry = new CommandRegistry(geyser, cloud, false); // applying root permission would be a breaking change because we can't register permission defaults } @Override @@ -123,20 +139,6 @@ public class GeyserVelocityPlugin implements GeyserBootstrap { } this.geyserLogger.setDebug(geyserConfig.isDebugMode()); GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger); - } else { - var sourceConverter = new CommandSourceConverter<>( - CommandSource.class, - id -> proxyServer.getPlayer(id).orElse(null), - proxyServer::getConsoleCommandSource, - VelocityCommandSource::new - ); - CommandManager cloud = new VelocityCommandManager<>( - container, - proxyServer, - ExecutionCoordinator.simpleCoordinator(), - sourceConverter - ); - this.commandRegistry = new CommandRegistry(geyser, cloud, false); // applying root permission would be a breaking change because we can't register permission defaults } GeyserImpl.start(); From 74034f078338e3bb27092b72e9067e6f5dd3968a Mon Sep 17 00:00:00 2001 From: RK_01 <50594595+RaphiMC@users.noreply.github.com> Date: Mon, 2 Sep 2024 22:08:43 +0200 Subject: [PATCH 09/18] Fix PARTICLES_DRAGON_BLOCK_BREAK translation (#5015) --- .../protocol/java/level/JavaLevelEventTranslator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelEventTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelEventTranslator.java index c10cfa018..f54de1b68 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelEventTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelEventTranslator.java @@ -293,7 +293,7 @@ public class JavaLevelEventTranslator extends PacketTranslator { effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_GENERIC_SPAWN); - effectPacket.setData(61); + effectPacket.setData(65); } case PARTICLES_WATER_EVAPORATING -> { effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_EVAPORATE_WATER); From 65cb15400a4ac28d9a622004713b7ac59969e90e Mon Sep 17 00:00:00 2001 From: chris Date: Tue, 3 Sep 2024 01:04:44 +0200 Subject: [PATCH 10/18] Fix: Broadcast port system property not being read on Geyser-Standalone (#4942) * this is supposed to work on standalone aswell * Update core/src/main/java/org/geysermc/geyser/GeyserImpl.java Co-authored-by: Konicai <71294714+Konicai@users.noreply.github.com> * Update core/src/main/java/org/geysermc/geyser/GeyserImpl.java Co-authored-by: Konicai <71294714+Konicai@users.noreply.github.com> * address review --------- Co-authored-by: Konicai <71294714+Konicai@users.noreply.github.com> --- .../java/org/geysermc/geyser/GeyserImpl.java | 36 ++++++++++--------- .../geyser/network/netty/GeyserServer.java | 5 --- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 8febf4d21..bc6108abf 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -363,22 +363,6 @@ public class GeyserImpl implements GeyserApi, EventRegistrar { } } - String broadcastPort = System.getProperty("geyserBroadcastPort", ""); - if (!broadcastPort.isEmpty()) { - int parsedPort; - try { - parsedPort = Integer.parseInt(broadcastPort); - if (parsedPort < 1 || parsedPort > 65535) { - throw new NumberFormatException("The broadcast port must be between 1 and 65535 inclusive!"); - } - } catch (NumberFormatException e) { - logger.error(String.format("Invalid broadcast port: %s! Defaulting to configured port.", broadcastPort + " (" + e.getMessage() + ")")); - parsedPort = config.getBedrock().port(); - } - config.getBedrock().setBroadcastPort(parsedPort); - logger.info("Broadcast port set from system property: " + parsedPort); - } - if (platformType != PlatformType.VIAPROXY) { boolean floodgatePresent = bootstrap.testFloodgatePluginPresent(); if (config.getRemote().authType() == AuthType.FLOODGATE && !floodgatePresent) { @@ -393,6 +377,26 @@ public class GeyserImpl implements GeyserApi, EventRegistrar { } } + // Now that the Bedrock port may have been changed, also check the broadcast port (configurable on all platforms) + String broadcastPort = System.getProperty("geyserBroadcastPort", ""); + if (!broadcastPort.isEmpty()) { + try { + int parsedPort = Integer.parseInt(broadcastPort); + if (parsedPort < 1 || parsedPort > 65535) { + throw new NumberFormatException("The broadcast port must be between 1 and 65535 inclusive!"); + } + config.getBedrock().setBroadcastPort(parsedPort); + logger.info("Broadcast port set from system property: " + parsedPort); + } catch (NumberFormatException e) { + logger.error(String.format("Invalid broadcast port from system property: %s! Defaulting to configured port.", broadcastPort + " (" + e.getMessage() + ")")); + } + } + + // It's set to 0 only if no system property or manual config value was set + if (config.getBedrock().broadcastPort() == 0) { + config.getBedrock().setBroadcastPort(config.getBedrock().port()); + } + String remoteAddress = config.getRemote().address(); // Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry. if (!remoteAddress.matches(IP_REGEX) && !remoteAddress.equalsIgnoreCase("localhost")) { diff --git a/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java b/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java index 5166bde4d..efbd8bdff 100644 --- a/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java +++ b/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java @@ -144,11 +144,6 @@ public final class GeyserServer { this.proxiedAddresses = null; } - // It's set to 0 only if no system property or manual config value was set - if (geyser.getConfig().getBedrock().broadcastPort() == 0) { - geyser.getConfig().getBedrock().setBroadcastPort(geyser.getConfig().getBedrock().port()); - } - this.broadcastPort = geyser.getConfig().getBedrock().broadcastPort(); } From f8884568ee717cda9314fb40ae0c7913bd601596 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Tue, 3 Sep 2024 00:54:50 -0400 Subject: [PATCH 11/18] Gradle: avoid cross-configuration and enable configuration-on-demand (#5012) --- ap/build.gradle.kts | 3 + bootstrap/bungeecord/build.gradle.kts | 5 ++ bootstrap/mod/build.gradle.kts | 4 ++ bootstrap/mod/fabric/build.gradle.kts | 5 ++ bootstrap/mod/neoforge/build.gradle.kts | 13 ++-- bootstrap/spigot/build.gradle.kts | 5 ++ bootstrap/standalone/build.gradle.kts | 1 + bootstrap/velocity/build.gradle.kts | 5 ++ bootstrap/viaproxy/build.gradle.kts | 4 ++ build-logic/build.gradle.kts | 5 ++ build-logic/settings.gradle.kts | 2 +- .../kotlin/geyser.base-conventions.gradle.kts | 69 ++++++++++++++----- .../main/kotlin/geyser.build-logic.gradle.kts | 45 ------------ .../geyser.modded-conventions.gradle.kts | 4 +- .../geyser.platform-conventions.gradle.kts | 19 ++++- build.gradle.kts | 52 +------------- common/build.gradle.kts | 1 + core/build.gradle.kts | 1 + gradle.properties | 1 + gradle/libs.versions.toml | 2 +- 20 files changed, 123 insertions(+), 123 deletions(-) delete mode 100644 build-logic/src/main/kotlin/geyser.build-logic.gradle.kts diff --git a/ap/build.gradle.kts b/ap/build.gradle.kts index e69de29bb..6c456c21b 100644 --- a/ap/build.gradle.kts +++ b/ap/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + id("geyser.base-conventions") +} diff --git a/bootstrap/bungeecord/build.gradle.kts b/bootstrap/bungeecord/build.gradle.kts index 5fe7ea3d1..1564b7f75 100644 --- a/bootstrap/bungeecord/build.gradle.kts +++ b/bootstrap/bungeecord/build.gradle.kts @@ -1,3 +1,8 @@ +plugins { + id("geyser.platform-conventions") + id("geyser.modrinth-uploading-conventions") +} + dependencies { api(projects.core) diff --git a/bootstrap/mod/build.gradle.kts b/bootstrap/mod/build.gradle.kts index 57f11b2c7..c43f123ec 100644 --- a/bootstrap/mod/build.gradle.kts +++ b/bootstrap/mod/build.gradle.kts @@ -1,3 +1,7 @@ +plugins { + id("geyser.modded-conventions") +} + architectury { common("neoforge", "fabric") } diff --git a/bootstrap/mod/fabric/build.gradle.kts b/bootstrap/mod/fabric/build.gradle.kts index fd9d7e99d..56bec322e 100644 --- a/bootstrap/mod/fabric/build.gradle.kts +++ b/bootstrap/mod/fabric/build.gradle.kts @@ -1,3 +1,8 @@ +plugins { + id("geyser.modded-conventions") + id("geyser.modrinth-uploading-conventions") +} + architectury { platformSetupLoomIde() fabric() diff --git a/bootstrap/mod/neoforge/build.gradle.kts b/bootstrap/mod/neoforge/build.gradle.kts index 81a35a58b..4ab005b4f 100644 --- a/bootstrap/mod/neoforge/build.gradle.kts +++ b/bootstrap/mod/neoforge/build.gradle.kts @@ -1,13 +1,18 @@ -// This is provided by "org.cloudburstmc.math.mutable" too, so yeet. -// NeoForge's class loader is *really* annoying. -provided("org.cloudburstmc.math", "api") -provided("com.google.errorprone", "error_prone_annotations") +plugins { + id("geyser.modded-conventions") + id("geyser.modrinth-uploading-conventions") +} architectury { platformSetupLoomIde() neoForge() } +// This is provided by "org.cloudburstmc.math.mutable" too, so yeet. +// NeoForge's class loader is *really* annoying. +provided("org.cloudburstmc.math", "api") +provided("com.google.errorprone", "error_prone_annotations") + val includeTransitive: Configuration = configurations.getByName("includeTransitive") dependencies { diff --git a/bootstrap/spigot/build.gradle.kts b/bootstrap/spigot/build.gradle.kts index f680b1949..feabfdd7a 100644 --- a/bootstrap/spigot/build.gradle.kts +++ b/bootstrap/spigot/build.gradle.kts @@ -1,3 +1,8 @@ +plugins { + id("geyser.platform-conventions") + id("geyser.modrinth-uploading-conventions") +} + dependencies { api(projects.core) api(libs.erosion.bukkit.common) { diff --git a/bootstrap/standalone/build.gradle.kts b/bootstrap/standalone/build.gradle.kts index fd81dad63..b210693c1 100644 --- a/bootstrap/standalone/build.gradle.kts +++ b/bootstrap/standalone/build.gradle.kts @@ -2,6 +2,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCach plugins { application + id("geyser.platform-conventions") } val terminalConsoleVersion = "1.2.0" diff --git a/bootstrap/velocity/build.gradle.kts b/bootstrap/velocity/build.gradle.kts index 93e0c9c93..05035e271 100644 --- a/bootstrap/velocity/build.gradle.kts +++ b/bootstrap/velocity/build.gradle.kts @@ -1,3 +1,8 @@ +plugins { + id("geyser.platform-conventions") + id("geyser.modrinth-uploading-conventions") +} + dependencies { annotationProcessor(libs.velocity.api) api(projects.core) diff --git a/bootstrap/viaproxy/build.gradle.kts b/bootstrap/viaproxy/build.gradle.kts index 254787743..c13862a27 100644 --- a/bootstrap/viaproxy/build.gradle.kts +++ b/bootstrap/viaproxy/build.gradle.kts @@ -1,3 +1,7 @@ +plugins { + id("geyser.platform-conventions") +} + dependencies { api(projects.core) diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index 190386667..b87490880 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -12,9 +12,14 @@ repositories { } dependencies { + // This is for the LibsAccessor.kt hack // this is OK as long as the same version catalog is used in the main build and build-logic // see https://github.com/gradle/gradle/issues/15383#issuecomment-779893192 implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location)) + + // This is for applying plugins, and using the version from the libs.versions.toml + // Unfortunately they still need to be applied by their string name in the convention scripts. + implementation(libs.lombok) implementation(libs.indra) implementation(libs.shadow) implementation(libs.architectury.plugin) diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts index 63bde189b..bd4560d11 100644 --- a/build-logic/settings.gradle.kts +++ b/build-logic/settings.gradle.kts @@ -8,4 +8,4 @@ dependencyResolutionManagement { } } -rootProject.name = "build-logic" \ No newline at end of file +rootProject.name = "build-logic" diff --git a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts index 950c0184b..093f0a8c0 100644 --- a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts @@ -3,9 +3,10 @@ plugins { id("net.kyori.indra") } -dependencies { - compileOnly("org.checkerframework", "checker-qual", "3.19.0") -} +val rootProperties: Map = project.rootProject.properties +group = rootProperties["group"] as String + "." + rootProperties["id"] as String +version = rootProperties["version"] as String +description = rootProperties["description"] as String indra { github("GeyserMC", "Geyser") { @@ -20,18 +21,52 @@ indra { } } -tasks { - processResources { - // Spigot, BungeeCord, Velocity, Fabric, ViaProxy, NeoForge - filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json", "fabric.mod.json", "viaproxy.yml", "META-INF/neoforge.mods.toml")) { - expand( - "id" to "geyser", - "name" to "Geyser", - "version" to project.version, - "description" to project.description, - "url" to "https://geysermc.org", - "author" to "GeyserMC" - ) - } +dependencies { + compileOnly("org.checkerframework", "checker-qual", libs.checker.qual.get().version) +} + +repositories { + // mavenLocal() + + mavenCentral() + + // Floodgate, Cumulus etc. + maven("https://repo.opencollab.dev/main") + + // Paper, Velocity + maven("https://repo.papermc.io/repository/maven-public") + + // Spigot + maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots") { + mavenContent { snapshotsOnly() } } -} \ No newline at end of file + + // BungeeCord + maven("https://oss.sonatype.org/content/repositories/snapshots") { + mavenContent { snapshotsOnly() } + } + + // NeoForge + maven("https://maven.neoforged.net/releases") { + mavenContent { releasesOnly() } + } + + // Minecraft + maven("https://libraries.minecraft.net") { + name = "minecraft" + mavenContent { releasesOnly() } + } + + // ViaVersion + maven("https://repo.viaversion.com") { + name = "viaversion" + } + + // Jitpack for e.g. MCPL + maven("https://jitpack.io") { + content { includeGroupByRegex("com\\.github\\..*") } + } + + // For Adventure snapshots + maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") +} diff --git a/build-logic/src/main/kotlin/geyser.build-logic.gradle.kts b/build-logic/src/main/kotlin/geyser.build-logic.gradle.kts deleted file mode 100644 index b6168507e..000000000 --- a/build-logic/src/main/kotlin/geyser.build-logic.gradle.kts +++ /dev/null @@ -1,45 +0,0 @@ -repositories { - // mavenLocal() - - mavenCentral() - - // Floodgate, Cumulus etc. - maven("https://repo.opencollab.dev/main") - - // Paper, Velocity - maven("https://repo.papermc.io/repository/maven-public") - - // Spigot - maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots") { - mavenContent { snapshotsOnly() } - } - - // BungeeCord - maven("https://oss.sonatype.org/content/repositories/snapshots") { - mavenContent { snapshotsOnly() } - } - - // NeoForge - maven("https://maven.neoforged.net/releases") { - mavenContent { releasesOnly() } - } - - // Minecraft - maven("https://libraries.minecraft.net") { - name = "minecraft" - mavenContent { releasesOnly() } - } - - // ViaVersion - maven("https://repo.viaversion.com") { - name = "viaversion" - } - - // Jitpack for e.g. MCPL - maven("https://jitpack.io") { - content { includeGroupByRegex("com\\.github\\..*") } - } - - // For Adventure snapshots - maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") -} diff --git a/build-logic/src/main/kotlin/geyser.modded-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.modded-conventions.gradle.kts index 8584c13d4..779d6446a 100644 --- a/build-logic/src/main/kotlin/geyser.modded-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.modded-conventions.gradle.kts @@ -2,11 +2,9 @@ import net.fabricmc.loom.task.RemapJarTask import org.gradle.kotlin.dsl.dependencies -import org.gradle.kotlin.dsl.maven plugins { - id("geyser.build-logic") - id("geyser.publish-conventions") + id("geyser.platform-conventions") id("architectury-plugin") id("dev.architectury.loom") } diff --git a/build-logic/src/main/kotlin/geyser.platform-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.platform-conventions.gradle.kts index 410e67404..7a342783b 100644 --- a/build-logic/src/main/kotlin/geyser.platform-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.platform-conventions.gradle.kts @@ -1,3 +1,20 @@ plugins { id("geyser.publish-conventions") -} \ No newline at end of file + id("io.freefair.lombok") +} + +tasks { + processResources { + // Spigot, BungeeCord, Velocity, Fabric, ViaProxy, NeoForge + filesMatching(listOf("plugin.yml", "bungee.yml", "velocity-plugin.json", "fabric.mod.json", "viaproxy.yml", "META-INF/neoforge.mods.toml")) { + expand( + "id" to "geyser", + "name" to "Geyser", + "version" to project.version, + "description" to project.description, + "url" to "https://geysermc.org", + "author" to "GeyserMC" + ) + } + } +} diff --git a/build.gradle.kts b/build.gradle.kts index dfbf9837f..7f700a2f6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,55 +1,5 @@ plugins { - `java-library` // Ensure AP works in eclipse (no effect on other IDEs) eclipse - id("geyser.build-logic") - alias(libs.plugins.lombok) apply false + id("geyser.base-conventions") } - -allprojects { - group = properties["group"] as String + "." + properties["id"] as String - version = properties["version"] as String - description = properties["description"] as String -} - -val basePlatforms = setOf( - projects.bungeecord, - projects.spigot, - projects.standalone, - projects.velocity, - projects.viaproxy -).map { it.dependencyProject } - -val moddedPlatforms = setOf( - projects.fabric, - projects.neoforge, - projects.mod -).map { it.dependencyProject } - -val modrinthPlatforms = setOf( - projects.bungeecord, - projects.fabric, - projects.neoforge, - projects.spigot, - projects.velocity -).map { it.dependencyProject } - -subprojects { - apply { - plugin("java-library") - plugin("io.freefair.lombok") - plugin("geyser.build-logic") - } - - when (this) { - in basePlatforms -> plugins.apply("geyser.platform-conventions") - in moddedPlatforms -> plugins.apply("geyser.modded-conventions") - else -> plugins.apply("geyser.base-conventions") - } - - // Not combined with platform-conventions as that also contains - // platforms which we cant publish to modrinth - if (modrinthPlatforms.contains(this)) { - plugins.apply("geyser.modrinth-uploading-conventions") - } -} \ No newline at end of file diff --git a/common/build.gradle.kts b/common/build.gradle.kts index efba08c8d..166ffe9f5 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -1,5 +1,6 @@ plugins { id("geyser.publish-conventions") + id("io.freefair.lombok") } dependencies { diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 8d022271b..d30e60298 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -3,6 +3,7 @@ plugins { idea alias(libs.plugins.blossom) id("geyser.publish-conventions") + id("io.freefair.lombok") } dependencies { diff --git a/gradle.properties b/gradle.properties index 814529d6c..dd1bc915a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,7 @@ # Gradle settings org.gradle.jvmargs=-Xmx4G org.gradle.daemon=false +org.gradle.configureondemand=true org.gradle.parallel=true org.gradle.caching=true org.gradle.vfs.watch=false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c564720a3..f2a29ee74 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -134,6 +134,7 @@ protocol-connection = { group = "org.cloudburstmc.protocol", name = "bedrock-con math = { group = "org.cloudburstmc.math", name = "immutable", version = "2.0" } # plugins +lombok = { group = "io.freefair.gradle", name = "lombok-plugin", version.ref = "lombok" } indra = { group = "net.kyori", name = "indra-common", version.ref = "indra" } shadow = { group = "com.github.johnrengelman", name = "shadow", version.ref = "shadow" } architectury-plugin = { group = "architectury-plugin", name = "architectury-plugin.gradle.plugin", version.ref = "architectury-plugin" } @@ -141,7 +142,6 @@ architectury-loom = { group = "dev.architectury.loom", name = "dev.architectury. minotaur = { group = "com.modrinth.minotaur", name = "Minotaur", version.ref = "minotaur" } [plugins] -lombok = { id = "io.freefair.lombok", version.ref = "lombok" } indra = { id = "net.kyori.indra", version.ref = "indra" } blossom = { id = "net.kyori.blossom", version.ref = "blossom" } From 9dad34d0a82a7cb47b61016c15bb5e51dbcc2860 Mon Sep 17 00:00:00 2001 From: RK_01 <50594595+RaphiMC@users.noreply.github.com> Date: Tue, 3 Sep 2024 22:25:49 +0200 Subject: [PATCH 12/18] Unhardcode PARTICLES_DRAGON_BLOCK_BREAK id (#5018) --- .../protocol/java/level/JavaLevelEventTranslator.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelEventTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelEventTranslator.java index f54de1b68..5b4ff1de7 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelEventTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaLevelEventTranslator.java @@ -291,10 +291,7 @@ public class JavaLevelEventTranslator extends PacketTranslator { - effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_GENERIC_SPAWN); - effectPacket.setData(65); - } + case PARTICLES_DRAGON_BLOCK_BREAK -> effectPacket.setType(ParticleType.DRAGON_DESTROY_BLOCK); case PARTICLES_WATER_EVAPORATING -> { effectPacket.setType(org.cloudburstmc.protocol.bedrock.data.LevelEvent.PARTICLE_EVAPORATE_WATER); effectPacket.setPosition(pos.add(-0.5f, 0.5f, -0.5f)); From 34f5d71e58ff0b07fafdefc1aed5169f3eb8e00f Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Fri, 6 Sep 2024 13:48:00 -0400 Subject: [PATCH 13/18] Validate sleep message arguments (#5023) --- .../java/JavaSystemChatTranslator.java | 40 ++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaSystemChatTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaSystemChatTranslator.java index 7e7a97a4b..27481aa26 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaSystemChatTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaSystemChatTranslator.java @@ -25,7 +25,10 @@ package org.geysermc.geyser.translator.protocol.java; +import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TranslatableComponent; +import net.kyori.adventure.text.TranslationArgument; +import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.protocol.bedrock.data.LevelEvent; import org.cloudburstmc.protocol.bedrock.packet.LevelEventGenericPacket; @@ -55,16 +58,18 @@ public class JavaSystemChatTranslator extends PacketTranslator Date: Sat, 7 Sep 2024 22:29:27 -0400 Subject: [PATCH 14/18] Cancel Erosion futures when disconnecting/switching servers (#5026) --- .../erosion/ErosionCancellationException.java | 34 ++++++++++++ .../GeyserboundHandshakePacketHandler.java | 2 +- .../erosion/GeyserboundPacketHandlerImpl.java | 13 ++++- .../geyser/level/GeyserWorldManager.java | 9 ++++ .../registry/PacketTranslatorRegistry.java | 3 ++ .../geyser/session/GeyserSession.java | 11 ++-- .../protocol/java/JavaLoginTranslator.java | 6 --- .../JavaStartConfigurationTranslator.java | 52 +++++++++++++++++++ 8 files changed, 119 insertions(+), 11 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/erosion/ErosionCancellationException.java create mode 100644 core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaStartConfigurationTranslator.java diff --git a/core/src/main/java/org/geysermc/geyser/erosion/ErosionCancellationException.java b/core/src/main/java/org/geysermc/geyser/erosion/ErosionCancellationException.java new file mode 100644 index 000000000..ae283895b --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/erosion/ErosionCancellationException.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.erosion; + +import java.io.Serial; +import java.util.concurrent.CancellationException; + +public class ErosionCancellationException extends CancellationException { + @Serial + private static final long serialVersionUID = 1L; +} diff --git a/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundHandshakePacketHandler.java b/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundHandshakePacketHandler.java index 0b4f03643..88a7e0cd3 100644 --- a/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundHandshakePacketHandler.java +++ b/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundHandshakePacketHandler.java @@ -42,7 +42,6 @@ public final class GeyserboundHandshakePacketHandler extends AbstractGeyserbound public void handleHandshake(GeyserboundHandshakePacket packet) { boolean useTcp = packet.getTransportType().getSocketAddress() == null; GeyserboundPacketHandlerImpl handler = new GeyserboundPacketHandlerImpl(session, useTcp ? new GeyserErosionPacketSender(session) : new NettyPacketSender<>()); - session.setErosionHandler(handler); if (!useTcp) { if (session.getGeyser().getErosionUnixListener() == null) { session.disconnect("Erosion configurations using Unix socket handling are not supported on this hardware!"); @@ -52,6 +51,7 @@ public final class GeyserboundHandshakePacketHandler extends AbstractGeyserbound } else { handler.onConnect(); } + session.setErosionHandler(handler); session.ensureInEventLoop(() -> session.getChunkCache().clear()); } diff --git a/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundPacketHandlerImpl.java b/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundPacketHandlerImpl.java index c8cbe384b..7202db449 100644 --- a/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundPacketHandlerImpl.java +++ b/core/src/main/java/org/geysermc/geyser/erosion/GeyserboundPacketHandlerImpl.java @@ -171,10 +171,10 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke @Override public void handleHandshake(GeyserboundHandshakePacket packet) { - this.close(); var handler = new GeyserboundHandshakePacketHandler(this.session); session.setErosionHandler(handler); handler.handleHandshake(packet); + this.close(); } @Override @@ -198,6 +198,17 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke public void close() { this.packetSender.close(); + + if (pendingLookup != null) { + pendingLookup.completeExceptionally(new ErosionCancellationException()); + } + if (pendingBatchLookup != null) { + pendingBatchLookup.completeExceptionally(new ErosionCancellationException()); + } + if (pickBlockLookup != null) { + pickBlockLookup.completeExceptionally(new ErosionCancellationException()); + } + asyncPendingLookups.forEach(($, future) -> future.completeExceptionally(new ErosionCancellationException())); } public int getNextTransactionId() { diff --git a/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java b/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java index 9cf2c0179..befcfa4b7 100644 --- a/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java +++ b/core/src/main/java/org/geysermc/geyser/level/GeyserWorldManager.java @@ -35,6 +35,7 @@ import org.geysermc.erosion.packet.backendbound.BackendboundBatchBlockRequestPac import org.geysermc.erosion.packet.backendbound.BackendboundBlockRequestPacket; import org.geysermc.erosion.packet.backendbound.BackendboundPickBlockPacket; import org.geysermc.erosion.util.BlockPositionIterator; +import org.geysermc.geyser.erosion.ErosionCancellationException; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; @@ -49,6 +50,8 @@ public class GeyserWorldManager extends WorldManager { var erosionHandler = session.getErosionHandler().getAsActive(); if (erosionHandler == null) { return session.getChunkCache().getBlockAt(x, y, z); + } else if (session.isClosed()) { + throw new ErosionCancellationException(); } CompletableFuture future = new CompletableFuture<>(); // Boxes erosionHandler.setPendingLookup(future); @@ -61,6 +64,8 @@ public class GeyserWorldManager extends WorldManager { var erosionHandler = session.getErosionHandler().getAsActive(); if (erosionHandler == null) { return super.getBlockAtAsync(session, x, y, z); + } else if (session.isClosed()) { + return CompletableFuture.failedFuture(new ErosionCancellationException()); } CompletableFuture future = new CompletableFuture<>(); // Boxes int transactionId = erosionHandler.getNextTransactionId(); @@ -74,6 +79,8 @@ public class GeyserWorldManager extends WorldManager { var erosionHandler = session.getErosionHandler().getAsActive(); if (erosionHandler == null) { return super.getBlocksAt(session, iter); + } else if (session.isClosed()) { + throw new ErosionCancellationException(); } CompletableFuture future = new CompletableFuture<>(); erosionHandler.setPendingBatchLookup(future); @@ -124,6 +131,8 @@ public class GeyserWorldManager extends WorldManager { var erosionHandler = session.getErosionHandler().getAsActive(); if (erosionHandler == null) { return super.getPickItemComponents(session, x, y, z, addNbtData); + } else if (session.isClosed()) { + return CompletableFuture.failedFuture(new ErosionCancellationException()); } CompletableFuture> future = new CompletableFuture<>(); erosionHandler.setPickBlockLookup(future); diff --git a/core/src/main/java/org/geysermc/geyser/registry/PacketTranslatorRegistry.java b/core/src/main/java/org/geysermc/geyser/registry/PacketTranslatorRegistry.java index 9a5b43816..b31f2b4f0 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/PacketTranslatorRegistry.java +++ b/core/src/main/java/org/geysermc/geyser/registry/PacketTranslatorRegistry.java @@ -31,6 +31,7 @@ import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.Clien import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundLightUpdatePacket; import io.netty.channel.EventLoop; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.erosion.ErosionCancellationException; import org.geysermc.geyser.registry.loader.RegistryLoaders; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; @@ -87,6 +88,8 @@ public class PacketTranslatorRegistry extends AbstractMappedRegistry { try { runnable.run(); + } catch (ErosionCancellationException e) { + geyser.getLogger().debug("Caught ErosionCancellationException"); } catch (Throwable e) { geyser.getLogger().error("Error thrown in " + this.bedrockUsername() + "'s event loop!", e); } @@ -1230,6 +1233,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { if (!closed) { runnable.run(); } + } catch (ErosionCancellationException e) { + geyser.getLogger().debug("Caught ErosionCancellationException"); } catch (Throwable e) { geyser.getLogger().error("Error thrown in " + this.bedrockUsername() + "'s event loop!", e); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java index a6d6e6c70..6470a5f0a 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaLoginTranslator.java @@ -33,7 +33,6 @@ import org.geysermc.erosion.Constants; import org.geysermc.floodgate.pluginmessage.PluginMessageChannels; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; -import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler; import org.geysermc.geyser.level.JavaDimension; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; @@ -57,11 +56,6 @@ public class JavaLoginTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, ClientboundStartConfigurationPacket packet) { + var erosionHandler = session.getErosionHandler(); + if (erosionHandler.isActive()) { + // Set new handler before closing + session.setErosionHandler(new GeyserboundHandshakePacketHandler(session)); + erosionHandler.close(); + } + } + + @Override + public boolean shouldExecuteInEventLoop() { + // Execute outside of event loop to cancel any pending erosion futures + return false; + } +} From c28522af6e91268c223941e842989c1dfc16cd64 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Sun, 8 Sep 2024 23:06:36 -0400 Subject: [PATCH 15/18] Delay clear weather packets (#5030) --- .../geyser/session/GeyserSession.java | 58 ++++++++++++++++++- .../protocol/java/JavaRespawnTranslator.java | 17 +----- .../java/level/JavaGameEventTranslator.java | 37 ++---------- .../geysermc/geyser/util/DimensionUtils.java | 16 +---- 4 files changed, 65 insertions(+), 63 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index a8b8480fb..f7ffcdd0e 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -66,6 +66,7 @@ import org.cloudburstmc.protocol.bedrock.data.ExperimentData; import org.cloudburstmc.protocol.bedrock.data.GamePublishSetting; import org.cloudburstmc.protocol.bedrock.data.GameRuleData; import org.cloudburstmc.protocol.bedrock.data.GameType; +import org.cloudburstmc.protocol.bedrock.data.LevelEvent; import org.cloudburstmc.protocol.bedrock.data.PlayerPermission; import org.cloudburstmc.protocol.bedrock.data.SoundEvent; import org.cloudburstmc.protocol.bedrock.data.SpawnBiomeType; @@ -85,6 +86,7 @@ import org.cloudburstmc.protocol.bedrock.packet.CreativeContentPacket; import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket; import org.cloudburstmc.protocol.bedrock.packet.GameRulesChangedPacket; import org.cloudburstmc.protocol.bedrock.packet.ItemComponentPacket; +import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket; import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEvent2Packet; import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket; import org.cloudburstmc.protocol.bedrock.packet.SetTimePacket; @@ -570,13 +572,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { /** * Caches current rain status. */ - @Setter private boolean raining = false; /** * Caches current thunder status. */ - @Setter private boolean thunder = false; /** @@ -2007,6 +2007,60 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { }; } + /** + * Sends a packet to update rain strength. + * Stops rain if strength is 0. + * + * @param strength value between 0 and 1 + */ + public void updateRain(float strength) { + this.raining = strength > 0; + + LevelEventPacket rainPacket = new LevelEventPacket(); + rainPacket.setType(this.raining ? LevelEvent.START_RAINING : LevelEvent.STOP_RAINING); + rainPacket.setData((int) (strength * 65535)); + rainPacket.setPosition(Vector3f.ZERO); + + if (this.raining) { + sendUpstreamPacket(rainPacket); + } else { + // The bedrock client might ignore this packet if it is sent in the same tick as another rain packet + // https://github.com/GeyserMC/Geyser/issues/3679 + scheduleInEventLoop(() -> { + if (!this.raining) { + sendUpstreamPacket(rainPacket); + } + }, 100, TimeUnit.MILLISECONDS); + } + } + + /** + * Sends a packet to update thunderstorm strength. + * Stops thunderstorm if strength is 0. + * + * @param strength value between 0 and 1 + */ + public void updateThunder(float strength) { + this.thunder = strength > 0; + + LevelEventPacket thunderPacket = new LevelEventPacket(); + thunderPacket.setType(this.thunder ? LevelEvent.START_THUNDERSTORM : LevelEvent.STOP_THUNDERSTORM); + thunderPacket.setData((int) (strength * 65535)); + thunderPacket.setPosition(Vector3f.ZERO); + + if (this.thunder) { + sendUpstreamPacket(thunderPacket); + } else { + // The bedrock client might ignore this packet if it is sent in the same tick as another thunderstorm packet + // https://github.com/GeyserMC/Geyser/issues/3679 + scheduleInEventLoop(() -> { + if (!this.thunder) { + sendUpstreamPacket(thunderPacket); + } + }, 100, TimeUnit.MILLISECONDS); + } + } + @Override public @NonNull String bedrockUsername() { return authData.name(); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java index ccd93ac97..5c477f23e 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaRespawnTranslator.java @@ -25,9 +25,6 @@ package org.geysermc.geyser.translator.protocol.java; -import org.cloudburstmc.math.vector.Vector3f; -import org.cloudburstmc.protocol.bedrock.data.LevelEvent; -import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket; import org.cloudburstmc.protocol.bedrock.packet.SetPlayerGameTypePacket; import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; @@ -76,21 +73,11 @@ public class JavaRespawnTranslator extends PacketTranslator { - // Strength of rainstorms and thunderstorms is a 0-1 float on Java, while on Bedrock it is a 0-65535 int - private static final int MAX_STORM_STRENGTH = 65535; - @Override public void translate(GeyserSession session, ClientboundGameEventPacket packet) { PlayerEntity entity = session.getPlayerEntity(); @@ -65,42 +60,20 @@ public class JavaGameEventTranslator extends PacketTranslator 0f; - LevelEventPacket changeRainPacket = new LevelEventPacket(); - changeRainPacket.setType(isCurrentlyRaining ? LevelEvent.START_RAINING : LevelEvent.STOP_RAINING); // This is the rain strength on LevelEventType.START_RAINING, but can be any value on LevelEventType.STOP_RAINING - changeRainPacket.setData((int) (rainStrength * MAX_STORM_STRENGTH)); - changeRainPacket.setPosition(Vector3f.ZERO); - session.sendUpstreamPacket(changeRainPacket); - session.setRaining(isCurrentlyRaining); + float rainStrength = ((RainStrengthValue) packet.getValue()).getStrength(); + session.updateRain(rainStrength); break; case THUNDER_STRENGTH: // See above, same process float thunderStrength = ((ThunderStrengthValue) packet.getValue()).getStrength(); - boolean isCurrentlyThundering = thunderStrength > 0f; - LevelEventPacket changeThunderPacket = new LevelEventPacket(); - changeThunderPacket.setType(isCurrentlyThundering ? LevelEvent.START_THUNDERSTORM : LevelEvent.STOP_THUNDERSTORM); - changeThunderPacket.setData((int) (thunderStrength * MAX_STORM_STRENGTH)); - changeThunderPacket.setPosition(Vector3f.ZERO); - session.sendUpstreamPacket(changeThunderPacket); - session.setThunder(isCurrentlyThundering); + session.updateThunder(thunderStrength); break; case CHANGE_GAMEMODE: GameMode gameMode = (GameMode) packet.getValue(); diff --git a/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java b/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java index f043631b6..8dc94a165 100644 --- a/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/DimensionUtils.java @@ -28,11 +28,9 @@ package org.geysermc.geyser.util; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3i; -import org.cloudburstmc.protocol.bedrock.data.LevelEvent; import org.cloudburstmc.protocol.bedrock.data.PlayerActionType; import org.cloudburstmc.protocol.bedrock.packet.ChangeDimensionPacket; import org.cloudburstmc.protocol.bedrock.packet.ChunkRadiusUpdatedPacket; -import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket; import org.cloudburstmc.protocol.bedrock.packet.MobEffectPacket; import org.cloudburstmc.protocol.bedrock.packet.PlayerActionPacket; import org.cloudburstmc.protocol.bedrock.packet.StopSoundPacket; @@ -89,18 +87,8 @@ public class DimensionUtils { entityEffects.clear(); // Always reset weather, as it sometimes suddenly starts raining. See https://github.com/GeyserMC/Geyser/issues/3679 - LevelEventPacket stopRainPacket = new LevelEventPacket(); - stopRainPacket.setType(LevelEvent.STOP_RAINING); - stopRainPacket.setData(0); - stopRainPacket.setPosition(Vector3f.ZERO); - session.sendUpstreamPacket(stopRainPacket); - session.setRaining(false); - LevelEventPacket stopThunderPacket = new LevelEventPacket(); - stopThunderPacket.setType(LevelEvent.STOP_THUNDERSTORM); - stopThunderPacket.setData(0); - stopThunderPacket.setPosition(Vector3f.ZERO); - session.sendUpstreamPacket(stopThunderPacket); - session.setThunder(false); + session.updateRain(0); + session.updateThunder(0); finalizeDimensionSwitch(session, player); From 723840c7fc13dc468f5bd3ae84b89ef80f9225f1 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 9 Sep 2024 15:42:57 -0400 Subject: [PATCH 16/18] More clear message when logging in without a paid Java account --- .../org/geysermc/geyser/session/GeyserSession.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index f7ffcdd0e..9a50d7acb 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -40,6 +40,7 @@ import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; import net.kyori.adventure.key.Key; +import net.raphimc.minecraftauth.responsehandler.exception.MinecraftRequestException; import net.raphimc.minecraftauth.step.java.StepMCProfile; import net.raphimc.minecraftauth.step.java.StepMCToken; import net.raphimc.minecraftauth.step.java.session.StepFullJavaSession; @@ -231,6 +232,7 @@ import java.util.Queue; import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -877,7 +879,14 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { return task.getAuthentication().handle((result, ex) -> { if (ex != null) { geyser.getLogger().error("Failed to log in with Microsoft code!", ex); - disconnect(ex.toString()); + if (ex instanceof CompletionException ce + && ce.getCause() instanceof MinecraftRequestException mre + && mre.getResponse().getStatusCode() == 404) { + // Player is trying to join with a Microsoft account that doesn't have Java Edition purchased + disconnect(GeyserLocale.getPlayerLocaleString("geyser.network.remote.invalid_account", locale())); + } else { + disconnect(ex.toString()); + } return false; } From 14cf104cff850dc06438358b5415af32d43d8bd2 Mon Sep 17 00:00:00 2001 From: AJ Ferguson Date: Mon, 9 Sep 2024 18:23:19 -0400 Subject: [PATCH 17/18] Do not send thunder strength if not raining (#5031) * Do not send thunder strength if not raining * Address review * Minor optimization --- .../geyser/session/GeyserSession.java | 71 +++++++++++-------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 9a50d7acb..4589afe23 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -572,14 +572,16 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { private float walkSpeed; /** - * Caches current rain status. + * Caches current rain strength. + * Value between 0 and 1. */ - private boolean raining = false; + private float rainStrength = 0.0f; /** - * Caches current thunder status. + * Caches current thunder strength. + * Value between 0 and 1. */ - private boolean thunder = false; + private float thunderStrength = 0.0f; /** * Stores a map of all statistics sent from the server. @@ -2023,23 +2025,30 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { * @param strength value between 0 and 1 */ public void updateRain(float strength) { - this.raining = strength > 0; + boolean wasRaining = isRaining(); + this.rainStrength = strength; LevelEventPacket rainPacket = new LevelEventPacket(); - rainPacket.setType(this.raining ? LevelEvent.START_RAINING : LevelEvent.STOP_RAINING); + rainPacket.setType(isRaining() ? LevelEvent.START_RAINING : LevelEvent.STOP_RAINING); rainPacket.setData((int) (strength * 65535)); rainPacket.setPosition(Vector3f.ZERO); + sendUpstreamPacket(rainPacket); - if (this.raining) { - sendUpstreamPacket(rainPacket); - } else { - // The bedrock client might ignore this packet if it is sent in the same tick as another rain packet - // https://github.com/GeyserMC/Geyser/issues/3679 - scheduleInEventLoop(() -> { - if (!this.raining) { - sendUpstreamPacket(rainPacket); - } - }, 100, TimeUnit.MILLISECONDS); + // Keep thunder in sync with rain when starting/stopping a storm + if ((wasRaining != isRaining()) && isThunder()) { + if (isRaining()) { + LevelEventPacket thunderPacket = new LevelEventPacket(); + thunderPacket.setType(LevelEvent.START_THUNDERSTORM); + thunderPacket.setData((int) (this.thunderStrength * 65535)); + thunderPacket.setPosition(Vector3f.ZERO); + sendUpstreamPacket(thunderPacket); + } else { + LevelEventPacket thunderPacket = new LevelEventPacket(); + thunderPacket.setType(LevelEvent.STOP_THUNDERSTORM); + thunderPacket.setData(0); + thunderPacket.setPosition(Vector3f.ZERO); + sendUpstreamPacket(thunderPacket); + } } } @@ -2050,24 +2059,28 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { * @param strength value between 0 and 1 */ public void updateThunder(float strength) { - this.thunder = strength > 0; + this.thunderStrength = strength; + + // Do not send thunder packet if not raining + // The bedrock client will start raining automatically when updating thunder strength + // https://github.com/GeyserMC/Geyser/issues/3679 + if (!isRaining()) { + return; + } LevelEventPacket thunderPacket = new LevelEventPacket(); - thunderPacket.setType(this.thunder ? LevelEvent.START_THUNDERSTORM : LevelEvent.STOP_THUNDERSTORM); + thunderPacket.setType(isThunder() ? LevelEvent.START_THUNDERSTORM : LevelEvent.STOP_THUNDERSTORM); thunderPacket.setData((int) (strength * 65535)); thunderPacket.setPosition(Vector3f.ZERO); + sendUpstreamPacket(thunderPacket); + } - if (this.thunder) { - sendUpstreamPacket(thunderPacket); - } else { - // The bedrock client might ignore this packet if it is sent in the same tick as another thunderstorm packet - // https://github.com/GeyserMC/Geyser/issues/3679 - scheduleInEventLoop(() -> { - if (!this.thunder) { - sendUpstreamPacket(thunderPacket); - } - }, 100, TimeUnit.MILLISECONDS); - } + public boolean isRaining() { + return this.rainStrength > 0; + } + + public boolean isThunder() { + return this.thunderStrength > 0; } @Override From 73f7259b6dd508680582418bbb7963ad0b460907 Mon Sep 17 00:00:00 2001 From: Eclipse Date: Tue, 10 Sep 2024 20:10:31 +0000 Subject: [PATCH 18/18] Add proper text component parsing from NBT (#5029) * Attempt creating a simple NBT text component parser * Fix style merging * Rename TextDecoration to ChatDecoration, use better style deserialization in ChatDecoration * Remove unused code * containsKey optimisations, update documentation, improve getStyleFromNbtMap performance slightly, more slight tweaks * Remove unnecessary deserializeStyle method --- .../geyser/item/enchantment/Enchantment.java | 2 +- .../geysermc/geyser/level/JukeboxSong.java | 2 +- .../geyser/session/cache/RegistryCache.java | 4 +- ...extDecoration.java => ChatDecoration.java} | 34 ++---- .../translator/text/MessageTranslator.java | 103 ++++++++++++++++-- 5 files changed, 105 insertions(+), 40 deletions(-) rename core/src/main/java/org/geysermc/geyser/text/{TextDecoration.java => ChatDecoration.java} (74%) diff --git a/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java b/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java index 3c0caa60c..5cac45534 100644 --- a/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java +++ b/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java @@ -69,7 +69,7 @@ public record Enchantment(String identifier, // TODO - description is a component. So if a hardcoded literal string is given, this will display normally on Java, // but Geyser will attempt to lookup the literal string as translation - and will fail, displaying an empty string as enchantment name. - String description = bedrockEnchantment == null ? MessageTranslator.deserializeDescription(data) : null; + String description = bedrockEnchantment == null ? MessageTranslator.deserializeDescription(context.session(), data) : null; return new Enchantment(context.id().asString(), effects, supportedItems, maxLevel, description, anvilCost, exclusiveSet, bedrockEnchantment); diff --git a/core/src/main/java/org/geysermc/geyser/level/JukeboxSong.java b/core/src/main/java/org/geysermc/geyser/level/JukeboxSong.java index b00dc9f98..86d66e209 100644 --- a/core/src/main/java/org/geysermc/geyser/level/JukeboxSong.java +++ b/core/src/main/java/org/geysermc/geyser/level/JukeboxSong.java @@ -44,7 +44,7 @@ public record JukeboxSong(String soundEvent, String description) { soundEvent = ""; GeyserImpl.getInstance().getLogger().debug("Sound event for " + context.id() + " was of an unexpected type! Expected string or NBT map, got " + soundEventObject); } - String description = MessageTranslator.deserializeDescription(data); + String description = MessageTranslator.deserializeDescription(context.session(), data); return new JukeboxSong(soundEvent, description); } } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java index a393d461d..4a4167f15 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java @@ -49,7 +49,7 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.registry.JavaRegistry; import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; import org.geysermc.geyser.session.cache.registry.SimpleJavaRegistry; -import org.geysermc.geyser.text.TextDecoration; +import org.geysermc.geyser.text.ChatDecoration; import org.geysermc.geyser.translator.level.BiomeTranslator; import org.geysermc.geyser.util.MinecraftKey; import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; @@ -78,7 +78,7 @@ public final class RegistryCache { private static final Map>> REGISTRIES = new HashMap<>(); static { - register("chat_type", cache -> cache.chatTypes, TextDecoration::readChatType); + register("chat_type", cache -> cache.chatTypes, ChatDecoration::readChatType); register("dimension_type", cache -> cache.dimensions, JavaDimension::read); register("enchantment", cache -> cache.enchantments, Enchantment::read); register("jukebox_song", cache -> cache.jukeboxSongs, JukeboxSong::read); diff --git a/core/src/main/java/org/geysermc/geyser/text/TextDecoration.java b/core/src/main/java/org/geysermc/geyser/text/ChatDecoration.java similarity index 74% rename from core/src/main/java/org/geysermc/geyser/text/TextDecoration.java rename to core/src/main/java/org/geysermc/geyser/text/ChatDecoration.java index 94aec22ef..fc7597cfd 100644 --- a/core/src/main/java/org/geysermc/geyser/text/TextDecoration.java +++ b/core/src/main/java/org/geysermc/geyser/text/ChatDecoration.java @@ -25,17 +25,19 @@ package org.geysermc.geyser.text; -import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.Style; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtType; import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; +import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatType; import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatTypeDecoration; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; -public record TextDecoration(String translationKey, List parameters, Style deserializedStyle) implements ChatTypeDecoration { +public record ChatDecoration(String translationKey, List parameters, Style deserializedStyle) implements ChatTypeDecoration { @Override public NbtMap style() { @@ -53,38 +55,22 @@ public record TextDecoration(String translationKey, List parameters, String translationKey = chat.getString("translation_key"); NbtMap styleTag = chat.getCompound("style"); - Style style = deserializeStyle(styleTag); + Style style = MessageTranslator.getStyleFromNbtMap(styleTag); List parameters = new ArrayList<>(); List parametersNbt = chat.getList("parameters", NbtType.STRING); for (String parameter : parametersNbt) { parameters.add(ChatTypeDecoration.Parameter.valueOf(parameter.toUpperCase(Locale.ROOT))); } - return new ChatType(new TextDecoration(translationKey, parameters, style), null); + return new ChatType(new ChatDecoration(translationKey, parameters, style), null); } return new ChatType(null, null); } public static Style getStyle(ChatTypeDecoration decoration) { - if (decoration instanceof TextDecoration textDecoration) { - return textDecoration.deserializedStyle(); + if (decoration instanceof ChatDecoration chatDecoration) { + return chatDecoration.deserializedStyle(); } - return deserializeStyle(decoration.style()); - } - - private static Style deserializeStyle(NbtMap styleTag) { - Style.Builder builder = Style.style(); - if (!styleTag.isEmpty()) { - String color = styleTag.getString("color", null); - if (color != null) { - builder.color(NamedTextColor.NAMES.value(color)); - } - //TODO implement the rest - boolean italic = styleTag.getBoolean("italic"); - if (italic) { - builder.decorate(net.kyori.adventure.text.format.TextDecoration.ITALIC); - } - } - return builder.build(); + return MessageTranslator.getStyleFromNbtMap(decoration.style()); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java index 152bf4160..0547a21c9 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java @@ -26,16 +26,21 @@ package org.geysermc.geyser.translator.text; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.JoinConfiguration; import net.kyori.adventure.text.ScoreComponent; import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.flattener.ComponentFlattener; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.renderer.TranslatableComponentRenderer; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.legacy.CharacterAndFormat; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.cloudburstmc.nbt.NbtMap; +import org.cloudburstmc.nbt.NbtType; import org.cloudburstmc.protocol.bedrock.packet.TextPacket; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.session.GeyserSession; @@ -341,16 +346,16 @@ public class MessageTranslator { // Though, Bedrock cannot care about the signed stuff. TranslatableComponent.Builder withDecoration = Component.translatable() .key(chat.translationKey()) - .style(TextDecoration.getStyle(chat)); + .style(ChatDecoration.getStyle(chat)); List parameters = chat.parameters(); List args = new ArrayList<>(3); - if (parameters.contains(TextDecoration.Parameter.TARGET)) { + if (parameters.contains(ChatDecoration.Parameter.TARGET)) { args.add(targetName); } - if (parameters.contains(TextDecoration.Parameter.SENDER)) { + if (parameters.contains(ChatDecoration.Parameter.SENDER)) { args.add(sender); } - if (parameters.contains(TextDecoration.Parameter.CONTENT)) { + if (parameters.contains(ChatDecoration.Parameter.CONTENT)) { args.add(message); } withDecoration.arguments(args); @@ -426,17 +431,91 @@ public class MessageTranslator { } /** - * Deserialize an NbtMap provided from a registry into a string. + * Deserialize an NbtMap with a description text component (usually provided from a registry) into a Bedrock-formatted string. */ - // This may be a Component in the future. - public static String deserializeDescription(NbtMap tag) { + public static String deserializeDescription(GeyserSession session, NbtMap tag) { NbtMap description = tag.getCompound("description"); - String translate = description.getString("translate", null); - if (translate == null) { - GeyserImpl.getInstance().getLogger().debug("Don't know how to read description! " + tag); - return ""; + Component parsed = componentFromNbtTag(description); + return convertMessage(session, parsed); + } + + public static Component componentFromNbtTag(Object nbtTag) { + return componentFromNbtTag(nbtTag, Style.empty()); + } + + private static Component componentFromNbtTag(Object nbtTag, Style style) { + if (nbtTag instanceof String literal) { + return Component.text(literal).style(style); + } else if (nbtTag instanceof List list) { + return Component.join(JoinConfiguration.noSeparators(), componentsFromNbtList(list, style)); + } else if (nbtTag instanceof NbtMap map) { + Component component = null; + String text = map.getString("text", null); + if (text != null) { + component = Component.text(text); + } else { + String translateKey = map.getString("translate", null); + if (translateKey != null) { + String fallback = map.getString("fallback", ""); + List args = new ArrayList<>(); + + Object with = map.get("with"); + if (with instanceof List list) { + args = componentsFromNbtList(list, style); + } else if (with != null) { + args.add(componentFromNbtTag(with, style)); + } + component = Component.translatable(translateKey, fallback, args); + } + } + + if (component != null) { + Style newStyle = getStyleFromNbtMap(map, style); + component = component.style(newStyle); + + Object extra = map.get("extra"); + if (extra != null) { + component = component.append(componentFromNbtTag(extra, newStyle)); + } + + return component; + } } - return translate; + + throw new IllegalArgumentException("Expected tag to be a literal string, a list of components, or a component object with a text/translate key"); + } + + private static List componentsFromNbtList(List list, Style style) { + List components = new ArrayList<>(); + for (Object entry : list) { + components.add(componentFromNbtTag(entry, style)); + } + return components; + } + + public static Style getStyleFromNbtMap(NbtMap map) { + Style.Builder style = Style.style(); + + String colorString = map.getString("color", null); + if (colorString != null) { + if (colorString.startsWith(TextColor.HEX_PREFIX)) { + style.color(TextColor.fromHexString(colorString)); + } else { + style.color(NamedTextColor.NAMES.value(colorString)); + } + } + + map.listenForBoolean("bold", value -> style.decoration(TextDecoration.BOLD, value)); + map.listenForBoolean("italic", value -> style.decoration(TextDecoration.ITALIC, value)); + map.listenForBoolean("underlined", value -> style.decoration(TextDecoration.UNDERLINED, value)); + map.listenForBoolean("strikethrough", value -> style.decoration(TextDecoration.STRIKETHROUGH, value)); + map.listenForBoolean("obfuscated", value -> style.decoration(TextDecoration.OBFUSCATED, value)); + + return style.build(); + } + + public static Style getStyleFromNbtMap(NbtMap map, Style base) { + return base.merge(getStyleFromNbtMap(map)); } public static void init() {