Reorganized Imports/Packages

This commit is contained in:
Frank 2022-05-18 23:56:18 +02:00
parent a8beba9196
commit 770a5b4046
854 changed files with 42775 additions and 41811 deletions

View file

@ -0,0 +1,12 @@
package org.betterx.betterend.mixin.client;
import net.minecraft.client.resources.sounds.AbstractSoundInstance;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(AbstractSoundInstance.class)
public interface AbstractSoundInstanceAccessor {
@Accessor("volume")
void setVolume(float volume);
}

View file

@ -0,0 +1,26 @@
package org.betterx.betterend.mixin.client;
import net.minecraft.client.model.ArmorStandArmorModel;
import net.minecraft.client.renderer.entity.ArmorStandRenderer;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.world.entity.decoration.ArmorStand;
import org.betterx.betterend.client.render.ArmoredElytraLayer;
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.CallbackInfo;
@Mixin(ArmorStandRenderer.class)
public abstract class ArmorStandRendererMixin extends LivingEntityRenderer<ArmorStand, ArmorStandArmorModel> {
public ArmorStandRendererMixin(EntityRendererProvider.Context context, ArmorStandArmorModel entityModel, float f) {
super(context, entityModel, f);
}
@Inject(method = "<init>*", at = @At("TAIL"))
public void be_addCustomLayer(EntityRendererProvider.Context context, CallbackInfo ci) {
addLayer(new ArmoredElytraLayer<>(this, context.getModelSet()));
}
}

View file

@ -0,0 +1,62 @@
package org.betterx.betterend.mixin.client;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BiomeColors;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.fabricmc.loader.api.FabricLoader;
import org.betterx.bclib.util.ColorUtil;
import org.betterx.bclib.util.MHelper;
import org.betterx.betterend.client.ClientOptions;
import org.betterx.betterend.registry.EndBlocks;
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 java.awt.*;
import java.util.Arrays;
import java.util.Comparator;
@Mixin(BiomeColors.class)
public class BiomeColorsMixin {
private static final int POISON_COLOR = ColorUtil.color(92, 160, 78);
private static final int STREAM_COLOR = ColorUtil.color(105, 213, 244);
private static final Point[] OFFSETS;
private static final boolean HAS_SODIUM;
@Inject(method = "getAverageWaterColor", at = @At("RETURN"), cancellable = true)
private static void be_getWaterColor(BlockAndTintGetter world, BlockPos pos, CallbackInfoReturnable<Integer> info) {
if (ClientOptions.useSulfurWaterColor()) {
BlockAndTintGetter view = HAS_SODIUM ? Minecraft.getInstance().level : world;
MutableBlockPos mut = new MutableBlockPos();
mut.setY(pos.getY());
for (int i = 0; i < OFFSETS.length; i++) {
mut.setX(pos.getX() + OFFSETS[i].x);
mut.setZ(pos.getZ() + OFFSETS[i].y);
if ((view.getBlockState(mut).is(EndBlocks.BRIMSTONE))) {
info.setReturnValue(i < 4 ? POISON_COLOR : STREAM_COLOR);
return;
}
}
}
}
static {
HAS_SODIUM = FabricLoader.getInstance().isModLoaded("sodium");
int index = 0;
OFFSETS = new Point[20];
for (int x = -2; x < 3; x++) {
for (int z = -2; z < 3; z++) {
if ((x != 0 || z != 0) && (Math.abs(x) != 2 || Math.abs(z) != 2)) {
OFFSETS[index++] = new Point(x, z);
}
}
}
Arrays.sort(OFFSETS, Comparator.comparingInt(pos -> MHelper.sqr(pos.x) + MHelper.sqr(pos.y)));
}
}

View file

@ -0,0 +1,36 @@
package org.betterx.betterend.mixin.client;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.layers.CapeLayer;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.ItemStack;
import com.mojang.blaze3d.vertex.PoseStack;
import org.betterx.betterend.item.ArmoredElytra;
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.CallbackInfo;
@Mixin(CapeLayer.class)
public class CapeLayerMixin {
@Inject(method = "render", at = @At("HEAD"), cancellable = true)
public void be_checkCustomElytra(PoseStack poseStack,
MultiBufferSource multiBufferSource,
int i,
AbstractClientPlayer abstractClientPlayer,
float f,
float g,
float h,
float j,
float k,
float l,
CallbackInfo info) {
ItemStack itemStack = abstractClientPlayer.getItemBySlot(EquipmentSlot.CHEST);
if (itemStack.getItem() instanceof ArmoredElytra) {
info.cancel();
}
}
}

View file

@ -0,0 +1,27 @@
package org.betterx.betterend.mixin.client;
import net.minecraft.client.renderer.DimensionSpecialEffects.EndEffects;
import net.minecraft.world.phys.Vec3;
import org.betterx.betterend.integration.Integrations;
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;
@Mixin(value = EndEffects.class, priority = 10)
public class EndEffectsMixin {
@Inject(method = "getBrightnessDependentFogColor", at = @At("HEAD"), cancellable = true)
private void be_restoreBrightness(Vec3 color, float sunHeight, CallbackInfoReturnable<Vec3> info) {
if (Integrations.ENDERSCAPE.modIsInstalled()) {
info.setReturnValue(color.scale(0.15000000596046448D));
}
}
@Inject(method = "isFoggyAt", at = @At("HEAD"), cancellable = true)
private void be_restoreFog(int camX, int camY, CallbackInfoReturnable<Boolean> info) {
if (Integrations.ENDERSCAPE.modIsInstalled()) {
info.setReturnValue(false);
}
}
}

View file

@ -0,0 +1,32 @@
package org.betterx.betterend.mixin.client;
import net.minecraft.client.model.HumanoidModel;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.entity.HumanoidMobRenderer;
import net.minecraft.client.renderer.entity.MobRenderer;
import net.minecraft.world.entity.Mob;
import org.betterx.betterend.client.render.ArmoredElytraLayer;
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.CallbackInfo;
@Mixin(HumanoidMobRenderer.class)
public abstract class HumanoidMobRendererMixin<T extends Mob, M extends HumanoidModel<T>> extends MobRenderer<T, M> {
public HumanoidMobRendererMixin(EntityRendererProvider.Context context, M entityModel, float f) {
super(context, entityModel, f);
}
@Inject(method = "<init>(Lnet/minecraft/client/renderer/entity/EntityRendererProvider$Context;Lnet/minecraft/client/model/HumanoidModel;FFFF)V", at = @At("TAIL"))
public void be_addCustomLayer(EntityRendererProvider.Context context,
M humanoidModel,
float f,
float g,
float h,
float i,
CallbackInfo ci) {
addLayer(new ArmoredElytraLayer<>(this, context.getModelSet()));
}
}

View file

@ -0,0 +1,25 @@
package org.betterx.betterend.mixin.client;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import org.betterx.betterend.events.ItemTooltipCallback;
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 java.util.List;
@Mixin(ItemStack.class)
public class ItemStackMixin {
@Inject(method = "getTooltipLines", at = @At("RETURN"))
private void be_getTooltip(Player entity,
TooltipFlag tooltipContext,
CallbackInfoReturnable<List<Component>> info) {
ItemTooltipCallback.EVENT.invoker()
.getTooltip(entity, ItemStack.class.cast(this), tooltipContext, info.getReturnValue());
}
}

View file

