summaryrefslogtreecommitdiff
path: root/src/main/java/com/keuin/ohmyvanillamc/mixins
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/keuin/ohmyvanillamc/mixins')
-rw-r--r--src/main/java/com/keuin/ohmyvanillamc/mixins/fix/disableEntityTrackerEntrySpamming/EntityTrackerEntryMixin.java32
-rw-r--r--src/main/java/com/keuin/ohmyvanillamc/mixins/rule/disableFishSchooling/SchoolingFishEntityMixin.java50
-rw-r--r--src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/AbstractPlantStemBlockMixin.java86
-rw-r--r--src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/BambooBlockMixin.java90
-rw-r--r--src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/CactusBlockMixin.java81
-rw-r--r--src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/ChorusFlowerBlockMixin.java162
-rw-r--r--src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/SugarCaneBlockMixin.java75
-rw-r--r--src/main/java/com/keuin/ohmyvanillamc/mixins/rule/reintroduceLlamaItemDuping/PlayerManagerMixin.java105
8 files changed, 681 insertions, 0 deletions
diff --git a/src/main/java/com/keuin/ohmyvanillamc/mixins/fix/disableEntityTrackerEntrySpamming/EntityTrackerEntryMixin.java b/src/main/java/com/keuin/ohmyvanillamc/mixins/fix/disableEntityTrackerEntrySpamming/EntityTrackerEntryMixin.java
new file mode 100644
index 0000000..5b885a0
--- /dev/null
+++ b/src/main/java/com/keuin/ohmyvanillamc/mixins/fix/disableEntityTrackerEntrySpamming/EntityTrackerEntryMixin.java
@@ -0,0 +1,32 @@
+package com.keuin.ohmyvanillamc.mixins.fix.disableEntityTrackerEntrySpamming;
+
+import com.keuin.ohmyvanillamc.DummyLogger;
+import net.minecraft.server.network.EntityTrackerEntry;
+import org.apache.logging.log4j.Logger;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+@Mixin(EntityTrackerEntry.class)
+public class EntityTrackerEntryMixin {
+
+ static {
+ setLOGGER(new DummyLogger());
+ }
+
+ @Shadow
+ @Final
+ private static Logger LOGGER;
+
+ @Accessor("LOGGER")
+ private static Logger getLOGGER() {
+ throw new AssertionError();
+ }
+
+ @Accessor("LOGGER")
+ private static void setLOGGER(Logger logger) {
+ throw new AssertionError();
+ }
+
+}
diff --git a/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/disableFishSchooling/SchoolingFishEntityMixin.java b/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/disableFishSchooling/SchoolingFishEntityMixin.java
new file mode 100644
index 0000000..9e82f09
--- /dev/null
+++ b/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/disableFishSchooling/SchoolingFishEntityMixin.java
@@ -0,0 +1,50 @@
+package com.keuin.ohmyvanillamc.mixins.rule.disableFishSchooling;
+
+import com.keuin.ohmyvanillamc.OmvmSettings;
+import net.minecraft.entity.EntityType;
+import net.minecraft.entity.ai.goal.FollowGroupLeaderGoal;
+import net.minecraft.entity.passive.FishEntity;
+import net.minecraft.entity.passive.SchoolingFishEntity;
+import net.minecraft.world.World;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Overwrite;
+import org.spongepowered.asm.mixin.Shadow;
+
+@Mixin(SchoolingFishEntity.class)
+public abstract class SchoolingFishEntityMixin extends FishEntity {
+
+ public SchoolingFishEntityMixin(EntityType<? extends FishEntity> type, World world) {
+ super(type, world);
+ }
+
+ @Shadow
+ public abstract boolean hasLeader();
+
+ @Shadow
+ private SchoolingFishEntity leader;
+
+ /**
+ * @reason To disable SchoolingFish schooling.
+ * @author trueKeuin
+ */
+ @Overwrite
+ public void moveTowardLeader() {
+ if (!OmvmSettings.disableFishSchooling) {
+ if (this.hasLeader()) {
+ this.getNavigation().startMovingTo(this.leader, 1.0D);
+ }
+ }
+ }
+
+ /**
+ * @reason To disable SchoolingFish schooling.
+ * @author trueKeuin
+ */
+ @Overwrite
+ public void initGoals() {
+ super.initGoals();
+ if (!OmvmSettings.disableFishSchooling) {
+ this.goalSelector.add(5, new FollowGroupLeaderGoal((SchoolingFishEntity) (Object) this));
+ }
+ }
+}
diff --git a/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/AbstractPlantStemBlockMixin.java b/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/AbstractPlantStemBlockMixin.java
new file mode 100644
index 0000000..1092816
--- /dev/null
+++ b/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/AbstractPlantStemBlockMixin.java
@@ -0,0 +1,86 @@
+package com.keuin.ohmyvanillamc.mixins.rule.forceRipening;
+
+import com.keuin.ohmyvanillamc.OmvmSettings;
+import net.minecraft.block.AbstractPlantPartBlock;
+import net.minecraft.block.AbstractPlantStemBlock;
+import net.minecraft.block.BlockState;
+import net.minecraft.server.world.ServerWorld;
+import net.minecraft.state.property.IntProperty;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Direction;
+import net.minecraft.util.shape.VoxelShape;
+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 java.util.Random;
+
+@Mixin(AbstractPlantStemBlock.class)
+public abstract class AbstractPlantStemBlockMixin extends AbstractPlantPartBlock {
+
+ protected AbstractPlantStemBlockMixin(Settings settings, Direction growthDirection, VoxelShape outlineShape, boolean tickWater) {
+ super(settings, growthDirection, outlineShape, tickWater);
+ }
+
+ @Shadow
+ @Final
+ public static IntProperty AGE;
+
+ @Shadow
+ @Final
+ private double growthChance;
+
+ @Shadow
+ protected abstract boolean chooseStemState(BlockState state);
+
+ /**
+ * @reason Revert to the super class's impl (Block's).
+ * @author trueKeuin
+ */
+ @Overwrite
+ public boolean hasRandomTicks(BlockState state) {
+ return super.hasRandomTicks(state);
+ }
+
+ /**
+ * Reintroduce the MC-113809 glitch for kelp, twisted vine and weeping vine. The implementation is identical to Minecraft 1.15.2.
+ *
+ * @author trueKeuin
+ * @reason reintroduce MC-113809 for kelp, twisted vine and weeping vine.
+ */
+ @Override
+ public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
+ if (!state.canPlaceAt(world, pos)) {
+ world.breakBlock(pos, true);
+ } else if (OmvmSettings.enableStemForceRipening) {
+ realGrow(state, world, pos, random);
+ }
+ }
+
+ /**
+ * Revert to base class (Block) implementation of randomTick: just simply call scheduledTick.
+ * (both 1.15.2 and 1.16.4 are the same)
+ *
+ * @author trueKeuin
+ * @reason revert to the base class `Block` implementation.
+ */
+ @Overwrite
+ public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
+ if (OmvmSettings.enableStemForceRipening) {
+ scheduledTick(state, world, pos, random);
+ } else {
+ realGrow(state, world, pos, random);
+ }
+ }
+
+ private void realGrow(BlockState state, ServerWorld world, BlockPos pos, Random random) {
+ if (state.get(AGE) < 25 && random.nextDouble() < this.growthChance) {
+ BlockPos blockPos = pos.offset(this.growthDirection);
+ if (this.chooseStemState(world.getBlockState(blockPos))) {
+ world.setBlockState(blockPos, state.cycle(AGE));
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/BambooBlockMixin.java b/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/BambooBlockMixin.java
new file mode 100644
index 0000000..0dd9be6
--- /dev/null
+++ b/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/BambooBlockMixin.java
@@ -0,0 +1,90 @@
+package com.keuin.ohmyvanillamc.mixins.rule.forceRipening;
+
+import com.keuin.ohmyvanillamc.OmvmSettings;
+import net.minecraft.block.BambooBlock;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.server.world.ServerWorld;
+import net.minecraft.state.property.IntProperty;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.BlockView;
+import net.minecraft.world.World;
+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 java.util.Random;
+
+@Mixin(BambooBlock.class)
+public abstract class BambooBlockMixin extends Block {
+
+ public BambooBlockMixin(Settings settings) {
+ super(settings);
+ }
+
+ @Shadow
+ @Final
+ public static IntProperty STAGE;
+
+ @Shadow
+ protected abstract int countBambooBelow(BlockView world, BlockPos pos);
+
+ @Shadow
+ protected abstract void updateLeaves(BlockState state, World world, BlockPos pos, Random random, int height);
+
+ /**
+ * Reintroduce the base class's implementation.
+ *
+ * @reason reintroduce base class's implementation.
+ * @author trueKeuin
+ */
+ @Overwrite
+ public boolean hasRandomTicks(BlockState state) {
+ boolean zf = OmvmSettings.enableBambooForceRipening;
+ return ((state.get(STAGE) == 0) && !zf) || (randomTicks && zf);
+ }
+
+ /**
+ * Revert to base class (Block) implementation of randomTick: just simply call scheduledTick.
+ * (both 1.15.2 and 1.16.4 are the same)
+ *
+ * @author trueKeuin
+ * @reason revert to the base class `Block` implementation.
+ */
+ @Overwrite
+ public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
+ if (OmvmSettings.enableBambooForceRipening) {
+ scheduledTick(state, world, pos, random);
+ } else {
+ realGrow(state, world, pos, random);
+ }
+ }
+
+ /**
+ * Reintroduce the MC-113809 glitch for bamboo. The implementation is identical to Minecraft 1.15.2.
+ *
+ * @author trueKeuin
+ * @reason reintroduce MC-113809 for bamboo.
+ */
+ @Overwrite
+ public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
+ if (!state.canPlaceAt(world, pos)) {
+ world.breakBlock(pos, true);
+ } else if (OmvmSettings.enableBambooForceRipening) {
+ realGrow(state, world, pos, random);
+ }
+ }
+
+ private void realGrow(BlockState state, ServerWorld world, BlockPos pos, Random random) {
+ if (state.get(STAGE) == 0) {
+ if (random.nextInt(3) == 0 && world.isAir(pos.up()) && world.getBaseLightLevel(pos.up(), 0) >= 9) {
+ int i = this.countBambooBelow(world, pos) + 1;
+ if (i < 16) {
+ this.updateLeaves(state, world, pos, random, i);
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/CactusBlockMixin.java b/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/CactusBlockMixin.java
new file mode 100644
index 0000000..d5feaf9
--- /dev/null
+++ b/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/CactusBlockMixin.java
@@ -0,0 +1,81 @@
+package com.keuin.ohmyvanillamc.mixins.rule.forceRipening;
+
+import com.keuin.ohmyvanillamc.OmvmSettings;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.block.CactusBlock;
+import net.minecraft.server.world.ServerWorld;
+import net.minecraft.state.property.IntProperty;
+import net.minecraft.util.math.BlockPos;
+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 java.util.Random;
+
+@Mixin(CactusBlock.class)
+public abstract class CactusBlockMixin extends Block {
+
+ public CactusBlockMixin(Settings settings) {
+ super(settings);
+ }
+
+ @Shadow
+ @Final
+ public static IntProperty AGE;
+
+ /**
+ * Revert to base class (Block) implementation of randomTick: just simply call scheduledTick.
+ * (both 1.15.2 and 1.16.4 are the same)
+ *
+ * @author trueKeuin
+ * @reason revert to the base class `Block` implementation.
+ */
+ @Overwrite
+ public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
+ if (OmvmSettings.enableCactusForceRipening) {
+ scheduledTick(state, world, pos, random);
+ } else {
+ // here goes 1.16.4 version randomTick impl.
+ realGrow(state, world, pos);
+ }
+ }
+
+ /**
+ * Reintroduce the MC-113809 glitch for cactus. The implementation is identical to Minecraft 1.15.2.
+ *
+ * @author trueKeuin
+ * @reason reintroduce MC-113809 for cactus.
+ */
+ @Overwrite
+ public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
+ if (!state.canPlaceAt(world, pos)) {
+ world.breakBlock(pos, true);
+ } else if (OmvmSettings.enableCactusForceRipening) {
+ realGrow(state, world, pos);
+ }
+ }
+
+ private void realGrow(BlockState state, ServerWorld world, BlockPos pos) {
+ BlockPos blockPos = pos.up();
+ if (world.isAir(blockPos)) {
+ int i;
+ for (i = 1; world.getBlockState(pos.down(i)).isOf(this); ++i) {
+ }
+
+ if (i < 3) {
+ int j = state.get(AGE);
+ if (j == 15) {
+ world.setBlockState(blockPos, this.getDefaultState());
+ BlockState blockState = state.with(AGE, 0);
+ world.setBlockState(pos, blockState, 4);
+ blockState.neighborUpdate(world, blockPos, this, pos, false);
+ } else {
+ world.setBlockState(pos, state.with(AGE, j + 1), 4);
+ }
+
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/ChorusFlowerBlockMixin.java b/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/ChorusFlowerBlockMixin.java
new file mode 100644
index 0000000..fb79848
--- /dev/null
+++ b/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/ChorusFlowerBlockMixin.java
@@ -0,0 +1,162 @@
+package com.keuin.ohmyvanillamc.mixins.rule.forceRipening;
+
+import com.keuin.ohmyvanillamc.OmvmSettings;
+import net.minecraft.block.*;
+import net.minecraft.server.world.ServerWorld;
+import net.minecraft.state.property.IntProperty;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Direction;
+import net.minecraft.world.World;
+import net.minecraft.world.WorldView;
+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 java.util.Random;
+
+@Mixin(ChorusFlowerBlock.class)
+public abstract class ChorusFlowerBlockMixin extends Block {
+
+ public ChorusFlowerBlockMixin(Settings settings) {
+ super(settings);
+ }
+
+ @Shadow
+ @Final
+ public static IntProperty AGE;
+
+ @Shadow
+ @Final
+ private ChorusPlantBlock plantBlock;
+
+ @Shadow
+ private static boolean isSurroundedByAir(WorldView world, BlockPos pos, @Nullable Direction exceptDirection) {
+ throw new RuntimeException("Mixin error. Program should not go here.");
+ }
+
+ @Shadow
+ private void grow(World world, BlockPos pos, int age) {
+ throw new RuntimeException("Mixin error. Program should not go here.");
+ }
+
+ @Shadow
+ private void die(World world, BlockPos pos) {
+ throw new RuntimeException("Mixin error. Program should not go here.");
+ }
+
+ /**
+ * Reintroduce the MC-113809 glitch for chorus flower. The implementation is identical to Minecraft 1.15.2.
+ *
+ * @author trueKeuin
+ * @reason reintroduce MC-113809 for chorus flower.
+ */
+ @Overwrite
+ public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
+ if (!state.canPlaceAt(world, pos)) {
+ world.breakBlock(pos, true);
+ } else if (OmvmSettings.enableChorusFlowerForceRipening) {
+ realGrow(state, world, pos, random);
+ }
+
+ }
+
+ /**
+ * Reintroduce the base class's implementation.
+ *
+ * @reason reintroduce base class's implementation.
+ * @author trueKeuin
+ */
+ @Overwrite
+ public boolean hasRandomTicks(BlockState state) {
+ boolean zf = OmvmSettings.enableChorusFlowerForceRipening;
+ return ((state.get(AGE) < 5) && !zf) || (randomTicks && zf);
+ }
+
+ /**
+ * Revert to base class (Block) implementation of randomTick: just simply call scheduledTick.
+ * (both 1.15.2 and 1.16.4 are the same)
+ *
+ * @author trueKeuin
+ * @reason revert to the base class `Block` implementation.
+ */
+ @Overwrite
+ public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
+ if (OmvmSettings.enableChorusFlowerForceRipening) {
+ scheduledTick(state, world, pos, random);
+ } else {
+ realGrow(state, world, pos, random);
+ }
+
+ }
+
+ private void realGrow(BlockState state, ServerWorld world, BlockPos pos, Random random) {
+ BlockPos blockPos = pos.up();
+ if (world.isAir(blockPos) && blockPos.getY() < 256) {
+ int i = state.get(AGE);
+ if (i < 5) {
+ boolean bl = false;
+ boolean bl2 = false;
+ BlockState blockState = world.getBlockState(pos.down());
+ Block block = blockState.getBlock();
+ int l;
+ if (block == Blocks.END_STONE) {
+ bl = true;
+ } else if (block == this.plantBlock) {
+ l = 1;
+
+ for (int k = 0; k < 4; ++k) {
+ Block block2 = world.getBlockState(pos.down(l + 1)).getBlock();
+ if (block2 != this.plantBlock) {
+ if (block2 == Blocks.END_STONE) {
+ bl2 = true;
+ }
+ break;
+ }
+
+ ++l;
+ }
+
+ if (l < 2 || l <= random.nextInt(bl2 ? 5 : 4)) {
+ bl = true;
+ }
+ } else if (blockState.isAir()) {
+ bl = true;
+ }
+
+ if (bl && isSurroundedByAir(world, blockPos, (Direction) null) && world.isAir(pos.up(2))) {
+ world.setBlockState(pos, this.plantBlock.withConnectionProperties(world, pos), 2);
+ this.grow(world, blockPos, i);
+ } else if (i < 4) {
+ l = random.nextInt(4);
+ if (bl2) {
+ ++l;
+ }
+
+ boolean bl3 = false;
+
+ for (int m = 0; m < l; ++m) {
+ Direction direction = Direction.Type.HORIZONTAL.random(random);
+ BlockPos blockPos2 = pos.offset(direction);
+ if (world.isAir(blockPos2) && world.isAir(blockPos2.down()) && isSurroundedByAir(world, blockPos2, direction.getOpposite())) {
+ this.grow(world, blockPos2, i + 1);
+ bl3 = true;
+ }
+ }
+
+ if (bl3) {
+ world.setBlockState(pos, this.plantBlock.withConnectionProperties(world, pos), 2);
+ } else {
+ this.die(world, pos);
+ }
+ } else {
+ this.die(world, pos);
+ }
+
+ }
+ }
+ }
+
+
+}
diff --git a/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/SugarCaneBlockMixin.java b/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/SugarCaneBlockMixin.java
new file mode 100644
index 0000000..6cce2a3
--- /dev/null
+++ b/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/forceRipening/SugarCaneBlockMixin.java
@@ -0,0 +1,75 @@
+package com.keuin.ohmyvanillamc.mixins.rule.forceRipening;
+
+import com.keuin.ohmyvanillamc.OmvmSettings;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.block.SugarCaneBlock;
+import net.minecraft.server.world.ServerWorld;
+import net.minecraft.state.property.IntProperty;
+import net.minecraft.util.math.BlockPos;
+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 java.util.Random;
+
+@Mixin(SugarCaneBlock.class)
+public abstract class SugarCaneBlockMixin extends Block {
+
+ public SugarCaneBlockMixin(Settings settings) {
+ super(settings);
+ }
+
+ @Shadow
+ @Final
+ public static IntProperty AGE;
+
+ /**
+ * Revert to base class (Block) implementation of randomTick: just simply call scheduledTick.
+ * (both 1.15.2 and 1.16.4 are the same)
+ *
+ * @author trueKeuin
+ * @reason revert to the base class `Block` implementation.
+ */
+ @Overwrite
+ public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
+ if (OmvmSettings.enableSugarCaneForceRipening) {
+ scheduledTick(state, world, pos, random);
+ } else if (world.isAir(pos.up())) { // here goes 1.16.4 version randomTick impl.
+ realGrow(state, world, pos);
+ }
+
+ }
+
+ /**
+ * Reintroduce the MC-113809 glitch for sugar cane. The implementation is identical to Minecraft 1.15.2.
+ *
+ * @author trueKeuin
+ * @reason reintroduce MC-113809 for sugar cane.
+ */
+ @Overwrite
+ public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
+ if (!state.canPlaceAt(world, pos)) {
+ world.breakBlock(pos, true);
+ } else if (world.isAir(pos.up()) && OmvmSettings.enableSugarCaneForceRipening) {
+ realGrow(state, world, pos);
+ }
+ }
+
+ private void realGrow(BlockState state, ServerWorld world, BlockPos pos) {
+ int i;
+ for (i = 1; world.getBlockState(pos.down(i)).isOf((SugarCaneBlock) (Object) this); ++i) {
+ }
+
+ if (i < 3) {
+ int j = state.get(AGE);
+ if (j == 15) {
+ world.setBlockState(pos.up(), this.getDefaultState());
+ world.setBlockState(pos, state.with(AGE, 0), 4);
+ } else {
+ world.setBlockState(pos, state.with(AGE, j + 1), 4);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/reintroduceLlamaItemDuping/PlayerManagerMixin.java b/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/reintroduceLlamaItemDuping/PlayerManagerMixin.java
new file mode 100644
index 0000000..f4f8bec
--- /dev/null
+++ b/src/main/java/com/keuin/ohmyvanillamc/mixins/rule/reintroduceLlamaItemDuping/PlayerManagerMixin.java
@@ -0,0 +1,105 @@
+package com.keuin.ohmyvanillamc.mixins.rule.reintroduceLlamaItemDuping;
+
+import com.keuin.ohmyvanillamc.OmvmSettings;
+import net.minecraft.advancement.PlayerAdvancementTracker;
+import net.minecraft.entity.Entity;
+import net.minecraft.network.Packet;
+import net.minecraft.network.packet.s2c.play.PlayerListS2CPacket;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.PlayerManager;
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.server.world.ServerWorld;
+import net.minecraft.stat.ServerStatHandler;
+import net.minecraft.stat.Stats;
+import org.apache.logging.log4j.Logger;
+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 java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * Reintroduce MC-161754 glitch
+ */
+@Mixin(PlayerManager.class)
+public abstract class PlayerManagerMixin {
+
+ @Shadow
+ protected abstract void savePlayerData(ServerPlayerEntity player);
+
+ @Shadow
+ @Final
+ private static Logger LOGGER;
+
+ @Shadow
+ @Final
+ private List<ServerPlayerEntity> players;
+
+ @Shadow
+ @Final
+ private MinecraftServer server;
+
+ @Shadow
+ @Final
+ private Map<UUID, ServerPlayerEntity> playerMap;
+
+ @Shadow
+ @Final
+ private Map<UUID, ServerStatHandler> statisticsMap;
+
+ @Shadow
+ @Final
+ private Map<UUID, PlayerAdvancementTracker> advancementTrackers;
+
+ @Shadow
+ public abstract void sendToAll(Packet<?> packet);
+
+ /**
+ * @reason re-introduce llama item duplicating glitch.
+ * @author trueKeuin
+ */
+ @Overwrite
+ public void remove(ServerPlayerEntity player) {
+ ServerWorld serverWorld = player.getServerWorld();
+ player.incrementStat(Stats.LEAVE_GAME);
+ this.savePlayerData(player);
+ if (player.hasVehicle()) {
+ Entity entity = player.getRootVehicle();
+ if (entity.hasPlayerRider()) {
+ LOGGER.debug("Removing player mount");
+ player.stopRiding();
+ serverWorld.removeEntity(entity);
+ // entity.removed = true
+ entity.removed = !OmvmSettings.reintroduceLlamaItemDuplicating || entity.removed;
+
+ Entity entity2;
+ for (Iterator var4 = entity.getPassengersDeep().iterator(); var4.hasNext(); // entity2.removed = true
+ entity2.removed = !OmvmSettings.reintroduceLlamaItemDuplicating || entity2.removed) {
+ entity2 = (Entity) var4.next();
+ serverWorld.removeEntity(entity2);
+ }
+
+ serverWorld.getChunk(player.chunkX, player.chunkZ).markDirty();
+ }
+ }
+
+ player.detach();
+ serverWorld.removePlayer(player);
+ player.getAdvancementTracker().clearCriteria();
+ this.players.remove(player);
+ this.server.getBossBarManager().onPlayerDisconnect(player);
+ UUID uUID = player.getUuid();
+ ServerPlayerEntity serverPlayerEntity = (ServerPlayerEntity) this.playerMap.get(uUID);
+ if (serverPlayerEntity == player) {
+ this.playerMap.remove(uUID);
+ this.statisticsMap.remove(uUID);
+ this.advancementTrackers.remove(uUID);
+ }
+
+ this.sendToAll(new PlayerListS2CPacket(PlayerListS2CPacket.Action.REMOVE_PLAYER, player));
+ }
+}