Merge branch 'feature/1.20.5' into feature/1.20.5

This commit is contained in:
Hasan 2024-04-24 07:23:11 +03:00 committed by GitHub
commit a627333697
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 398 additions and 283 deletions

View File

@ -64,7 +64,7 @@ jobs:
with:
name: Geyser NeoForge
path: geyser/bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
#if-no-files-found: error // TODO 1.20.5 until neoforge updates
if-no-files-found: error
- name: Archive artifacts (Geyser Standalone)
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
if: success()

View File

@ -68,7 +68,7 @@ jobs:
with:
name: Geyser NeoForge
path: bootstrap/mod/neoforge/build/libs/Geyser-NeoForge.jar
#if-no-files-found: error // TODO 1.20.5 - currently no neoforge artifacts
if-no-files-found: error
- name: Archive artifacts (Geyser Standalone)
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3
if: success()

View File

@ -53,10 +53,10 @@ jobs:
with:
appID: ${{ secrets.RELEASE_APP_ID }}
appPrivateKey: ${{ secrets.RELEASE_APP_PK }}
# neoforge:Geyser-NeoForge.jar // TODO 1.20.5
files: |
bungeecord:Geyser-BungeeCord.jar
fabric:Geyser-Fabric.jar
neoforge:Geyser-NeoForge.jar
spigot:Geyser-Spigot.jar
standalone:Geyser-Standalone.jar
velocity:Geyser-Velocity.jar

View File

@ -1,6 +1,5 @@
architectury {
common("fabric")
//common("neoforge", "fabric") // todo 1.20.5
common("neoforge", "fabric")
}
loom {

View File

@ -25,6 +25,6 @@
"depends": {
"fabricloader": ">=0.15.10",
"fabric": "*",
"minecraft": ">=1.20.4"
"minecraft": ">=1.20.5"
}
}

View File

@ -14,12 +14,12 @@ config = "geyser.mixins.json"
[[dependencies.geyser_neoforge]]
modId="neoforge"
type="required"
versionRange="[20.4.48-beta,)"
versionRange="[20.5.0-beta,)"
ordering="NONE"
side="BOTH"
[[dependencies.geyser_neoforge]]
modId="minecraft"
type="required"
versionRange="[1.20,1.21)"
versionRange="[1.20.4,1.21)"
ordering="NONE"
side="BOTH"

View File