@ -0,0 +1,47 @@
package org.betterx.betterend.mixin.client;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.network.protocol.game.ServerboundPlayerCommandPacket;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.ProfilePublicKey;
import net.minecraft.world.item.ElytraItem;
import net.minecraft.world.item.ItemStack;
import com.mojang.authlib.GameProfile;
import org.betterx.betterend.interfaces.FallFlyingItem;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.At.Shift;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.jetbrains.annotations.Nullable;
@Mixin(LocalPlayer.class)
public abstract class LocalPlayerMixin extends AbstractClientPlayer {
@Final
@Shadow
public ClientPacketListener connection;
public LocalPlayerMixin(ClientLevel clientLevel,
GameProfile gameProfile,
@Nullable ProfilePublicKey profilePublicKey) {
super(clientLevel, gameProfile, profilePublicKey);
}
@Inject(method = "aiStep", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/player/LocalPlayer;getItemBySlot(Lnet/minecraft/world/entity/EquipmentSlot;)Lnet/minecraft/world/item/ItemStack;", shift = Shift.AFTER))
public void be_aiStep(CallbackInfo info) {
ItemStack itemStack = getItemBySlot(EquipmentSlot.CHEST);
if (itemStack.getItem() instanceof FallFlyingItem && ElytraItem.isFlyEnabled(itemStack) && tryToStartFallFlying()) {
connection.send(new ServerboundPlayerCommandPacket(
LocalPlayer.class.cast(this),
ServerboundPlayerCommandPacket.Action.START_FALL_FLYING
));
}
}
}

View file

@ -0,0 +1,57 @@
package org.betterx.betterend.mixin.client;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.WinScreen;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.sounds.Music;
import net.minecraft.sounds.Musics;
import net.minecraft.world.level.Level;
import org.betterx.bclib.util.MHelper;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
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.CallbackInfoReturnable;
@Mixin(Minecraft.class)
public class MinecraftClientMixin {
@Shadow
public LocalPlayer player;
@Shadow
public Screen screen;
@Final
@Shadow
public Gui gui;
@Shadow
public ClientLevel level;
@Inject(method = "getSituationalMusic", at = @At("HEAD"), cancellable = true)
private void be_getEndMusic(CallbackInfoReturnable<Music> info) {
if (!(this.screen instanceof WinScreen) && this.player != null) {
if (this.player.level.dimension() == Level.END) {
if (this.gui.getBossOverlay().shouldPlayMusic() && MHelper.lengthSqr(
this.player.getX(),
this.player.getZ()
) < 250000) {
info.setReturnValue(Musics.END_BOSS);
} else {
Music sound = this.level.getBiomeManager()
.getNoiseBiomeAtPosition(this.player.blockPosition())
.value()
.getBackgroundMusic()
.orElse(Musics.END);
info.setReturnValue(sound);
}
info.cancel();
}
}
}
}

View file

@ -0,0 +1,29 @@
package org.betterx.betterend.mixin.client;
import net.minecraft.client.resources.model.ModelBakery;
import net.minecraft.resources.ResourceLocation;
import org.betterx.betterend.BetterEnd;
import org.betterx.betterend.world.generator.GeneratorOptions;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
@Mixin(ModelBakery.class)
public abstract class ModelLoaderMixin {
@ModifyVariable(method = "loadModel", ordinal = 2, at = @At(value = "INVOKE"))
public ResourceLocation be_switchModel(ResourceLocation id) {
if (GeneratorOptions.changeChorusPlant() && be_changeModel(id)) {
String path = id.getPath().replace("chorus", "custom_chorus");
id = BetterEnd.makeID(path);
}
return id;
}
private boolean be_changeModel(ResourceLocation id) {
return id.getNamespace().equals("minecraft")
&& id.getPath().startsWith("blockstates/")
&& id.getPath().contains("chorus")
&& !id.getPath().contains("custom_");
}
}

View file

@ -0,0 +1,105 @@
package org.betterx.betterend.mixin.client;
import net.minecraft.client.Minecraft;
import net.minecraft.client.resources.sounds.AbstractSoundInstance;
import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.client.sounds.MusicManager;
import net.minecraft.sounds.Music;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import org.betterx.bclib.api.biomes.BiomeAPI;
import org.betterx.betterend.client.ClientOptions;
import org.betterx.betterend.world.biome.EndBiome;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
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;
@Mixin(MusicManager.class)
public abstract class MusicTrackerMixin {
@Final
@Shadow
private Minecraft minecraft;
@Final
@Shadow
private RandomSource random;
@Shadow
private SoundInstance currentMusic;
@Shadow
private int nextSongDelay;
private static float volume = 1;
private static float srcVolume = 0;
private static long time;
@Inject(method = "tick", at = @At("HEAD"), cancellable = true)
public void be_onTick(CallbackInfo info) {
if (ClientOptions.blendBiomeMusic()) {
Music musicSound = minecraft.getSituationalMusic();
if (be_checkNullSound(musicSound) && volume > 0 && be_shouldChangeSound(musicSound) && be_isCorrectBiome()) {
if (volume > 0) {
if (srcVolume < 0) {
srcVolume = currentMusic.getVolume();
}
if (currentMusic instanceof AbstractSoundInstance) {
((AbstractSoundInstanceAccessor) currentMusic).setVolume(volume);
}
minecraft.getSoundManager()
.updateSourceVolume(currentMusic.getSource(), currentMusic.getVolume() * volume);
long t = System.currentTimeMillis();
if (volume == 1 && time == 0) {
time = t;
}
float delta = (t - time) * 0.0005F;
time = t;
volume -= delta;
if (volume < 0) {
volume = 0;
}
}
if (volume == 0) {
volume = 1;
time = 0;
srcVolume = -1;
this.minecraft.getSoundManager().stop(this.currentMusic);
this.nextSongDelay = Mth.nextInt(this.random, 0, musicSound.getMinDelay() / 2);
this.currentMusic = null;
}
if (this.currentMusic == null && this.nextSongDelay-- <= 0) {
this.startPlaying(musicSound);
}
info.cancel();
} else {
volume = 1;
}
}
}
private boolean be_isCorrectBiome() {
if (minecraft.level == null) {
return false;
}
return BiomeAPI.getRenderBiome(minecraft.level.getBiome(minecraft.player.blockPosition())
.value()) instanceof EndBiome;
}
private boolean be_shouldChangeSound(Music musicSound) {
return currentMusic != null && !musicSound
.getEvent()
.getLocation()
.equals(this.currentMusic.getLocation()) && musicSound.replaceCurrentMusic();
}
private boolean be_checkNullSound(Music musicSound) {
return musicSound != null && musicSound.getEvent() != null;
}
@Shadow
public abstract void startPlaying(Music type);
}

View file

@ -0,0 +1,28 @@
package org.betterx.betterend.mixin.client;
import net.minecraft.client.model.PlayerModel;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
import org.betterx.betterend.client.render.ArmoredElytraLayer;
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.CallbackInfo;
@Mixin(PlayerRenderer.class)
public abstract class PlayerRendererMixin extends LivingEntityRenderer<AbstractClientPlayer, PlayerModel<AbstractClientPlayer>> {
public PlayerRendererMixin(EntityRendererProvider.Context context,
PlayerModel<AbstractClientPlayer> entityModel,
float f) {
super(context, entityModel, f);
}
@Inject(method = "<init>*", at = @At("TAIL"))
public void be_addCustomLayer(EntityRendererProvider.Context context, boolean bl, CallbackInfo ci) {
addLayer(new ArmoredElytraLayer<>(this, context.getModelSet()));
}
}

View file

@ -0,0 +1,49 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.util.Mth;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
import com.google.common.collect.Lists;
import org.betterx.bclib.util.MHelper;
import org.betterx.betterend.item.tool.EndHammerItem;
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 java.util.List;
@Mixin(BlockBehaviour.class)
public abstract class BlockBehaviourMixin {
@Inject(method = "getDrops", at = @At("HEAD"), cancellable = true)
public void be_getDroppedStacks(BlockState state,
LootContext.Builder builder,
CallbackInfoReturnable<List<ItemStack>> info) {
if (state.is(Blocks.GLOWSTONE)) {
ItemStack tool = builder.getParameter(LootContextParams.TOOL);
if (tool != null && tool.getItem() instanceof EndHammerItem) {
int min = 3;
int max = 4;
int count = 0;
int fortune = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_FORTUNE, tool);
if (fortune > 0) {
fortune /= Enchantments.BLOCK_FORTUNE.getMaxLevel();
min = Mth.clamp(min + fortune, min, max);
if (min == max) {
info.setReturnValue(Lists.newArrayList(new ItemStack(Items.GLOWSTONE_DUST, max)));
}
}
count = MHelper.randRange(min, max, MHelper.RANDOM_SOURCE);
info.setReturnValue(Lists.newArrayList(new ItemStack(Items.GLOWSTONE_DUST, count)));
}
}
}
}

View file

@ -0,0 +1,100 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.ChorusFlowerBlock;
import net.minecraft.world.level.block.ChorusPlantBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.betterx.bclib.api.tag.CommonBlockTags;
import org.betterx.bclib.util.BlocksHelper;
import org.betterx.betterend.registry.EndBlocks;
import org.betterx.betterend.world.generator.GeneratorOptions;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
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;
@Mixin(value = ChorusFlowerBlock.class, priority = 100)
public abstract class ChorusFlowerBlockMixin extends Block {
private static final VoxelShape SHAPE_FULL = Block.box(0, 0, 0, 16, 16, 16);
private static final VoxelShape SHAPE_HALF = Block.box(0, 0, 0, 16, 4, 16);
public ChorusFlowerBlockMixin(Properties settings) {
super(settings);
}
@Final
@Shadow
private ChorusPlantBlock plant;
@Inject(method = "canSurvive", at = @At("HEAD"), cancellable = true)
private void be_canSurvive(BlockState state,
LevelReader world,
BlockPos pos,
CallbackInfoReturnable<Boolean> info) {
if (world.getBlockState(pos.below()).is(EndBlocks.CHORUS_NYLIUM)) {
info.setReturnValue(true);
info.cancel();
}
}
@Inject(method = "randomTick", at = @At("HEAD"), cancellable = true)
private void be_randomTick(BlockState state,
ServerLevel world,
BlockPos pos,
RandomSource random,
CallbackInfo info) {
if (world.getBlockState(pos.below()).is(CommonBlockTags.END_STONES)) {
BlockPos up = pos.above();
if (world.isEmptyBlock(up) && up.getY() < 256) {
int i = state.getValue(ChorusFlowerBlock.AGE);
if (i < 5) {
this.placeGrownFlower(world, up, i + 1);
BlocksHelper.setWithoutUpdate(world,
pos,
plant.defaultBlockState()
.setValue(ChorusPlantBlock.UP, true)
.setValue(ChorusPlantBlock.DOWN, true));
info.cancel();
}
}
}
}
@Shadow
private void placeGrownFlower(Level world, BlockPos pos, int age) {
}
@Override
@SuppressWarnings("deprecation")
public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) {
if (GeneratorOptions.changeChorusPlant()) {
return state.getValue(ChorusFlowerBlock.AGE) == 5 ? SHAPE_HALF : SHAPE_FULL;
} else {
return super.getShape(state, world, pos, context);
}
}
@Inject(method = "placeDeadFlower", at = @At("HEAD"), cancellable = true)
private void be_placeDeadFlower(Level world, BlockPos pos, CallbackInfo info) {
BlockState down = world.getBlockState(pos.below());
if (down.is(Blocks.CHORUS_PLANT) || down.is(CommonBlockTags.GEN_END_STONES)) {
world.setBlock(pos, this.defaultBlockState().setValue(BlockStateProperties.AGE_5, 5), 2);
world.levelEvent(1034, pos, 0);
}
info.cancel();
}
}

View file

