Inspired by 2020's April Fools' 20w14infinite Snapshot, this mod brings endless randomly generated dimensions into Minecraft.
at master 343 lines 13 kB view raw
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}