@ -25,7 +25,10 @@
package org.geysermc.geyser.platform.mod.world;
import com.github.steveice10.mc.protocol.data.game.Holder;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.item.component.BannerPatternLayer;
import com.github.steveice10.mc.protocol.data.game.item.component.DataComponentType;
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
@ -43,6 +46,7 @@ import net.minecraft.world.item.component.WrittenBookContent;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BannerBlockEntity;
import net.minecraft.world.level.block.entity.BannerPatternLayers;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.LecternBlockEntity;
import net.minecraft.world.level.chunk.ChunkAccess;
@ -62,6 +66,7 @@ import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.BlockEntityUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@ -217,8 +222,8 @@ public class GeyserModWorldManager extends GeyserWorldManager {
@NonNull
@Override
public CompletableFuture<com.github.steveice10.opennbt.tag.builtin.CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
CompletableFuture<com.github.steveice10.opennbt.tag.builtin.CompoundTag> future = new CompletableFuture<>();
public CompletableFuture<com.github.steveice10.mc.protocol.data.game.item.component.DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addNbtData) {
CompletableFuture<com.github.steveice10.mc.protocol.data.game.item.component.DataComponents> future = new CompletableFuture<>();
server.execute(() -> {
ServerPlayer player = getPlayer(session);
if (player == null) {
@ -235,7 +240,22 @@ public class GeyserModWorldManager extends GeyserWorldManager {
// the banner might have a custom name, both of which a Java client knows and caches
ItemStack itemStack = banner.getItem();
future.complete(null); // todo 1.20.5
com.github.steveice10.mc.protocol.data.game.item.component.DataComponents components =
new com.github.steveice10.mc.protocol.data.game.item.component.DataComponents(new HashMap<>());
components.put(DataComponentType.DAMAGE, itemStack.getDamageValue());
Component customName = itemStack.getComponents().get(DataComponents.CUSTOM_NAME);
if (customName != null) {
components.put(DataComponentType.CUSTOM_NAME, toKyoriComponent(customName));
}
BannerPatternLayers pattern = itemStack.get(DataComponents.BANNER_PATTERNS);
if (pattern != null) {
components.put(DataComponentType.BANNER_PATTERNS, toPatternList(pattern));
}
future.complete(components);
return;
}
future.complete(null);
@ -262,8 +282,7 @@ public class GeyserModWorldManager extends GeyserWorldManager {
if (writtenBookContent != null) {
return writtenBookContent.pages().stream()
.map(Filterable::raw)
.map((component) -> Component.Serializer.toJson(component, RegistryAccess.EMPTY))
.map((json -> LEGACY_SERIALIZER.serialize(GSON_SERIALIZER.deserializeOr(json, net.kyori.adventure.text.Component.empty()))))
.map(GeyserModWorldManager::fromComponent)
.toList();
} else {
WritableBookContent writableBookContent = itemStack.get(DataComponents.WRITABLE_BOOK_CONTENT);
@ -275,4 +294,25 @@ public class GeyserModWorldManager extends GeyserWorldManager {
.toList();
}
}
private static String fromComponent(Component component) {
String json = Component.Serializer.toJson(component, RegistryAccess.EMPTY);
return LEGACY_SERIALIZER.serialize(GSON_SERIALIZER.deserializeOr(json, net.kyori.adventure.text.Component.empty()));
}
private static net.kyori.adventure.text.Component toKyoriComponent(Component component) {
String json = Component.Serializer.toJson(component, RegistryAccess.EMPTY);
return GSON_SERIALIZER.deserializeOr(json, net.kyori.adventure.text.Component.empty());
}
private static List<BannerPatternLayer> toPatternList(BannerPatternLayers patternLayers) {
return patternLayers.layers().stream()
.map(layer -> {
BannerPatternLayer.BannerPattern pattern = new BannerPatternLayer.BannerPattern(
layer.pattern().value().assetId().toString(), layer.pattern().value().translationKey()
);
return new BannerPatternLayer(Holder.ofCustom(pattern), layer.color().getId());
})
.toList();
}
}

View File

@ -26,8 +26,8 @@
package org.geysermc.geyser.platform.spigot.world.manager;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.item.component.DataComponents;
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
@ -39,7 +39,6 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.nbt.NbtMap;
import org.geysermc.erosion.bukkit.BukkitLecterns;
import org.geysermc.erosion.bukkit.BukkitUtils;
import org.geysermc.erosion.bukkit.PickBlockUtils;
import org.geysermc.erosion.bukkit.SchedulerUtils;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.level.GameRule;
@ -205,8 +204,8 @@ public class GeyserSpigotWorldManager extends WorldManager {
}
@Override
public @NonNull CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
CompletableFuture<@Nullable CompoundTag> future = new CompletableFuture<>();
public @NonNull CompletableFuture<@Nullable DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addNbtData) {
CompletableFuture<@Nullable DataComponents> future = new CompletableFuture<>();
Player bukkitPlayer;
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {
future.complete(null);
@ -215,7 +214,7 @@ public class GeyserSpigotWorldManager extends WorldManager {
Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
// Paper 1.19.3 complains about async access otherwise.
// java.lang.IllegalStateException: Tile is null, asynchronous access?
SchedulerUtils.runTask(this.plugin, () -> future.complete(PickBlockUtils.pickBlock(block)), block);
SchedulerUtils.runTask(this.plugin, () -> future.complete(/*PickBlockUtils.pickBlock(block)*/ null), block); // TODO fix erosion once clear how to handle this
return future;
}

View File

@ -39,7 +39,7 @@ provided("io.netty", "netty-resolver-dns-native-macos")
provided("org.ow2.asm", "asm")
architectury {
minecraft = "1.20.4"
minecraft = "1.20.5"
}
loom {
@ -110,7 +110,7 @@ afterEvaluate {
}
dependencies {
minecraft("com.mojang:minecraft:1.20.5-rc3")
minecraft("com.mojang:minecraft:1.20.5")
mappings(loom.officialMojangMappings())
}

View File

@ -22,7 +22,7 @@ val basePlatforms = setOf(
val moddedPlatforms = setOf(
projects.fabric,
//projects.neoforge, // todo 1.20.5
projects.neoforge,
projects.mod
).map { it.dependencyProject }

View File

@ -53,6 +53,7 @@ import org.geysermc.geyser.translator.text.MessageTranslator;
public final class EntityDefinitions {
public static final EntityDefinition<AllayEntity> ALLAY;
public static final EntityDefinition<AreaEffectCloudEntity> AREA_EFFECT_CLOUD;
public static final EntityDefinition<ArmadilloEntity> ARMADILLO;
public static final EntityDefinition<ArmorStandEntity> ARMOR_STAND;
public static final EntityDefinition<TippedArrowEntity> ARROW;
public static final EntityDefinition<AxolotlEntity> AXOLOTL;
@ -770,6 +771,11 @@ public final class EntityDefinitions {
// Extends ageable
{
ARMADILLO = EntityDefinition.inherited(ArmadilloEntity::new, ageableEntityBase)
.type(EntityType.ARMADILLO)
.height(0.65f).width(0.7f)
.addTranslator(null)
.build();
AXOLOTL = EntityDefinition.inherited(AxolotlEntity::new, ageableEntityBase)
.type(EntityType.AXOLOTL)
.height(0.42f).width(0.7f)
@ -937,8 +943,7 @@ public final class EntityDefinitions {
LLAMA = EntityDefinition.inherited(LlamaEntity::new, chestedHorseEntityBase)
.type(EntityType.LLAMA)
.height(1.87f).width(0.9f)
.addTranslator(MetadataType.INT, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityDataTypes.STRENGTH, entityMetadata.getValue()))
.addTranslator(MetadataType.INT, LlamaEntity::setCarpetedColor)
.addTranslator(MetadataType.INT, LlamaEntity::setStrength)
.addTranslator(MetadataType.INT, (entity, entityMetadata) -> entity.getDirtyMetadata().put(EntityDataTypes.VARIANT, entityMetadata.getValue()))
.build();
TRADER_LLAMA = EntityDefinition.inherited(TraderLlamaEntity::new, LLAMA)

View File

@ -31,6 +31,7 @@ import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
@ -49,7 +50,7 @@ public class DolphinEntity extends WaterEntity {
@NonNull
@Override
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
if (!itemInHand.isEmpty() && session.getTagCache().isFish(itemInHand)) {
if (!itemInHand.isEmpty() && session.getTagCache().is(ItemTag.FISHES, itemInHand)) {
return InteractiveTag.FEED;
}
return super.testMobInteraction(hand, itemInHand);
@ -58,7 +59,7 @@ public class DolphinEntity extends WaterEntity {
@NonNull
@Override
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
if (!itemInHand.isEmpty() && session.getTagCache().isFish(itemInHand)) {
if (!itemInHand.isEmpty() && session.getTagCache().is(ItemTag.FISHES, itemInHand)) {
// Feed
return InteractionResult.SUCCESS;
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2019-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.entity.type.living.animal;
import org.cloudburstmc.math.vector.Vector3f;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.session.GeyserSession;
import java.util.UUID;
public class ArmadilloEntity extends AnimalEntity {
public ArmadilloEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
}
}

View File

@ -36,6 +36,7 @@ import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.geyser.util.InteractionResult;
@ -61,7 +62,7 @@ public class AxolotlEntity extends AnimalEntity {
@Override
public boolean canEat(Item item) {
return session.getTagCache().isAxolotlFood(item);
return session.getTagCache().is(ItemTag.AXOLOTL_FOOD, item);
}
@Override

View File

@ -35,6 +35,7 @@ import org.cloudburstmc.protocol.bedrock.packet.EntityEventPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import java.util.UUID;
@ -67,6 +68,6 @@ public class BeeEntity extends AnimalEntity {
@Override
public boolean canEat(Item item) {
return session.getTagCache().isFlower(item);
return session.getTagCache().is(ItemTag.FLOWERS, item);
}
}

View File

@ -33,6 +33,7 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import java.util.UUID;
@ -56,6 +57,6 @@ public class FoxEntity extends AnimalEntity {
@Override
public boolean canEat(Item item) {
return session.getTagCache().isFoxFood(item);
return session.getTagCache().is(ItemTag.FOX_FOOD, item);
}
}

View File

@ -33,8 +33,8 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.FlowerItem;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
@ -77,7 +77,7 @@ public class MooshroomEntity extends AnimalEntity {
} else if (!isBaby && isAlive() && itemInHand.asItem() == Items.SHEARS) {
// Shear items
return InteractionResult.SUCCESS;
} else if (isBrown && session.getTagCache().isSmallFlower(itemInHand) && itemInHand.asItem() instanceof FlowerItem) {
} else if (isBrown && session.getTagCache().is(ItemTag.SMALL_FLOWERS, itemInHand)) {
// ?
return InteractionResult.SUCCESS;
}

View File

@ -39,6 +39,7 @@ import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.type.Tickable;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import java.util.UUID;
@ -72,7 +73,7 @@ public class SnifferEntity extends AnimalEntity implements Tickable {
@Override
public boolean canEat(Item item) {
return session.getTagCache().isSnifferFood(item);
return session.getTagCache().is(ItemTag.SNIFFER_FOOD, item);
}
public void setSnifferState(ObjectEntityMetadata<SnifferState> entityMetadata) {

View File

@ -26,18 +26,23 @@
package org.geysermc.geyser.entity.type.living.animal.horse;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import lombok.Getter;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.packet.MobArmorEquipmentPacket;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.MathUtils;
import java.util.UUID;
public class LlamaEntity extends ChestedHorseEntity {
/**
* Used to calculate inventory size
*/
@Getter
private int strength = 1;
public LlamaEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
@ -45,28 +50,9 @@ public class LlamaEntity extends ChestedHorseEntity {
dirtyMetadata.put(EntityDataTypes.CONTAINER_STRENGTH_MODIFIER, 3); // Presumably 3 slots for every 1 strength
}
/**
* Color equipped on the llama
*/
public void setCarpetedColor(IntEntityMetadata entityMetadata) {
// Bedrock treats llama decoration as armor
MobArmorEquipmentPacket equipmentPacket = new MobArmorEquipmentPacket();
equipmentPacket.setRuntimeEntityId(geyserId);
// -1 means no armor
int carpetIndex = entityMetadata.getPrimitiveValue();
if (carpetIndex > -1 && carpetIndex <= 15) {
// The damage value is the dye color that Java sends us, for pre-1.16.220
// The item is always going to be a carpet
equipmentPacket.setChestplate(session.getItemMappings().getCarpets().get(carpetIndex));
} else {
equipmentPacket.setChestplate(ItemData.AIR);
}
// Required to fill out the rest of the equipment or Bedrock ignores it, including above else statement if removing armor
equipmentPacket.setBoots(ItemData.AIR);
equipmentPacket.setHelmet(ItemData.AIR);
equipmentPacket.setLeggings(ItemData.AIR);
session.sendUpstreamPacket(equipmentPacket);
public void setStrength(IntEntityMetadata entityMetadata) {
strength = MathUtils.constrain(entityMetadata.getPrimitiveValue(), 1, 5);
this.dirtyMetadata.put(EntityDataTypes.STRENGTH, strength);
}
@Override

View File

@ -133,16 +133,29 @@ public class WolfEntity extends TameableEntity {
if (itemInHand.asItem() == Items.BONE && !getFlag(EntityFlag.TAMED)) {
// Bone and untamed - can tame
return InteractiveTag.TAME;
} else {
if (itemInHand.asItem() instanceof DyeItem item) {
}
if (getFlag(EntityFlag.TAMED) && ownerBedrockId == session.getPlayerEntity().getGeyserId()) {
if (itemInHand.asItem() instanceof DyeItem dyeItem) {
// If this fails, as of Java Edition 1.18.1, you cannot toggle sit/stand
if (item.dyeColor() != this.collarColor) {
if (dyeItem.dyeColor() != this.collarColor) {
return InteractiveTag.DYE;
} else {
return super.testMobInteraction(hand, itemInHand);
}
} else if (getFlag(EntityFlag.TAMED) && ownerBedrockId == session.getPlayerEntity().getGeyserId()) {
// Tamed and owned by player - can sit/stand
return getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT;
}
if (itemInHand.asItem() == Items.WOLF_ARMOR && !this.chestplate.isValid() && !getFlag(EntityFlag.BABY)) {
return InteractiveTag.EQUIP_WOLF_ARMOR;
}
if (itemInHand.asItem() == Items.SHEARS && this.chestplate.isValid()) { // TODO: check curse of binding
return InteractiveTag.REMOVE_WOLF_ARMOR;
}
if (Items.WOLF_ARMOR.isValidRepairItem(itemInHand.asItem()) && getFlag(EntityFlag.SITTING) &&
this.chestplate.isValid() && this.chestplate.getTag() != null &&
this.chestplate.getTag().getInt("Damage") > 0) {
return InteractiveTag.REPAIR_WOLF_ARMOR;
}
// Tamed and owned by player - can sit/stand
return getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT;
}
return super.testMobInteraction(hand, itemInHand);
}

View File

@ -35,6 +35,7 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
@ -65,7 +66,7 @@ public class CreeperEntity extends MonsterEntity {
@NonNull
@Override
protected InteractiveTag testMobInteraction(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
if (session.getTagCache().isCreeperIgniter(itemInHand.asItem())) {
if (session.getTagCache().is(ItemTag.CREEPER_IGNITERS, itemInHand)) {
return InteractiveTag.IGNITE_CREEPER;
} else {
return super.testMobInteraction(hand, itemInHand);
@ -75,7 +76,7 @@ public class CreeperEntity extends MonsterEntity {
@NonNull
@Override
protected InteractionResult mobInteract(@NonNull Hand hand, @NonNull GeyserItemStack itemInHand) {
if (session.getTagCache().isCreeperIgniter(itemInHand.asItem())) {
if (session.getTagCache().is(ItemTag.CREEPER_IGNITERS, itemInHand)) {
// Ignite creeper - as of 1.19.3
session.playSoundEvent(SoundEvent.IGNITE, position);
return InteractionResult.SUCCESS;

View File

@ -35,6 +35,7 @@ import org.geysermc.geyser.entity.EntityDefinition;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import org.geysermc.geyser.util.InteractionResult;
import org.geysermc.geyser.util.InteractiveTag;
@ -65,7 +66,7 @@ public class PiglinEntity extends BasePiglinEntity {
@Override
public void updateOffHand(GeyserSession session) {
// Check if the Piglin is holding Gold and set the ADMIRING flag accordingly so its pose updates
setFlag(EntityFlag.ADMIRING, session.getTagCache().shouldPiglinAdmire(session.getItemMappings().getMapping(this.offHand).getJavaItem()));
setFlag(EntityFlag.ADMIRING, session.getTagCache().is(ItemTag.PIGLIN_LOVED, session.getItemMappings().getMapping(this.offHand).getJavaItem()));
super.updateBedrockMetadata();
super.updateOffHand(session);

View File

@ -25,8 +25,8 @@
package org.geysermc.geyser.erosion;
import com.github.steveice10.mc.protocol.data.game.item.component.DataComponents;
import com.github.steveice10.mc.protocol.data.game.level.block.value.PistonValueType;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import io.netty.channel.Channel;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
@ -43,7 +43,14 @@ import org.geysermc.erosion.packet.ErosionPacketHandler;
import org.geysermc.erosion.packet.ErosionPacketSender;
import org.geysermc.erosion.packet.backendbound.BackendboundInitializePacket;
import org.geysermc.erosion.packet.backendbound.BackendboundPacket;
import org.geysermc.erosion.packet.geyserbound.*;
import org.geysermc.erosion.packet.geyserbound.GeyserboundBatchBlockIdPacket;
import org.geysermc.erosion.packet.geyserbound.GeyserboundBlockEntityPacket;
import org.geysermc.erosion.packet.geyserbound.GeyserboundBlockIdPacket;
import org.geysermc.erosion.packet.geyserbound.GeyserboundBlockLookupFailPacket;
import org.geysermc.erosion.packet.geyserbound.GeyserboundBlockPlacePacket;
import org.geysermc.erosion.packet.geyserbound.GeyserboundHandshakePacket;
import org.geysermc.erosion.packet.geyserbound.GeyserboundPickBlockPacket;
import org.geysermc.erosion.packet.geyserbound.GeyserboundPistonEventPacket;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.level.physics.Direction;
import org.geysermc.geyser.network.GameProtocol;
@ -64,7 +71,7 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke
@Setter
private CompletableFuture<int[]> pendingBatchLookup = null;
@Setter
private CompletableFuture<CompoundTag> pickBlockLookup = null;
private CompletableFuture<DataComponents> pickBlockLookup = null;
private final AtomicInteger nextTransactionId = new AtomicInteger(1);
@ -140,7 +147,7 @@ public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacke
@Override
public void handlePickBlock(GeyserboundPickBlockPacket packet) {
if (this.pickBlockLookup != null) {
this.pickBlockLookup.complete(packet.getTag());
//this.pickBlockLookup.complete(packet.getTag()); // TODO 1.20.5
}
}

View File

@ -26,8 +26,8 @@
package org.geysermc.geyser.level;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.item.component.DataComponents;
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
@ -36,7 +36,11 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.geysermc.erosion.packet.backendbound.*;
import org.geysermc.erosion.packet.backendbound.BackendboundBatchBlockEntityPacket;
import org.geysermc.erosion.packet.backendbound.BackendboundBatchBlockRequestPacket;
import org.geysermc.erosion.packet.backendbound.BackendboundBlockEntityPacket;
import org.geysermc.erosion.packet.backendbound.BackendboundBlockRequestPacket;
import org.geysermc.erosion.packet.backendbound.BackendboundPickBlockPacket;
import org.geysermc.erosion.util.BlockPositionIterator;
import org.geysermc.erosion.util.LecternUtils;
import org.geysermc.geyser.session.GeyserSession;
@ -174,12 +178,12 @@ public class GeyserWorldManager extends WorldManager {
@NonNull
@Override
public CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
public CompletableFuture<@Nullable DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addNbtData) {
var erosionHandler = session.getErosionHandler().getAsActive();
if (erosionHandler == null) {
return super.getPickItemNbt(session, x, y, z, addNbtData);
return super.getPickItemComponents(session, x, y, z, addNbtData);
}
CompletableFuture<CompoundTag> future = new CompletableFuture<>();
CompletableFuture<DataComponents> future = new CompletableFuture<>();
erosionHandler.setPickBlockLookup(future);
erosionHandler.sendPacket(new BackendboundPickBlockPacket(Vector3i.from(x, y, z)));
return future;

View File

@ -26,9 +26,9 @@
package org.geysermc.geyser.level;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.item.component.DataComponents;
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.math.vector.Vector3i;
@ -220,7 +220,7 @@ public abstract class WorldManager {
* @return expected NBT for this item.
*/
@NonNull
public CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
public CompletableFuture<@Nullable DataComponents> getPickItemComponents(GeyserSession session, int x, int y, int z, boolean addExtraData) {
return CompletableFuture.completedFuture(null);
}
}

View File

@ -33,6 +33,7 @@ import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.MobArmorEquipment
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.MobEquipmentSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.PlayerHotbarSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.SetEntityLinkSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v291.serializer.SetEntityMotionSerializer_v291;
import org.cloudburstmc.protocol.bedrock.codec.v390.serializer.PlayerSkinSerializer_v390;
import org.cloudburstmc.protocol.bedrock.codec.v407.serializer.InventoryContentSerializer_v407;
import org.cloudburstmc.protocol.bedrock.codec.v407.serializer.InventorySlotSerializer_v407;
@ -50,7 +51,6 @@ import org.cloudburstmc.protocol.bedrock.packet.CodeBuilderSourcePacket;
import org.cloudburstmc.protocol.bedrock.packet.CraftingEventPacket;
import org.cloudburstmc.protocol.bedrock.packet.CreatePhotoPacket;
import org.cloudburstmc.protocol.bedrock.packet.DebugInfoPacket;
import org.cloudburstmc.protocol.bedrock.packet.DisconnectPacket;
import org.cloudburstmc.protocol.bedrock.packet.EditorNetworkPacket;
import org.cloudburstmc.protocol.bedrock.packet.EntityFallPacket;
import org.cloudburstmc.protocol.bedrock.packet.GameTestRequestPacket;
@ -182,9 +182,18 @@ class CodecProcessor {
};
/**
* Serializer that does nothing when trying to deserialize SetEntityMotionPacket since it is not used from the client.
* Serializer that does nothing when trying to deserialize SetEntityMotionPacket since it is not used from the client for codec v291.
*/
private static final BedrockPacketSerializer<SetEntityMotionPacket> SET_ENTITY_MOTION_SERIALIZER = new SetEntityMotionSerializer_v662() {
private static final BedrockPacketSerializer<SetEntityMotionPacket> SET_ENTITY_MOTION_SERIALIZER_V291 = new SetEntityMotionSerializer_v291() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, SetEntityMotionPacket packet) {
}
};
/**
* Serializer that does nothing when trying to deserialize SetEntityMotionPacket since it is not used from the client for codec v662.
*/
private static final BedrockPacketSerializer<SetEntityMotionPacket> SET_ENTITY_MOTION_SERIALIZER_V662 = new SetEntityMotionSerializer_v662() {
@Override
public void deserialize(ByteBuf buffer, BedrockCodecHelper helper, SetEntityMotionPacket packet) {
}
@ -251,7 +260,9 @@ class CodecProcessor {
.updateSerializer(PlayerHotbarPacket.class, PLAYER_HOTBAR_SERIALIZER)
.updateSerializer(PlayerSkinPacket.class, PLAYER_SKIN_SERIALIZER)
.updateSerializer(SetEntityDataPacket.class, SET_ENTITY_DATA_SERIALIZER)
.updateSerializer(SetEntityMotionPacket.class, SET_ENTITY_MOTION_SERIALIZER)
.updateSerializer(SetEntityMotionPacket.class, codec.getProtocolVersion() < 662 ?
SET_ENTITY_MOTION_SERIALIZER_V291 :
SET_ENTITY_MOTION_SERIALIZER_V662)
.updateSerializer(SetEntityLinkPacket.class, SET_ENTITY_LINK_SERIALIZER)
// Valid serverbound packets where reading of some fields can be skipped
.updateSerializer(MobEquipmentPacket.class, MOB_EQUIPMENT_SERIALIZER)
@ -261,7 +272,6 @@ class CodecProcessor {
.updateSerializer(ScriptMessagePacket.class, ILLEGAL_SERIALIZER)
// // Ignored bidirectional packets
.updateSerializer(ClientCacheStatusPacket.class, IGNORED_SERIALIZER)
.updateSerializer(DisconnectPacket.class, IGNORED_SERIALIZER)
.updateSerializer(SimpleEventPacket.class, IGNORED_SERIALIZER)
.updateSerializer(TickSyncPacket.class, IGNORED_SERIALIZER)
.updateSerializer(MultiplayerSettingsPacket.class, IGNORED_SERIALIZER)

View File

@ -159,7 +159,6 @@ public class ItemRegistryPopulator {
Object2ObjectMap<CustomBlockData, ItemDefinition> customBlockItemDefinitions = new Object2ObjectOpenHashMap<>();
List<ItemDefinition> buckets = new ObjectArrayList<>();
List<ItemData> carpets = new ObjectArrayList<>();
List<ItemMapping> mappings = new ObjectArrayList<>();
// Temporary mapping to create stored items
@ -458,14 +457,6 @@ public class ItemRegistryPopulator {
if (javaItem.javaIdentifier().contains("bucket") && !javaItem.javaIdentifier().contains("milk")) {
buckets.add(definition);
} else if (javaItem.javaIdentifier().contains("_carpet") && !javaItem.javaIdentifier().contains("moss")) {
// This should be the numerical order Java sends as an integer value for llamas
carpets.add(ItemData.builder()
.definition(definition)
.damage(mapping.getBedrockData())
.count(1)
.blockDefinition(mapping.getBedrockBlockDefinition())
.build());
} else if (javaItem.javaIdentifier().startsWith("minecraft:music_disc_")) {
// The Java record level event uses the item ID as the "key" to play the record
Registries.RECORDS.register(javaItem.javaId(), SoundEvent.valueOf("RECORD_" +
@ -589,7 +580,6 @@ public class ItemRegistryPopulator {
.storedItems(new StoredItemMappings(javaItemToMapping))
.javaOnlyItems(javaOnlyItems)
.buckets(buckets)
.carpets(carpets)
.componentItemData(componentItemData)
.lodestoneCompass(lodestoneEntry)
.customIdMappings(customIdMappings)

View File

@ -69,7 +69,6 @@ public class ItemMappings implements DefinitionRegistry<ItemDefinition> {
List<ItemDefinition> buckets;
List<ItemDefinition> boats;
List<ItemData> carpets;
List<ComponentItemData> componentItemData;
Int2ObjectMap<String> customIdMappings;
@ -99,9 +98,9 @@ public class ItemMappings implements DefinitionRegistry<ItemDefinition> {
return javaId >= 0 && javaId < this.items.length ? this.items[javaId] : ItemMapping.AIR;
}
@Nullable
@NonNull
public ItemMapping getMapping(Item javaItem) {
return getMapping(javaItem.javaIdentifier());
return getMapping(javaItem.javaId());
}
/**

View File

@ -57,7 +57,7 @@ import java.util.function.ToIntFunction;
* Stores any information sent via Java registries. May not contain all data in a given registry - we'll strip what's
* unneeded.
*
* Crafted as of 1.20.5 for easy "add new registry" in the future.
* Crafted as of 1.20.5 for easy "add new registry" functionality in the future.
*/
@Accessors(fluent = true)
@Getter
@ -118,6 +118,7 @@ public final class RegistryCache {
REGISTRIES.put("minecraft:" + registry, (registryCache, entries) -> {
Int2ObjectMap<T> localCache = localCacheFunction.apply(registryCache);
// Clear each local cache every time a new registry entry is given to us
// (e.g. proxy server switches)
localCache.clear();
for (int i = 0; i < entries.size(); i++) {
RegistryEntry entry = entries.get(i);

View File

@ -27,64 +27,45 @@ package org.geysermc.geyser.session.cache;
import com.github.steveice10.mc.protocol.packet.common.clientbound.ClientboundUpdateTagsPacket;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntLists;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.registry.type.BlockMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.BlockTag;
import org.geysermc.geyser.session.cache.tags.ItemTag;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
/**
* Manages information sent from the {@link ClientboundUpdateTagsPacket}. If that packet is not sent, all lists here
* will remain empty, matching Java Edition behavior.
*
* This system is designed for easy extensibility - just add an enum to {@link BlockTag} or {@link ItemTag}.
*/
@ParametersAreNonnullByDefault
public class TagCache {
/* Blocks */
private IntList leaves;
private IntList wool;
public final class TagCache {
// Put these here so the enums can load without a static map
public static final Map<String, BlockTag> ALL_BLOCK_TAGS = new HashMap<>();
public static final Map<String, ItemTag> ALL_ITEM_TAGS = new HashMap<>();
private IntList axeEffective;
private IntList hoeEffective;
private IntList pickaxeEffective;
private IntList shovelEffective;
private IntList requiresStoneTool;
private IntList requiresIronTool;
private IntList requiresDiamondTool;
/* Items */
private IntList axolotlFood;
private IntList creeperIgniters;
private IntList fishes;
private IntList flowers;
private IntList foxFood;
private IntList piglinLoved;
private IntList smallFlowers;
private IntList snifferFood;
public TagCache() {
// Ensure all lists are non-null
clear();
}
private final Map<BlockTag, IntList> blocks = new EnumMap<>(BlockTag.class);
private final Map<ItemTag, IntList> items = new EnumMap<>(ItemTag.class);
public void loadPacket(GeyserSession session, ClientboundUpdateTagsPacket packet) {
Map<String, int[]> blockTags = packet.getTags().get("minecraft:block");
this.leaves = IntList.of(blockTags.get("minecraft:leaves"));
this.wool = IntList.of(blockTags.get("minecraft:wool"));
this.axeEffective = IntList.of(blockTags.get("minecraft:mineable/axe"));
this.hoeEffective = IntList.of(blockTags.get("minecraft:mineable/hoe"));
this.pickaxeEffective = IntList.of(blockTags.get("minecraft:mineable/pickaxe"));
this.shovelEffective = IntList.of(blockTags.get("minecraft:mineable/shovel"));
this.requiresStoneTool = IntList.of(blockTags.get("minecraft:needs_stone_tool"));
this.requiresIronTool = IntList.of(blockTags.get("minecraft:needs_iron_tool"));
this.requiresDiamondTool = IntList.of(blockTags.get("minecraft:needs_diamond_tool"));
this.blocks.clear();
ALL_BLOCK_TAGS.forEach((location, tag) -> {
int[] values = blockTags.get(location);
if (values != null) {
this.blocks.put(tag, IntList.of(values));
} else {
session.getGeyser().getLogger().debug("Block tag not found from server: " + location);
}
});
// Hack btw
GeyserLogger logger = session.getGeyser().getLogger();
@ -96,14 +77,15 @@ public class TagCache {
}
Map<String, int[]> itemTags = packet.getTags().get("minecraft:item");
this.axolotlFood = IntList.of(itemTags.get("minecraft:axolotl_food"));
this.creeperIgniters = load(itemTags.get("minecraft:creeper_igniters"));
this.fishes = IntList.of(itemTags.get("minecraft:fishes"));
this.flowers = IntList.of(itemTags.get("minecraft:flowers"));
this.foxFood = IntList.of(itemTags.get("minecraft:fox_food"));
this.piglinLoved = IntList.of(itemTags.get("minecraft:piglin_loved"));
this.smallFlowers = IntList.of(itemTags.get("minecraft:small_flowers"));
this.snifferFood = load(itemTags.get("minecraft:sniffer_food"));
this.items.clear();
ALL_ITEM_TAGS.forEach((location, tag) -> {
int[] values = itemTags.get(location);
if (values != null) {
this.items.put(tag, IntList.of(values));
} else {
session.getGeyser().getLogger().debug("Item tag not found from server: " + location);
}
});
// Hack btw
boolean emulatePost1_13Logic = itemTags.get("minecraft:signs").length > 1;
@ -113,98 +95,32 @@ public class TagCache {
}
}
private IntList load(int @Nullable[] tags) {
if (tags == null) {
return IntLists.EMPTY_LIST;
/**
* @return true if the block tag is present and contains this block mapping's Java ID.
*/
public boolean is(BlockTag tag, BlockMapping mapping) {
IntList values = this.blocks.get(tag);
if (values != null) {
return values.contains(mapping.getJavaBlockId());
}
return IntList.of(tags);
return false;
}
public void clear() {
this.leaves = IntLists.emptyList();
this.wool = IntLists.emptyList();
this.axeEffective = IntLists.emptyList();
this.hoeEffective = IntLists.emptyList();
this.pickaxeEffective = IntLists.emptyList();
this.shovelEffective = IntLists.emptyList();
this.requiresStoneTool = IntLists.emptyList();
this.requiresIronTool = IntLists.emptyList();
this.requiresDiamondTool = IntLists.emptyList();
this.axolotlFood = IntLists.emptyList();
this.creeperIgniters = IntLists.emptyList();
this.fishes = IntLists.emptyList();
this.flowers = IntLists.emptyList();
this.foxFood = IntLists.emptyList();
this.piglinLoved = IntLists.emptyList();
this.smallFlowers = IntLists.emptyList();
this.snifferFood = IntLists.emptyList();
/**
* @return true if the item tag is present and contains this item stack's Java ID.
*/
public boolean is(ItemTag tag, GeyserItemStack itemStack) {
return is(tag, itemStack.asItem());
}
public boolean isAxolotlFood(Item item) {
return axolotlFood.contains(item.javaId());
}
public boolean isCreeperIgniter(Item item) {
return creeperIgniters.contains(item.javaId());
}
public boolean isFish(GeyserItemStack itemStack) {
return fishes.contains(itemStack.getJavaId());
}
public boolean isFlower(Item item) {
return flowers.contains(item.javaId());
}
public boolean isFoxFood(Item item) {
return foxFood.contains(item.javaId());
}
public boolean shouldPiglinAdmire(Item item) {
return piglinLoved.contains(item.javaId());
}
public boolean isSmallFlower(GeyserItemStack itemStack) {
return smallFlowers.contains(itemStack.getJavaId());
}
public boolean isSnifferFood(Item item) {
return snifferFood.contains(item.javaId());
}
public boolean isAxeEffective(BlockMapping blockMapping) {
return axeEffective.contains(blockMapping.getJavaBlockId());
}
public boolean isHoeEffective(BlockMapping blockMapping) {
return hoeEffective.contains(blockMapping.getJavaBlockId());
}
public boolean isPickaxeEffective(BlockMapping blockMapping) {
return pickaxeEffective.contains(blockMapping.getJavaBlockId());
}
public boolean isShovelEffective(BlockMapping blockMapping) {
return shovelEffective.contains(blockMapping.getJavaBlockId());
}
public boolean isShearsEffective(BlockMapping blockMapping) {
int javaBlockId = blockMapping.getJavaBlockId();
return leaves.contains(javaBlockId) || wool.contains(javaBlockId);
}
public boolean requiresStoneTool(BlockMapping blockMapping) {
return requiresStoneTool.contains(blockMapping.getJavaBlockId());
}
public boolean requiresIronTool(BlockMapping blockMapping) {
return requiresIronTool.contains(blockMapping.getJavaBlockId());
}
public boolean requiresDiamondTool(BlockMapping blockMapping) {
return requiresDiamondTool.contains(blockMapping.getJavaBlockId());
/**
* @return true if the item tag is present and contains this item's Java ID.
*/
public boolean is(ItemTag tag, Item item) {
IntList values = this.items.get(tag);
if (values != null) {
return values.contains(item.javaId());
}
return false;
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2019-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.session.cache.tags;
import org.geysermc.geyser.session.cache.TagCache;
public enum BlockTag {
LEAVES("leaves"),
WOOL("wool"),
AXE_EFFECTIVE("mineable/axe"),
HOE_EFFECTIVE("mineable/hoe"),
PICKAXE_EFFECTIVE("mineable/pickaxe"),
SHOVEL_EFFECTIVE("mineable/shovel"),
NEEDS_STONE_TOOL("needs_stone_tool"),
NEEDS_IRON_TOOL("needs_iron_tool"),
NEEDS_DIAMOND_TOOL("needs_diamond_tool");
BlockTag(String identifier) {
register(identifier, this);
}
private static void register(String name, BlockTag tag) {
TagCache.ALL_BLOCK_TAGS.put("minecraft:" + name, tag);
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2019-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.session.cache.tags;
import org.geysermc.geyser.session.cache.TagCache;
public enum ItemTag {
AXOLOTL_FOOD("axolotl_food"),
CREEPER_IGNITERS("creeper_igniters"),
FISHES("fishes"),
FLOWERS("flowers"),
FOX_FOOD("fox_food"),
PIGLIN_LOVED("piglin_loved"),
SMALL_FLOWERS("small_flowers"),
SNIFFER_FOOD("sniffer_food");
ItemTag(String identifier) {
register(identifier, this);
}
private static void register(String name, ItemTag tag) {
TagCache.ALL_ITEM_TAGS.put("minecraft:" + name, tag);
}
}

View File

@ -47,10 +47,6 @@ public class BiomeTranslator {
public static int loadServerBiome(RegistryEntry entry) {
String javaIdentifier = entry.getId();
return Registries.BIOME_IDENTIFIERS.get().getOrDefault(javaIdentifier, 0);
// if (javaId == 0) {
// // Matches Java behavior when it sees an invalid biome - it just replaces it with ID 0
// biomeTranslations.defaultReturnValue(bedrockId);
// }
}
public static BlockStorage toNewBedrockBiome(GeyserSession session, DataPalette biomeData) {

View File

@ -43,12 +43,16 @@ public abstract class BlockEntityTranslator {
public abstract void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState);
public NbtMap getBlockEntityTag(GeyserSession session, BlockEntityType type, int x, int y, int z, CompoundTag tag, int blockState) {
NbtMapBuilder tagBuilder = getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId(type), x, y, z);
NbtMapBuilder tagBuilder = getConstantBedrockTag(type, x, y, z);
translateTag(tagBuilder, tag, blockState);
return tagBuilder.build();
}
protected NbtMapBuilder getConstantBedrockTag(String bedrockId, int x, int y, int z) {
public static NbtMapBuilder getConstantBedrockTag(BlockEntityType type, int x, int y, int z) {
return getConstantBedrockTag(BlockEntityUtils.getBedrockBlockEntityId(type), x, y, z);
}
public static NbtMapBuilder getConstantBedrockTag(String bedrockId, int x, int y, int z) {
return NbtMap.builder()
.putInt("x", x)
.putInt("y", y)

View File

@ -26,9 +26,6 @@
package org.geysermc.geyser.translator.protocol.bedrock;
import com.github.steveice10.mc.protocol.data.game.item.ItemStack;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.protocol.bedrock.packet.BlockPickRequestPacket;
import org.geysermc.geyser.entity.EntityDefinitions;
@ -68,33 +65,23 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator<BlockPic
}
BlockMapping blockMapping = BlockRegistries.JAVA_BLOCKS.getOrDefault(blockToPick, BlockMapping.DEFAULT);
boolean addNbtData = packet.isAddUserData() && blockMapping.isBlockEntity(); // Holding down CTRL
/*if (BlockStateValues.getBannerColor(blockToPick) != -1 || addNbtData) { //TODO
session.getGeyser().getWorldManager().getPickItemNbt(session, vector.getX(), vector.getY(), vector.getZ(), addNbtData)
.whenComplete((tag, ex) -> session.ensureInEventLoop(() -> {
if (tag == null) {
boolean addExtraData = packet.isAddUserData() && blockMapping.isBlockEntity(); // Holding down CTRL
if (BlockStateValues.getBannerColor(blockToPick) != -1 || addExtraData) { //TODO
session.getGeyser().getWorldManager().getPickItemComponents(session, vector.getX(), vector.getY(), vector.getZ(), addExtraData)
.whenComplete((components, ex) -> session.ensureInEventLoop(() -> {
if (components == null) {
pickItem(session, blockMapping);
return;
}
if (addNbtData) {
ListTag lore = new ListTag("Lore");
lore.add(new StringTag("", "\"(+NBT)\""));
CompoundTag display = tag.get("display");
if (display == null) {
display = new CompoundTag("display");
tag.put(display);
}
display.put(lore);
}
// I don't really like this... I'd rather get an ID from the block mapping I think
ItemMapping mapping = session.getItemMappings().getMapping(blockMapping.getPickItem());
ItemStack itemStack = new ItemStack(mapping.getJavaItem().javaId(), 1, tag);
ItemStack itemStack = new ItemStack(mapping.getJavaItem().javaId(), 1, components);
InventoryUtils.findOrCreateItem(session, itemStack);
}));
return;
}*/
}
pickItem(session, blockMapping);
}

View File

@ -74,7 +74,8 @@ public class JavaSetEquipmentTranslator extends PacketTranslator<ClientboundSetE
livingEntity.setHelmet(item);
armorUpdated = true;
}
case CHESTPLATE -> {
case CHESTPLATE, BODY -> {
// BODY is sent for llamas with a carpet equipped, as of 1.20.5
livingEntity.setChestplate(item);
armorUpdated = true;
}

View File

@ -29,6 +29,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.inventory.Cli
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType;
import org.cloudburstmc.protocol.bedrock.packet.UpdateEquipPacket;
import org.geysermc.geyser.entity.type.Entity;
@ -114,16 +115,25 @@ public class JavaHorseScreenOpenTranslator extends PacketTranslator<ClientboundH
// Since 1.20.5, the armor slot is not included in the container size,
// but everything is still indexed the same.
int slotCount = packet.getNumberOfSlots() + 1;
int slotCount = 2; // Don't depend on slot count sent from server
InventoryTranslator inventoryTranslator;
if (entity instanceof LlamaEntity) {
if (entity instanceof LlamaEntity llamaEntity) {
if (entity.getFlag(EntityFlag.CHESTED)) {
slotCount += llamaEntity.getStrength() * 3;
}
inventoryTranslator = new LlamaInventoryTranslator(slotCount);
slots.add(CARPET_SLOT);
} else if (entity instanceof ChestedHorseEntity) {
if (entity.getFlag(EntityFlag.CHESTED)) {
slotCount += 15;
}
inventoryTranslator = new DonkeyInventoryTranslator(slotCount);
slots.add(SADDLE_SLOT);
} else if (entity instanceof CamelEntity) {
if (entity.getFlag(EntityFlag.CHESTED)) {
slotCount += 15;
}
// The camel has an invisible armor slot and needs special handling, same as the donkey
inventoryTranslator = new DonkeyInventoryTranslator(slotCount);
slots.add(SADDLE_SLOT);

View File

@ -393,10 +393,9 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
for (BlockEntityInfo blockEntity : blockEntities) {
BlockEntityType type = blockEntity.getType();
CompoundTag tag = blockEntity.getNbt();
if (type == null || tag == null) {
if (type == null) {
// As an example: ViaVersion will send -1 if it cannot find the block entity type
// Vanilla Minecraft gracefully handles this
// Since 1.20.5: tags sent here can be null, at which point the block entity is not translated
continue;
}
int x = blockEntity.getX(); // Relative to chunk
@ -421,8 +420,13 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
continue;
}
BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(type);
bedrockBlockEntities.add(blockEntityTranslator.getBlockEntityTag(session, type, x + chunkBlockX, y, z + chunkBlockZ, tag, blockState));
if (tag != null) {
BlockEntityTranslator blockEntityTranslator = BlockEntityUtils.getBlockEntityTranslator(type);
bedrockBlockEntities.add(blockEntityTranslator.getBlockEntityTag(session, type, x + chunkBlockX, y, z + chunkBlockZ, tag, blockState));
} else {
// Since 1.20.5, tags can be null, but Bedrock still needs a default tag to render the item
bedrockBlockEntities.add(BlockEntityTranslator.getConstantBedrockTag(type, x + chunkBlockX, y, z + chunkBlockZ).build());
}
// Check for custom skulls
// TODO: The tag layout follows new format (profille, etc...)

View File

@ -36,17 +36,18 @@ import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.type.BlockMapping;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.tags.BlockTag;
import org.geysermc.geyser.translator.collision.BlockCollision;
public final class BlockUtils {
private static boolean correctTool(GeyserSession session, BlockMapping blockMapping, String itemToolType) {
return switch (itemToolType) {
case "axe" -> session.getTagCache().isAxeEffective(blockMapping);
case "hoe" -> session.getTagCache().isHoeEffective(blockMapping);
case "pickaxe" -> session.getTagCache().isPickaxeEffective(blockMapping);
case "shears" -> session.getTagCache().isShearsEffective(blockMapping);
case "shovel" -> session.getTagCache().isShovelEffective(blockMapping);
case "axe" -> session.getTagCache().is(BlockTag.AXE_EFFECTIVE, blockMapping);
case "hoe" -> session.getTagCache().is(BlockTag.HOE_EFFECTIVE, blockMapping);
case "pickaxe" -> session.getTagCache().is(BlockTag.PICKAXE_EFFECTIVE, blockMapping);
case "shears" -> session.getTagCache().is(BlockTag.LEAVES, blockMapping) || session.getTagCache().is(BlockTag.WOOL, blockMapping);
case "shovel" -> session.getTagCache().is(BlockTag.SHOVEL_EFFECTIVE, blockMapping);
case "sword" -> blockMapping.getJavaBlockId() == BlockStateValues.JAVA_COBWEB_ID;
default -> {
session.getGeyser().getLogger().warning("Unknown tool type: " + itemToolType);
@ -79,15 +80,15 @@ public final class BlockUtils {
switch (toolTier) {
// Use intentional fall-throughs to check each tier with this block
default:
if (session.getTagCache().requiresStoneTool(blockMapping)) {
if (session.getTagCache().is(BlockTag.NEEDS_STONE_TOOL, blockMapping)) {
return false;
}
case "stone":
if (session.getTagCache().requiresIronTool(blockMapping)) {
if (session.getTagCache().is(BlockTag.NEEDS_IRON_TOOL, blockMapping)) {
return false;
}
case "iron":
if (session.getTagCache().requiresDiamondTool(blockMapping)) {
if (session.getTagCache().is(BlockTag.NEEDS_DIAMOND_TOOL, blockMapping)) {
return false;
}
}
@ -131,7 +132,7 @@ public final class BlockUtils {
}
public static double getBreakTime(GeyserSession session, BlockMapping blockMapping, ItemMapping item, @Nullable DataComponents components, boolean isSessionPlayer) {
boolean isShearsEffective = session.getTagCache().isShearsEffective(blockMapping); //TODO called twice
boolean isShearsEffective = session.getTagCache().is(BlockTag.LEAVES, blockMapping) || session.getTagCache().is(BlockTag.WOOL, blockMapping); //TODO called twice
boolean canHarvestWithHand = blockMapping.isCanBreakWithHand();
String toolType = "";
String toolTier = "";

View File

@ -70,7 +70,10 @@ public enum InteractiveTag {
READ,
WAKE_VILLAGER("wakevillager"),
BARTER,
GIVE_ITEM_TO_ALLAY("allay");
GIVE_ITEM_TO_ALLAY("allay"),
EQUIP_WOLF_ARMOR("equipwolfarmor"),
REMOVE_WOLF_ARMOR("removewolfarmor"),
REPAIR_WOLF_ARMOR("repairwolfarmor");
/**
* The full string that should be passed on to the client.

View File

@ -15,7 +15,7 @@ protocol-connection = "3.0.0.Beta1-20240411.165033-128"
raknet = "1.0.0.CR3-20240416.144209-1"
blockstateupdater="1.20.80-20240411.142413-1"
mcauthlib = "d9d773e"
mcprotocollib = "4ee05b62" # Revert from jitpack after release
mcprotocollib = "3c81cc80" # Revert from jitpack after release
adventure = "4.14.0"
adventure-platform = "4.3.0"
junit = "5.9.2"
@ -30,11 +30,11 @@ commodore = "2.2"
bungeecord = "a7c6ede"
velocity = "3.1.1"
viaproxy = "3.2.0-SNAPSHOT"
fabric-minecraft = "1.20.4"
fabric-loader = "0.15.2"
fabric-api = "0.91.2+1.20.4"
fabric-minecraft = "1.20.5"
fabric-loader = "0.15.10"
fabric-api = "0.97.6+1.20.5"
fabric-permissions = "0.2-SNAPSHOT"
neoforge-minecraft = "20.4.48-beta"
neoforge-minecraft = "20.5.0-beta"
mixin = "0.8.5"
# plugin versions
@ -118,9 +118,9 @@ websocket = { group = "org.java-websocket", name = "Java-WebSocket", version.ref
#protocol-common = { group = "org.cloudburstmc.protocol", name = "common", version.ref = "protocol-connection" }
#protocol-codec = { group = "org.cloudburstmc.protocol", name = "bedrock-codec", version.ref = "protocol" }
#protocol-connection = { group = "org.cloudburstmc.protocol", name = "bedrock-connection", version.ref = "protocol-connection" }
protocol-common = { group = "com.github.Kas-tle.Protocol", name = "common", version = "24146ed" } # Remove before merge
protocol-codec = { group = "com.github.Kas-tle.Protocol", name = "bedrock-codec", version = "24146ed" } # Remove before merge
protocol-connection = { group = "com.github.Kas-tle.Protocol", name = "bedrock-connection", version = "24146ed" } # Remove before merge
protocol-common = { group = "com.github.GeyserMC.Protocol", name = "common", version = "ade21be" }
protocol-codec = { group = "com.github.GeyserMC.Protocol", name = "bedrock-codec", version = "ade21be" }
protocol-connection = { group = "com.github.GeyserMC.Protocol", name = "bedrock-connection", version = "ade21be" }
math = { group = "org.cloudburstmc.math", name = "immutable", version = "2.0" }

View File

@ -66,7 +66,7 @@ include(":ap")
include(":api")
include(":bungeecord")
include(":fabric")
//include(":neoforge") // todo 1.20.5
include(":neoforge")
include(":mod")
include(":spigot")
include(":standalone")
@ -78,7 +78,7 @@ include(":core")
// Specify project dirs
project(":bungeecord").projectDir = file("bootstrap/bungeecord")
project(":fabric").projectDir = file("bootstrap/mod/fabric")
//project(":neoforge").projectDir = file("bootstrap/mod/neoforge") // todo 1.20.5
project(":neoforge").projectDir = file("bootstrap/mod/neoforge")
project(":mod").projectDir = file("bootstrap/mod")
project(":spigot").projectDir = file("bootstrap/spigot")
project(":standalone").projectDir = file("bootstrap/standalone")