Inspired by 2020's April Fools' 20w14infinite Snapshot, this mod brings endless randomly generated dimensions into Minecraft.
1package net.lerariemann.infinity.registry.var;
2
3import com.mojang.serialization.Codec;
4import com.mojang.serialization.MapCodec;
5import com.mojang.serialization.codecs.RecordCodecBuilder;
6import dev.architectury.registry.registries.DeferredRegister;
7import net.lerariemann.infinity.InfinityMod;
8import net.lerariemann.infinity.util.core.CommonIO;
9import net.lerariemann.infinity.util.var.TextData;
10import net.minecraft.MethodsReturnNonnullByDefault;
11import net.minecraft.core.registries.Registries;
12import net.minecraft.util.KeyDispatchDataCodec;
13import net.minecraft.world.level.levelgen.SurfaceRules;
14import net.lerariemann.infinity.mixin.core.MaterialRuleContextAccess;
15import org.apache.commons.io.FileUtils;
16
17import java.io.File;
18import java.io.IOException;
19import java.nio.charset.StandardCharsets;
20import java.util.*;
21
22import static net.lerariemann.infinity.InfinityMod.LOGGER;
23import static net.lerariemann.infinity.InfinityMod.MOD_ID;
24
25@MethodsReturnNonnullByDefault
26public class ModMaterialConditions {
27 public record LinearCondition(double k_x, double k_y, double k_z, double min, double max, int separation) implements SurfaceRules.ConditionSource
28 {
29 public static final KeyDispatchDataCodec<LinearCondition> CODEC = KeyDispatchDataCodec.of(RecordCodecBuilder.mapCodec(instance -> instance.group(
30 (Codec.DOUBLE.fieldOf("k_x").orElse(1.0).forGetter(a -> a.k_x)),
31 (Codec.DOUBLE.fieldOf("k_y").orElse(0.0).forGetter(a -> a.k_y)),
32 (Codec.DOUBLE.fieldOf("k_z").orElse(1.0).forGetter(a -> a.k_z)),
33 (Codec.DOUBLE.fieldOf("min").orElse(0.0).forGetter(a -> a.min)),
34 (Codec.DOUBLE.fieldOf("max").orElse(0.5).forGetter(a -> a.max)),
35 (Codec.INT.fieldOf("separation").orElse(16).forGetter(a -> a.separation))
36 ).apply(instance, LinearCondition::new)));
37
38 @Override
39 public KeyDispatchDataCodec<? extends SurfaceRules.ConditionSource> codec() {
40 return CODEC;
41 }
42
43 @Override
44 public SurfaceRules.Condition apply(SurfaceRules.Context materialRuleContext) {
45 class LinearPredicate extends SurfaceRules.LazyYCondition {
46 LinearPredicate() {
47 super(materialRuleContext);
48 }
49
50 double scale() {
51 return Math.sqrt(k_x*k_x + k_y*k_y + k_z*k_z);
52 }
53 public boolean compute() {
54 int x = ((MaterialRuleContextAccess)(Object)(this.context)).getBlockX();
55 int y = ((MaterialRuleContextAccess)(Object)(this.context)).getBlockY();
56 int z = ((MaterialRuleContextAccess)(Object)(this.context)).getBlockZ();
57 double res = (k_x * x + k_y * y + k_z * z)/(separation*scale());
58 res = res - Math.floor(res);
59 return (res > min) && (res < max);
60 }
61 }
62 return new LinearPredicate();
63 }
64 }
65
66 public record CheckerboardCondition(int size) implements SurfaceRules.ConditionSource
67 {
68 public static final KeyDispatchDataCodec<CheckerboardCondition> CODEC = KeyDispatchDataCodec.of(RecordCodecBuilder.mapCodec(instance -> instance.group(
69 (Codec.INT.fieldOf("size").orElse(1).forGetter(a -> a.size))
70 ).apply(instance, CheckerboardCondition::new)));
71
72 @Override
73 public KeyDispatchDataCodec<? extends SurfaceRules.ConditionSource> codec() {
74 return CODEC;
75 }
76
77 @Override
78 public SurfaceRules.Condition apply(SurfaceRules.Context materialRuleContext) {
79 class CheckerboardPredicate extends SurfaceRules.LazyYCondition {
80 CheckerboardPredicate() {
81 super(materialRuleContext);
82 }
83
84 public int sign(int coord) {
85 if (coord < 0) return -sign(-1-coord);
86 return ((coord / size) % 2)*2 - 1;
87 }
88 public boolean compute() {
89 int x = sign(((MaterialRuleContextAccess)(Object)(this.context)).getBlockX());
90 int z = sign(((MaterialRuleContextAccess)(Object)(this.context)).getBlockZ());
91 return (x > 0) ^ (z > 0);
92 }
93 }
94
95 return new CheckerboardPredicate();
96 }
97 }
98
99 public record TextCondition(int font_size, int char_spacing, int line_spacing, int max_width, boolean read_from_file,
100 String text, TextData data) implements SurfaceRules.ConditionSource
101 {
102 public static final KeyDispatchDataCodec<TextCondition> CODEC = KeyDispatchDataCodec.of(RecordCodecBuilder.mapCodec(instance -> instance.group(
103 (Codec.INT.fieldOf("font_size").orElse(1).forGetter(a -> a.font_size)),
104 (Codec.INT.fieldOf("char_spacing").orElse(1).forGetter(a -> a.char_spacing)),
105 (Codec.INT.fieldOf("line_spacing").orElse(1).forGetter(a -> a.line_spacing)),
106 (Codec.INT.fieldOf("max_width").orElse(Integer.MAX_VALUE).forGetter(a -> a.max_width)),
107 (Codec.BOOL.fieldOf("read_from_file").orElse(false).forGetter(a -> a.read_from_file)),
108 (Codec.STRING.fieldOf("text").forGetter(a -> a.text))
109 ).apply(instance, TextCondition::of)));
110
111 static TextCondition of(int font_size, int char_spacing, int line_spacing, int max_width, boolean read_from_file, String text) {
112 String txt = text;
113 if (read_from_file) {
114 File file = InfinityMod.configPath.resolve(text).toFile();
115 if (file.exists()) {
116 try {
117 txt = FileUtils.readFileToString(file, StandardCharsets.UTF_8);
118 txt = txt.replace("\n", "$n");
119 } catch (IOException ignored) {
120 }
121 }
122 }
123 return new TextCondition(font_size, char_spacing, line_spacing, max_width, read_from_file, txt,
124 TextData.genData(char_spacing, max_width, txt));
125 }
126
127 public int find(int x, int line_num) {
128 int char_num = Collections.binarySearch(data.offsetMap().get(line_num), x);
129 if (char_num < 0) char_num = -char_num - 2;
130 return char_num;
131 }
132
133 @Override
134 public KeyDispatchDataCodec<? extends SurfaceRules.ConditionSource> codec() {
135 return CODEC;
136 }
137
138 @Override
139 public SurfaceRules.Condition apply(SurfaceRules.Context materialRuleContext) {
140 class CheckerboardPredicate extends SurfaceRules.LazyYCondition {
141 final List<List<Integer>> textmap;
142 final List<List<Character>> charmap;
143 final int longest_line;
144
145 CheckerboardPredicate() {
146 super(materialRuleContext);
147 textmap = data.offsetMap();
148 charmap = data.charMap();
149 longest_line = data.longest_line();
150 }
151
152 public boolean compute() {
153 int x = ((MaterialRuleContextAccess)(Object)(this.context)).getBlockX() / font_size;
154 int z = ((MaterialRuleContextAccess)(Object)(this.context)).getBlockZ() / font_size;
155 int factor = 8 + line_spacing;
156 if (x < 0 || z < 0 || x > longest_line || z >= factor * textmap.size()) return false;
157 int line_num = z / factor;
158 int char_num = find(x, line_num);
159 int char_z = z % factor;
160 if (char_z >= 8 || char_num < 0) return false;
161 int char_x = x - textmap.get(line_num).get(char_num);
162 return TextData.check(char_x, char_z, charmap.get(line_num).get(char_num));
163 }
164 }
165 return new CheckerboardPredicate();
166 }
167 }
168
169 public static final DeferredRegister<
170 //? if >1.21 {
171 MapCodec
172 //?} else {
173 /*Codec
174 *///?}
175 <? extends SurfaceRules.ConditionSource>> MATERIAL_CONDITIONS = DeferredRegister.create(MOD_ID, Registries.MATERIAL_CONDITION);
176
177
178 public static void registerConditions() {
179 MATERIAL_CONDITIONS.register("linear", LinearCondition.CODEC::codec);
180 MATERIAL_CONDITIONS.register("checkerboard", CheckerboardCondition.CODEC::codec);
181 MATERIAL_CONDITIONS.register("text", TextCondition.CODEC::codec);
182 MATERIAL_CONDITIONS.register();
183 }
184}