@ -0,0 +1,76 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.ChorusPlantBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import org.betterx.bclib.api.tag.CommonBlockTags;
import org.betterx.betterend.registry.EndBlocks;
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;
@Mixin(value = ChorusPlantBlock.class, priority = 100)
public abstract class ChorusPlantBlockMixin extends Block {
public ChorusPlantBlockMixin(Properties settings) {
super(settings);
}
@Inject(method = "getStateForPlacement(Lnet/minecraft/world/item/context/BlockPlaceContext;)Lnet/minecraft/world/level/block/state/BlockState;", at = @At("RETURN"), cancellable = true)
private void be_getStateForPlacement(BlockPlaceContext ctx, CallbackInfoReturnable<BlockState> info) {
BlockPos pos = ctx.getClickedPos();
Level world = ctx.getLevel();
BlockState plant = info.getReturnValue();
if (ctx.canPlace() && plant.is(Blocks.CHORUS_PLANT) && world.getBlockState(pos.below())
.is(CommonBlockTags.END_STONES)) {
info.setReturnValue(plant.setValue(BlockStateProperties.DOWN, true));
}
}
@Inject(method = "Lnet/minecraft/world/level/block/ChorusPlantBlock;getStateForPlacement" + "(Lnet/minecraft/world/level/BlockGetter;Lnet/minecraft/core/BlockPos;)" + "Lnet/minecraft/world/level/block/state/BlockState;", at = @At("RETURN"), cancellable = true)
private void be_getStateForPlacement(BlockGetter blockGetter,
BlockPos blockPos,
CallbackInfoReturnable<BlockState> info) {
BlockState plant = info.getReturnValue();
if (plant.is(Blocks.CHORUS_PLANT) && blockGetter.getBlockState(blockPos.below())
.is(CommonBlockTags.END_STONES)) {
info.setReturnValue(plant.setValue(BlockStateProperties.DOWN, true));
}
}
@Inject(method = "canSurvive", at = @At("HEAD"), cancellable = true)
private void be_canSurvive(BlockState state,
LevelReader world,
BlockPos pos,
CallbackInfoReturnable<Boolean> info) {
BlockState down = world.getBlockState(pos.below());
if (down.is(EndBlocks.CHORUS_NYLIUM) || down.is(Blocks.END_STONE)) {
info.setReturnValue(true);
}
}
@Inject(method = "updateShape", at = @At("RETURN"), cancellable = true)
private void be_updateShape(BlockState state,
Direction direction,
BlockState newState,
LevelAccessor world,
BlockPos pos,
BlockPos posFrom,
CallbackInfoReturnable<BlockState> info) {
BlockState plant = info.getReturnValue();
if (plant.is(Blocks.CHORUS_PLANT) && world.getBlockState(pos.below()).is(CommonBlockTags.END_STONES)) {
plant = plant.setValue(BlockStateProperties.DOWN, true);
info.setReturnValue(plant);
}
}
}

View file

@ -0,0 +1,44 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.core.BlockPos;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.ChorusFlowerBlock;
import net.minecraft.world.level.block.PipeBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.ChorusPlantFeature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
import org.betterx.bclib.util.BlocksHelper;
import org.betterx.bclib.util.MHelper;
import org.betterx.betterend.registry.EndBlocks;
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;
@Mixin(ChorusPlantFeature.class)
public class ChorusPlantFeatureMixin {
@Inject(method = "place", at = @At("HEAD"), cancellable = true)
private void be_place(FeaturePlaceContext<NoneFeatureConfiguration> featureConfig,
CallbackInfoReturnable<Boolean> info) {
final RandomSource random = featureConfig.random();
final BlockPos blockPos = featureConfig.origin();
final WorldGenLevel structureWorldAccess = featureConfig.level();
if (structureWorldAccess.isEmptyBlock(blockPos) && structureWorldAccess.getBlockState(blockPos.below())
.is(EndBlocks.CHORUS_NYLIUM)) {
ChorusFlowerBlock.generatePlant(structureWorldAccess, blockPos, random, MHelper.randRange(8, 16, random));
BlockState bottom = structureWorldAccess.getBlockState(blockPos);
if (bottom.is(Blocks.CHORUS_PLANT)) {
BlocksHelper.setWithoutUpdate(
structureWorldAccess,
blockPos,
bottom.setValue(PipeBlock.DOWN, true)
);
}
info.setReturnValue(true);
}
}
}

View file

@ -0,0 +1,29 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.ContainerLevelAccess;
import net.minecraft.world.inventory.CraftingMenu;
import net.minecraft.world.level.block.CraftingTableBlock;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
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.CallbackInfoReturnable;
@Mixin(CraftingMenu.class)
public abstract class CraftingMenuMixin {
@Final
@Shadow
private ContainerLevelAccess access;
@Inject(method = "stillValid", at = @At("HEAD"), cancellable = true)
private void be_stillValid(Player player, CallbackInfoReturnable<Boolean> info) {
if (access.evaluate((world, pos) -> {
return world.getBlockState(pos).getBlock() instanceof CraftingTableBlock;
}, true)) {
info.setReturnValue(true);
}
}
}

View file

@ -0,0 +1,147 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.core.Registry;
import net.minecraft.util.RandomSource;
import net.minecraft.world.Container;
import net.minecraft.world.inventory.*;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.item.enchantment.EnchantmentInstance;
import org.betterx.bclib.api.tag.CommonBlockTags;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
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 java.util.List;
@Mixin(EnchantmentMenu.class)
public abstract class EnchantmentMenuMixin extends AbstractContainerMenu {
@Final
@Shadow
private Container enchantSlots;
@Final
@Shadow
private ContainerLevelAccess access;
@Final
@Shadow
private RandomSource random;
@Final
@Shadow
private DataSlot enchantmentSeed;
@Shadow
@Final
public int[] costs;
@Shadow
@Final
public int[] enchantClue;
@Shadow
@Final
public int[] levelClue;
protected EnchantmentMenuMixin(MenuType<?> type, int syncId) {
super(type, syncId);
}
@Inject(method = "slotsChanged", at = @At("HEAD"), cancellable = true)
private void be_slotsChanged(Container inventory, CallbackInfo info) {
if (inventory == this.enchantSlots) {
ItemStack itemStack = inventory.getItem(0);
if (!itemStack.isEmpty() && itemStack.isEnchantable()) {
this.access.execute((world, blockPos) -> {
int i = 0;
int j;
for (j = -1; j <= 1; ++j) {
for (int k = -1; k <= 1; ++k) {
if ((j != 0 || k != 0) && world.isEmptyBlock(blockPos.offset(
k,
0,
j
)) && world.isEmptyBlock(
blockPos.offset(k, 1, j))) {
if (world.getBlockState(blockPos.offset(k * 2, 0, j * 2))
.is(CommonBlockTags.BOOKSHELVES)) {
++i;
}
if (world.getBlockState(blockPos.offset(k * 2, 1, j * 2))
.is(CommonBlockTags.BOOKSHELVES)) {
++i;
}
if (k != 0 && j != 0) {
if (world.getBlockState(blockPos.offset(k * 2, 0, j))
.is(CommonBlockTags.BOOKSHELVES)) {
++i;
}
if (world.getBlockState(blockPos.offset(k * 2, 1, j))
.is(CommonBlockTags.BOOKSHELVES)) {
++i;
}
if (world.getBlockState(blockPos.offset(k, 0, j * 2))
.is(CommonBlockTags.BOOKSHELVES)) {
++i;
}
if (world.getBlockState(blockPos.offset(k, 1, j * 2))
.is(CommonBlockTags.BOOKSHELVES)) {
++i;
}
}
}
}
}
random.setSeed(enchantmentSeed.get());
for (j = 0; j < 3; ++j) {
costs[j] = EnchantmentHelper.getEnchantmentCost(this.random, j, i, itemStack);
enchantClue[j] = -1;
levelClue[j] = -1;
if (costs[j] < j + 1) {
costs[j] = 0;
}
}
for (j = 0; j < 3; ++j) {
if (this.costs[j] > 0) {
List<EnchantmentInstance> list = this.getEnchantmentList(itemStack, j, this.costs[j]);
if (list != null && !list.isEmpty()) {
EnchantmentInstance enchantmentLevelEntry = list.get(this.random.nextInt(
list.size()));
enchantClue[j] = Registry.ENCHANTMENT.getId(enchantmentLevelEntry.enchantment);
levelClue[j] = enchantmentLevelEntry.level;
}
}
}
broadcastChanges();
});
} else {
for (int i = 0; i < 3; ++i) {
costs[i] = 0;
enchantClue[i] = -1;
levelClue[i] = -1;
}
}
info.cancel();
}
}
@Shadow
private List<EnchantmentInstance> getEnchantmentList(ItemStack stack, int slot, int level) {
return null;
}
}

View file

@ -0,0 +1,33 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.levelgen.WorldgenRandom;
import net.minecraft.world.level.levelgen.XoroshiroRandomSource;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.structures.EndCityStructure;
import org.betterx.betterend.world.generator.GeneratorOptions;
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 java.util.Optional;
@Mixin(EndCityStructure.class)
public class EndCityFeatureMixin {
@Inject(method = "findGenerationPoint", at = @At("HEAD"), cancellable = true)
private void be_findGenerationPoint(Structure.GenerationContext context,
CallbackInfoReturnable<Optional<Structure.GenerationStub>> info) {
final ChunkPos pos = context.chunkPos();
WorldgenRandom chunkRandom = new WorldgenRandom(new XoroshiroRandomSource(pos.x, pos.z));
if (GeneratorOptions.useNewGenerator()) {
int chance = GeneratorOptions.getEndCityFailChance();
if (chance > 0 && chunkRandom.nextInt(chance) != 0) {
info.setReturnValue(Optional.empty());
info.cancel();
}
}
}
}

View file

