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