diff --git a/patches/minecraft/net/minecraft/world/gen/feature/template/StructureProcessor.java.patch b/patches/minecraft/net/minecraft/world/gen/feature/template/StructureProcessor.java.patch
index f625590be..510d06032 100644
--- a/patches/minecraft/net/minecraft/world/gen/feature/template/StructureProcessor.java.patch
+++ b/patches/minecraft/net/minecraft/world/gen/feature/template/StructureProcessor.java.patch
@@ -1,6 +1,6 @@
--- a/net/minecraft/world/gen/feature/template/StructureProcessor.java
+++ b/net/minecraft/world/gen/feature/template/StructureProcessor.java
-@@ -9,8 +9,31 @@
+@@ -9,8 +9,53 @@
public abstract class StructureProcessor {
@Nullable
@@ -29,6 +29,28 @@
+ public Template.BlockInfo process(IWorldReader p_215194_1_, BlockPos p_215194_2_, Template.BlockInfo p_215194_3_, Template.BlockInfo p_215194_4_, PlacementSettings p_215194_5_, @Nullable Template template) {
+ return func_215194_a(p_215194_1_, p_215194_2_, p_215194_3_, p_215194_4_, p_215194_5_);
+ }
++
++ /**
++ * FORGE: Add entity processing.
++ *
++ * Use this method to process entities from a structure in much the same way as
++ * blocks, parameters are analogous.
++ *
++ * @param world
++ * @param seedPos
++ * @param rawEntityInfo
++ * @param entityInfo
++ * @param placementSettings
++ * @param template
++ *
++ * @see #process(IWorldReader, BlockPos,
++ * net.minecraft.world.gen.feature.template.Template.BlockInfo,
++ * net.minecraft.world.gen.feature.template.Template.BlockInfo,
++ * PlacementSettings)
++ */
++ public Template.EntityInfo processEntity(IWorldReader world, BlockPos seedPos, Template.EntityInfo rawEntityInfo, Template.EntityInfo entityInfo, PlacementSettings placementSettings, Template template) {
++ return entityInfo;
++ }
+
protected abstract IStructureProcessorType func_215192_a();
diff --git a/patches/minecraft/net/minecraft/world/gen/feature/template/Template.java.patch b/patches/minecraft/net/minecraft/world/gen/feature/template/Template.java.patch
index 77b8c0582..4d5017057 100644
--- a/patches/minecraft/net/minecraft/world/gen/feature/template/Template.java.patch
+++ b/patches/minecraft/net/minecraft/world/gen/feature/template/Template.java.patch
@@ -1,6 +1,18 @@
--- a/net/minecraft/world/gen/feature/template/Template.java
+++ b/net/minecraft/world/gen/feature/template/Template.java
-@@ -177,7 +177,7 @@
+@@ -152,6 +152,11 @@
+ return func_207669_a(p_186266_1_, p_186266_0_.func_186212_b(), p_186266_0_.func_186215_c(), p_186266_0_.func_207664_d());
+ }
+
++ // FORGE: Add overload accepting Vec3d
++ public static Vec3d transformedVec3d(PlacementSettings p_186266_0_, Vec3d p_186266_1_) {
++ return func_207667_a(p_186266_1_, p_186266_0_.func_186212_b(), p_186266_0_.func_186215_c(), p_186266_0_.func_207664_d());
++ }
++
+ public void func_186260_a(IWorld p_186260_1_, BlockPos p_186260_2_, PlacementSettings p_186260_3_) {
+ p_186260_3_.func_186224_i();
+ this.func_186253_b(p_186260_1_, p_186260_2_, p_186260_3_);
+@@ -177,7 +182,7 @@
int i1 = Integer.MIN_VALUE;
int j1 = Integer.MIN_VALUE;
@@ -9,15 +21,24 @@
BlockPos blockpos = template$blockinfo.field_186242_a;
if (mutableboundingbox == null || mutableboundingbox.func_175898_b(blockpos)) {
IFluidState ifluidstate = p_189962_3_.func_204763_l() ? p_189962_1_.func_204610_c(blockpos) : null;
-@@ -317,14 +317,19 @@
+@@ -288,7 +293,7 @@
+ }
+
+ if (!p_189962_3_.func_186221_e()) {
+- this.func_207668_a(p_189962_1_, p_189962_2_, p_189962_3_.func_186212_b(), p_189962_3_.func_186215_c(), p_189962_3_.func_207664_d(), mutableboundingbox);
++ this.addEntitiesToWorld(p_189962_1_, p_189962_2_, p_189962_3_, p_189962_3_.func_186212_b(), p_189962_3_.func_186215_c(), p_189962_3_.func_207664_d(), p_189962_3_.func_186213_g());
+ }
+
+ return true;
+@@ -317,14 +322,19 @@
});
}
-+ @Deprecated
++ @Deprecated // FORGE: Add template parameter
public static List func_215387_a(IWorld p_215387_0_, BlockPos p_215387_1_, PlacementSettings p_215387_2_, List p_215387_3_) {
+ return processBlockInfos(null, p_215387_0_, p_215387_1_, p_215387_2_, p_215387_3_);
+ }
-+
++
+ public static List processBlockInfos(@Nullable Template template, IWorld p_215387_0_, BlockPos p_215387_1_, PlacementSettings p_215387_2_, List p_215387_3_) {
List list = Lists.newArrayList();
@@ -30,3 +51,47 @@
;
}
+@@ -336,13 +346,41 @@
+ return list;
+ }
+
++ // FORGE: Add processing for entities
++ public static List processEntityInfos(@Nullable Template template, IWorld p_215387_0_, BlockPos p_215387_1_, PlacementSettings p_215387_2_, List p_215387_3_) {
++ List list = Lists.newArrayList();
++
++ for(Template.EntityInfo entityInfo : p_215387_3_) {
++ Vec3d pos = transformedVec3d(p_215387_2_, entityInfo.field_186247_a).func_178787_e(new Vec3d(p_215387_1_));
++ BlockPos blockpos = func_186266_a(p_215387_2_, entityInfo.field_186248_b).func_177971_a(p_215387_1_);
++ Template.EntityInfo entityInfo1 = new Template.EntityInfo(pos, blockpos, entityInfo.field_186249_c);
++
++ for(Iterator iterator = p_215387_2_.func_215221_j().iterator(); entityInfo1 != null && iterator.hasNext(); entityInfo1 = iterator.next().processEntity(p_215387_0_, p_215387_1_, entityInfo, entityInfo1, p_215387_2_, template)) {
++ ;
++ }
++
++ if (entityInfo1 != null) {
++ list.add(entityInfo1);
++ }
++ }
++
++ return list;
++ }
++
++ @Deprecated // FORGE: Add PlacementSettings parameter (below) to pass to entity processors
+ private void func_207668_a(IWorld p_207668_1_, BlockPos p_207668_2_, Mirror p_207668_3_, Rotation p_207668_4_, BlockPos p_207668_5_, @Nullable MutableBoundingBox p_207668_6_) {
+- for(Template.EntityInfo template$entityinfo : this.field_186271_b) {
++ addEntitiesToWorld(p_207668_1_, p_207668_2_, new PlacementSettings().func_186214_a(p_207668_3_).func_186220_a(p_207668_4_).func_207665_a(p_207668_5_).func_186223_a(p_207668_6_), p_207668_3_, p_207668_4_, p_207668_2_, p_207668_6_);
++ }
++
++ private void addEntitiesToWorld(IWorld p_207668_1_, BlockPos p_207668_2_, PlacementSettings placementIn, Mirror p_207668_3_, Rotation p_207668_4_, BlockPos p_207668_5_, @Nullable MutableBoundingBox p_207668_6_) {
++ for(Template.EntityInfo template$entityinfo : processEntityInfos(this, p_207668_1_, p_207668_2_, placementIn, this.field_186271_b)) {
+ BlockPos blockpos = func_207669_a(template$entityinfo.field_186248_b, p_207668_3_, p_207668_4_, p_207668_5_).func_177971_a(p_207668_2_);
++ blockpos = template$entityinfo.field_186248_b; // FORGE: Position will have already been transformed by processEntityInfos
+ if (p_207668_6_ == null || p_207668_6_.func_175898_b(blockpos)) {
+ CompoundNBT compoundnbt = template$entityinfo.field_186249_c;
+ Vec3d vec3d = func_207667_a(template$entityinfo.field_186247_a, p_207668_3_, p_207668_4_, p_207668_5_);
+- Vec3d vec3d1 = vec3d.func_72441_c((double)p_207668_2_.func_177958_n(), (double)p_207668_2_.func_177956_o(), (double)p_207668_2_.func_177952_p());
++ vec3d = vec3d.func_72441_c((double)p_207668_2_.func_177958_n(), (double)p_207668_2_.func_177956_o(), (double)p_207668_2_.func_177952_p());
++ Vec3d vec3d1 = template$entityinfo.field_186247_a; // FORGE: Position will have already been transformed by processEntityInfos
+ ListNBT listnbt = new ListNBT();
+ listnbt.add(new DoubleNBT(vec3d1.field_72450_a));
+ listnbt.add(new DoubleNBT(vec3d1.field_72448_b));
diff --git a/src/main/resources/forge.exc b/src/main/resources/forge.exc
index af4abfd50..c841520cd 100644
--- a/src/main/resources/forge.exc
+++ b/src/main/resources/forge.exc
@@ -71,7 +71,10 @@ net/minecraft/world/end/DragonFightManager.(Lnet/minecraft/world/server/Se
net/minecraft/world/gen/feature/OreFeatureConfig$FillerBlockType.create(Ljava/lang/String;Ljava/lang/String;Ljava/util/function/Predicate;)Lnet/minecraft/world/gen/feature/OreFeatureConfig$FillerBlockType;=|enumName,p_i50618_3_,p_i50618_4_
net/minecraft/world/gen/feature/jigsaw/JigsawPattern$PlacementBehaviour.create(Ljava/lang/String;Ljava/lang/String;Lcom/google/common/collect/ImmutableList;)Lnet/minecraft/world/gen/feature/jigsaw/JigsawPattern$PlacementBehaviour;=|enumName,p_i50487_3_,p_i50487_4_
net/minecraft/world/gen/feature/template/StructureProcessor.process(Lnet/minecraft/world/IWorldReader;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/gen/feature/template/Template$BlockInfo;Lnet/minecraft/world/gen/feature/template/Template$BlockInfo;Lnet/minecraft/world/gen/feature/template/PlacementSettings;Lnet/minecraft/world/gen/feature/template/Template;)Lnet/minecraft/world/gen/feature/template/Template$BlockInfo;=|p_215194_1_,p_215194_2_,p_215194_3_,p_215194_4_,p_215194_5_,template
+net/minecraft/world/gen/feature/template/Template.transformedVec3d(Lnet/minecraft/world/gen/feature/template/PlacementSettings;Lnet/minecraft/util/math/Vec3d;)Lnet/minecraft/util/math/Vec3d;=|p_186266_0_,p_186266_1_
net/minecraft/world/gen/feature/template/Template.processBlockInfos(Lnet/minecraft/world/gen/feature/template/Template;Lnet/minecraft/world/IWorld;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/gen/feature/template/PlacementSettings;Ljava/util/List;)Ljava/util/List;=|template,p_215387_0_,p_215387_1_,p_215387_2_,p_215387_3_
+net/minecraft/world/gen/feature/template/Template.processEntityInfos(Lnet/minecraft/world/gen/feature/template/Template;Lnet/minecraft/world/IWorld;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/gen/feature/template/PlacementSettings;Ljava/util/List;)Ljava/util/List;=|template,p_215387_0_,p_215387_1_,p_215387_2_,p_215387_3_
+net/minecraft/world/gen/feature/template/Template.addEntitiesToWorld(Lnet/minecraft/world/IWorld;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/gen/feature/template/PlacementSettings;Lnet/minecraft/util/Mirror;Lnet/minecraft/util/Rotation;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/MutableBoundingBox;)V=|p_207668_1_,p_207668_2_,placementIn,p_207668_3_,p_207668_4_,p_207668_5_,p_207668_6_
net/minecraft/entity/EntityType.(Lnet/minecraft/entity/EntityType$IFactory;Lnet/minecraft/entity/EntityClassification;ZZZLcom/mojang/datafixers/types/Type;Lnet/minecraft/entity/EntitySize;Ljava/util/function/Predicate;Ljava/util/function/ToIntFunction;Ljava/util/function/ToIntFunction;Ljava/util/function/BiFunction;)V=|p_i50385_1_,p_i50385_2_,p_i50385_3_,p_i50385_4_,p_i50385_5_,p_i50385_6_,p_i50385_7_,velocityUpdateSupplier,trackingRangeSupplier,updateIntervalSupplier,customClientFactory
net/minecraft/world/spawner/WorldEntitySpawner.getSpawnList(Lnet/minecraft/world/gen/ChunkGenerator;Lnet/minecraft/entity/EntityClassification;Ljava/util/Random;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/World;)Z|p_222264_0_,p_222264_1_,p_222264_2_,p_222264_3_,world