@ -0,0 +1,100 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.boss.enderdragon.EndCrystal;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.pattern.BlockPattern;
import net.minecraft.world.level.dimension.end.DragonRespawnAnimation;
import net.minecraft.world.level.dimension.end.EndDragonFight;
import net.minecraft.world.phys.AABB;
import com.google.common.collect.Lists;
import org.betterx.bclib.util.BlocksHelper;
import org.betterx.betterend.world.generator.GeneratorOptions;
import org.slf4j.Logger;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
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 java.util.List;
@Mixin(EndDragonFight.class)
public class EndDragonFightMixin {
@Shadow
private DragonRespawnAnimation respawnStage;
@Shadow
private boolean dragonKilled;
@Shadow
private BlockPos portalLocation;
@Shadow
@Final
private static Logger LOGGER;
@Final
@Shadow
private ServerLevel level;
@Shadow
private BlockPattern.BlockPatternMatch findExitPortal() {
return null;
}
@Shadow
private void spawnExitPortal(boolean bl) {
}
@Shadow
private void respawnDragon(List<EndCrystal> list) {
}
@Inject(method = "tryRespawn", at = @At("HEAD"), cancellable = true)
private void be_tryRespawnDragon(CallbackInfo info) {
if (GeneratorOptions.replacePortal() && GeneratorOptions.hasDragonFights() && this.dragonKilled && this.respawnStage == null) {
BlockPos blockPos = portalLocation;
if (blockPos == null) {
LOGGER.debug("Tried to respawn, but need to find the portal first.");
BlockPattern.BlockPatternMatch blockPatternMatch = this.findExitPortal();
if (blockPatternMatch == null) {
LOGGER.debug("Couldn't find a portal, so we made one.");
spawnExitPortal(true);
} else {
LOGGER.debug("Found the exit portal & temporarily using it.");
}
}
List<EndCrystal> crystals = Lists.newArrayList();
for (Direction dir : BlocksHelper.HORIZONTAL) {
BlockPos central = BlockPos.ZERO.relative(dir, 4);
List<EndCrystal> crystalList = level.getEntitiesOfClass(
EndCrystal.class,
new AABB(central.below(255).south().west(), central.above(255).north().east())
);
int count = crystalList.size();
for (int n = 0; n < count; n++) {
EndCrystal crystal = crystalList.get(n);
if (!level.getBlockState(crystal.blockPosition().below()).is(Blocks.BEDROCK)) {
crystalList.remove(n);
count--;
n--;
}
}
if (crystalList.isEmpty()) {
info.cancel();
return;
}
crystals.addAll(crystalList);
}
LOGGER.debug("Found all crystals, respawning dragon.");
respawnDragon(crystals);
info.cancel();
}
}
}

View file

@ -0,0 +1,93 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.levelgen.Heightmap.Types;
import net.minecraft.world.level.levelgen.feature.EndPodiumFeature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import org.betterx.bclib.api.WorldDataAPI;
import org.betterx.bclib.util.StructureHelper;
import org.betterx.betterend.BetterEnd;
import org.betterx.betterend.world.generator.GeneratorOptions;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
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.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Optional;
@Mixin(EndPodiumFeature.class)
public class EndPodiumFeatureMixin {
private static BlockPos be_portalPosition;
@Final
@Shadow
private boolean active;
@Inject(method = "place", at = @At("HEAD"), cancellable = true)
private void be_place(FeaturePlaceContext<NoneFeatureConfiguration> featurePlaceContext,
CallbackInfoReturnable<Boolean> info) {
if (!GeneratorOptions.hasPortal()) {
info.setReturnValue(false);
info.cancel();
} else if (GeneratorOptions.replacePortal()) {
RandomSource random = featurePlaceContext.random();
WorldGenLevel world = featurePlaceContext.level();
BlockPos blockPos = be_updatePortalPos(world);
StructureTemplate structure = StructureHelper.readStructure(BetterEnd.makeID(active
? "portal/end_portal_active"
: "portal/end_portal_inactive"));
Vec3i size = structure.getSize();
blockPos = blockPos.offset(-(size.getX() >> 1), -3, -(size.getZ() >> 1));
structure.placeInWorld(world, blockPos, blockPos, new StructurePlaceSettings(), random, 2);
info.setReturnValue(true);
info.cancel();
}
}
@ModifyVariable(method = "place", ordinal = 0, at = @At("HEAD"))
private FeaturePlaceContext<NoneFeatureConfiguration> be_setPosOnGround(FeaturePlaceContext<NoneFeatureConfiguration> featurePlaceContext) {
WorldGenLevel world = featurePlaceContext.level();
BlockPos pos = be_updatePortalPos(world);
return new FeaturePlaceContext<NoneFeatureConfiguration>(
Optional.empty(),
world,
featurePlaceContext.chunkGenerator(),
featurePlaceContext.random(),
pos,
featurePlaceContext.config()
);
}
private BlockPos be_updatePortalPos(WorldGenLevel world) {
CompoundTag compound = WorldDataAPI.getRootTag(BetterEnd.MOD_ID).getCompound("portal");
be_portalPosition = NbtUtils.readBlockPos(compound);
if (be_portalPosition.getY() == 0) {
/*if (GeneratorOptions.useNewGenerator()) {
int y = TerrainGenerator.getHeight(0, 0, world.getLevel().getChunkSource().getGenerator().getBiomeSource());
be_portalPosition = new BlockPos(0, y, 0);
}
else {
be_portalPosition = new BlockPos(0, 65, 0);
}*/
int y = world.getHeight(Types.WORLD_SURFACE, 0, 0);
be_portalPosition = new BlockPos(0, y, 0);
WorldDataAPI.getRootTag(BetterEnd.MOD_ID).put("portal", NbtUtils.writeBlockPos(be_portalPosition));
WorldDataAPI.saveFile(BetterEnd.MOD_ID);
}
return be_portalPosition;
}
}

View file

@ -0,0 +1,44 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.levelgen.feature.SpikeFeature.EndSpike;
import org.betterx.bclib.api.WorldDataAPI;
import org.betterx.betterend.BetterEnd;
import org.betterx.betterend.world.generator.GeneratorOptions;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
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.CallbackInfoReturnable;
@Mixin(EndSpike.class)
public class EndSpikeMixin {
@Final
@Shadow
private int height;
@Inject(method = "getHeight", at = @At("HEAD"), cancellable = true)
private void be_getSpikeHeight(CallbackInfoReturnable<Integer> info) {
if (!GeneratorOptions.isDirectSpikeHeight()) {
int x = getCenterX();
int z = getCenterZ();
String pillarID = String.format("%d_%d", x, z);
CompoundTag pillar = WorldDataAPI.getCompoundTag(BetterEnd.MOD_ID, "pillars");
int minY = pillar.contains(pillarID) ? pillar.getInt(pillarID) : 65;
int maxY = minY + height - 54;
info.setReturnValue(maxY);
}
}
@Shadow
public int getCenterX() {
return 0;
}
@Shadow
public int getCenterZ() {
return 0;
}
}

View file

@ -0,0 +1,26 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.monster.EnderMan;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import org.betterx.betterend.effects.EndStatusEffects;
import org.betterx.betterend.registry.EndEnchantments;
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;
@Mixin(EnderMan.class)
public abstract class EnderManMixin {
@Inject(method = "isLookingAtMe", at = @At("HEAD"), cancellable = true)
private void be_isLookingAtMe(Player player, CallbackInfoReturnable<Boolean> info) {
if (player.isCreative() || player.hasEffect(EndStatusEffects.END_VEIL) || EnchantmentHelper.getItemEnchantmentLevel(
EndEnchantments.END_VEIL,
player.getItemBySlot(EquipmentSlot.HEAD)
) > 0) {
info.setReturnValue(false);
}
}
}

View file

@ -0,0 +1,110 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.portal.PortalInfo;
import net.minecraft.world.phys.Vec3;
import org.betterx.betterend.interfaces.TeleportingEntity;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
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.CallbackInfoReturnable;
@Mixin(Entity.class)
public abstract class EntityMixin implements TeleportingEntity {
@Shadow
private float yRot;
@Shadow
private float xRot;
@Shadow
public Level level;
@Final
@Shadow
public abstract void unRide();
@Shadow
public abstract Vec3 getDeltaMovement();
@Shadow
public abstract EntityType<?> getType();
@Shadow
protected abstract PortalInfo findDimensionEntryPoint(ServerLevel destination);
@Shadow
protected abstract void removeAfterChangingDimensions();
@Shadow
public abstract boolean isRemoved();
private BlockPos exitPos;
@Inject(method = "changeDimension", at = @At("HEAD"), cancellable = true)
public void be_changeDimension(ServerLevel destination, CallbackInfoReturnable<Entity> info) {
if (!isRemoved() && be_canTeleport() && level instanceof ServerLevel) {
unRide();
level.getProfiler().push("changeDimension");
level.getProfiler().push("reposition");
PortalInfo teleportTarget = findDimensionEntryPoint(destination);
if (teleportTarget != null) {
level.getProfiler().popPush("reloading");
Entity entity = getType().create(destination);
if (entity != null) {
entity.restoreFrom(Entity.class.cast(this));
entity.moveTo(
teleportTarget.pos.x,
teleportTarget.pos.y,
teleportTarget.pos.z,
teleportTarget.yRot,
entity.getXRot()
);
entity.setDeltaMovement(teleportTarget.speed);
destination.addDuringTeleport(entity);
}
this.removeAfterChangingDimensions();
level.getProfiler().pop();
((ServerLevel) level).resetEmptyTime();
destination.resetEmptyTime();
level.getProfiler().pop();
be_resetExitPos();
info.setReturnValue(entity);
}
}
}
@Inject(method = "findDimensionEntryPoint", at = @At("HEAD"), cancellable = true)
protected void be_findDimensionEntryPoint(ServerLevel destination, CallbackInfoReturnable<PortalInfo> info) {
if (be_canTeleport()) {
info.setReturnValue(new PortalInfo(
new Vec3(exitPos.getX() + 0.5, exitPos.getY(), exitPos.getZ() + 0.5),
getDeltaMovement(),
yRot,
xRot
));
}
}
@Override
public void be_setExitPos(BlockPos pos) {
this.exitPos = pos.immutable();
}
@Override
public void be_resetExitPos() {
this.exitPos = null;
}
@Override
public boolean be_canTeleport() {
return this.exitPos != null;
}
}

