Inspired by 2020's April Fools' 20w14infinite Snapshot, this mod brings endless randomly generated dimensions into Minecraft.
1package net.lerariemann.infinity.util;
2
3import com.google.common.hash.HashCode;
4import com.google.common.hash.Hashing;
5import net.lerariemann.infinity.InfinityMod;
6import net.lerariemann.infinity.access.InfinityOptionsAccess;
7import net.lerariemann.infinity.access.Timebombable;
8import net.lerariemann.infinity.block.entity.TintableBlockEntity;
9import net.lerariemann.infinity.compat.ModCompat;
10import net.lerariemann.infinity.compat.PonderCompat;
11import net.lerariemann.infinity.registry.core.ModComponentTypes;
12import net.lerariemann.infinity.util.core.RandomProvider;
13import net.lerariemann.infinity.util.platform.InfinityPlatform;
14import net.minecraft.Util;
15import net.minecraft.core.BlockPos;
16import net.minecraft.core.Direction;
17import net.minecraft.core.Holder;
18import net.minecraft.network.chat.Component;
19import net.minecraft.network.chat.MutableComponent;
20import net.minecraft.resources.ResourceKey;
21import net.minecraft.resources.ResourceLocation;
22import net.minecraft.server.level.ServerLevel;
23import net.minecraft.server.level.ServerPlayer;
24import net.minecraft.util.Mth;
25import net.minecraft.util.RandomSource;
26import net.minecraft.world.item.ItemStack;
27import net.minecraft.world.level.*;
28import net.minecraft.world.level.biome.Biome;
29import net.minecraft.world.level.block.entity.BlockEntity;
30import net.minecraft.world.level.block.state.BlockState;
31import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
32import net.minecraft.world.level.levelgen.LegacyRandomSource;
33import net.minecraft.world.level.levelgen.synth.NormalNoise;
34//? if <1.21 {
35/*import net.minecraft.world.level.storage.LevelResource;
36import net.minecraft.core.registries.Registries;
37import net.lerariemann.infinity.registry.core.ModItems;
38*///?}
39import net.minecraft.world.level.storage.LevelStorageSource;
40import org.apache.commons.io.FileUtils;
41
42import java.nio.charset.StandardCharsets;
43import java.util.Arrays;
44import java.util.Objects;
45
46import static net.lerariemann.infinity.InfinityMod.MOD_ID;
47
48/** Common mod methods that work identically on Fabric and NeoForge.
49 * @see InfinityPlatform */
50@SuppressWarnings("unused")
51public interface InfinityMethods {
52 String ofRandomDim = "infinity:random";
53 NormalNoise sampler =
54 NormalNoise.create(new LegacyRandomSource(0L), -5, genOctaves(2));
55
56 static double[] genOctaves(int octaves){
57 double[] a = new double[octaves];
58 Arrays.fill(a, 1);
59 return a;
60 }
61 static double sample(BlockPos pos) {
62 return sampler.getValue(pos.getX(), pos.getY(), pos.getZ());
63 }
64
65 /**
66 * Converts a string to an identifier in the Infinite Dimensions namespace.
67 */
68 static ResourceLocation getId(String value){
69 return VersionMethods.id(MOD_ID, value);
70 }
71
72 /**
73 * Converts a dimension's long value to an identifier in the Infinite Dimensions namespace.
74 */
75 static ResourceLocation getDimId(long value){
76 return getId("generated_" + value);
77 }
78
79 /**
80 * Checks if a dimension is an Infinite Dimension.
81 */
82 static boolean isInfinity(Level w) {
83 return isInfinity(w.dimension());
84 }
85 static boolean isInfinity(ResourceKey<Level> key) {
86 return key.location().getNamespace().equals(MOD_ID);
87 }
88 static boolean isBiomeInfinity(LevelAccessor world, BlockPos pos) {
89 try {
90 //? if >1.21 {
91 return world.getBiome(pos).getRegisteredName().contains("infinity");
92 //?} else {
93 /*return Objects.requireNonNullElse(world.registryAccess().registryOrThrow(Registries.BIOME).getKey(world.getBiome(pos).value()), VersionMethods.id("the_void")).getNamespace().contains("infinity");
94 *///?}
95 } catch (IllegalStateException e) {
96 return false;
97 }
98 }
99 static boolean isBiomeInfinity(Holder<Biome> b) {
100 //? if >1.21 {
101 return b.getRegisteredName().contains("infinity");
102 //?} else {
103 /*return false;
104 *///?}
105 }
106 static boolean isBiomeInfinity(BlockGetter world, BlockPos pos) {
107 if (world instanceof LevelAccessor acc) {
108 return InfinityMethods.isBiomeInfinity(acc, pos);
109 }
110 //? if fabric {
111 else return InfinityPlatform.INSTANCE.isFabric()
112 && world.hasBiomes()
113 && world.getBiomeFabric(pos) != null
114 && InfinityMethods.isBiomeInfinity(world.getBiomeFabric(pos));
115 //?} else {
116 /*return false;
117 *///?}
118 }
119
120 static int properMod(int a, int b) {
121 int res = a%b;
122 return (res >= 0) ? res : b + res;
123 }
124
125 static void sendUnexpectedError(ServerPlayer player, String type) {
126 player.sendSystemMessage(Component.translatable("error.infinity." + type + ".unexpected"));
127 }
128
129 static String dimTextPreprocess(String text) {
130 if (text.isEmpty()) return "missingno";
131 if (RandomProvider.rule("forceLowercase")) text = text.toLowerCase();
132 text = text.replaceAll("\n", " ");
133 return text;
134 }
135
136 /**
137 * Convert a provided string into a dimension ID.
138 * This also checks if it matches an Easter Egg dimension.
139 */
140 static ResourceLocation dimTextToId(String text) {
141 ResourceLocation easterId = InfinityMod.provider.easterizer.getAsEaster(text);
142 if (easterId != null)
143 return easterId;
144 return InfinityMethods.getDimId(getDimensionSeed(text));
145 }
146
147 static boolean isTimebombed(ServerLevel world) {
148 return ((Timebombable)world).infinity$isTimebombed();
149 }
150
151 /**
152 * Check if a dimension exists and has not been timebombed.
153 */
154 static boolean dimExists(ServerLevel world) {
155 return (world != null && !isTimebombed(world));
156 }
157
158 /**
159 * Hashes text into dimension ID.
160 */
161 static long getDimensionSeed(String text) {
162 HashCode f = Hashing.sha256().hashString(text + InfinityMod.provider.salt, StandardCharsets.UTF_8);
163 return InfinityMethods.longArithmeticEnabled() ? f.asLong() & Long.MAX_VALUE : f.asInt() & Integer.MAX_VALUE;
164 }
165
166 static long getNumericFromId(ResourceLocation id) {
167 String dimensionName = id.getPath();
168 String numericId = dimensionName.substring(dimensionName.lastIndexOf("_") + 1);
169 long i;
170 try {
171 i = Long.parseLong(numericId);
172 } catch (Exception e) {
173 /* Simply hash the name if it isn't of "generated_..." format. */
174 i = getDimensionSeed(numericId);
175 }
176 return i;
177 }
178
179 private static float bookBoxSample(BlockPos pos, int offset) {
180 return Mth.clamp(0.5f * (1f + (float)sampler.getValue(4*pos.getX(), 4*(pos.getY() + offset), 4*pos.getZ())), 0f, 1f);
181 }
182 static int getBookBoxColor(BlockState state, BlockAndTintGetter world, BlockPos pos, int tintIndex) {
183 if (pos != null) {
184 float r = bookBoxSample(pos, -1000);
185 float g = bookBoxSample(pos, 0);
186 float b = bookBoxSample(pos, 1000);
187 //? if <1.21.2 {
188 /*return Mth.color(r, g, b);
189 *///?} else {
190 return Mth.hsvToRgb(r, g, b);
191 //?}
192
193 }
194 return 16777215;
195 }
196
197 static int getOverlayColorFromComponents(ItemStack stack, int layer) {
198 //? if >1.21 {
199 int color = stack.getComponents().getOrDefault(ModComponentTypes.COLOR.get(), 0xFFFFFF);
200 if (layer == 1) {
201 return VersionMethods.opaque(color);
202 }
203 return VersionMethods.opaque(0xFFFFFF);
204 //?} else {
205 /*if (stack.getTag() != null) {
206 if (layer == 1) {
207 if (stack.is(ModItems.TRANSFINITE_KEY.get()))
208 return stack.getTag().getInt(ModComponentTypes.COLOR);
209 else if (stack.is(ModItems.BIOME_BOTTLE_ITEM.get())) {
210 return stack.getTag().getCompound("BlockEntityTag").getInt("Color");
211 }
212 else if (stack.is(ModItems.F4.get())) {
213 return VersionMethods.getOrDefaultInt(stack, ModComponentTypes.COLOR, 10879231);
214 }
215 }
216 else if (stack.is(ModItems.CHROMATIC_MATTER.get()) || stack.is(ModItems.CHROMATIC_WOOL.get()) || stack.is(ModItems.CHROMATIC_CARPET.get()))
217 return stack.getTag().getInt(ModComponentTypes.COLOR);
218 }
219 return 0xFFFFFF;
220 *///?}
221 }
222 static int getItemColorFromComponents(ItemStack stack, int layer) {
223 int color = VersionMethods.getOrDefaultInt(stack, ModComponentTypes.COLOR, 0xFFFFFF);
224 return VersionMethods.opaque(color);
225 }
226 static int getDiscColorFromComponents(ItemStack stack, int layer) {
227 int color = getItemColorFromComponents(stack, layer);
228 return layer == 0 ? color : 0xFFFFFF ^ color;
229 }
230
231 /**
232 * For use in color providers with blocks which the block entity sets color for.
233 */
234 static int getBlockEntityColor(BlockState state, BlockAndTintGetter world, BlockPos pos, int tintIndex) {
235 if (world != null && pos != null) {
236 BlockEntity blockEntity = world.getBlockEntity(pos);
237 if (blockEntity instanceof TintableBlockEntity be) {
238 return be.getTint() & 0xFFFFFF;
239 }
240 }
241 return 0xFFFFFF;
242 }
243
244 static long getRandomSeed(java.util.Random random) {
245 return longArithmeticEnabled() ? random.nextLong() : random.nextInt();
246 }
247
248 static long getRandomSeed(RandomSource random) {
249 return longArithmeticEnabled() ? random.nextLong() : random.nextInt();
250 }
251
252 static ResourceLocation getRandomId(java.util.Random random) {
253 return getDimId(getRandomSeed(random));
254 }
255
256 static ResourceLocation getRandomId(RandomSource random) {
257 return getDimId(getRandomSeed(random));
258 }
259
260 static MutableComponent getDimensionNameAsText(ResourceLocation dimension) {
261 String name = dimension.toString();
262 // Randomly generated dimensions.
263 if (name.contains("infinity:generated_"))
264 return Component.translatable("tooltip.infinity.key.generated")
265 .append(name.replace("infinity:generated_", ""));
266 if (name.contains("footprint:"))
267 return Component.translatable("tooltip.infinity.key.exit");
268 if (name.equals(InfinityMethods.ofRandomDim))
269 return Component.translatable("tooltip.infinity.key.randomise");
270 // All other dimensions.
271 return Component.translatableWithFallback(
272 Util.makeDescriptionId("dimension", dimension),
273 InfinityMethods.formatAsTitleCase(dimension.getPath()));
274 }
275
276 /**
277 * Creates a fallback for texts without translation by replacing underscores
278 * with spaces and formatting the text as Title Case.
279 */
280 static String formatAsTitleCase(String text) {
281 text = text.replaceAll("[_./]", " ");
282 //i am sure java has a smarter way to do title case, but this works too
283 StringBuilder newText = new StringBuilder();
284 int i = 0;
285 for (Character c : text.toCharArray()) {
286 if (i == 0) {
287 c = c.toString().toUpperCase().charAt(0);
288 }
289 newText.append(c);
290 i++;
291 if (c == ' ') {
292 i = 0;
293 }
294 }
295 return newText.toString();
296 }
297
298 static boolean chaosMobsEnabled() {
299 return RandomProvider.rule("chaosMobsEnabled");
300 }
301
302 static boolean longArithmeticEnabled() {
303 return RandomProvider.rule("longArithmeticEnabled");
304 }
305
306 static boolean deleteLevel(LevelStorageSource.LevelStorageAccess session) {
307 return FileUtils.deleteQuietly(
308 //? if >1.21 {
309 session.getLevelDirectory().path().resolve("datapacks")
310 //?} else {
311 /*session.getLevelPath(LevelResource.DATAPACK_DIR)
312 *///?}
313 .toFile());
314 }
315
316
317 static double getMavity(Level level) {
318 if (ModCompat.PONDER && PonderCompat.isPonderLevel(level))
319 return 1;
320 do {
321 var infinityOptionsAccess = ((InfinityOptionsAccess) level);
322 if (infinityOptionsAccess == null)
323 break;
324 var infinityOptions = infinityOptionsAccess.infinity$getOptions();
325 if (infinityOptions == null)
326 break;
327 return infinityOptions.getMavity();
328 } while (false);
329
330 return 1.0;
331 }
332
333 static NoteBlockInstrument getRandomInstrument() {
334 NoteBlockInstrument[] instruments = NoteBlockInstrument.values();
335 return instruments[InfinityMod.random.nextInt(instruments.length-7)];
336 }
337
338 static Direction getRandomHorizontalDirection(RandomSource random) {
339 return Direction.fromAxisAndDirection(
340 random.nextBoolean() ? Direction.Axis.X : Direction.Axis.Z,
341 random.nextBoolean() ? Direction.AxisDirection.POSITIVE : Direction.AxisDirection.NEGATIVE);
342 }
343}