Skip to content

Commit f4b4715

Browse files
update multiblock API
1 parent 9e85abb commit f4b4715

File tree

14 files changed

+459
-94
lines changed

14 files changed

+459
-94
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ forge*changelog.txt
2727
# personnal info
2828
.env
2929
/logs/
30+
/run-data/

LICENSE.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 RealAntEngineer
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

build.gradle

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,19 @@ dependencies {
154154
runtimeOnly fg.deobf("top.theillusivec4.curios:curios-forge:${curios_version}")
155155
compileOnly fg.deobf("top.theillusivec4.curios:curios-forge:${curios_version}:api")
156156

157-
annotationProcessor "org.spongepowered:mixin:${mixin_version}:processor"
158157

159158
//unit test tool
160159
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0'
161160
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
162161
//x chart to plot things
163162
implementation("org.knowm.xchart:xchart:3.2.2")
164163

164+
// Prevent Mixin annotation processor from getting into IntelliJ's annotation processor settings
165+
// This allows 'Settings > Build, Execution, and Deployment > Build Tools > Gradle > Build and run using' set to IntelliJ to work correctly
166+
if (!Boolean.getBoolean('idea.sync.active')) {
167+
annotationProcessor "org.spongepowered:mixin:${mixin_version}:processor"
168+
}
169+
165170
}
166171
mixin {
167172
add sourceSets.main, 'formicapi.refmap.json'
@@ -190,7 +195,8 @@ tasks.named('processResources', ProcessResources).configure {
190195
}
191196

192197
// Example for how to get properties into the manifest for reading at runtime.
193-
tasks.named('jar', Jar).configure {
198+
jar {
199+
archiveClassifier = 'slim'
194200
manifest {
195201
attributes([
196202
'Specification-Title' : mod_id,
@@ -203,24 +209,30 @@ tasks.named('jar', Jar).configure {
203209
'MixinConfig':'formicapi.mixins.json'
204210
])
205211
}
206-
207-
// This is the preferred method to reobfuscate your jar file
208-
finalizedBy 'reobfJar'
212+
}
213+
task jarJarRelease {
214+
group = 'jarjar'
215+
doLast {
216+
tasks.jarJar {
217+
archiveClassifier = ''
218+
}
219+
}
220+
finalizedBy tasks.jarJar
209221
}
210222

211-
tasks.named('build').configure {
212-
dependsOn 'reobfJar'
223+
java {
224+
withSourcesJar()
225+
withJavadocJar()
213226
}
214227

215228
tasks.test {
216229
useJUnitPlatform()
217230
//debug = true;
218231
}
219-
// However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing:
220232

221-
tasks.named('publish').configure {
222-
dependsOn 'reobfJar'
223-
}
233+
jar.finalizedBy('reobfJar')
234+
tasks.jarJar.finalizedBy('reobfJarJar')
235+
// However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing:
224236

225237
// Example configuration to allow publishing using the maven-publish plugin
226238

@@ -232,11 +244,15 @@ tasks.named('publish').configure {
232244
publishing {
233245
publications {
234246
mavenJava(MavenPublication) {
235-
from components.java
236247
artifactId = "FormicAPI" // ✅ no spaces or invalid chars
248+
249+
from components.java
250+
fg.component(it)
251+
jarJar.component(it)
237252
}
238253
}
239254
}
255+
/*
240256
tasks.register("fixModuleMetadata") {
241257
dependsOn("publishMavenJavaPublicationToMavenLocal")
242258
doLast {
@@ -274,7 +290,7 @@ afterEvaluate {
274290
}
275291
}
276292
}
277-
}
293+
}*/
278294
tasks.withType(JavaCompile).configureEach {
279295
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
280296
}

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ mod_id=formicapi
2020
mod_name=Formic API
2121
mod_license=MIT
2222

23-
mod_version=1.4.2
23+
mod_version=1.4.3
2424

2525
create_version = 6.0.2-50
2626
flywheel_version = 1.0.1

src/main/java/com/rae/formicapi/FormicAPI.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
11
package com.rae.formicapi;
22

33
import com.rae.formicapi.config.FormicAPIConfigs;
4-
import com.rae.formicapi.data.managers.FloatMapDataLoader;
54
import com.rae.formicapi.data.managers.TwoDTabulatedFunctionLoader;
65
import com.rae.formicapi.data.providers.TwoDTabulatedFunctionProvider;
76
import com.rae.formicapi.math.data.StepMode;
8-
import com.rae.formicapi.thermal_utilities.EOSLibrary;
9-
import com.rae.formicapi.thermal_utilities.eos.CubicEOS;
107
import com.rae.formicapi.thermal_utilities.helper.WaterCubicEOS;
11-
import com.rae.formicapi.thermal_utilities.helper.WaterTableBased;
128
import net.minecraft.data.DataGenerator;
139
import net.minecraft.data.PackOutput;
1410
import net.minecraft.resources.ResourceLocation;
15-
import net.minecraft.world.level.block.Block;
1611
import net.minecraftforge.common.MinecraftForge;
1712
import net.minecraftforge.data.event.GatherDataEvent;
1813
import net.minecraftforge.event.AddReloadListenerEvent;
@@ -21,7 +16,6 @@
2116
import net.minecraftforge.fml.ModLoadingContext;
2217
import net.minecraftforge.fml.common.Mod;
2318
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
24-
import net.minecraftforge.registries.ForgeRegistries;
2519
import org.apache.logging.log4j.LogManager;
2620
import org.apache.logging.log4j.Logger;
2721

@@ -81,8 +75,7 @@ output, resource("water/pressure_entropy_to_temperature"),
8175
float x = WaterCubicEOS.get_x(P, initialH, initialT);
8276
return WaterCubicEOS.getT(initialT, x, P, S);
8377
}
84-
,1e2f,1e-3f, 2e7f, 10e3f,30,100,StepMode.LOGARITHMIC, StepMode.LINEAR, true
85-
78+
,1e2f,1e-3f, 2e7f, 10e3f,300,1000,StepMode.LOGARITHMIC, StepMode.LINEAR, true
8679
)
8780
);
8881
/*generator.addProvider(event.includeServer(), new TwoDTabulatedFunctionProvider(

src/main/java/com/rae/formicapi/math/data/OneDTabulatedFunction.java

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,29 @@ public OneDTabulatedFunction(TreeMap<Float, Float> table, float step, StepMode m
2222
this.clamp = clamp;
2323
}
2424

25-
private float evaluate(double input, TreeMap<Float, Float> table) {
25+
public float evaluate(float input) {
2626
if (table.isEmpty()) {
2727
throw new IllegalStateException("Function table is empty");
2828
}
2929

30+
// Handle out-of-bounds
31+
if (input <= table.firstKey()) {
32+
if (clamp) {
33+
return table.firstEntry().getValue();
34+
}
35+
return extrapolateBelow(input);
36+
} else if (input >= table.lastKey()) {
37+
if (clamp) {
38+
return table.lastEntry().getValue();
39+
}
40+
return extrapolateAbove(input);
41+
}
42+
43+
// Interpolation inside the range
44+
return interpolate(input);
45+
}
46+
47+
private float interpolate(double input) {
3048
float index = (float) (mode.forward.applyAsDouble(input) / step);
3149
int lowerIndex = (int) Math.floor(index);
3250
float frac = index - lowerIndex;
@@ -40,16 +58,15 @@ private float evaluate(double input, TreeMap<Float, Float> table) {
4058
Map.Entry<Float, Float> upper = table.ceilingEntry((float) input);
4159

4260
if (lower == null || upper == null) {
43-
return table.get(table.firstKey());
61+
return table.get(table.firstKey()); // fallback
4462
}
4563

4664
float T_lower = lower.getKey();
4765
float T_upper = upper.getKey();
4866
if (T_lower == T_upper) {
49-
return table.get(T_lower);
67+
return lower.getValue();
5068
}
5169
float fracAlt = (float) ((input - T_lower) / (T_upper - T_lower));
52-
5370
return lower.getValue() * (1 - fracAlt) + upper.getValue() * fracAlt;
5471
}
5572

@@ -59,33 +76,27 @@ private float evaluate(double input, TreeMap<Float, Float> table) {
5976
return P1 * (1 - frac) + P2 * frac;
6077
}
6178

62-
public float evaluate(float output) {
63-
return evaluate(output, table);
64-
}
65-
66-
private float extrapolateBelow(TreeMap<Float, Float> searchMap) {
67-
Map.Entry<Float, Float> lower = searchMap.firstEntry();
68-
Map.Entry<Float, Float> upper = searchMap.higherEntry(lower.getKey());
69-
if (upper == null) return lower.getValue();
70-
return linear(searchMap, lower, upper);
79+
private float extrapolateBelow(float query) {
80+
Map.Entry<Float, Float> lower = table.firstEntry();
81+
Map.Entry<Float, Float> upper = table.higherEntry(lower.getKey());
82+
if (upper == null) return lower.getValue(); // only one point in table
83+
return linear(query, lower, upper);
7184
}
7285

73-
private float extrapolateAbove(TreeMap<Float, Float> searchMap) {
74-
Map.Entry<Float, Float> upper = searchMap.lastEntry();
75-
Map.Entry<Float, Float> lower = searchMap.lowerEntry(upper.getKey());
76-
if (lower == null) return upper.getValue();
77-
return linear(searchMap, lower, upper);
86+
private float extrapolateAbove(float query) {
87+
Map.Entry<Float, Float> upper = table.lastEntry();
88+
Map.Entry<Float, Float> lower = table.lowerEntry(upper.getKey());
89+
if (lower == null) return upper.getValue(); // only one point in table
90+
return linear(query, lower, upper);
7891
}
7992

80-
private float linear(TreeMap<Float, Float> map, Map.Entry<Float, Float> a, Map.Entry<Float, Float> b) {
93+
private float linear(float query, Map.Entry<Float, Float> a, Map.Entry<Float, Float> b) {
8194
float x1 = a.getKey();
8295
float x2 = b.getKey();
8396
float y1 = a.getValue();
8497
float y2 = b.getValue();
85-
float query = map.firstKey(); // doesn't matter in this context
8698
float t = (query - x1) / (x2 - x1);
8799
return y1 * (1 - t) + y2 * t;
88100
}
89-
90-
91101
}
102+

src/main/java/com/rae/formicapi/math/data/TwoDTabulatedFunction.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,6 @@ public class TwoDTabulatedFunction {
3131
Codec.BOOL.fieldOf("clamp").forGetter(f -> f.clamp)
3232
).apply(instance, TwoDTabulatedFunction::new));
3333

34-
// StreamCodec example (Fabric API style)
35-
/*public static final StreamCodec<TwoDTabulatedFunction> STREAM_CODEC = StreamCodec.composite(
36-
StreamCodec.map(StreamCodec.FLOAT, StreamCodec.map(StreamCodec.FLOAT, StreamCodec.FLOAT)).fieldOf(TwoDTabulatedFunction::getTable),
37-
StreamCodec.FLOAT.fieldOf(f -> f.xStep),
38-
StreamCodec.FLOAT.fieldOf(f -> f.yStep),
39-
StepMode.STREAM_CODEC.fieldOf(f -> f.xMode),
40-
StepMode.STREAM_CODEC.fieldOf(f -> f.yMode),
41-
StreamCodec.BOOL.fieldOf(f -> f.clamp),
42-
TwoDTabulatedFunction::new
43-
);*/
4434
public TwoDTabulatedFunction(TreeMap<Float, TreeMap<Float, Float>> table, float xStep, float yStep, StepMode xMode, StepMode yMode, boolean clamp) {
4535
this.table = table;
4636
this.xStep = xStep;
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package com.rae.formicapi.multiblock;
2+
3+
import com.rae.crowns.CROWNS;
4+
import net.minecraft.core.BlockPos;
5+
import net.minecraft.core.Direction;
6+
import net.minecraft.core.Vec3i;
7+
import net.minecraft.world.level.BlockGetter;
8+
import net.minecraft.world.level.Level;
9+
import net.minecraft.world.level.block.DirectionalBlock;
10+
import net.minecraft.world.level.block.state.BlockState;
11+
import net.minecraft.world.phys.shapes.CollisionContext;
12+
import net.minecraft.world.phys.shapes.VoxelShape;
13+
14+
import java.util.ArrayDeque;
15+
import java.util.HashSet;
16+
import java.util.Queue;
17+
import java.util.Set;
18+
19+
public interface IMBController {
20+
Vec3i getDefaultOffset();
21+
Vec3i getDefaultSize();
22+
VoxelShape getGlobalShape(BlockState state, BlockGetter worldIn, BlockPos pos, CollisionContext context);
23+
MBStructureBlock getStructure();
24+
default Vec3i getOffset(Direction facing, boolean mirrorOnDir){
25+
final int dirMultiply = facing.getAxisDirection() == Direction.AxisDirection.NEGATIVE && mirrorOnDir ? -1 : 1;
26+
Vec3i defaultOffset = getDefaultOffset();
27+
return switch (facing.getAxis()){
28+
case Z -> new Vec3i( defaultOffset.getZ(), defaultOffset.getY(), dirMultiply *defaultOffset.getX());
29+
case Y -> new Vec3i( defaultOffset.getY(), dirMultiply *defaultOffset.getX(),defaultOffset.getZ());
30+
default -> new Vec3i( dirMultiply *defaultOffset.getX(),defaultOffset.getY(), defaultOffset.getZ());
31+
};
32+
}
33+
34+
default Vec3i getSize(Direction facing){
35+
Vec3i defaultSize = getDefaultSize();
36+
return switch (facing.getAxis()){
37+
case Z -> new Vec3i(defaultSize.getZ(), defaultSize.getY(), defaultSize.getX());
38+
case Y -> new Vec3i(defaultSize.getY(), defaultSize.getX(),defaultSize.getZ());
39+
default -> defaultSize;
40+
};
41+
}
42+
43+
44+
default void repairStructure(Level level, BlockPos controlPos, Direction facing) {
45+
if (level.isClientSide()) return;
46+
MBStructureBlock structure = getStructure();
47+
Set<BlockPos> visited = new HashSet<>();
48+
Queue<Node> toVisit = new ArrayDeque<>();
49+
50+
Vec3i off = getOffset(facing, false);
51+
Vec3i size = getSize(facing);
52+
BlockPos minCorner = controlPos.offset(off);
53+
54+
for (Direction dir : Direction.values()) {
55+
BlockPos neighborPos = controlPos.relative(dir);
56+
57+
if (isInsideBounds(neighborPos, minCorner, size)) {
58+
toVisit.add(new Node(dir, neighborPos));
59+
}
60+
}
61+
int i = 0;
62+
while (!toVisit.isEmpty()) {
63+
Node node = toVisit.poll();
64+
BlockState current = level.getBlockState(node.pos);
65+
66+
67+
if (!current.is(structure)) {
68+
level.setBlockAndUpdate(node.pos, structure.defaultBlockState().setValue(DirectionalBlock.FACING, node.fromDir.getOpposite()));
69+
visited.add(node.pos);
70+
71+
for (Direction dir : Direction.values()) {
72+
BlockPos neighborPos = node.pos.relative(dir);
73+
//System.out.println(neighborPos);
74+
75+
if (isInsideBounds(neighborPos, minCorner, size) && !neighborPos.equals(controlPos) && !visited.contains(neighborPos)) {
76+
toVisit.add(new Node(dir, neighborPos));
77+
}
78+
}
79+
}
80+
i++;
81+
if (i > 100) {
82+
CROWNS.LOGGER.warn("More than 100 blocks");
83+
break;
84+
}
85+
}
86+
}
87+
88+
default boolean isInsideBounds(BlockPos pos, BlockPos minCorner, Vec3i size) {
89+
int dx = minCorner.getX() - pos.getX();
90+
int dy = minCorner.getY() - pos.getY() ;
91+
int dz = minCorner.getZ() - pos.getZ();
92+
return dx >= 0 && dx < size.getX() &&
93+
dy >= 0 && dy < size.getY() &&
94+
dz >= 0 && dz < size.getZ();
95+
}
96+
record Node(Direction fromDir, BlockPos pos){
97+
}
98+
}

0 commit comments

Comments
 (0)