View file

@ -0,0 +1,25 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import org.betterx.betterend.world.generator.GeneratorOptions;
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;
@Mixin(Level.class)
public class LevelMixin {
@Inject(method = "getSharedSpawnPos", at = @At("HEAD"), cancellable = true)
private void be_getSharedSpawnPos(CallbackInfoReturnable<BlockPos> info) {
if (GeneratorOptions.changeSpawn()) {
if (ServerLevel.class.cast(this).dimension() == Level.END) {
BlockPos pos = GeneratorOptions.getSpawn();
info.setReturnValue(pos);
}
}
}
}

View file

@ -0,0 +1,288 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.tags.EntityTypeTags;
import net.minecraft.util.Mth;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.*;
import net.minecraft.world.entity.ai.attributes.AttributeMap;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.animal.FlyingAnimal;
import net.minecraft.world.item.ElytraItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.betterx.betterend.BetterEnd;
import org.betterx.betterend.interfaces.FallFlyingItem;
import org.betterx.betterend.interfaces.MobEffectApplier;
import org.betterx.betterend.item.CrystaliteArmor;
import org.betterx.betterend.registry.EndAttributes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.At.Shift;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Collection;
@Mixin(value = LivingEntity.class, priority = 200)
public abstract class LivingEntityMixin extends Entity {
public LivingEntityMixin(EntityType<?> entityType, Level level) {
super(entityType, level);
}
@Shadow
protected int fallFlyTicks;
@Shadow
public abstract boolean hasEffect(MobEffect mobEffect);
@Shadow
public abstract ItemStack getItemBySlot(EquipmentSlot equipmentSlot);
@Shadow
public abstract void calculateEntityAnimation(LivingEntity livingEntity, boolean b);
@Shadow
protected abstract SoundEvent getFallDamageSound(int i);
@Shadow
public abstract boolean isFallFlying();
@Shadow
public abstract AttributeMap getAttributes();
private Entity lastAttacker;
@Inject(method = "createLivingAttributes", at = @At("RETURN"), cancellable = true)
private static void be_addLivingAttributes(CallbackInfoReturnable<AttributeSupplier.Builder> info) {
EndAttributes.addLivingEntityAttributes(info.getReturnValue());
}
@Inject(method = "tickEffects", at = @At("HEAD"))
protected void be_applyEffects(CallbackInfo info) {
if (!level.isClientSide()) {
LivingEntity owner = LivingEntity.class.cast(this);
if (CrystaliteArmor.hasFullSet(owner)) {
CrystaliteArmor.applySetEffect(owner);
}
getArmorSlots().forEach(itemStack -> {
if (itemStack.getItem() instanceof MobEffectApplier) {
((MobEffectApplier) itemStack.getItem()).applyEffect(owner);
}
});
}
}
@Inject(method = "canBeAffected", at = @At("HEAD"), cancellable = true)
public void be_canBeAffected(MobEffectInstance mobEffectInstance, CallbackInfoReturnable<Boolean> info) {
try {
if (mobEffectInstance.getEffect() == MobEffects.BLINDNESS && getAttributes().getValue(EndAttributes.BLINDNESS_RESISTANCE) > 0.0) {
info.setReturnValue(false);
}
} catch (Exception ex) {
BetterEnd.LOGGER.warning("Blindness resistance attribute haven't been registered.");
}
}
@Inject(method = "hurt", at = @At("HEAD"))
public void be_hurt(DamageSource source, float amount, CallbackInfoReturnable<Boolean> info) {
this.lastAttacker = source.getEntity();
}
@ModifyArg(method = "hurt", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;knockback(DDD)V"), index = 0)
private double be_increaseKnockback(double value, double x, double z) {
if (lastAttacker != null && lastAttacker instanceof LivingEntity) {
LivingEntity attacker = (LivingEntity) lastAttacker;
value += this.be_getKnockback(attacker.getMainHandItem().getItem());
}
return value;
}
// FlyFallingLib (part of Origin) redirected the call to updateFallFlying,
// so we inject our code before the actual call and cancel the execution if the player is still
// flying. That means we have to replicate all vanilla code that happens after the call to
// updateFallFlying. We do this in vanillaAfterUpdateFallFlying
@Inject(method = "aiStep", cancellable = true, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;updateFallFlying()V"))
private void be_updateFallFlying_originFix(CallbackInfo info) {
//run be_updateFallFlying instead
if (!BetterEnd.RUNS_FALL_FLYING_LIB) return;
if (be_updateFallFlyingCommon()) {
vanillaAfterUpdateFallFlying();
info.cancel();
}
}
@Inject(method = "updateFallFlying", at = @At("HEAD"), cancellable = true)
private void be_updateFallFlying(CallbackInfo info) {
//run be_updateFallFlying_originFix instead?
if (BetterEnd.RUNS_FALL_FLYING_LIB) return;
if (be_updateFallFlyingCommon()) {
info.cancel();
}
}
private boolean be_updateFallFlyingCommon() {
ItemStack itemStack = getItemBySlot(EquipmentSlot.CHEST);
if (!level.isClientSide && itemStack.getItem() instanceof FallFlyingItem) {
boolean isFlying = getSharedFlag(7);
if (isFlying && !onGround && !isPassenger() && !hasEffect(MobEffects.LEVITATION)) {
if (ElytraItem.isFlyEnabled(itemStack)) {
int level = 20 + EnchantmentHelper.getItemEnchantmentLevel(Enchantments.UNBREAKING, itemStack) * 5;
if ((fallFlyTicks + 1) % level == 0) {
itemStack.hurtAndBreak(
1,
LivingEntity.class.cast(this),
livingEntity -> livingEntity.broadcastBreakEvent(EquipmentSlot.CHEST)
);
}
isFlying = true;
} else {
isFlying = false;
}
} else {
isFlying = false;
}
setSharedFlag(7, isFlying);
return isFlying;
}
return false;
}
@Shadow
protected abstract void removeFrost();
@Shadow
protected abstract void tryAddFrost();
@Shadow
protected abstract void pushEntities();
@Shadow
protected abstract void checkAutoSpinAttack(AABB aABB, AABB aABB2);
@Shadow
protected int autoSpinAttackTicks;
private void vanillaAfterUpdateFallFlying() {
LivingEntity self = (LivingEntity) (Object) this;
AABB aABB = this.getBoundingBox();
self.travel(new Vec3(self.xxa, self.yya, self.zza));
this.level.getProfiler().pop();
this.level.getProfiler().push("freezing");
boolean bl2 = this.getType().is(EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES);
int o;
if (!this.level.isClientSide && !self.isDeadOrDying()) {
o = this.getTicksFrozen();
if (this.isInPowderSnow && this.canFreeze()) {
this.setTicksFrozen(Math.min(this.getTicksRequiredToFreeze(), o + 1));
} else {
this.setTicksFrozen(Math.max(0, o - 2));
}
}
this.removeFrost();
this.tryAddFrost();
if (!this.level.isClientSide && this.tickCount % 40 == 0 && this.isFullyFrozen() && this.canFreeze()) {
o = bl2 ? 5 : 1;
this.hurt(DamageSource.FREEZE, (float) o);
}
this.level.getProfiler().pop();
this.level.getProfiler().push("push");
if (this.autoSpinAttackTicks > 0) {
--this.autoSpinAttackTicks;
this.checkAutoSpinAttack(aABB, this.getBoundingBox());
}
this.pushEntities();
this.level.getProfiler().pop();
if (!this.level.isClientSide && self.isSensitiveToWater() && this.isInWaterRainOrBubble()) {
this.hurt(DamageSource.DROWN, 1.0F);
}
}
@Inject(method = "travel", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/LivingEntity;isFallFlying()Z", shift = Shift.AFTER), cancellable = true)
public void be_travel(Vec3 vec3, CallbackInfo info) {
ItemStack itemStack = getItemBySlot(EquipmentSlot.CHEST);
if (isFallFlying() && itemStack.getItem() instanceof FallFlyingItem) {
Vec3 moveDelta = getDeltaMovement();
if (moveDelta.y > -0.5D) {
fallDistance = 1.0F;
}
Vec3 lookAngle = getLookAngle();
double d = 0.08D;
float rotX = getXRot() * 0.017453292F;
double k = Math.sqrt(lookAngle.x * lookAngle.x + lookAngle.z * lookAngle.z);
double l = moveDelta.horizontalDistance();
double lookLen = lookAngle.length();
float n = Mth.cos(rotX);
n = (float) (n * n * Math.min(1.0D, lookLen / 0.4D));
moveDelta = getDeltaMovement().add(0.0D, d * (-1.0D + (double) n * 0.75D), 0.0D);
double coef;
if (moveDelta.y < 0.0D && k > 0.0D) {
coef = moveDelta.y * -0.1D * (double) n;
moveDelta = moveDelta.add(lookAngle.x * coef / k, coef, lookAngle.z * coef / k);
}
if (rotX < 0.0F && k > 0.0D) {
coef = l * (double) (-Mth.sin(rotX)) * 0.04D;
moveDelta = moveDelta.add(-lookAngle.x * coef / k, coef * 3.2D, -lookAngle.z * coef / k);
}
if (k > 0.0D) {
moveDelta = moveDelta.add(
(lookAngle.x / k * l - moveDelta.x) * 0.1D,
0.0D,
(lookAngle.z / k * l - moveDelta.z) * 0.1D
);
}
moveDelta = moveDelta.multiply(0.9900000095367432D, 0.9800000190734863D, 0.9900000095367432D);
double movementFactor = ((FallFlyingItem) itemStack.getItem()).getMovementFactor();
moveDelta = moveDelta.multiply(movementFactor, 1.0D, movementFactor);
setDeltaMovement(moveDelta);
move(MoverType.SELF, moveDelta);
if (!level.isClientSide) {
if (horizontalCollision) {
coef = moveDelta.horizontalDistance();
double r = l - coef;
float dmg = (float) (r * 10.0D - 3.0D);
if (dmg > 0.0F) {
playSound(getFallDamageSound((int) dmg), 1.0F, 1.0F);
hurt(DamageSource.FLY_INTO_WALL, dmg);
}
}
if (onGround) {
setSharedFlag(7, false);
}
}
calculateEntityAnimation(LivingEntity.class.cast(this), this instanceof FlyingAnimal);
info.cancel();
}
}
private double be_getKnockback(Item tool) {
if (tool == null) return 0.0D;
Collection<AttributeModifier> modifiers = tool.getDefaultAttributeModifiers(EquipmentSlot.MAINHAND)
.get(Attributes.ATTACK_KNOCKBACK);
if (modifiers.size() > 0) {
return modifiers.iterator().next().getAmount();
}
return 0.0D;
}
}

View file

@ -0,0 +1,18 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.core.MappedRegistry;
import net.minecraft.resources.ResourceKey;
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.CallbackInfo;
@Mixin(MappedRegistry.class)
public class MappedRegistryMixin<T> {
// TODO Make this a part of BCLib (implement froze/unfroze methods)
@Inject(method = "validateWrite", at = @At("HEAD"), cancellable = true)
private void be_validateWrite(ResourceKey<T> resourceKey, CallbackInfo info) {
info.cancel();
}
}

View file

@ -0,0 +1,37 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.core.BlockPos;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.entity.monster.EnderMan;
import net.minecraft.world.entity.monster.Monster;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.phys.AABB;
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 java.util.List;
@Mixin(Monster.class)
public class MonsterMixin {
@Inject(method = "checkMonsterSpawnRules", at = @At(value = "RETURN"), cancellable = true)
private static void be_checkMonsterSpawnRules(EntityType<? extends Monster> type,
ServerLevelAccessor serverWorldAccess,
MobSpawnType spawnReason,
BlockPos pos,
RandomSource random,
CallbackInfoReturnable<Boolean> info) {
boolean canSpawn = info.getReturnValue();
if (canSpawn && spawnReason == MobSpawnType.NATURAL && type == EntityType.ENDERMAN) {
AABB box = new AABB(pos).inflate(16);
List<EnderMan> entities = serverWorldAccess.getEntitiesOfClass(EnderMan.class, box, (entity) -> {
return true;
});
info.setReturnValue(entities.size() < 6);
}
}
}

View file

@ -0,0 +1,14 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.core.Holder;
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(NoiseBasedChunkGenerator.class)
public interface NoiseBasedChunkGeneratorAccessor {
@Accessor("settings")
Holder<NoiseGeneratorSettings> be_getSettings();
}

View file

@ -0,0 +1,25 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.world.level.levelgen.NoiseChunk;
import net.minecraft.world.level.levelgen.NoiseSettings;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(NoiseChunk.class)
public interface NoiseChunkAccessor {
@Accessor("noiseSettings")
NoiseSettings bnv_getNoiseSettings();
@Accessor("cellCountXZ")
int bnv_getCellCountXZ();
@Accessor("cellCountY")
int bnv_getCellCountY();
@Accessor("firstCellZ")
int bnv_getFirstCellZ();
@Accessor("cellNoiseMinY")
int bnv_getCellNoiseMinY();
}

View file

@ -0,0 +1,79 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.world.level.levelgen.*;
import net.minecraft.world.level.levelgen.blending.Blender;
import org.betterx.bclib.BCLib;
import org.betterx.betterend.interfaces.BETargetChecker;
import org.betterx.betterend.world.generator.TerrainGenerator;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
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 java.util.List;
@Mixin(NoiseChunk.class)
public class NoiseChunkMixin implements BETargetChecker {
private boolean be_isEndGenerator;
@Inject(method = "<init>*", at = @At("TAIL"))
private void be_onNoiseChunkInit(int i,
RandomState randomState,
int j,
int k,
NoiseSettings noiseSettings,
DensityFunctions.BeardifierOrMarker beardifierOrMarker,
NoiseGeneratorSettings noiseGeneratorSettings,
Aquifer.FluidPicker fluidPicker,
Blender blender,
CallbackInfo ci) {
var o = BETargetChecker.class.cast(noiseGeneratorSettings);
if (o != null) be_isEndGenerator = o.be_isTarget();
else BCLib.LOGGER.warning(noiseGeneratorSettings + " has unknown implementation.");
}
@Override
public boolean be_isTarget() {
return be_isEndGenerator;
}
@Override
public void be_setTarget(boolean target) {
be_isEndGenerator = target;
}
@Shadow
@Final
private List<NoiseChunk.NoiseInterpolator> interpolators;
@Inject(method = "fillSlice", at = @At("HEAD"), cancellable = true)
private void be_fillSlice(boolean primarySlice, int x, CallbackInfo info) {
if (!be_isTarget()) return;
info.cancel();
NoiseChunkAccessor accessor = (NoiseChunkAccessor) this;
NoiseSettings noiseSettings = accessor.bnv_getNoiseSettings();
final int sizeY = noiseSettings.getCellHeight();
final int sizeXZ = noiseSettings.getCellWidth();
final int cellSizeXZ = accessor.bnv_getCellCountXZ() + 1;
final int firstCellZ = accessor.bnv_getFirstCellZ();
x *= sizeXZ;
for (int cellXZ = 0; cellXZ < cellSizeXZ; ++cellXZ) {
int z = (firstCellZ + cellXZ) * sizeXZ;
for (NoiseChunk.NoiseInterpolator noiseInterpolator : this.interpolators) {
if (noiseInterpolator instanceof NoiseInterpolatorAccessor interpolator) {
final double[] ds = (primarySlice
? interpolator.be_getSlice0()
: interpolator.be_getSlice1())[cellXZ];
TerrainGenerator.fillTerrainDensity(ds, x, z, sizeXZ, sizeY);
}
}
}
}
}

View file

@ -0,0 +1,21 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
import org.betterx.betterend.interfaces.BETargetChecker;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(NoiseGeneratorSettings.class)
public class NoiseGeneratorSettingsMixin implements BETargetChecker {
private boolean be_isTargetGenerator;
@Override
public boolean be_isTarget() {
return be_isTargetGenerator;
}
@Override
public void be_setTarget(boolean target) {
be_isTargetGenerator = target;
}
}

View file

@ -0,0 +1,15 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.world.level.levelgen.NoiseChunk;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(NoiseChunk.NoiseInterpolator.class)
public interface NoiseInterpolatorAccessor {
@Accessor("slice0")
double[][] be_getSlice0();
@Accessor("slice1")
double[][] be_getSlice1();
}

View file

@ -0,0 +1,28 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.advancements.Advancement;
import net.minecraft.server.PlayerAdvancements;
import net.minecraft.server.level.ServerPlayer;
import org.betterx.betterend.events.PlayerAdvancementsCallback;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.At.Shift;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(PlayerAdvancements.class)
public abstract class PlayerAdvancementsMixin {
@Shadow
private ServerPlayer player;
@Inject(method = "award", at = @At(value = "INVOKE", target = "Lnet/minecraft/advancements/AdvancementRewards;grant(Lnet/minecraft/server/level/ServerPlayer;)V", shift = Shift.AFTER))
public void be_award(Advancement advancement, String criterionName, CallbackInfoReturnable<Boolean> info) {
PlayerAdvancementsCallback.PLAYER_ADVANCEMENT_COMPLETE.invoker()
.onAdvancementComplete(player,
advancement,
criterionName
);
}
}

View file

@ -0,0 +1,84 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ElytraItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import org.betterx.bclib.blocks.BlockProperties;
import org.betterx.bclib.blocks.BlockProperties.TripleShape;
import org.betterx.bclib.util.BlocksHelper;
import org.betterx.bclib.util.MHelper;
import org.betterx.betterend.interfaces.FallFlyingItem;
import org.betterx.betterend.registry.EndBlocks;
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 java.util.Optional;
@Mixin(value = Player.class, priority = 200)
public abstract class PlayerMixin extends LivingEntity {
protected PlayerMixin(EntityType<? extends LivingEntity> entityType, Level level) {
super(entityType, level);
}
private static Direction[] horizontal;
@Inject(method = "findRespawnPositionAndUseSpawnBlock", at = @At(value = "HEAD"), cancellable = true)
private static void be_findRespawnPositionAndUseSpawnBlock(ServerLevel world,
BlockPos pos,
float f,
boolean bl,
boolean bl2,
CallbackInfoReturnable<Optional<Vec3>> info) {
BlockState blockState = world.getBlockState(pos);
if (blockState.is(EndBlocks.RESPAWN_OBELISK)) {
info.setReturnValue(be_obeliskRespawnPosition(world, pos, blockState));
info.cancel();
}
}
@Inject(method = "tryToStartFallFlying", at = @At("HEAD"), cancellable = true)
public void be_tryToStartFlying(CallbackInfoReturnable<Boolean> info) {
if (!onGround && !isFallFlying() && !isInWater() && !hasEffect(MobEffects.LEVITATION)) {
ItemStack itemStack = getItemBySlot(EquipmentSlot.CHEST);
if (itemStack.getItem() instanceof FallFlyingItem && ElytraItem.isFlyEnabled(itemStack)) {
setSharedFlag(7, true);
info.setReturnValue(true);
System.out.println("Started");
info.cancel();
}
}
}
private static Optional<Vec3> be_obeliskRespawnPosition(ServerLevel world, BlockPos pos, BlockState state) {
if (state.getValue(BlockProperties.TRIPLE_SHAPE) == TripleShape.TOP) {
pos = pos.below(2);
} else if (state.getValue(BlockProperties.TRIPLE_SHAPE) == TripleShape.MIDDLE) {
pos = pos.below();
}
if (horizontal == null) {
horizontal = BlocksHelper.makeHorizontal();
}
MHelper.shuffle(horizontal, world.getRandom());
for (Direction dir : horizontal) {
BlockPos p = pos.relative(dir);
BlockState state2 = world.getBlockState(p);
if (!state2.getMaterial().blocksMotion() && state2.getCollisionShape(world, pos).isEmpty()) {
return Optional.of(Vec3.atLowerCornerOf(p).add(0.5, 0, 0.5));
}
}
return Optional.empty();
}
}

View file

@ -0,0 +1,124 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.progress.ChunkProgressListener;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.dimension.BuiltinDimensionTypes;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.dimension.LevelStem;
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess;
import net.minecraft.world.level.storage.ServerLevelData;
import net.minecraft.world.level.storage.WritableLevelData;
import org.betterx.bclib.api.biomes.BiomeAPI;
import org.betterx.betterend.BetterEnd;
import org.betterx.betterend.interfaces.BETargetChecker;
import org.betterx.betterend.registry.EndBlocks;
import org.betterx.betterend.world.generator.GeneratorOptions;
import org.betterx.betterend.world.generator.TerrainGenerator;
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.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
@Mixin(ServerLevel.class)
public abstract class ServerLevelMixin extends Level {
protected ServerLevelMixin(WritableLevelData writableLevelData,
ResourceKey<Level> resourceKey,
Holder<DimensionType> holder,
Supplier<ProfilerFiller> supplier,
boolean bl,
boolean bl2,
long l,
int i) {
super(writableLevelData, resourceKey, holder, supplier, bl, bl2, l, i);
}
private final static List<ResourceKey<DimensionType>> BE_TEST_DIMENSIONS = List.of(BuiltinDimensionTypes.OVERWORLD,
BuiltinDimensionTypes.OVERWORLD_CAVES,
BuiltinDimensionTypes.NETHER);
@ModifyArg(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/Holder;is(Lnet/minecraft/resources/ResourceKey;)Z"))
ResourceKey<DimensionType> be_dragonFight(ResourceKey<DimensionType> resourceKey) {
if (!GeneratorOptions.hasDragonFights()) {
//this object would pass the test for the End-Dimension, so make sure we compare against something else to disabled the Dragon-Fight
if (this.dimensionTypeRegistration().is(BuiltinDimensionTypes.END)) return BuiltinDimensionTypes.OVERWORLD;
}
return resourceKey;
}
@Inject(method = "<init>*", at = @At("TAIL"))
private void be_onServerWorldInit(MinecraftServer minecraftServer,
Executor executor,
LevelStorageAccess levelStorageAccess,
ServerLevelData serverLevelData,
ResourceKey resourceKey,
LevelStem levelStem,
ChunkProgressListener chunkProgressListener,
boolean bl,
long seed,
List list,
boolean bl2,
CallbackInfo ci) {
ServerLevel level = ServerLevel.class.cast(this);
if (level.dimension() == Level.END) {
final ChunkGenerator chunkGenerator = levelStem.generator();
if (chunkGenerator instanceof NoiseBasedChunkGenerator) {
Holder<NoiseGeneratorSettings> sHolder = ((NoiseBasedChunkGeneratorAccessor) chunkGenerator)
.be_getSettings();
BETargetChecker.class.cast(sHolder.value()).be_setTarget(true);
}
TerrainGenerator.initNoise(seed,
chunkGenerator.getBiomeSource(),
level.getChunkSource().randomState().sampler());
}
}
@Inject(method = "makeObsidianPlatform", at = @At("HEAD"), cancellable = true)
private static void be_createObsidianPlatform(ServerLevel serverLevel, CallbackInfo info) {
if (!GeneratorOptions.generateObsidianPlatform()) {
info.cancel();
} else if (GeneratorOptions.changeSpawn()) {
BlockPos blockPos = GeneratorOptions.getSpawn();
int i = blockPos.getX();
int j = blockPos.getY() - 2;
int k = blockPos.getZ();
BlockPos.betweenClosed(i - 2, j + 1, k - 2, i + 2, j + 3, k + 2).forEach((blockPosx) -> {
serverLevel.setBlockAndUpdate(blockPosx, Blocks.AIR.defaultBlockState());
});
BlockPos.betweenClosed(i - 2, j, k - 2, i + 2, j, k + 2).forEach((blockPosx) -> {
serverLevel.setBlockAndUpdate(blockPosx, Blocks.OBSIDIAN.defaultBlockState());
});
info.cancel();
}
}
@ModifyArg(method = "tickChunk", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerLevel;setBlockAndUpdate(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)Z"))
private BlockState be_modifyTickState(BlockPos pos, BlockState state) {
if (state.is(Blocks.ICE)) {
ResourceLocation biome = BiomeAPI.getBiomeID(getBiome(pos));
if (biome.getNamespace().equals(BetterEnd.MOD_ID)) {
state = EndBlocks.EMERALD_ICE.defaultBlockState();
}
}
return state;
}
}

View file

@ -0,0 +1,182 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.network.protocol.game.*;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.server.players.PlayerList;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.player.ProfilePublicKey;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.portal.PortalInfo;
import net.minecraft.world.level.storage.LevelData;
import net.minecraft.world.phys.Vec3;
import com.mojang.authlib.GameProfile;
import org.betterx.betterend.interfaces.TeleportingEntity;
import org.betterx.betterend.world.generator.GeneratorOptions;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
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 org.jetbrains.annotations.Nullable;
@Mixin(ServerPlayer.class)
public abstract class ServerPlayerMixin extends Player implements TeleportingEntity {
@Shadow
public ServerGamePacketListenerImpl connection;
@Final
@Shadow
public ServerPlayerGameMode gameMode;
@Final
@Shadow
public MinecraftServer server;
@Shadow
private boolean isChangingDimension;
@Shadow
private float lastSentHealth;
@Shadow
private int lastSentFood;
@Shadow
private int lastSentExp;
private BlockPos exitPos;
private int be_teleportDelay = 0;
public ServerPlayerMixin(Level level,
BlockPos blockPos,
float f,
GameProfile gameProfile,
@Nullable ProfilePublicKey profilePublicKey) {
super(level, blockPos, f, gameProfile, profilePublicKey);
}
@Inject(method = "createEndPlatform", at = @At("HEAD"), cancellable = true)
private void be_createEndSpawnPlatform(ServerLevel world, BlockPos centerPos, CallbackInfo info) {
if (!GeneratorOptions.generateObsidianPlatform()) {
info.cancel();
}
}
@Inject(method = "findDimensionEntryPoint", at = @At("HEAD"), cancellable = true)
protected void be_getTeleportTarget(ServerLevel destination, CallbackInfoReturnable<PortalInfo> info) {
if (be_canTeleport()) {
info.setReturnValue(new PortalInfo(
new Vec3(exitPos.getX() + 0.5, exitPos.getY(), exitPos.getZ() + 0.5),
getDeltaMovement(),
getYRot(),
getXRot()
));
} else if (GeneratorOptions.changeSpawn() && destination.dimension() == Level.END) {
BlockPos spawn = GeneratorOptions.getSpawn();
Vec3 pos = new Vec3(spawn.getX() + 0.5, spawn.getY(), spawn.getZ() + 0.5);
info.setReturnValue(new PortalInfo(pos, Vec3.ZERO, 90.0F, 0.0F));
}
}
@Inject(method = "changeDimension", at = @At("HEAD"), cancellable = true)
public void be_changeDimension(ServerLevel destination, CallbackInfoReturnable<Entity> info) {
if (be_canTeleport() && level instanceof ServerLevel) {
isChangingDimension = true;
ServerLevel serverWorld = getLevel();
LevelData worldProperties = destination.getLevelData();
ServerPlayer player = ServerPlayer.class.cast(this);
connection.send(new ClientboundRespawnPacket(
new Holder.Direct(destination.dimensionType()),
destination.dimension(),
BiomeManager.obfuscateSeed(destination.getSeed()),
gameMode.getGameModeForPlayer(),
gameMode.getPreviousGameModeForPlayer(),
destination.isDebug(),
destination.isFlat(),
true
));
connection.send(new ClientboundChangeDifficultyPacket(
worldProperties.getDifficulty(),
worldProperties.isDifficultyLocked()
));
PlayerList playerManager = server.getPlayerList();
playerManager.sendPlayerPermissionLevel(player);
serverWorld.removePlayerImmediately(player, RemovalReason.CHANGED_DIMENSION);
unsetRemoved();
PortalInfo teleportTarget = findDimensionEntryPoint(destination);
if (teleportTarget != null) {
serverWorld.getProfiler().push("moving");
serverWorld.getProfiler().pop();
serverWorld.getProfiler().push("placing");
this.level = destination;
destination.addDuringPortalTeleport(player);
setRot(teleportTarget.yRot, teleportTarget.xRot);
moveTo(teleportTarget.pos.x, teleportTarget.pos.y, teleportTarget.pos.z);
serverWorld.getProfiler().pop();
triggerDimensionChangeTriggers(serverWorld);
gameMode.setLevel(destination);
connection.send(new ClientboundPlayerAbilitiesPacket(getAbilities()));
playerManager.sendLevelInfo(player, destination);
playerManager.sendAllPlayerInfo(player);
for (MobEffectInstance statusEffectInstance : getActiveEffects()) {
connection.send(new ClientboundUpdateMobEffectPacket(getId(), statusEffectInstance));
}
connection.send(new ClientboundLevelEventPacket(1032, BlockPos.ZERO, 0, false));
lastSentExp = -1;
lastSentHealth = -1.0F;
lastSentFood = -1;
}
be_teleportDelay = 100;
be_resetExitPos();
info.setReturnValue(player);
}
}
@Inject(method = "tick", at = @At("TAIL"))
public void be_decreaseCooldawn(CallbackInfo info) {
if (be_teleportDelay > 0) be_teleportDelay--;
}
@Override
public int getDimensionChangingDelay() {
if (be_teleportDelay > 0) {
return be_teleportDelay;
}
return super.getDimensionChangingDelay();
}
@Shadow
public abstract ServerLevel getLevel();
@Shadow
abstract void triggerDimensionChangeTriggers(ServerLevel origin);
@Shadow
@Override
protected abstract PortalInfo findDimensionEntryPoint(ServerLevel destination);
@Override
public void be_setExitPos(BlockPos pos) {
this.exitPos = pos.immutable();
}
@Override
public void be_resetExitPos() {
this.exitPos = null;
}
@Override
public boolean be_canTeleport() {
return this.exitPos != null;
}
}

View file

@ -0,0 +1,31 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.monster.Slime;
import net.minecraft.world.level.Level;
import org.betterx.betterend.interfaces.ISlime;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(Slime.class)
public abstract class SlimeMixin extends Entity implements ISlime {
public SlimeMixin(EntityType<? extends Slime> entityType, Level level) {
super(entityType, level);
}
@Shadow
public void setSize(int size, boolean heal) {
}
@Override
public void be_setSlimeSize(int size, boolean heal) {
setSize(size, heal);
}
@Override
public void entityRemove(Entity.RemovalReason removalReason) {
super.remove(removalReason);
}
}

View file

@ -0,0 +1,186 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockPos.MutableBlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.boss.enderdragon.EndCrystal;
import net.minecraft.world.level.ServerLevelAccessor;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.IronBarsBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.Heightmap.Types;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.feature.SpikeFeature;
import net.minecraft.world.level.levelgen.feature.configurations.SpikeConfiguration;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import org.betterx.bclib.api.WorldDataAPI;
import org.betterx.bclib.util.BlocksHelper;
import org.betterx.bclib.util.StructureHelper;
import org.betterx.betterend.BetterEnd;
import org.betterx.betterend.world.generator.GeneratorOptions;
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.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(SpikeFeature.class)
public class SpikeFeatureMixin {
@Inject(method = "place", at = @At("HEAD"), cancellable = true)
private void be_place(FeaturePlaceContext<SpikeConfiguration> featurePlaceContext,
CallbackInfoReturnable<Boolean> info) {
if (!GeneratorOptions.hasPillars()) {
info.setReturnValue(false);
}
}
@Inject(method = "placeSpike", at = @At("HEAD"), cancellable = true)
private void be_placeSpike(ServerLevelAccessor world,
RandomSource random,
SpikeConfiguration config,
SpikeFeature.EndSpike spike,
CallbackInfo info) {
int x = spike.getCenterX();
int z = spike.getCenterZ();
int radius = spike.getRadius();
int minY = 0;
long lx = x;
long lz = z;
if (lx * lx + lz * lz < 10000) {
String pillarID = String.format("%d_%d", x, z);
CompoundTag pillar = WorldDataAPI.getCompoundTag(BetterEnd.MOD_ID, "pillars");
boolean haveValue = pillar.contains(pillarID);
minY = haveValue
? pillar.getInt(pillarID)
: world.getChunk(x >> 4, z >> 4).getHeight(Types.WORLD_SURFACE, x & 15, z);
if (!haveValue) {
pillar.putInt(pillarID, minY);
WorldDataAPI.saveFile(BetterEnd.MOD_ID);
}
} else {
minY = world.getChunk(x >> 4, z >> 4).getHeight(Types.WORLD_SURFACE, x & 15, z);
}
GeneratorOptions.setDirectSpikeHeight();
int maxY = minY + spike.getHeight() - 64;
if (GeneratorOptions.replacePillars() && be_radiusInRange(radius)) {
radius--;
StructureTemplate base = StructureHelper.readStructure(BetterEnd.makeID("pillars/pillar_base_" + radius));
StructureTemplate top = StructureHelper.readStructure(BetterEnd.makeID("pillars/pillar_top_" + radius + (
spike
.isGuarded()
? "_cage"
: "")));
Vec3i side = base.getSize();
BlockPos pos1 = new BlockPos(x - (side.getX() >> 1), minY - 3, z - (side.getZ() >> 1));
minY = pos1.getY() + side.getY();
side = top.getSize();
BlockPos pos2 = new BlockPos(x - (side.getX() >> 1), maxY, z - (side.getZ() >> 1));
maxY = pos2.getY();
StructurePlaceSettings data = new StructurePlaceSettings();
base.placeInWorld(world, pos1, pos1, data, random, 2);
top.placeInWorld(world, pos2, pos2, data, random, 2);
int r2 = radius * radius + 1;
MutableBlockPos mut = new MutableBlockPos();
for (int px = -radius; px <= radius; px++) {
mut.setX(x + px);
int x2 = px * px;
for (int pz = -radius; pz <= radius; pz++) {
mut.setZ(z + pz);
int z2 = pz * pz;
if (x2 + z2 <= r2) {
for (int py = minY; py < maxY; py++) {
mut.setY(py);
if (world.getBlockState(mut).getMaterial().isReplaceable()) {
if ((px == radius || px == -radius || pz == radius || pz == -radius) && random.nextInt(
24) == 0) {
BlocksHelper.setWithoutUpdate(world, mut, Blocks.CRYING_OBSIDIAN);
} else {
BlocksHelper.setWithoutUpdate(world, mut, Blocks.OBSIDIAN);
}
}
}
}
}
}
} else {
minY -= 15;
int r2 = radius * radius + 1;
MutableBlockPos mut = new MutableBlockPos();
for (int px = -radius; px <= radius; px++) {
mut.setX(x + px);
int x2 = px * px;
for (int pz = -radius; pz <= radius; pz++) {
mut.setZ(z + pz);
int z2 = pz * pz;
if (x2 + z2 <= r2) {
for (int py = minY; py < maxY; py++) {
mut.setY(py);
if (world.getBlockState(mut).getMaterial().isReplaceable()) {
BlocksHelper.setWithoutUpdate(world, mut, Blocks.OBSIDIAN);
}
}
}
}
}
mut.setX(x);
mut.setZ(z);
mut.setY(maxY);
BlocksHelper.setWithoutUpdate(world, mut, Blocks.BEDROCK);
EndCrystal crystal = EntityType.END_CRYSTAL.create(world.getLevel());
crystal.setBeamTarget(config.getCrystalBeamTarget());
crystal.setInvulnerable(config.isCrystalInvulnerable());
crystal.moveTo(x + 0.5D, maxY + 1, z + 0.5D, random.nextFloat() * 360.0F, 0.0F);
world.addFreshEntity(crystal);
if (spike.isGuarded()) {
for (int px = -2; px <= 2; ++px) {
boolean bl = Mth.abs(px) == 2;
for (int pz = -2; pz <= 2; ++pz) {
boolean bl2 = Mth.abs(pz) == 2;
for (int py = 0; py <= 3; ++py) {
boolean bl3 = py == 3;
if (bl || bl2 || bl3) {
boolean bl4 = px == -2 || px == 2 || bl3;
boolean bl5 = pz == -2 || pz == 2 || bl3;
BlockState blockState = Blocks.IRON_BARS
.defaultBlockState()
.setValue(IronBarsBlock.NORTH, bl4 && pz != -2).setValue(
IronBarsBlock.SOUTH,
bl4 && pz != 2
).setValue(
IronBarsBlock.WEST,
bl5 && px != -2).setValue(
IronBarsBlock.EAST,
bl5 && px != 2
);
BlocksHelper.setWithoutUpdate(
world,
mut.set(spike.getCenterX() + px, maxY + py, spike.getCenterZ() + pz),
blockState
);
}
}
}
}
}
}
info.cancel();
}
private boolean be_radiusInRange(int radius) {
return radius > 1 && radius < 6;
}
}

View file

@ -0,0 +1,29 @@
package org.betterx.betterend.mixin.common;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkAccess;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
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.CallbackInfoReturnable;
@Mixin(WorldGenRegion.class)
public class WorldGenRegionMixin {
@Final
@Shadow
private ChunkAccess center;
@Inject(method = "ensureCanWrite", at = @At("HEAD"), cancellable = true)
private void be_alterBlockCheck(BlockPos blockPos, CallbackInfoReturnable<Boolean> info) {
ChunkPos cPos = center.getPos();
int x = blockPos.getX() >> 4;
int z = blockPos.getZ() >> 4;
WorldGenRegion region = (WorldGenRegion) (Object) this;
info.setReturnValue(Math.abs(x - cPos.x) < 2 && Math.abs(z - cPos.z) < 2);
}
}