Inspired by 2020's April Fools' 20w14infinite Snapshot, this mod brings endless randomly generated dimensions into Minecraft.
at master 253 lines 10 kB view raw
1package net.lerariemann.infinity.block.custom; 2 3import net.lerariemann.infinity.block.entity.BiomeBottleBlockEntity; 4import net.lerariemann.infinity.registry.core.ModBlockEntities; 5import net.lerariemann.infinity.util.VersionMethods; 6import net.minecraft.core.BlockPos; 7import net.minecraft.core.Holder; 8import net.minecraft.core.QuartPos; 9//? if >1.21 { 10import com.mojang.serialization.MapCodec; 11import net.lerariemann.infinity.registry.core.ModComponentTypes; 12import net.minecraft.core.component.DataComponentMap; 13import net.minecraft.core.component.DataComponents; 14import net.minecraft.world.item.component.BlockItemStateProperties; 15import net.minecraft.world.level.LevelReader; 16//?} 17import net.minecraft.core.Registry; 18import net.minecraft.core.particles.ParticleTypes; 19import net.minecraft.core.registries.Registries; 20import net.minecraft.resources.ResourceLocation; 21import net.minecraft.server.level.ServerLevel; 22import net.minecraft.sounds.SoundEvents; 23import net.minecraft.sounds.SoundSource; 24import net.minecraft.util.Mth; 25import net.minecraft.world.item.ItemStack; 26import net.minecraft.world.item.Rarity; 27import net.minecraft.world.level.BlockGetter; 28import net.minecraft.world.level.ChunkPos; 29import net.minecraft.world.level.Level; 30import net.minecraft.world.level.biome.Biome; 31import net.minecraft.world.level.block.BaseEntityBlock; 32import net.minecraft.world.level.block.Block; 33import net.minecraft.world.level.block.RenderShape; 34import net.minecraft.world.level.block.entity.BlockEntity; 35import net.minecraft.world.level.block.entity.BlockEntityTicker; 36import net.minecraft.world.level.block.entity.BlockEntityType; 37import net.minecraft.world.level.block.state.BlockState; 38import net.minecraft.world.level.block.state.StateDefinition; 39import net.minecraft.world.level.block.state.properties.IntegerProperty; 40import net.minecraft.world.level.chunk.ChunkAccess; 41import net.minecraft.world.phys.shapes.CollisionContext; 42import net.minecraft.world.phys.shapes.Shapes; 43import net.minecraft.world.phys.shapes.VoxelShape; 44import org.jspecify.annotations.Nullable; 45 46import java.util.*; 47import java.util.stream.Collectors; 48 49public class BiomeBottleBlock extends BaseEntityBlock { 50 //? if >1.21 { 51 public static final MapCodec<BiomeBottleBlock> CODEC = simpleCodec(BiomeBottleBlock::new); 52 //?} 53 public static final IntegerProperty LEVEL = IntegerProperty.create("level", 0, 10); 54 public static final VoxelShape MAIN = Block.box(2, 0, 2, 14, 12, 14); 55 public static final VoxelShape TIP = Block.box(6, 12, 6, 10, 16, 10); 56 public static final VoxelShape CORK = Block.box(5, 14, 5, 11, 15, 11); 57 public static final VoxelShape SHAPE = Shapes.or(MAIN, TIP, CORK); 58 @Override 59 public VoxelShape getCollisionShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { 60 return SHAPE; 61 } 62 @Override 63 public VoxelShape getShape(BlockState state, BlockGetter world, BlockPos pos, CollisionContext context) { 64 return SHAPE; 65 } 66 67 public BiomeBottleBlock(Properties settings) { 68 super(settings); 69 this.registerDefaultState(defaultBlockState().setValue(LEVEL, 0)); 70 } 71 72 @Override 73 protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) { 74 builder.add(LEVEL); 75 } 76 77 //? if >1.21 { 78 @Override 79 protected MapCodec<? extends BaseEntityBlock> codec() { 80 return CODEC; 81 } 82 //?} 83 @Override 84 public RenderShape getRenderShape(BlockState state) { 85 return RenderShape.MODEL; 86 } 87 @Nullable 88 @Override 89 public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { 90 return new BiomeBottleBlockEntity(pos, state); 91 } 92 @Nullable 93 @Override 94 public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level world, BlockState state, BlockEntityType<T> type) { 95 return world.isClientSide() ? null : createTickerHelper(type, ModBlockEntities.BIOME_BOTTLE.get(), BiomeBottleBlockEntity::serverTick); 96 } 97 98 99 @Override 100 public ItemStack getCloneItemStack( 101 //? if >1.21 { 102 LevelReader world 103 //?} else { 104 /*BlockGetter world 105 *///?} 106 , BlockPos pos, BlockState state 107 //? if >1.21.2 108 , boolean bl 109 ) { 110 return world.getBlockEntity(pos) instanceof BiomeBottleBlockEntity bbbe 111 ? bbbe.asStack() 112 : super.getCloneItemStack(world, pos, state 113 //? if >1.21.2 114 , bl 115 ); 116 } 117 118 public static final int maxAllowedCharge = 10000; 119 120 public static ResourceLocation defaultBiome() { 121 return VersionMethods.id("plains"); 122 } 123 124 public static Rarity getRarity(int charge) { 125 return charge < 1000 ? Rarity.COMMON : charge < 9000 ? Rarity.UNCOMMON : Rarity.RARE; 126 } 127 128 //? if >1.21 { 129 public static DataComponentMap.Builder updateCharge(DataComponentMap.Builder builder, int charge) { 130 return builder.set(ModComponentTypes.CHARGE.get(), charge) 131 .set(DataComponents.RARITY, getRarity(charge)) 132 .set(DataComponents.BLOCK_STATE, (new BlockItemStateProperties(Map.of())) 133 .with(LEVEL, getLevel(charge))); 134 } 135 public static void updateCharge(ItemStack stack, int charge) { 136 stack.applyComponents(updateCharge(DataComponentMap.builder(), charge).build()); 137 } 138 public static void updateCharge(ItemStack stack) { 139 int charge = getCharge(stack); 140 if (charge == 0) stack.remove(ModComponentTypes.BIOME_CONTENTS.get()); 141 updateCharge(stack, charge); 142 } 143 //?} else { 144 /*public static void updateCharge(ItemStack stack, int charge) { 145 if (stack.hasTag()) { 146 assert stack.getTag() != null; 147 stack.getTag().getCompound("BlockEntityTag").putInt("Charge", charge); 148 } 149 } 150 public static void updateCharge(ItemStack stack) { 151 int charge = getCharge(stack); 152 if (charge > 0) updateCharge(stack, charge); 153 } 154 *///?} 155 156 public static int getLevel(int charge) { 157 return Mth.clamp(charge / 100, 0, 10); 158 } 159 160 public static boolean isEmpty(ItemStack stack) { 161 return getCharge(stack) == 0; 162 } 163 164 public static ResourceLocation getBiome(ItemStack stack) { 165 //? if >1.21 { 166 return stack.getComponents().getOrDefault(ModComponentTypes.BIOME_CONTENTS.get(), defaultBiome()); 167 //?} else { 168 /*if (stack.hasTag()) { 169 assert stack.getTag() != null; 170 return VersionMethods.id(stack.getTag().getCompound("BlockEntityTag").getString("Biome")); 171 } 172 return defaultBiome(); 173 *///?} 174 } 175 176 public static int getCharge(ItemStack stack) { 177 //? if >1.21 { 178 return stack.getComponents().getOrDefault(ModComponentTypes.CHARGE.get(), 0); 179 //?} else { 180 /*if (stack.hasTag()) { 181 assert stack.getTag() != null; 182 return stack.getTag().getCompound("BlockEntityTag").getInt("Charge"); 183 } 184 return 0; 185 *///?} 186 187 } 188 189 public static void playSploosh(ServerLevel world, BlockPos pos) { 190 world.playSound(null, pos, SoundEvents.GENERIC_SPLASH, SoundSource.BLOCKS, 1f, 1f); 191 world.sendParticles(ParticleTypes.SPLASH, pos.getX() + 0.5, 192 pos.getY() + 0.5, pos.getZ() + 0.5, 30, 0.5, 0.5, 0.5, 0.2); 193 } 194 195 //? if >1.21 { 196 public static DataComponentMap.Builder addComponents(DataComponentMap.Builder componentMapBuilder, 197 ResourceLocation biome, int color, int charge) { 198 componentMapBuilder.set(ModComponentTypes.BIOME_CONTENTS.get(), biome); 199 componentMapBuilder.set(ModComponentTypes.COLOR.get(), color); 200 updateCharge(componentMapBuilder, charge); 201 return componentMapBuilder; 202 } 203 //?} 204 205 public static void spreadCircle(ServerLevel world, BlockPos origin, ResourceLocation biomeId, int charge) { 206 spreadRing(world, origin, biomeId, 0, charge); 207 } 208 public static void spreadRing(ServerLevel world, BlockPos origin, ResourceLocation biomeId, int chargemin, int chargemax) { 209 Set<BlockPos> posSet = new HashSet<>(); 210 Set<ChunkAccess> set = new HashSet<>(); 211 origin = origin.below(origin.getY()); 212 double ramax = chargemax / Math.PI; 213 double ramin = chargemin / Math.PI; 214 for (int i = 0; i*i < ramax; i++) { 215 for (int j = 0; i*i + j*j < ramax; j++) if (i*i + j*j >= ramin) { 216 List<BlockPos> signs = offsets(origin, i, j); 217 posSet.addAll(signs); 218 set.addAll(signs.stream().map(ChunkPos::new) 219 .map(chunkPos -> world.getChunk(chunkPos.getWorldPosition())) 220 .filter(Objects::nonNull).collect(Collectors.toSet())); 221 } 222 } 223 spread(world, set, posSet, VersionMethods.getFromId(VersionMethods.getRegistry(world.getServer().registryAccess(), Registries.BIOME), biomeId)); 224 } 225 226 public static void spread(ServerLevel world, Set<ChunkAccess> set, Set<BlockPos> posSet, @Nullable Holder<Biome> biome) { 227 if (biome == null) return; 228 set.forEach(chunk -> { 229 if (chunk != null) { 230 chunk.fillBiomesFromNoise((x, y, z, noise) -> { 231 int i = QuartPos.toBlock(x); 232 int k = QuartPos.toBlock(z); 233 Holder<Biome> registryEntry2 = chunk.getNoiseBiome(x, y, z); 234 if (posSet.contains(new BlockPos(i, 0, k))) { 235 return biome; 236 } 237 return registryEntry2; 238 }, world.getChunkSource().randomState().sampler()); 239 //? if >1.21.2 { 240 chunk.markUnsaved(); 241 //?} else { 242 /*chunk.setUnsaved(true); 243 *///?} 244 } 245 }); 246 world.getChunkSource().chunkMap.resendBiomesForChunks(set.stream().toList()); 247 } 248 249 public static List<BlockPos> offsets(BlockPos origin, int i, int j) { 250 return List.of(origin.offset(i, 0, j), origin.offset(j, 0, i), origin.offset(-i, 0, j), origin.offset(j, 0, -i), 251 origin.offset(i, 0, -j), origin.offset(-j, 0, i), origin.offset(-i, 0, -j), origin.offset(-j, 0, -i)); 252 } 253}