diff --git a/README.md b/README.md index 49c78c692..0f2c1fb2c 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ A Bukkit server implementation utilizing Mixin. |:--------------------:|:------:|:------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| | Net (1.20.2) | 48.0.4 | ACTIVE | [![1.20.2 Status](https://img.shields.io/github/actions/workflow/status/IzzelAliz/Arclight/gradle.yml?branch=Net&style=flat-square)](https://github.com/IzzelAliz/Arclight/actions?query=branch%3ANet) | | Trials (1.20-1.20.1) | 47.2.0 | LTS | [![1.20.1 Status](https://img.shields.io/github/actions/workflow/status/IzzelAliz/Arclight/gradle.yml?branch=Trials&style=flat-square)](https://github.com/IzzelAliz/Arclight/actions?query=branch%3ATrials) | +| Horn (1.19-1.19.2) | 43.3.2 | LTS | [![Horn Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight-19?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-19) | **Legacy versions**: @@ -17,10 +18,9 @@ A Bukkit server implementation utilizing Mixin. |:-------------------:|:-------:|:----------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| | Executions (1.19.4) | 45.1.0 | LEGACY | [![1.19.4 Status](https://img.shields.io/github/actions/workflow/status/IzzelAliz/Arclight/gradle.yml?branch=Executions&style=flat-square)](https://github.com/IzzelAliz/Arclight/actions?query=branch%3AExecutions) | | Great Horn (1.19.3) | 44.1.22 | [LEGACY](https://github.com/IzzelAliz/Arclight/releases/tag/GreatHorn/1.0.3) | [![1.19 Status](https://img.shields.io/github/actions/workflow/status/IzzelAliz/Arclight/gradle.yml?branch=GreatHorn&style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-19) | -| Horn (1.19-1.19.2) | 43.2.11 | [LEGACY](https://github.com/IzzelAliz/Arclight/releases/tag/horn/1.0.2) | [![Horn Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight-19?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-19) | -| 1.18.x | 40.1.80 | LEGACY | [![1.18 Status](https://img.shields.io/github/actions/workflow/status/IzzelAliz/Arclight/gradle.yml?branch=1.18&style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-18) | +| 1.18.x | 40.2.10 | [LEGACY](https://github.com/IzzelAliz/Arclight/releases/tag/1.18%2F1.0.9) | [![1.18 Status](https://img.shields.io/github/actions/workflow/status/IzzelAliz/Arclight/gradle.yml?branch=1.18&style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-18) | | 1.17.x | 37.1.0 | [LEGACY](https://github.com/IzzelAliz/Arclight/releases/tag/1.17/1.0.2) | [![1.17 Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight-17?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-17) | -| 1.16.x | 36.2.39 | LEGACY | [![1.16 Status](https://img.shields.io/github/actions/workflow/status/IzzelAliz/Arclight/gradle.yml?branch=1.16&style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-16) | +| 1.16.x | 36.2.39 | [LEGACY](https://github.com/IzzelAliz/Arclight/releases/tag/1.16%2F1.0.25) | [![1.16 Status](https://img.shields.io/github/actions/workflow/status/IzzelAliz/Arclight/gradle.yml?branch=1.16&style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-16) | | 1.15.x | 31.2.48 | [LEGACY](https://github.com/IzzelAliz/Arclight/releases/tag/1.15/1.0.19) | [![1.15 Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight-15?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight-15) | | 1.14.x | 28.2.0 | [LEGACY](https://github.com/IzzelAliz/Arclight/releases/tag/1.0.6) | [![1.14 Status](https://img.shields.io/appveyor/build/IzzelAliz/arclight?style=flat-square)](https://ci.appveyor.com/project/IzzelAliz/arclight) | diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/bridge/core/world/level/levelgen/StructureStartBridge.java b/arclight-common/src/main/java/io/izzel/arclight/common/bridge/core/world/level/levelgen/StructureStartBridge.java new file mode 100644 index 000000000..a111b990b --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/bridge/core/world/level/levelgen/StructureStartBridge.java @@ -0,0 +1,8 @@ +package io.izzel.arclight.common.bridge.core.world.level.levelgen; + +import org.bukkit.event.world.AsyncStructureGenerateEvent; + +public interface StructureStartBridge { + + void bridge$setGenerateCause(AsyncStructureGenerateEvent.Cause cause); +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/bridge/core/world/storage/loot/LootTableBridge.java b/arclight-common/src/main/java/io/izzel/arclight/common/bridge/core/world/storage/loot/LootTableBridge.java index c6ec750ca..4c68f7cae 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/bridge/core/world/storage/loot/LootTableBridge.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/bridge/core/world/storage/loot/LootTableBridge.java @@ -1,5 +1,8 @@ package io.izzel.arclight.common.bridge.core.world.storage.loot; +import org.bukkit.craftbukkit.v.CraftLootTable; + public interface LootTableBridge { + void bridge$setCraftLootTable(CraftLootTable lootTable); } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/PotionTypeMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/PotionTypeMixin.java new file mode 100644 index 000000000..07a464ba3 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/PotionTypeMixin.java @@ -0,0 +1,16 @@ +package io.izzel.arclight.common.mixin.bukkit; + +import org.bukkit.NamespacedKey; +import org.bukkit.potion.PotionType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(PotionType.class) +public class PotionTypeMixin { + + @Redirect(method = "", at = @At(value = "INVOKE", target = "Lorg/bukkit/NamespacedKey;minecraft(Ljava/lang/String;)Lorg/bukkit/NamespacedKey;")) + private NamespacedKey arclight$nsFromString(String key) { + return NamespacedKey.fromString(key); + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/network/ServerPlayNetHandlerMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/network/ServerPlayNetHandlerMixin.java index 4d81627c2..0a6ce8817 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/network/ServerPlayNetHandlerMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/network/ServerPlayNetHandlerMixin.java @@ -56,6 +56,7 @@ import net.minecraft.network.protocol.game.ServerboundPlayerAbilitiesPacket; import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket; import net.minecraft.network.protocol.game.ServerboundPlayerCommandPacket; +import net.minecraft.network.protocol.game.ServerboundRecipeBookChangeSettingsPacket; import net.minecraft.network.protocol.game.ServerboundSelectTradePacket; import net.minecraft.network.protocol.game.ServerboundSetCarriedItemPacket; import net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket; @@ -1601,6 +1602,11 @@ public void handleContainerClick(ServerboundContainerClickPacket packet) { } } + @Inject(method = "handleRecipeBookChangeSettingsPacket", at = @At(value = "INVOKE", target = "Lnet/minecraft/stats/ServerRecipeBook;setBookSetting(Lnet/minecraft/world/inventory/RecipeBookType;ZZ)V")) + private void arclight$recipeBookSettings(ServerboundRecipeBookChangeSettingsPacket packet, CallbackInfo ci) { + CraftEventFactory.callRecipeBookSettingsEvent(this.player, packet.getBookType(), packet.isOpen(), packet.isFiltering()); + } + @Redirect(method = "handlePlaceRecipe", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/game/ServerboundPlaceRecipePacket;getRecipe()Lnet/minecraft/resources/ResourceLocation;")) private ResourceLocation arclight$recipeBookClick(ServerboundPlaceRecipePacket instance) { var location = instance.getRecipe(); diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/server/commands/PlaceCommandMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/server/commands/PlaceCommandMixin.java new file mode 100644 index 000000000..0423c1fc2 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/server/commands/PlaceCommandMixin.java @@ -0,0 +1,27 @@ +package io.izzel.arclight.common.mixin.core.server.commands; + +import io.izzel.arclight.common.bridge.core.world.level.levelgen.StructureStartBridge; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.server.commands.PlaceCommand; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.StructureStart; +import org.bukkit.event.world.AsyncStructureGenerateEvent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(PlaceCommand.class) +public class PlaceCommandMixin { + + @Inject(method = "placeStructure", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/levelgen/structure/StructureStart;getBoundingBox()Lnet/minecraft/world/level/levelgen/structure/BoundingBox;")) + private static void arclight$cause(CommandSourceStack p_214588_, Holder.Reference p_251799_, BlockPos p_214590_, CallbackInfoReturnable cir, + ServerLevel level, Structure structure, ChunkGenerator chunkGenerator, StructureStart structureStart) { + ((StructureStartBridge) (Object) structureStart).bridge$setGenerateCause(AsyncStructureGenerateEvent.Cause.COMMAND); + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/projectile/AbstractArrowMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/projectile/AbstractArrowMixin.java index 970953b7f..6e07fbfaa 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/projectile/AbstractArrowMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/projectile/AbstractArrowMixin.java @@ -60,7 +60,7 @@ public void playerTouch(Player playerEntity) { ItemStack itemstack = this.getPickupItem(); if (this.pickup == net.minecraft.world.entity.projectile.AbstractArrow.Pickup.ALLOWED && !itemstack.isEmpty() && ((PlayerInventoryBridge) playerEntity.getInventory()).bridge$canHold(itemstack) > 0) { ItemEntity item = new ItemEntity(this.level(), this.getX(), this.getY(), this.getZ(), itemstack); - PlayerPickupArrowEvent event = new PlayerPickupArrowEvent(((ServerPlayerEntityBridge) playerEntity).bridge$getBukkitEntity(), new CraftItem(((CraftServer) Bukkit.getServer()), (net.minecraft.world.entity.projectile.AbstractArrow) (Object) this, item), (AbstractArrow) this.getBukkitEntity()); + PlayerPickupArrowEvent event = new PlayerPickupArrowEvent(((ServerPlayerEntityBridge) playerEntity).bridge$getBukkitEntity(), new CraftItem(((CraftServer) Bukkit.getServer()), item), (AbstractArrow) this.getBukkitEntity()); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { return; diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/projectile/ThrownExperienceBottleMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/projectile/ThrownExperienceBottleMixin.java index 19a38ce1a..8332b3bc0 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/projectile/ThrownExperienceBottleMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/projectile/ThrownExperienceBottleMixin.java @@ -23,7 +23,7 @@ protected void onHit(HitResult result) { super.onHit(result); if (!this.level().isClientSide) { int i = 3 + this.level().random.nextInt(5) + this.level().random.nextInt(5); - ExpBottleEvent event = CraftEventFactory.callExpBottleEvent((ThrownExperienceBottle) (Object) this, i); + ExpBottleEvent event = CraftEventFactory.callExpBottleEvent((ThrownExperienceBottle) (Object) this, result, i); i = event.getExperience(); if (event.getShowEffect()) { this.level().levelEvent(2002, this.blockPosition(), PotionUtils.getColor(Potions.WATER)); diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/projectile/ThrownPotionMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/projectile/ThrownPotionMixin.java index 2210227a8..2e3e5eee8 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/projectile/ThrownPotionMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/entity/projectile/ThrownPotionMixin.java @@ -18,6 +18,7 @@ import net.minecraft.world.level.block.CampfireBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.HitResult; import org.bukkit.craftbukkit.v.entity.CraftLivingEntity; import org.bukkit.craftbukkit.v.event.CraftEventFactory; import org.bukkit.event.entity.EntityPotionEffectEvent; @@ -25,6 +26,7 @@ import org.bukkit.event.entity.PotionSplashEvent; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; @@ -39,11 +41,19 @@ @Mixin(ThrownPotion.class) public abstract class ThrownPotionMixin extends ThrowableItemProjectileMixin { + @Unique private transient HitResult arclight$hitResult; + @Redirect(method = "onHit", at = @At(value = "INVOKE", remap = false, ordinal = 1, target = "Ljava/util/List;isEmpty()Z")) - private boolean arclight$callEvent(List list) { + private boolean arclight$callEvent(List list, HitResult hitResult) { + arclight$hitResult = hitResult; return false; } + @Inject(method = "onHit", at = @At("RETURN")) + private void arclight$resetResult(HitResult p_37543_, CallbackInfo ci) { + arclight$hitResult = null; + } + /** * @author IzzelAliz * @reason @@ -68,7 +78,7 @@ private void applySplash(List list, @Nullable Entity entity) } } } - PotionSplashEvent event = CraftEventFactory.callPotionSplashEvent((ThrownPotion) (Object) this, affected); + PotionSplashEvent event = CraftEventFactory.callPotionSplashEvent((ThrownPotion) (Object) this, arclight$hitResult, affected); if (!event.isCancelled() && list != null && !list.isEmpty()) { for (org.bukkit.entity.LivingEntity victim : event.getAffectedEntities()) { if (!(victim instanceof CraftLivingEntity)) { @@ -101,7 +111,7 @@ private void applySplash(List list, @Nullable Entity entity) @Inject(method = "makeAreaOfEffectCloud", cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;addFreshEntity(Lnet/minecraft/world/entity/Entity;)Z")) private void arclight$makeCloud(ItemStack p_190542_1_, Potion p_190542_2_, CallbackInfo ci, AreaEffectCloud entity) { - LingeringPotionSplashEvent event = CraftEventFactory.callLingeringPotionSplashEvent((ThrownPotion) (Object) this, entity); + LingeringPotionSplashEvent event = CraftEventFactory.callLingeringPotionSplashEvent((ThrownPotion) (Object) this, arclight$hitResult, entity); if (event.isCancelled() || entity.isRemoved()) { ci.cancel(); entity.discard(); diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/levelgen/structure/StructurePieceMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/levelgen/structure/StructurePieceMixin.java new file mode 100644 index 000000000..af005d79b --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/levelgen/structure/StructurePieceMixin.java @@ -0,0 +1,68 @@ +package io.izzel.arclight.common.mixin.core.world.level.levelgen.structure; + +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.ServerLevelAccessor; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.structure.BoundingBox; +import net.minecraft.world.level.levelgen.structure.StructurePiece; +import org.bukkit.craftbukkit.v.block.CraftBlockEntityState; +import org.bukkit.craftbukkit.v.block.CraftBlockState; +import org.bukkit.craftbukkit.v.block.CraftBlockStates; +import org.bukkit.craftbukkit.v.block.CraftCreatureSpawner; +import org.bukkit.craftbukkit.v.util.TransformerGeneratorAccess; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(StructurePiece.class) +public class StructurePieceMixin { + + @Inject(method = "placeBlock", cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/WorldGenLevel;setBlock(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;I)Z")) + private void arclight$useTransformer(WorldGenLevel level, BlockState p_73436_, int p_73437_, int p_73438_, int p_73439_, BoundingBox p_73440_, CallbackInfo ci, BlockPos pos) { + if (level instanceof TransformerGeneratorAccess transformerGeneratorAccess) { + transformerGeneratorAccess.setCraftBlock(pos, (CraftBlockState) CraftBlockStates.getBlockState(pos, p_73436_, null), 2); + ci.cancel(); + } + } + + @Unique + protected boolean placeCraftBlockEntity(ServerLevelAccessor worldAccess, BlockPos position, CraftBlockEntityState craftBlockEntityState, int i) { + if (worldAccess instanceof TransformerGeneratorAccess transformerAccess) { + return transformerAccess.setCraftBlock(position, craftBlockEntityState, i); + } + boolean result = worldAccess.setBlock(position, craftBlockEntityState.getHandle(), i); + var tileEntity = worldAccess.getBlockEntity(position); + if (tileEntity != null) { + tileEntity.load(craftBlockEntityState.getSnapshotNBT()); + } + return result; + } + + @Unique + protected void placeCraftSpawner(ServerLevelAccessor worldAccess, BlockPos position, org.bukkit.entity.EntityType entityType, int i) { + // This method is used in structures that are generated by code and place spawners as they set the entity after the block was placed making it impossible for plugins to access that information + var spawner = (CraftCreatureSpawner) CraftBlockStates.getBlockState(position, Blocks.SPAWNER.defaultBlockState(), null); + spawner.setSpawnedType(entityType); + placeCraftBlockEntity(worldAccess, position, spawner, i); + } + + @Unique + protected void setCraftLootTable(ServerLevelAccessor worldAccess, BlockPos position, RandomSource randomSource, ResourceLocation loottableKey) { + // This method is used in structures that use data markers to a loot table to loot containers as otherwise plugins won't have access to that information. + var tileEntity = worldAccess.getBlockEntity(position); + if (tileEntity instanceof RandomizableContainerBlockEntity tileEntityLootable) { + tileEntityLootable.setLootTable(loottableKey, randomSource.nextLong()); + if (worldAccess instanceof TransformerGeneratorAccess transformerAccess) { + transformerAccess.setCraftBlock(position, (CraftBlockState) CraftBlockStates.getBlockState(position, tileEntity.getBlockState(), tileEntityLootable.saveWithFullMetadata()), 3); + } + } + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/levelgen/structure/StructureStartMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/levelgen/structure/StructureStartMixin.java new file mode 100644 index 000000000..34f7a086e --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/levelgen/structure/StructureStartMixin.java @@ -0,0 +1,71 @@ +package io.izzel.arclight.common.mixin.core.world.level.levelgen.structure; + +import io.izzel.arclight.common.bridge.core.world.level.levelgen.StructureStartBridge; +import net.minecraft.core.BlockPos; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.StructureManager; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.levelgen.structure.BoundingBox; +import net.minecraft.world.level.levelgen.structure.Structure; +import net.minecraft.world.level.levelgen.structure.StructurePiece; +import net.minecraft.world.level.levelgen.structure.StructureStart; +import net.minecraft.world.level.levelgen.structure.pieces.PiecesContainer; +import org.bukkit.craftbukkit.v.util.CraftStructureTransformer; +import org.bukkit.craftbukkit.v.util.TransformerGeneratorAccess; +import org.bukkit.event.world.AsyncStructureGenerateEvent; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +import java.util.List; + +@Mixin(StructureStart.class) +public class StructureStartMixin implements StructureStartBridge { + + @Shadow @Final private PiecesContainer pieceContainer; + @Shadow @Final private Structure structure; + + @Unique private AsyncStructureGenerateEvent.Cause arclight$cause = AsyncStructureGenerateEvent.Cause.WORLD_GENERATION; + + @Override + public void bridge$setGenerateCause(AsyncStructureGenerateEvent.Cause cause) { + this.arclight$cause = cause; + } + + /** + * @author IzzelAliz + * @reason + */ + @Overwrite + public void placeInChunk(WorldGenLevel p_226851_, StructureManager p_226852_, ChunkGenerator p_226853_, RandomSource p_226854_, BoundingBox p_226855_, ChunkPos p_226856_) { + List list = this.pieceContainer.pieces(); + if (!list.isEmpty()) { + BoundingBox boundingbox = (list.get(0)).getBoundingBox(); + BlockPos blockpos = boundingbox.getCenter(); + BlockPos blockpos1 = new BlockPos(blockpos.getX(), boundingbox.minY(), blockpos.getZ()); + + //for (StructurePiece structurepiece : list) { + // if (structurepiece.getBoundingBox().intersects(p_226855_)) { + // structurepiece.postProcess(p_226851_, p_226852_, p_226853_, p_226854_, p_226855_, p_226856_, blockpos1); + // } + //} + + List pieces = list.stream().filter(piece -> piece.getBoundingBox().intersects(p_226855_)).toList(); + if (!pieces.isEmpty()) { + var transformerAccess = new TransformerGeneratorAccess(); + transformerAccess.setHandle(p_226851_); + transformerAccess.setStructureTransformer(new CraftStructureTransformer(arclight$cause, p_226851_, p_226852_, structure, p_226855_, p_226856_)); + for (StructurePiece piece : pieces) { + piece.postProcess(transformerAccess, p_226852_, p_226853_, p_226854_, p_226855_, p_226856_, blockpos1); + } + transformerAccess.getStructureTransformer().discard(); + } + + this.structure.afterPlace(p_226851_, p_226852_, p_226853_, p_226854_, p_226855_, p_226856_, this.pieceContainer); + } + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/levelgen/structure/templatesystem/StructureTemplateMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/levelgen/structure/templatesystem/StructureTemplateMixin.java index ef8c07ba0..487e8250c 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/levelgen/structure/templatesystem/StructureTemplateMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/levelgen/structure/templatesystem/StructureTemplateMixin.java @@ -1,19 +1,60 @@ package io.izzel.arclight.common.mixin.core.world.level.levelgen.structure.templatesystem; +import com.google.common.collect.Lists; +import com.mojang.datafixers.util.Pair; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.core.HolderGetter; +import net.minecraft.core.Vec3i; import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.RandomSource; +import net.minecraft.world.Clearable; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.ServerLevelAccessor; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.LiquidBlockContainer; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.structure.BoundingBox; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape; +import net.minecraft.world.phys.shapes.DiscreteVoxelShape; +import org.bukkit.craftbukkit.v.block.CraftBlockEntityState; +import org.bukkit.craftbukkit.v.block.CraftBlockState; +import org.bukkit.craftbukkit.v.block.CraftBlockStates; +import org.bukkit.craftbukkit.v.block.CraftLootable; import org.bukkit.craftbukkit.v.persistence.CraftPersistentDataContainer; import org.bukkit.craftbukkit.v.persistence.CraftPersistentDataTypeRegistry; +import org.bukkit.craftbukkit.v.util.CraftStructureTransformer; +import org.bukkit.craftbukkit.v.util.TransformerGeneratorAccess; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.Iterator; +import java.util.List; + @Mixin(StructureTemplate.class) -public class StructureTemplateMixin { +public abstract class StructureTemplateMixin { + + // @formatter:off + @Shadow @Final public List palettes; + @Shadow @Final public List entityInfoList; + @Shadow private Vec3i size; + @Shadow public static List processBlockInfos(ServerLevelAccessor p_278297_, BlockPos p_74519_, BlockPos p_74520_, StructurePlaceSettings p_74521_, List p_74522_, @Nullable StructureTemplate template) { return null; } + @Shadow public static void updateShapeAtEdge(LevelAccessor p_74511_, int p_74512_, DiscreteVoxelShape p_74513_, int p_74514_, int p_74515_, int p_74516_) { } + @Shadow protected abstract void addEntitiesToWorld(ServerLevelAccessor p_74524_, BlockPos p_74525_, StructurePlaceSettings placementIn); + // @formatter:on private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry(); public CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(DATA_TYPE_REGISTRY); @@ -32,4 +73,175 @@ public class StructureTemplateMixin { this.persistentDataContainer.putAll(compoundTag); } } + + /** + * @author IzzelAliz + * @reason + */ + @Overwrite + public boolean placeInWorld(ServerLevelAccessor p_230329_, BlockPos p_230330_, BlockPos p_230331_, StructurePlaceSettings p_230332_, RandomSource p_230333_, int p_230334_) { + if (this.palettes.isEmpty()) { + return false; + } else { + // CraftBukkit start + // We only want the TransformerGeneratorAccess at certain locations because in here are many "block update" calls that shouldn't be transformed + var wrappedAccess = p_230329_; + CraftStructureTransformer structureTransformer = null; + if (wrappedAccess instanceof TransformerGeneratorAccess transformerAccess) { + p_230329_ = transformerAccess.getHandle(); + structureTransformer = transformerAccess.getStructureTransformer(); + // The structureTransformer is not needed if we can not transform blocks therefore we can save a little bit of performance doing this + if (structureTransformer != null && !structureTransformer.canTransformBlocks()) { + structureTransformer = null; + } + } + // CraftBukkit end + List list = p_230332_.getRandomPalette(this.palettes, p_230330_).blocks(); + if ((!list.isEmpty() || !p_230332_.isIgnoreEntities() && !this.entityInfoList.isEmpty()) && this.size.getX() >= 1 && this.size.getY() >= 1 && this.size.getZ() >= 1) { + BoundingBox boundingbox = p_230332_.getBoundingBox(); + List list1 = Lists.newArrayListWithCapacity(p_230332_.shouldKeepLiquids() ? list.size() : 0); + List list2 = Lists.newArrayListWithCapacity(p_230332_.shouldKeepLiquids() ? list.size() : 0); + List> list3 = Lists.newArrayListWithCapacity(list.size()); + int i = Integer.MAX_VALUE; + int j = Integer.MAX_VALUE; + int k = Integer.MAX_VALUE; + int l = Integer.MIN_VALUE; + int i1 = Integer.MIN_VALUE; + int j1 = Integer.MIN_VALUE; + + for (StructureTemplate.StructureBlockInfo structuretemplate$structureblockinfo : processBlockInfos(p_230329_, p_230330_, p_230331_, p_230332_, list, (StructureTemplate) (Object) this)) { + BlockPos blockpos = structuretemplate$structureblockinfo.pos(); + if (boundingbox == null || boundingbox.isInside(blockpos)) { + FluidState fluidstate = p_230332_.shouldKeepLiquids() ? p_230329_.getFluidState(blockpos) : null; + BlockState blockstate = structuretemplate$structureblockinfo.state().mirror(p_230332_.getMirror()).rotate(p_230332_.getRotation()); + if (structuretemplate$structureblockinfo.nbt() != null) { + BlockEntity blockentity = p_230329_.getBlockEntity(blockpos); + Clearable.tryClear(blockentity); + p_230329_.setBlock(blockpos, Blocks.BARRIER.defaultBlockState(), 20); + } + // CraftBukkit start + if (structureTransformer != null) { + var craftBlockState = (CraftBlockState) CraftBlockStates.getBlockState(blockpos, blockstate, null); + if (structuretemplate$structureblockinfo.nbt() != null && craftBlockState instanceof CraftBlockEntityState entityState) { + entityState.loadData(structuretemplate$structureblockinfo.nbt()); + if (craftBlockState instanceof CraftLootable craftLootable) { + craftLootable.setSeed(p_230333_.nextLong()); + } + } + craftBlockState = structureTransformer.transformCraftState(craftBlockState); + blockstate = craftBlockState.getHandle(); + structuretemplate$structureblockinfo = new StructureTemplate.StructureBlockInfo(blockpos, blockstate, (craftBlockState instanceof CraftBlockEntityState craftBlockEntityState ? craftBlockEntityState.getSnapshotNBT() : null)); + } + // CraftBukkit end + + if (p_230329_.setBlock(blockpos, blockstate, p_230334_)) { + i = Math.min(i, blockpos.getX()); + j = Math.min(j, blockpos.getY()); + k = Math.min(k, blockpos.getZ()); + l = Math.max(l, blockpos.getX()); + i1 = Math.max(i1, blockpos.getY()); + j1 = Math.max(j1, blockpos.getZ()); + list3.add(Pair.of(blockpos, structuretemplate$structureblockinfo.nbt())); + if (structuretemplate$structureblockinfo.nbt() != null) { + BlockEntity blockentity1 = p_230329_.getBlockEntity(blockpos); + if (blockentity1 != null) { + if (structureTransformer == null && blockentity1 instanceof RandomizableContainerBlockEntity) { + structuretemplate$structureblockinfo.nbt().putLong("LootTableSeed", p_230333_.nextLong()); + } + + blockentity1.load(structuretemplate$structureblockinfo.nbt()); + } + } + + if (fluidstate != null) { + if (blockstate.getFluidState().isSource()) { + list2.add(blockpos); + } else if (blockstate.getBlock() instanceof LiquidBlockContainer) { + ((LiquidBlockContainer) blockstate.getBlock()).placeLiquid(p_230329_, blockpos, blockstate, fluidstate); + if (!fluidstate.isSource()) { + list1.add(blockpos); + } + } + } + } + } + } + + boolean flag = true; + Direction[] adirection = new Direction[]{Direction.UP, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST}; + + while (flag && !list1.isEmpty()) { + flag = false; + Iterator iterator = list1.iterator(); + + while (iterator.hasNext()) { + BlockPos blockpos3 = iterator.next(); + FluidState fluidstate2 = p_230329_.getFluidState(blockpos3); + + for (int i2 = 0; i2 < adirection.length && !fluidstate2.isSource(); ++i2) { + BlockPos blockpos1 = blockpos3.relative(adirection[i2]); + FluidState fluidstate1 = p_230329_.getFluidState(blockpos1); + if (fluidstate1.isSource() && !list2.contains(blockpos1)) { + fluidstate2 = fluidstate1; + } + } + + if (fluidstate2.isSource()) { + BlockState blockstate1 = p_230329_.getBlockState(blockpos3); + Block block = blockstate1.getBlock(); + if (block instanceof LiquidBlockContainer) { + ((LiquidBlockContainer) block).placeLiquid(p_230329_, blockpos3, blockstate1, fluidstate2); + flag = true; + iterator.remove(); + } + } + } + } + + if (i <= l) { + if (!p_230332_.getKnownShape()) { + DiscreteVoxelShape discretevoxelshape = new BitSetDiscreteVoxelShape(l - i + 1, i1 - j + 1, j1 - k + 1); + int k1 = i; + int l1 = j; + int j2 = k; + + for (Pair pair1 : list3) { + BlockPos blockpos2 = pair1.getFirst(); + discretevoxelshape.fill(blockpos2.getX() - k1, blockpos2.getY() - l1, blockpos2.getZ() - j2); + } + + updateShapeAtEdge(p_230329_, p_230334_, discretevoxelshape, k1, l1, j2); + } + + for (Pair pair : list3) { + BlockPos blockpos4 = pair.getFirst(); + if (!p_230332_.getKnownShape()) { + BlockState blockstate2 = p_230329_.getBlockState(blockpos4); + BlockState blockstate3 = Block.updateFromNeighbourShapes(blockstate2, p_230329_, blockpos4); + if (blockstate2 != blockstate3) { + p_230329_.setBlock(blockpos4, blockstate3, p_230334_ & -2 | 16); + } + + p_230329_.blockUpdated(blockpos4, blockstate3.getBlock()); + } + + if (pair.getSecond() != null) { + BlockEntity blockentity2 = p_230329_.getBlockEntity(blockpos4); + if (blockentity2 != null) { + blockentity2.setChanged(); + } + } + } + } + + if (!p_230332_.isIgnoreEntities()) { + this.addEntitiesToWorld(wrappedAccess, p_230330_, p_230332_); + } + + return true; + } else { + return false; + } + } + } } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/storage/loot/LootDataManagerMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/storage/loot/LootDataManagerMixin.java index 1fcff84bc..2c137d998 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/storage/loot/LootDataManagerMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/storage/loot/LootDataManagerMixin.java @@ -1,10 +1,13 @@ package io.izzel.arclight.common.mixin.core.world.level.storage.loot; -import com.google.common.collect.ImmutableMap; +import io.izzel.arclight.common.bridge.core.world.storage.loot.LootTableBridge; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.storage.loot.LootDataId; import net.minecraft.world.level.storage.loot.LootDataManager; import net.minecraft.world.level.storage.loot.LootDataType; +import net.minecraft.world.level.storage.loot.LootTable; +import org.bukkit.craftbukkit.v.CraftLootTable; +import org.bukkit.craftbukkit.v.util.CraftNamespacedKey; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -20,12 +23,12 @@ public class LootDataManagerMixin { @Shadow private Map, ?> elements; // @formatter:on - public Map lootTableToKey = ImmutableMap.of(); - @Inject(method = "apply", at = @At("RETURN")) private void arclight$buildRev(Map, Map> p_279426_, CallbackInfo ci) { - ImmutableMap.Builder lootTableToKeyBuilder = ImmutableMap.builder(); - this.elements.forEach((key, lootTable) -> lootTableToKeyBuilder.put(lootTable, key.location())); - this.lootTableToKey = lootTableToKeyBuilder.build(); + this.elements.forEach((key, value) -> { + if (value instanceof LootTable lootTable) { + ((LootTableBridge) lootTable).bridge$setCraftLootTable(new CraftLootTable(CraftNamespacedKey.fromMinecraft(key.location()), lootTable)); + } + }); } } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/storage/loot/LootTableMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/storage/loot/LootTableMixin.java index b316b6eb3..044eeeec9 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/storage/loot/LootTableMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/storage/loot/LootTableMixin.java @@ -11,6 +11,7 @@ import net.minecraft.world.level.storage.loot.LootParams; import net.minecraft.world.level.storage.loot.LootTable; import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import org.bukkit.craftbukkit.v.CraftLootTable; import org.bukkit.craftbukkit.v.event.CraftEventFactory; import org.bukkit.craftbukkit.v.inventory.CraftItemStack; import org.bukkit.event.world.LootGenerateEvent; @@ -18,6 +19,7 @@ import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -36,6 +38,13 @@ public abstract class LootTableMixin implements LootTableBridge { @Shadow protected abstract void shuffleAndSplitItems(ObjectArrayList p_230925_, int p_230926_, RandomSource p_230927_); // @formatter:on + @Unique public CraftLootTable craftLootTable; + + @Override + public void bridge$setCraftLootTable(CraftLootTable lootTable) { + this.craftLootTable = lootTable; + } + @Eject(method = "fill", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/storage/loot/LootTable;getRandomItems(Lnet/minecraft/world/level/storage/loot/LootContext;)Lit/unimi/dsi/fastutil/objects/ObjectArrayList;")) private ObjectArrayList arclight$nonPluginEvent(LootTable lootTable, LootContext context, CallbackInfo ci, Container inv) { ObjectArrayList list = this.getRandomItems(context); diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/BukkitRegistry.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/BukkitRegistry.java index 7ca0292ec..c7d55e975 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/BukkitRegistry.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/BukkitRegistry.java @@ -27,13 +27,11 @@ import net.minecraft.stats.StatType; import net.minecraft.stats.Stats; import net.minecraft.world.effect.MobEffect; -import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.entity.MobCategory; import net.minecraft.world.entity.boss.enderdragon.phases.EnderDragonPhase; import net.minecraft.world.entity.npc.VillagerProfession; import net.minecraft.world.item.Item; import net.minecraft.world.item.Items; -import net.minecraft.world.item.alchemy.Potions; import net.minecraft.world.item.crafting.CookingBookCategory; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; @@ -51,7 +49,6 @@ import org.bukkit.craftbukkit.v.CraftCrashReport; import org.bukkit.craftbukkit.v.CraftStatistic; import org.bukkit.craftbukkit.v.inventory.CraftRecipe; -import org.bukkit.craftbukkit.v.potion.CraftPotionUtil; import org.bukkit.craftbukkit.v.util.CraftMagicNumbers; import org.bukkit.craftbukkit.v.util.CraftNamespacedKey; import org.bukkit.craftbukkit.v.util.CraftSpawnCategory; @@ -387,21 +384,19 @@ private static void loadPotions() { } } PotionEffectType.stopAcceptingRegistrations(); - ArclightMod.LOGGER.info("registry.potion", size - origin); + ArclightMod.LOGGER.debug("registry.potion", size - origin); int typeId = PotionType.values().length; List newTypes = new ArrayList<>(); - BiMap map = HashBiMap.create(Unsafe.getStatic(CraftPotionUtil.class, "regular")); - putStatic(CraftPotionUtil.class, "regular", map); for (var potion : ForgeRegistries.POTIONS) { var location = ForgeRegistries.POTIONS.getKey(potion); - if (CraftPotionUtil.toBukkit(location.toString()).getType() == PotionType.UNCRAFTABLE && potion != Potions.EMPTY) { - String name = ResourceLocationUtil.standardize(location); - MobEffectInstance effectInstance = potion.getEffects().isEmpty() ? null : potion.getEffects().get(0); + String name = ResourceLocationUtil.standardize(location); + try { + PotionType.valueOf(name); + } catch (Exception e) { PotionType potionType = EnumHelper.makeEnum(PotionType.class, name, typeId++, - Arrays.asList(PotionEffectType.class, boolean.class, boolean.class), - Arrays.asList(effectInstance == null ? null : PotionEffectType.getByKey(CraftNamespacedKey.fromMinecraft(ForgeRegistries.MOB_EFFECTS.getKey(effectInstance.getEffect()))), false, false)); + List.of(String.class), + List.of(location.toString())); newTypes.add(potionType); - map.put(potionType, location.toString()); ArclightMod.LOGGER.debug("Registered {} as potion type {}", location, potionType); } } diff --git a/arclight-common/src/main/resources/META-INF/accesstransformer.cfg b/arclight-common/src/main/resources/META-INF/accesstransformer.cfg index e4ec56c7e..a3f3516d5 100644 --- a/arclight-common/src/main/resources/META-INF/accesstransformer.cfg +++ b/arclight-common/src/main/resources/META-INF/accesstransformer.cfg @@ -1,5 +1,11 @@ # Arclight 1.20.2 public net.minecraft.server.network.ServerCommonPacketListenerImpl f_291338_ # connection +public net.minecraft.world.level.levelgen.structure.StructurePiece f_73383_ # boundingBox +public net.minecraft.world.level.levelgen.structure.StructurePiece f_73381_ # SHAPE_CHECK_BLOCKS +public net.minecraft.world.entity.LightningBolt f_20860_ # life +public net.minecraft.world.entity.LightningBolt f_20861_ # flashes +public net.minecraft.world.entity.Entity f_19796_ # random +public net.minecraft.world.entity.projectile.EvokerFangs f_36916_ # warmupDelayTicks # Arclight 1.20 public net.minecraft.world.entity.item.FallingBlockEntity f_31947_ # cancelDrop public net.minecraft.server.dedicated.DedicatedServerProperties$WorldDimensionData (Lcom/google/gson/JsonObject;Ljava/lang/String;)V diff --git a/arclight-common/src/main/resources/mixins.arclight.bukkit.json b/arclight-common/src/main/resources/mixins.arclight.bukkit.json index ceef44e54..2a2ab70fe 100644 --- a/arclight-common/src/main/resources/mixins.arclight.bukkit.json +++ b/arclight-common/src/main/resources/mixins.arclight.bukkit.json @@ -41,6 +41,7 @@ "MaterialMixin", "PluginClassLoaderMixin", "PotionEffectTypeMixin", + "PotionTypeMixin", "RecipeIteratorMixin", "Registry_SimpleRegistryMixin", "WatchdogThreadMixin" diff --git a/arclight-common/src/main/resources/mixins.arclight.core.json b/arclight-common/src/main/resources/mixins.arclight.core.json index d237383fd..d70170976 100644 --- a/arclight-common/src/main/resources/mixins.arclight.core.json +++ b/arclight-common/src/main/resources/mixins.arclight.core.json @@ -57,6 +57,7 @@ "server.WorldLoaderMixin", "server.commands.EffectCommandMixin", "server.commands.GameRuleCommandMixin", + "server.commands.PlaceCommandMixin", "server.commands.ReloadCommandMixin", "server.commands.SetSpawnCommandMixin", "server.commands.SummonCommandMixin", @@ -484,6 +485,8 @@ "world.level.entity.PersistentEntitySectionManagerMixin", "world.level.gameevent.GameEventDispatcherMixin", "world.level.gameevent.vibrations.VibrationListenerMixin", + "world.level.levelgen.structure.StructurePieceMixin", + "world.level.levelgen.structure.StructureStartMixin", "world.level.levelgen.structure.templatesystem.StructureTemplateMixin", "world.level.portal.PortalForcerMixin", "world.level.redstone.NeighborUpdaterMixin",