Inspired by 2020's April Fools' 20w14infinite Snapshot, this mod brings endless randomly generated dimensions into Minecraft.
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}